diff options
Diffstat (limited to 'qemu/roms/seabios')
251 files changed, 78057 insertions, 0 deletions
diff --git a/qemu/roms/seabios/.gitignore b/qemu/roms/seabios/.gitignore new file mode 100644 index 000000000..f9990feba --- /dev/null +++ b/qemu/roms/seabios/.gitignore @@ -0,0 +1,4 @@ +out +*.pyc +.config +.config.old diff --git a/qemu/roms/seabios/.version b/qemu/roms/seabios/.version new file mode 100644 index 000000000..a96337339 --- /dev/null +++ b/qemu/roms/seabios/.version @@ -0,0 +1 @@ +rel-1.8.2-0-g33fbe13 diff --git a/qemu/roms/seabios/COPYING b/qemu/roms/seabios/COPYING new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/qemu/roms/seabios/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 <http://www.gnu.org/licenses/>. + +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: + + <program> Copyright (C) <year> <name of author> + 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 +<http://www.gnu.org/licenses/>. + + 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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/qemu/roms/seabios/COPYING.LESSER b/qemu/roms/seabios/COPYING.LESSER new file mode 100644 index 000000000..fc8a5de7e --- /dev/null +++ b/qemu/roms/seabios/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/qemu/roms/seabios/Makefile b/qemu/roms/seabios/Makefile new file mode 100644 index 000000000..83cdff377 --- /dev/null +++ b/qemu/roms/seabios/Makefile @@ -0,0 +1,280 @@ +# SeaBIOS build system +# +# Copyright (C) 2008-2012 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU LGPLv3 license. + +# Output directory +OUT=out/ + +# Common command definitions +export HOSTCC := $(CC) +export CONFIG_SHELL := sh +export KCONFIG_AUTOHEADER := autoconf.h +export KCONFIG_CONFIG := $(CURDIR)/.config +export LC_ALL := C +CROSS_PREFIX= +ifneq ($(CROSS_PREFIX),) +CC=$(CROSS_PREFIX)gcc +endif +AS=$(CROSS_PREFIX)as +LD=$(CROSS_PREFIX)ld +OBJCOPY=$(CROSS_PREFIX)objcopy +OBJDUMP=$(CROSS_PREFIX)objdump +STRIP=$(CROSS_PREFIX)strip +PYTHON=python +CPP=cpp +IASL:=iasl +LD32BIT_FLAG:=-melf_i386 + +# Source files +SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \ + system.c serial.c clock.c resume.c pnpbios.c vgahooks.c pcibios.c apm.c \ + hw/pci.c hw/timer.c hw/rtc.c hw/dma.c hw/pic.c hw/ps2port.c hw/serialio.c \ + hw/usb.c hw/usb-uhci.c hw/usb-ohci.c hw/usb-ehci.c \ + hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \ + hw/blockcmd.c hw/floppy.c hw/ata.c hw/ramdisk.c \ + hw/virtio-ring.c hw/virtio-pci.c hw/virtio-blk.c hw/virtio-scsi.c \ + hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c +SRC16=$(SRCBOTH) +SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c romfile.c x86.c optionroms.c \ + pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c \ + hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \ + fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ + fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \ + fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c +SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c +DIRS=src src/hw src/fw vgasrc + +# Default compiler flags +cc-option=$(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`" \ + ; then echo "$(2)"; else echo "$(3)"; fi ;) + +CPPFLAGS = -P -MD -MT $@ + +COMMONCFLAGS := -I$(OUT) -Isrc -Os -MD -g \ + -Wall -Wno-strict-aliasing -Wold-style-definition \ + $(call cc-option,$(CC),-Wtype-limits,) \ + -m32 -march=i386 -mregparm=3 -mpreferred-stack-boundary=2 \ + -minline-all-stringops -fomit-frame-pointer \ + -freg-struct-return -ffreestanding -fno-delete-null-pointer-checks \ + -ffunction-sections -fdata-sections -fno-common -fno-merge-constants +COMMONCFLAGS += $(call cc-option,$(CC),-nopie,) +COMMONCFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) +COMMONCFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) +COMMA := , + +CFLAGS32FLAT := $(COMMONCFLAGS) -DMODE16=0 -DMODESEGMENT=0 +CFLAGSSEG := $(COMMONCFLAGS) -DMODESEGMENT=1 -fno-defer-pop \ + $(call cc-option,$(CC),-fno-jump-tables,-DMANUAL_NO_JUMP_TABLE) \ + $(call cc-option,$(CC),-fno-tree-switch-conversion,) +CFLAGS32SEG := $(CFLAGSSEG) -DMODE16=0 +CFLAGS16 := $(CFLAGSSEG) -DMODE16=1 \ + $(call cc-option,$(CC),-m16,-Wa$(COMMA)src/code16gcc.s) \ + $(call cc-option,$(CC),--param large-stack-frame=4,-fno-inline) + +# Run with "make V=1" to see the actual compile commands +ifdef V +Q= +else +Q=@ +MAKEFLAGS += --no-print-directory +endif + +# Default targets +-include $(KCONFIG_CONFIG) + +target-y := +target-$(CONFIG_QEMU) += $(OUT)bios.bin +target-$(CONFIG_CSM) += $(OUT)Csm16.bin +target-$(CONFIG_COREBOOT) += $(OUT)bios.bin.elf +target-$(CONFIG_BUILD_VGABIOS) += $(OUT)vgabios.bin + +all: $(target-y) + +# Make definitions +.PHONY : all clean distclean FORCE +.DELETE_ON_ERROR: + + +################ Common build rules + +# Verify the build environment works. +TESTGCC:=$(shell OUT="$(OUT)" CC="$(CC)" LD="$(LD)" IASL="$(IASL)" scripts/test-build.sh) +ifeq "$(TESTGCC)" "-1" +$(error "Please upgrade the build environment") +endif + +ifeq "$(TESTGCC)" "0" +# Use -fwhole-program +CFLAGSWHOLE=-fwhole-program -DWHOLE_PROGRAM +endif + +# Do a whole file compile by textually including all C code. +define whole-compile +@echo " Compiling whole program $3" +$(Q)printf '$(foreach i,$2,#include "$i"\n)' > $3.tmp.c +$(Q)$(CC) -I. $1 $(CFLAGSWHOLE) -c $3.tmp.c -o $3 +endef + +%.strip.o: %.o + @echo " Stripping $@" + $(Q)$(STRIP) $< -o $@ + +$(OUT)%.s: %.c + @echo " Compiling to assembler $@" + $(Q)$(CC) $(CFLAGS16) -S -c $< -o $@ + +$(OUT)%.o: %.c $(OUT)autoconf.h + @echo " Compile checking $@" + $(Q)$(CC) $(CFLAGS32FLAT) -c $< -o $@ + +$(OUT)%.lds: %.lds.S + @echo " Precompiling $@" + $(Q)$(CPP) $(CPPFLAGS) -D__ASSEMBLY__ $< -o $@ + + +################ Main BIOS build rules + +$(OUT)asm-offsets.s: $(OUT)autoconf.h + +$(OUT)asm-offsets.h: $(OUT)src/asm-offsets.s + @echo " Generating offset file $@" + $(Q)./scripts/gen-offsets.sh $< $@ + +$(OUT)ccode16.o: $(OUT)autoconf.h $(patsubst %.c, $(OUT)src/%.o,$(SRC16)) ; $(call whole-compile, $(CFLAGS16), $(addprefix src/, $(SRC16)),$@) + +$(OUT)code32seg.o: $(OUT)autoconf.h $(patsubst %.c, $(OUT)src/%.o,$(SRC32SEG)) ; $(call whole-compile, $(CFLAGS32SEG), $(addprefix src/, $(SRC32SEG)),$@) + +$(OUT)ccode32flat.o: $(OUT)autoconf.h $(patsubst %.c, $(OUT)src/%.o,$(SRC32FLAT)) ; $(call whole-compile, $(CFLAGS32FLAT), $(addprefix src/, $(SRC32FLAT)),$@) + +$(OUT)romlayout.o: src/romlayout.S $(OUT)autoconf.h $(OUT)asm-offsets.h + @echo " Compiling (16bit) $@" + $(Q)$(CC) $(CFLAGS16) -c -D__ASSEMBLY__ $< -o $@ + +$(OUT)romlayout16.lds: $(OUT)ccode32flat.o $(OUT)code32seg.o $(OUT)ccode16.o $(OUT)romlayout.o scripts/layoutrom.py scripts/buildversion.sh + @echo " Building ld scripts" + $(Q)BUILD_VERSION="$(VERSION)" ./scripts/buildversion.sh $(OUT)version.c + $(Q)$(CC) $(CFLAGS32FLAT) -c $(OUT)version.c -o $(OUT)version.o + $(Q)$(LD) $(LD32BIT_FLAG) -r $(OUT)ccode32flat.o $(OUT)version.o -o $(OUT)code32flat.o + $(Q)$(LD) $(LD32BIT_FLAG) -r $(OUT)ccode16.o $(OUT)romlayout.o -o $(OUT)code16.o + $(Q)$(OBJDUMP) -thr $(OUT)code32flat.o > $(OUT)code32flat.o.objdump + $(Q)$(OBJDUMP) -thr $(OUT)code32seg.o > $(OUT)code32seg.o.objdump + $(Q)$(OBJDUMP) -thr $(OUT)code16.o > $(OUT)code16.o.objdump + $(Q)$(PYTHON) ./scripts/layoutrom.py $(OUT)code16.o.objdump $(OUT)code32seg.o.objdump $(OUT)code32flat.o.objdump $(OUT)$(KCONFIG_AUTOHEADER) $(OUT)romlayout16.lds $(OUT)romlayout32seg.lds $(OUT)romlayout32flat.lds + +# These are actually built by scripts/layoutrom.py above, but by pulling them +# into an extra rule we prevent make -j from spawning layoutrom.py 4 times. +$(OUT)romlayout32seg.lds $(OUT)romlayout32flat.lds $(OUT)code32flat.o $(OUT)code16.o: $(OUT)romlayout16.lds + +$(OUT)rom16.o: $(OUT)code16.o $(OUT)romlayout16.lds + @echo " Linking $@" + $(Q)$(LD) -T $(OUT)romlayout16.lds $< -o $@ + +$(OUT)rom32seg.o: $(OUT)code32seg.o $(OUT)romlayout32seg.lds + @echo " Linking $@" + $(Q)$(LD) -T $(OUT)romlayout32seg.lds $< -o $@ + +$(OUT)rom.o: $(OUT)rom16.strip.o $(OUT)rom32seg.strip.o $(OUT)code32flat.o $(OUT)romlayout32flat.lds + @echo " Linking $@" + $(Q)$(LD) -T $(OUT)romlayout32flat.lds $(OUT)rom16.strip.o $(OUT)rom32seg.strip.o $(OUT)code32flat.o -o $@ + +$(OUT)bios.bin.prep: $(OUT)rom.o scripts/checkrom.py + @echo " Prepping $@" + $(Q)rm -f $(OUT)bios.bin $(OUT)Csm16.bin $(OUT)bios.bin.elf + $(Q)$(OBJDUMP) -thr $< > $<.objdump + $(Q)$(OBJCOPY) -O binary $< $(OUT)bios.bin.raw + $(Q)$(PYTHON) ./scripts/checkrom.py $<.objdump $(CONFIG_ROM_SIZE) $(OUT)bios.bin.raw $(OUT)bios.bin.prep + +$(OUT)bios.bin: $(OUT)bios.bin.prep + @echo " Creating $@" + $(Q)cp $< $@ + +$(OUT)Csm16.bin: $(OUT)bios.bin.prep + @echo " Creating $@" + $(Q)cp $< $@ + +$(OUT)bios.bin.elf: $(OUT)rom.o $(OUT)bios.bin.prep + @echo " Creating $@" + $(Q)$(STRIP) -R .comment $< -o $(OUT)bios.bin.elf + + +################ VGA build rules + +# VGA src files +SRCVGA=src/output.c src/string.c src/hw/pci.c src/hw/serialio.c \ + vgasrc/vgainit.c vgasrc/vgabios.c vgasrc/vgafb.c \ + vgasrc/vgafonts.c vgasrc/vbe.c \ + vgasrc/stdvga.c vgasrc/stdvgamodes.c vgasrc/stdvgaio.c \ + vgasrc/clext.c vgasrc/bochsvga.c vgasrc/geodevga.c \ + src/fw/coreboot.c vgasrc/cbvga.c + +ifeq "$(CONFIG_VGA_FIXUP_ASM)" "y" +$(OUT)vgaccode16.raw.s: $(OUT)autoconf.h $(patsubst %.c, $(OUT)%.o,$(SRCVGA)) ; $(call whole-compile, $(filter-out -fomit-frame-pointer,$(CFLAGS16)) -fno-omit-frame-pointer -S -Isrc, $(SRCVGA),$@) + +$(OUT)vgaccode16.o: $(OUT)vgaccode16.raw.s scripts/vgafixup.py + @echo " Fixup VGA rom assembler" + $(Q)$(PYTHON) ./scripts/vgafixup.py $< $(OUT)vgaccode16.s + $(Q)$(AS) --32 src/code16gcc.s $(OUT)vgaccode16.s -o $@ +else +$(OUT)vgaccode16.o: $(OUT)autoconf.h $(patsubst %.c, $(OUT)%.o,$(SRCVGA)) ; $(call whole-compile, $(CFLAGS16) -Isrc, $(SRCVGA),$@) +endif + +$(OUT)vgaentry.o: vgasrc/vgaentry.S $(OUT)autoconf.h $(OUT)asm-offsets.h + @echo " Compiling (16bit) $@" + $(Q)$(CC) $(CFLAGS16) -c -D__ASSEMBLY__ $< -o $@ + +$(OUT)vgarom.o: $(OUT)vgaccode16.o $(OUT)vgaentry.o $(OUT)vgasrc/vgalayout.lds scripts/buildversion.sh + @echo " Linking $@" + $(Q)BUILD_VERSION="$(VERSION)" ./scripts/buildversion.sh $(OUT)vgaversion.c VAR16 + $(Q)$(CC) $(CFLAGS16) -c $(OUT)vgaversion.c -o $(OUT)vgaversion.o + $(Q)$(LD) --gc-sections -T $(OUT)vgasrc/vgalayout.lds $(OUT)vgaccode16.o $(OUT)vgaentry.o $(OUT)vgaversion.o -o $@ + +$(OUT)vgabios.bin.raw: $(OUT)vgarom.o + @echo " Extracting binary $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +$(OUT)vgabios.bin: $(OUT)vgabios.bin.raw scripts/buildrom.py + @echo " Finalizing rom $@" + $(Q)$(PYTHON) ./scripts/buildrom.py $< $@ + + +################ DSDT build rules + +iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ + ; then echo "$(2)"; else echo "$(3)"; fi ;) + +$(OUT)%.hex: %.dsl ./scripts/acpi_extract_preprocess.py ./scripts/acpi_extract.py + @echo " Compiling IASL $@" + $(Q)$(CPP) $(CPPFLAGS) $< -o $(OUT)$*.dsl.i.orig + $(Q)$(PYTHON) ./scripts/acpi_extract_preprocess.py $(OUT)$*.dsl.i.orig > $(OUT)$*.dsl.i + $(Q)$(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $(OUT)$* $(OUT)$*.dsl.i + $(Q)$(PYTHON) ./scripts/acpi_extract.py $(OUT)$*.lst > $(OUT)$*.off + $(Q)cat $(OUT)$*.off > $@ + +$(OUT)src/fw/acpi.o: $(OUT)src/fw/acpi-dsdt.hex $(OUT)src/fw/ssdt-proc.hex $(OUT)src/fw/ssdt-pcihp.hex $(OUT)src/fw/ssdt-misc.hex $(OUT)src/fw/q35-acpi-dsdt.hex + +################ Kconfig rules + +define do-kconfig +$(Q)mkdir -p $(OUT)/scripts/kconfig/lxdialog +$(Q)mkdir -p $(OUT)/include/config +$(Q)mkdir -p $(addprefix $(OUT), $(DIRS)) +$(Q)$(MAKE) -C $(OUT) -f $(CURDIR)/scripts/kconfig/Makefile srctree=$(CURDIR) src=scripts/kconfig obj=scripts/kconfig Q=$(Q) Kconfig=$(CURDIR)/src/Kconfig $1 +endef + +$(OUT)autoconf.h : $(KCONFIG_CONFIG) ; $(call do-kconfig, silentoldconfig) +$(KCONFIG_CONFIG): src/Kconfig vgasrc/Kconfig ; $(call do-kconfig, olddefconfig) +%onfig: ; $(call do-kconfig, $@) +help: ; $(call do-kconfig, $@) + + +################ Generic rules + +clean: + $(Q)rm -rf $(OUT) + +distclean: clean + $(Q)rm -f .config .config.old + +-include $(patsubst %,$(OUT)%/*.d,$(DIRS)) diff --git a/qemu/roms/seabios/README b/qemu/roms/seabios/README new file mode 100644 index 000000000..e18be16fc --- /dev/null +++ b/qemu/roms/seabios/README @@ -0,0 +1,17 @@ +Welcome to the SeaBIOS project! This project implements an X86 legacy +bios that is built with standard GNU tools. + +Please see build and developer information at: + + http://seabios.org/Developer_Documentation + +For the impatient, SeaBIOS is built for QEMU and tested on QEMU with: + + make + qemu -bios out/bios.bin + +SeaBIOS can be configured with kconfig. To change the default +configuration one can run "make menuconfig" prior to running "make". + +For other types of builds, and for more detailed developer +documentation, please see the online documentation listed above. diff --git a/qemu/roms/seabios/docs/Build_overview.md b/qemu/roms/seabios/docs/Build_overview.md new file mode 100644 index 000000000..26db22691 --- /dev/null +++ b/qemu/roms/seabios/docs/Build_overview.md @@ -0,0 +1,80 @@ +The SeaBIOS code can be built using standard GNU tools. A recent Linux +distribution should be able to build SeaBIOS using the standard +compiler tools. + +Building SeaBIOS +================ + +First, [obtain the code](Download). SeaBIOS can be compiled for +several different build targets. It is also possible to configure +additional compile time options - run **make menuconfig** to do this. + +Build for QEMU (along with KVM, Xen, and Bochs) +----------------------------------------------- + +To build for QEMU (and similar), one should be able to run "make" in +the main directory. The resulting file "out/bios.bin" contains the +processed bios image. + +One can use the resulting binary with QEMU by using QEMU's "-bios" +option. For example: + +`qemu -bios out/bios.bin -fda myfdimage.img` + +One can also use the resulting binary with Bochs. For example: + +`bochs -q 'floppya: 1_44=myfdimage.img' 'romimage: file=out/bios.bin'` + +Build for coreboot +------------------ + +To build for coreboot please see the coreboot build instructions at: +<http://www.coreboot.org/SeaBIOS> + +Build as a UEFI Compatibility Support Module (CSM) +-------------------------------------------------- + +To build as a CSM, first run kconfig (make menuconfig) and enable +CONFIG_CSM. Then build SeaBIOS (make) - the resulting binary will be +in "out/Csm16.bin". + +This binary may be used with the OMVF/EDK-II UEFI firmware. It will +provide "legacy" BIOS services for booting non-EFI operating systems +and will also allow OVMF to display on otherwise unsupported video +hardware by using the traditional VGA BIOS. (Windows 2008r2 is known +to use INT 10h BIOS calls even when booted via EFI, and the presence +of a CSM makes this work as expected too.) + +Having built SeaBIOS with CONFIG_CSM, one should be able to drop the +result (out/Csm16.bin) into an OVMF build tree at +OvmfPkg/Csm/Csm16/Csm16.bin and then build OVMF with 'build -D +CSM_ENABLE'. The SeaBIOS binary will be included as a discrete file +within the 'Flash Volume' which is created, and there are tools which +will extract it and allow it to be replaced. + +Overview of files in the repository +=================================== + +The **src/** directory contains the main bios source code. The +**src/hw/** directory contains source code specific to hardware +drivers. The **src/fw/** directory contains source code for platform +firmware initialization. The **src/std/** directory contains header +files describing standard bios, firmware, and hardware interfaces. + +The **vgasrc/** directory contains code for VGA BIOS implementations. +This code is separate from the main BIOS code in the src/ directory. +When the build is configured to produce a VGA BIOS the resulting +binary is found in out/vgabios.bin. The VGA BIOS code is always +compiled in 16bit mode. + +The **scripts/** directory contains helper utilities for manipulating +and building the final roms. + +The **out/** directory is created by the build process - it contains +all intermediate and final files. + +When reading the C code be aware that code that runs in 16bit mode can +not arbitrarily access non-stack memory - see [Memory Model](Memory +Model) for more details. For information on the major C code functions +and where code execution starts see [Execution and code +flow](Execution and code flow). diff --git a/qemu/roms/seabios/docs/Debugging.md b/qemu/roms/seabios/docs/Debugging.md new file mode 100644 index 000000000..03567de4d --- /dev/null +++ b/qemu/roms/seabios/docs/Debugging.md @@ -0,0 +1,106 @@ +This page describes the process of obtaining diagnostic information +from SeaBIOS and for reporting problems. + +Diagnostic information +====================== + +SeaBIOS has the ability to output diagnostic messages. This is +implemented in the code via calls to the "dprintf()" C function. + +On QEMU these messages are written to a special debug port. One can +view these messages by adding '-chardev stdio,id=seabios -device +isa-debugcon,iobase=0x402,chardev=seabios' to the QEMU command line. +Once this is done, one should see status messages on the console. + +On coreboot these messages are generally written to the "cbmem" +console (CONFIG_DEBUG_COREBOOT). If SeaBIOS launches a Linux operating +system, one can obtain the cbmem tool from the coreboot repository and +run "cbmem -c" to view the SeaBIOS diagnostic messages. + +Additionally, if a serial port is available, one may compile SeaBIOS +to send the diagnostic messages to the serial port. See the SeaBIOS +CONFIG_DEBUG_SERIAL option. + +Trouble reporting +================= + +If you are experiencing problems with SeaBIOS, it's useful to increase +the debugging level. This is done by running "make menuconfig" and +setting CONFIG_DEBUG_LEVEL to a higher value. A debug level of 8 will +show a lot of diagnostic information without flooding the serial port +(levels above 8 will frequently cause too much data). + +To report an issue, please collect the serial boot log with SeaBIOS +set to a debug level of 8 and forward the full log along with a +description of the problem to the SeaBIOS [mailing list](Mailinglist). + +Timing debug messages +===================== + +The SeaBIOS repository has a tool (**scripts/readserial.py**) that can +timestamp each diagnostic message produced. The timestamps can provide +some additional information on how long internal processes take. It +also provides a simple profiling mechanism. + +The tool can be used on coreboot builds that have diagnostic messages +sent to a serial port. Make sure SeaBIOS is configured with +CONFIG_DEBUG_SERIAL and run the following on the host receiving serial +output: + +`/path/to/seabios/scripts/readserial.py /dev/ttyS0 115200` + +Update the above command with the appropriate serial device and baud +rate. + +The tool can also timestamp the messages from the QEMU debug port. To +use with QEMU run the following: + +`mkfifo qemudebugpipe`\ +`qemu -chardev pipe,path=qemudebugpipe,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios ...` + +and then in another session: + +`/path/to/seabios/scripts/readserial.py -nf qemudebugpipe` + +The mkfifo command only needs to be run once to create the pipe file. + +When readserial.py is running, it shows a timestamp with millisecond +precision of the amount of time since the start of the log. If one +presses the "enter" key in the readserial.py session it will add a +blank line to the screen and also reset the time back to zero. The +readserial.py program also keeps a log of all output in files that +look like "seriallog-YYYYMMDD_HHMMSS.log". + +Debugging with gdb on QEMU +========================== + +One can use gdb with QEMU to debug system images. To do this, add '-s +-S' to the qemu command line. For example: + +`qemu -bios out/bios.bin -fda myfdimage.img -s -S` + +Then, in another session, run gdb with either out/rom16.o (to debug +bios 16bit code) or out/rom.o (to debug bios 32bit code). For example: + +`gdb out/rom16.o` + +Once in gdb, use the command "target remote localhost:1234" to have +gdb connect to QEMU. See the QEMU documentation for more information +on using gdb and QEMU in this mode. + +When debugging 16bit code, also run the following commands in gdb: + +`set architecture i8086`\ +`add-symbol-file out/rom16.o 0xf0000` + +The second command loads the 16bit symbols a second time at an offset +of 0xf0000, which helps gdb set and catch breakpoints correctly. + +To debug a VGA BIOS image, run "gdb out/vgarom.o" add use the gdb +command "add-symbol-file out/vgarom.o 0xc0000" to load the 16bit VGA +BIOS symbols twice. + +If debugging the 32bit SeaBIOS initialization code with gdb, note that +SeaBIOS does self relocation by default. This relocation will alter +the location of initialization code symbols. Disable +CONFIG_RELOCATE_INIT to prevent SeaBIOS from doing this. diff --git a/qemu/roms/seabios/docs/Developer_Documentation.md b/qemu/roms/seabios/docs/Developer_Documentation.md new file mode 100644 index 000000000..d50455d36 --- /dev/null +++ b/qemu/roms/seabios/docs/Developer_Documentation.md @@ -0,0 +1,24 @@ +This page is intended for developers interested in understanding and +enhancing SeaBIOS. Please also consider joining the [mailing +list](Mailinglist). + +The SeaBIOS code can be obtained via the [download](Download) +page. For specific information on building SeaBIOS for coreboot, +please see the [coreboot SeaBIOS](http://www.coreboot.org/SeaBIOS) +page. + +See details on [building SeaBIOS](Build overview). + +There is also information on the SeaBIOS [Memory Model](Memory Model). + +Along with information on SeaBIOS [Execution and code flow](Execution +and code flow). + +A description of the process of linking the final SeaBIOS binary is +available at [Linking overview](Linking overview). + +To debug SeaBIOS and report problems see SeaBIOS +[debugging](Debugging). + +Useful links to specifications is available at [Developer +links](Developer links). diff --git a/qemu/roms/seabios/docs/Developer_links.md b/qemu/roms/seabios/docs/Developer_links.md new file mode 100644 index 000000000..67a047e43 --- /dev/null +++ b/qemu/roms/seabios/docs/Developer_links.md @@ -0,0 +1,86 @@ +Links to pages with more information. + +BIOS interfaces +=============== + +Ralf Brown's interrupt list + +* <http://www.cs.cmu.edu/~ralf/files.html> + +Memory layout info + +* <http://stanislavs.org/helppc/bios_data_area.html> + +Old PNP BIOS spec + +* <ftp://download.intel.com/support/motherboards/desktop/sb/pnpbiosspecificationv10a.pdf> + +T13 BIOS Enhanced Disk Drive (drafts): + +* <http://www.t10.org/t13/#Project_drafts> + +Exported BIOS tables +==================== + +ACPI spec + +* <http://www.acpi.info/> + +PCI IRQ Routing Table Specification + +* <http://www.microsoft.com/whdc/archive/pciirq.mspx> + +MP configuration table + +* <http://www.intel.com/design/pentium/datashts/242016.htm> + +SM BIOS (aka DMI): + +* <http://www.dmtf.org/standards/smbios/> + +Hardware information +==================== + +info on PIC + +* <http://www.beyondlogic.org/interrupts/interupt.htm> + +info on kbd + +* <http://www.computer-engineering.org/ps2protocol/> + +info on vga + +* <http://www.osdever.net/FreeVGA/home.htm> + +info on lpt + +* <http://www.beyondlogic.org/spp/parallel.htm> + +info on floppy + +* <http://www.isdaman.com/alsos/hardware/fdc/floppy.htm> + +info on ata + +* <http://ata.wiki.kernel.org/index.php/Developer_Resources> +* <http://www.t10.org/t13/#Project_drafts> + +info on serial + +* <http://www.national.com/ds/PC/PC16550D.pdf> + +General information +=================== + +Bochs tech document list + +* <http://bochs.sourceforge.net/techdata.html> + +Phoenix documents + +* <http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/PC+Industry+Specifications.htm> + +Dosemu information + +* <http://www.dosemu.org/docs/README-tech> diff --git a/qemu/roms/seabios/docs/Download.md b/qemu/roms/seabios/docs/Download.md new file mode 100644 index 000000000..a49c6fb74 --- /dev/null +++ b/qemu/roms/seabios/docs/Download.md @@ -0,0 +1,25 @@ +SeaBIOS may be distributed under the terms of the [GNU +LGPLv3](http://www.gnu.org/licenses/lgpl-3.0-standalone.html) license. +Both source code and binaries are available. + +Latest source code +================== + +The SeaBIOS project uses the [git](http://git-scm.com/) revision +control system. To download the latest source from revision control, +run: + +`$ git clone git://git.seabios.org/seabios.git seabios`\ +`$ cd seabios` + +There's also a [website](http://git.seabios.org/) to browse the latest +source code online. + +Released versions +================= + +Released versions of the source code are available at: + +<http://code.coreboot.org/p/seabios/downloads/> + +Please see [releases](Releases) for information on each release. diff --git a/qemu/roms/seabios/docs/Execution_and_code_flow.md b/qemu/roms/seabios/docs/Execution_and_code_flow.md new file mode 100644 index 000000000..9396ecaa4 --- /dev/null +++ b/qemu/roms/seabios/docs/Execution_and_code_flow.md @@ -0,0 +1,178 @@ +This page provides a high-level description of some of the major code +phases that SeaBIOS transitions through and general information on +overall code flow. + +SeaBIOS code phases +=================== + +The SeaBIOS code goes through a few distinct code phases during its +execution lifecycle. Understanding these code phases can help when +reading and enhancing the code. + +POST phase +---------- + +The Power On Self Test (POST) phase is the initialization phase of the +BIOS. This phase is entered when SeaBIOS first starts execution. The +goal of the phase is to initialize internal state, initialize external +interfaces, detect and setup hardware, and to then start the boot +phase. + +On emulators, this phase starts when the CPU starts execution in 16bit +mode at 0xFFFF0000:FFF0. The emulators map the SeaBIOS binary to this +address, and SeaBIOS arranges for romlayout.S:reset_vector() to be +present there. This code calls romlayout.S:entry_post() which then +calls post.c:handle_post() in 32bit mode. + +On coreboot, the build arranges for romlayout.S:entry_elf() to be +called in 32bit mode. This then calls post.c:handle_post(). + +On CSM, the build arranges for romlayout.S:entry_csm() to be called +(in 16bit mode). This then calls csm.c:handle_csm() in 32bit mode. +Unlike on the emulators and coreboot, the SeaBIOS CSM POST phase is +orchastrated with UEFI and there are several calls back and forth +between SeaBIOS and UEFI via handle_csm() throughout the POST +process. + +The POST phase itself has several sub-phases. + +* The "preinit" sub-phase: code run prior to code relocation. +* The "init" sub-phase: code to initialize internal variables and + interfaces. +* The "setup" sub-phase: code to setup hardware and drivers. +* The "prepboot" sub-phase: code to finalize interfaces and prepare + for the boot phase. + +At completion of the POST phase, SeaBIOS invokes an "int 0x19" +software interrupt in 16bit mode which begins the boot phase. + +Boot phase +---------- + +The goal of the boot phase is to load the first portion of the +operating system's boot loader into memory and start execution of that +boot loader. This phase starts when a software interrupt ("int 0x19" +or "int 0x18") is invoked. The code flow starts in 16bit mode in +romlayout.S:entry_19() or romlayout.S:entry_18() which then +transition to 32bit mode and call boot.c:handle_19() or +boot.c:handle_18(). + +The boot phase is technically also part of the "runtime" phase of +SeaBIOS. It is typically invoked immiediately after the POST phase, +but it can also be invoked by an operating system or be invoked +multiple times in an attempt to find a valid boot media. Although the +boot phase C code runs in 32bit mode it does not have write access to +the 0x0f0000-0x100000 memory region and can not call the various +malloc_X() calls. See [Memory Model](Memory Model) for +more information. + +Main runtime phase +------------------ + +The main runtime phase occurs after the boot phase starts the +operating system. Once in this phase, the SeaBIOS code may be invoked +by the operating system using various 16bit and 32bit calls. The goal +of this phase is to support these legacy calling interfaces and to +provide compatibility with BIOS standards. There are multiple entry +points for the BIOS - see the entry_XXX() assembler functions in +romlayout.S. + +Callers use most of these legacy entry points by setting up a +particular CPU register state, invoking the BIOS, and then inspecting +the returned CPU register state. To handle this, SeaBIOS will backup +the current register state into a "struct bregs" (see romlayout.S, +entryfuncs.S, and bregs.h) on call entry and then pass this struct to +the C code. The C code can then inspect the register state and modify +it. The assembler entry functions will then restore the (possibly +modified) register state from the "struct bregs" on return to the +caller. + +Resume and reboot +----------------- + +As noted above, on emulators SeaBIOS handles the 0xFFFF0000:FFF0 +machine startup execution vector. This vector is also called on +machine faults and on some machine "resume" events. It can also be +called (as 0xF0000:FFF0) by software as a request to reboot the +machine (on emulators, coreboot, and CSM). + +The SeaBIOS "resume and reboot" code handles these calls and attempts +to determine the desired action of the caller. Code flow starts in +16bit mode in romlayout.S:reset_vector() which calls +romlayout.S:entry_post() which calls romlayout.S:entry_resume() which +calls resume.c:handle_resume(). Depending on the request the +handle_resume() code may transition to 32bit mode. + +Technically this code is part of the "runtime" phase, so even though +parts of it run in 32bit mode it still has the same limitations of the +runtime phase. + +Threads +======= + +Internally SeaBIOS implements a simple cooperative multi-tasking +system. The system works by giving each "thread" its own stack, and +the system round-robins between these stacks whenever a thread issues +a yield() call. This "threading" system may be more appropriately +described as [coroutines](http://en.wikipedia.org/wiki/Coroutine). +These "threads" do not run on multiple CPUs and are not preempted, so +atomic memory accesses and complex locking is not required. + +The goal of these threads is to reduce overall boot time by +parallelizing hardware delays. (For example, by allowing the wait for +an ATA harddrive to spinup and respond to commands to occur in +parallel with the wait for a PS/2 keyboard to respond to a setup +command.) These hardware setup threads are only available during the +"setup" sub-phase of the [POST phase](#POST_phase). + +The code that implements threads is in stacks.c. + +Hardware interrupts +=================== + +The SeaBIOS C code always runs with hardware interrupts disabled. All +of the C code entry points (see romlayout.S) are careful to explicitly +disable hardware interrupts (via "cli"). Because running with +interrupts disabled increases interrupt latency, any C code that could +loop for a significant amount of time (more than about 1 ms) should +periodically call yield(). The yield() call will briefly enable +hardware interrupts to occur, then disable interrupts, and then resume +execution of the C code. + +There are two main reasons why SeaBIOS always runs C code with +interrupts disabled. The first reason is that external software may +override the default SeaBIOS handlers that are called on a hardware +interrupt event. Indeed, it is common for DOS based applications to do +this. These legacy third party interrupt handlers may have +undocumented expections (such as stack location and stack size) and +may attempt to call back into the various SeaBIOS software services. +Greater compatibility and more reproducible results can be achieved by +only permitting hardware interrupts at specific points (via yield() +calls). The second reason is that much of SeaBIOS runs in 32bit mode. +Attempting to handle interrupts in both 16bit mode and 32bit mode and +switching between modes to delegate those interrupts is an unneeded +complexity. Although disabling interrupts can increase interrupt +latency, this only impacts legacy systems where the small increase in +interrupt latency is unlikely to be noticeable. + +Extra 16bit stack +================= + +SeaBIOS implements 16bit real mode handlers for both hardware +interrupts and software request "interrupts". In a traditional BIOS, +these requests would use the caller's stack space. However, the +minimum amount of space the caller must provide has not been +standardized and very old DOS programs have been observed to allocate +very small amounts of stack space (100 bytes or less). + +By default, SeaBIOS now switches to its own stack on most 16bit real +mode entry points. This extra stack space is allocated in ["low +memory"](Memory Model). It ensures SeaBIOS uses a minimal amount of a +callers stack (typically no more than 16 bytes) for these legacy +calls. (More recently defined BIOS interfaces such as those that +support 16bit protected and 32bit protected mode calls standardize a +minimum stack size with adequete space, and SeaBIOS generally will not +use its extra stack in these cases.) + +The code to implement this stack "hopping" is in romlayout.S and in +stacks.c. diff --git a/qemu/roms/seabios/docs/Linking_overview.md b/qemu/roms/seabios/docs/Linking_overview.md new file mode 100644 index 000000000..fb938b632 --- /dev/null +++ b/qemu/roms/seabios/docs/Linking_overview.md @@ -0,0 +1,166 @@ +This page describes the process that the SeaBIOS build uses to link +the compiled code into the final binary objects. + +Unfortunately, the SeaBIOS linking phase is complex. This complexity +is due to several unusual requirements: + +* Some BIOS entry points must reside at specific hardcoded memory + locations. The build must support positioning code and variables at + specific locations. +* In order to support multiple [memory models](Memory Model) the same + C code can be complied in three modes (16bit mode, 32bit segmented + mode, and 32bit "flat" mode). Binary code from these three modes + must be able to co-exist and on occasion reference each other. +* There is a finite amount of memory available to the BIOS. The build + will attempt to weed out unused code and variables from the final + binary. It also supports self-relocation of one-time initialization + code. + +Code layout +=========== + +To support the unusual build requirements, several +[gcc](http://en.wikipedia.org/wiki/GNU_Compiler_Collection) compiler +options are used. The "-ffunction-sections" and "-fdata-sections" +flags instruct the compiler to place each variable and function into +its own +[ELF](http://en.wikipedia.org/wiki/Executable_and_Linkable_Format) +section. + +The C code is compiled three times into three separate objects for +each of the major supported [memory models](Memory Model): +**code16.o**, **code32seg.o**, and **code32flat.o**. Information on +the sections and symbols of these three objects are extracted (using +**objdump**) and passed in to the **scripts/layoutrom.py** python +script. This script analyzes this information and produces gnu +[ld](http://en.wikipedia.org/wiki/GNU_linker) "linker scripts" which +provide precise location information to the linker. These linker +scripts are then used during the link phase which produces a **rom.o** +object containing all the code. + +Fixed location entry points +--------------------------- + +The build supports placing code entry points and variables at fixed +memory locations. This support is required in order to support the +legacy BIOS standards. For example, a program might execute an "int +0x15" to request system information from the BIOS, but another old +program might use "ljmpw $0xf000, $0xf859" instead. Both must provide +the same results and so the build must position the 0x15 interrupt +entry point in physical memory at 0xff859. + +This support is accomplished by placing the given code/variables into +ELF sections that have a name containing the substring +".fixedaddr.0x1234" (where 0x1234 is the desired address). For +variables in C code this is accomplished by marking the variables with +the VARFSEGFIXED(0x1234) macro. For assembler entry points the ORG +macro is used (see **romlayout.S**). + +During the build, the **layoutrom.py** script will detect sections +that contain the ".fixedaddr." substring and will arrange for the +final linker scripts to specify the desired address for the given +section. + +Due to the sparse nature of these fixed address sections, the +layoutrom.py script will also arrange to pack in other unrelated 16bit +code into the free space between fixed address sections (see +layoutrom.py:fitSections()). This maximizes the space available and +reduces the overall size of the final binary. + +C code in three modes +--------------------- + +SeaBIOS must support multiple [memory models](Memory Model). This is +accomplished by compiling the C code three separate times into three +separate objects. + +The C code within a mode must not accidentally call a C function in +another mode, but multiple modes must all access the same single copy +of global variables. Further, it is occasionally necessary for the C +code in one mode to obtain the address of C code in another mode. + +In order to use the same global variables between all modes, the +layoutrom.py script will detect references to global variables and +emit specific symbol definitions for those global variables in the +linker scripts so that all references use the same physical memory +address (see layoutrom.py:outXRefs()). + +To ensure C code does not accidentally call C code compiled in a +different mode, the build will ensure the symbols for C code in each +mode are isolated from each other during the linking stage. To support +those situations where an address of a C function in another mode is +required the build supports symbols with a special "\_cfuncX_" +prefix. The layoutrom.py script detects these references and will emit +a corresponding symbol definitions in the linker script that points to +the C code of the specified mode. This is typically seen with code +like: + +`extern void _cfunc32flat_process_op(void);`\ +`return call32(_cfunc32flat_process_op, 0, 0);` + +In the above example, when the build finds the symbol +"\_cfunc32flat_process_op" it will emit that symbol with the physical +address of the 32bit "flat" version of the process_op() C function. + +Build garbage collection +------------------------ + +To reduce the overall size of the final SeaBIOS binary the build +supports automatically weeding out of unused code and variables. This +is done with two separate processes: when supported the gcc +"-fwhole-program" compilation flag is used, and the layoutrom.py +script checks for unreferenced ELF sections. The layoutrom.py script +builds the final linker scripts with only referenced ELF sections, and +thus unreferenced sections are weeded out from the final objects. + +When writing C code, it is necessary to mark C functions with the +VISIBLE16, VISIBLE32SEG, or VISIBLE32FLAT macros if the functions are +ever referenced from assembler code. These macros ensure the +corresponding C function is emitted by the C compiler when compiling +for the given memory mode. These macros, however, do not affect the +layoutrom.py reference check, so even a function decorated with one of +the above macros can be weeded out from the final object if it is +never referenced. + +Code relocation +--------------- + +To further reduce the runtime memory size of the BIOS, the build +supports runtime self-relocation. Normally SeaBIOS is loaded into +memory in the memory region at 0xC0000-0x100000. This is convenient +for initial binary deployment, but the space competes with memory +requirements for Option ROMs, BIOS tables, and runtime storage. By +default, SeaBIOS will self-relocate its one-time initialization code +to free up space in this region. + +To support this feature, the build attempts to automatically detect +which C code is exclusively initialization phase code (see +layoutrom.py:checkRuntime()). It does this by finding all functions +decorated with the VISIBLE32INIT macro and all functions only +reachable via functions with that macro. These "init only" functions +are then grouped together and their location and size is stored in the +binary for the runtime code to relocate (see post.c:reloc_preinit()). + +The build also locates all cross section code references along with +all absolute memory addresses in the "init only" code. These addresses +need to be modified with the new run-time address in order for the +code to successfully run at a new address. The build finds the +location of the addresses (see layoutrom.py:getRelocs()) and stores +the information in the final binary. + +Final binary checks +=================== + +At the conclusion of the main linking stage, the code is contained in +the file **rom.o**. This object file contains all of the assembler +code, variables, and the C code from all three memory model modes. + +At this point the **scripts/checkrom.py** script is run to perform +final checks on the code. The script performs some sanity checks, it +may update some tables in the binary, and it reports some size +information. + +After the checkrom.py script is run the final user visible binary is +produced. The name of the final binary is either **bios.bin**, +**Csm16.bin**, or **bios.bin.elf** depending on the SeaBIOS build +requested. diff --git a/qemu/roms/seabios/docs/Mailinglist.md b/qemu/roms/seabios/docs/Mailinglist.md new file mode 100644 index 000000000..21e74a4b4 --- /dev/null +++ b/qemu/roms/seabios/docs/Mailinglist.md @@ -0,0 +1,8 @@ +For questions and general information about SeaBIOS, please subscribe +to the [SeaBIOS mailing +list](http://www.seabios.org/mailman/listinfo/seabios). If you're not +subscribed, your post will be held temporarily for moderator approval +(to combat spam). + +A mailing list archive is available at +<http://www.seabios.org/pipermail/seabios/> diff --git a/qemu/roms/seabios/docs/Memory_Model.md b/qemu/roms/seabios/docs/Memory_Model.md new file mode 100644 index 000000000..0668bd8f9 --- /dev/null +++ b/qemu/roms/seabios/docs/Memory_Model.md @@ -0,0 +1,253 @@ +The SeaBIOS code is required to support multiple x86 CPU memory +models. This requirement impacts the code layout and internal storage +of SeaBIOS. + +x86 Memory Models +================= + +The x86 line of CPUs has evolved over many years. The original 8086 +chip used 16bit pointers and could only address 1 megabyte of memory. +The 80286 CPU still used 16bit pointers, but could address up to 16 +megabytes of memory. The 80386 chips could process 32bit instructions +and could access up to 4 gigabyte of memory. The most recent x86 chips +can process 64bit instructions and access 16 exabytes of ram. + +During the evolution of the x86 CPUs from the 8086 to the 80386 the +BIOS was extended to handle calls in the various modes that the CPU +implemented. + +This section outlines the five different x86 CPU execution and memory +access models that SeaBIOS supports. + +16bit real mode +--------------- + +This mode is a +[segmented](http://en.wikipedia.org/wiki/Memory_segmentation) memory +mode invoked by callers. The CPU defaults to executing 16bit +instructions. Callers typically invoke the BIOS by issuing an "int x" +instruction which causes a software +[interrupt](http://en.wikipedia.org/wiki/Interrupt) that is handled by +the BIOS. The SeaBIOS code also handles hardware interrupts in this +mode. SeaBIOS can only access the first 1 megabyte of memory in this +mode, but it can access any part of that first megabyte. + +16bit bigreal mode +------------------ + +This mode is a segmented memory mode that is used for [option +roms](http://en.wikipedia.org/wiki/Option_ROM). The CPU defaults to +executing 16bit instructions and segmented memory accesses are still +used. However, the segment limits are increased so that the entire +first 4 gigabytes of memory is fully accessible. Callers can invoke +all the [16bit real mode](#16bit_real_mode) functions while in this +mode and can also invoke the Post Memory Manager (PMM) functions that +are available during option rom execution. + +16bit protected mode +-------------------- + +CPU execution in this mode is similar to [16bit real +mode](#16bit_real_mode). The CPU defaults to executing 16bit +instructions. However, each segment register indexes a "descriptor +table", and it is difficult or impossible to know what the physical +address of each segment is. Generally speaking, the BIOS can only +access code and data in the f-segment. The PCIBIOS, APM BIOS, and PNP +BIOS all have documented 16bit protected mode entry points. + +Some old code may attempt to invoke the standard [16bit real +mode](#16bit_real_mode) entry points while in 16bit protected +mode. The PCI BIOS specification explicitly requires that the legacy +"int 1a" real mode entry point support 16bit protected mode calls if +they are for the PCI BIOS. Callers of other legacy entry points in +protected mode have not been observed and SeaBIOS does not support +them. + +32bit segmented mode +-------------------- + +In this mode the processor runs in 32bit mode, but the segment +registers may have a limit and may have a non-zero offset. In effect, +this mode has all of the limitations of [16bit protected +mode](#16bit_protected_mode) - the main difference between the modes +is that the processor defaults to executing 32bit instructions. In +addition to these limitations, callers may also run the SeaBIOS code +at varying virtual addresses and so the code must support code +relocation. The PCI BIOS specification and APM BIOS specification +define 32bit segmented mode interfaces. + +32bit flat mode +--------------- + +In this mode the processor defaults to executing 32bit instructions, +and all segment registers have an offset of zero and allow access to +the entire first 4 gigabytes of memory. This is the only "sane" mode +for 32bit code - modern compilers and modern operating systems will +generally only support this mode (when running 32bit code). +Ironically, it's the only mode that is not strictly required for a +BIOS to support. SeaBIOS uses this mode internally to support the POST +and BOOT [phases of execution](Execution and code flow). + +code16gcc +========= + +In order to produce code that can run when the processor is in a 16bit +mode, SeaBIOS uses the +[binutils](http://en.wikipedia.org/wiki/GNU_Binutils) ".code16gcc" +assembler flag. This instructs the assembler to emit extra prefix +opcodes so that the 32bit code produced by +[gcc](http://en.wikipedia.org/wiki/GNU_Compiler_Collection) will run +even when the processor is in 16bit mode. Note that gcc always +produces 32bit code - it does not know about the ".code16gcc" flag and +does not know that the code will run in a 16bit mode. + +SeaBIOS uses the same code for all of the 16bit modes ([16bit real +mode](#16bit_real_mode), [16bit bigreal mode](#16bit_bigreal_mode), +and [16bit protected mode](#16bit_protected_mode)) and that code is +assembled using ".code16gcc". SeaBIOS is careful to use segment +registers properly so that the same code can run in the different +16bit modes that it needs to support. + +C code mode flags +================= + +Two compile time flags are available to determine the memory model the +code is intended for: MODE16 and MODESEGMENT. When compiling for the +16 bit modes, MODE16 is true and MODESEGMENT is true. In 32bit +segmented mode, MODE16 is false and MODESEGMENT is true. In 32bit flat +mode both MODE16 and MODESEGMENT are false. + +Common memory used at run-time +============================== + +There are several memory areas that the SeaBIOS "runtime" +[phase](Execution and code flow) makes use of: + +* 0x000000-0x000400: Interrupt descriptor table (IDT). This area + defines 256 interrupt vectors as defined by the Intel CPU + specification for 16bit irq handlers. This area is read/writable at + runtime and can be accessed from 16bit real mode and 16bit bigreal + mode calls. SeaBIOS only uses this area to maintain compatibility + with legacy systems. + +* 0x000400-0x000500: BIOS Data Area (BDA). This area contains various + legacy flags and attributes. The area is read/writable at runtime + and can be accessed from 16bit real mode and 16bit bigreal mode + calls. SeaBIOS only uses this area to maintain compatibility with + legacy systems. + +* 0x09FC00-0x0A0000 (typical): Extended BIOS Data Area (EBDA). This + area contains a few legacy flags and attributes. The area is + typically located at 0x9FC00, but it can be moved by option roms, by + legacy operating systems, and by SeaBIOS if + CONFIG_MALLOC_UPPERMEMORY is not set. Its actual location is + determined by a pointer in the BDA. The area is read/writable at + runtime and can be accessed from 16bit real mode and 16bit bigreal + mode calls. SeaBIOS only uses this area to maintain compatibility + with legacy systems. + +* 0x0E0000-0x0F0000 (typical): "low" memory. This area is used for + custom read/writable storage internal to SeaBIOS. The area is + read/writable at runtime and can be accessed from 16bit real mode + and 16bit bigreal mode calls. The area is typically located at the + end of the e-segment, but the build may position it anywhere in the + 0x0C0000-0x0F0000 region. However, if CONFIG_MALLOC_UPPERMEMORY is + not set, then this region is between 0x090000-0x0A0000. Space is + allocated in this region by either marking a global variable with + the "VARLOW" flag or by calling malloc_low() during + initialization. The area can be grown dynamically (via malloc_low), + but it will never exceed 64K. + +* 0x0F0000-0x100000: The BIOS segment. This area is used for both + runtime code and static variables. Space is allocated in this region + by either marking a global variable with VAR16, one of the VARFSEG + flags, or by calling malloc_fseg() during initialization. The area + is read-only at runtime and can be accessed from 16bit real mode, + 16bit bigreal mode, 16bit protected mode, and 32bit segmented mode + calls. + +All of the above areas are also read/writable during the SeaBIOS +initialization phase and are accessible when in 32bit flat mode. + +Segmented mode memory access +============================ + +The assembler entry functions for segmented mode calls (all modes +except [32bit flat mode](#32bit_flat_mode)) will arrange +to set the data segment (%ds) to be the same as the stack segment +(%ss) before calling any C code. This permits all C variables located +on the stack and C pointers to data located on the stack to work as +normal. + +However, all code running in segmented mode must wrap non-stack memory +accesses in special macros. These macros ensure the correct segment +register is used. Failure to use the correct macro will result in an +incorrect memory access that will likely cause hard to find errors. + +There are three low-level memory access macros: + +* GET_VAR / SET_VAR : Accesses a variable using the specified segment + register. This isn't typically used directly by C code. + +* GET_FARVAR / SET_FARVAR : Assigns the extra segment (%es) to the + given segment id and then performs the given memory access via %es. + +* GET_FLATVAR / SET_FLATVAR : These macros take a 32bit pointer, + construct a segment/offset pair valid in real mode, and then perform + the given access. These macros must not be used in 16bit protected + mode or 32bit segmented mode. + +Since most memory accesses are to [common memory used at +run-time](#Common_memory_used_at_run-time), several helper +macros are also available. + +* GET_IDT / SET_IDT : Access the interrupt descriptor table (IDT). + +* GET_BDA / SET_BDA : Access the BIOS Data Area (BDA). + +* GET_EBDA / SET_EBDA : Access the Extended BIOS Data Area (EBDA). + +* GET_LOW / SET_LOW : Access internal variables marked with + VARLOW. (There are also related macros GET_LOWFLAT / SET_LOWFLAT for + accessing storage allocated with malloc_low). + +* GET_GLOBAL : Access internal variables marked with the VAR16 or + VARFSEG flags. (There is also the related macro GET_GLOBALFLAT for + accessing storage allocated with malloc_fseg). + +Memory available during initialization +====================================== + +During the POST [phase](Execution and code flow) the code +can fully access the first 4 gigabytes of memory. However, memory +accesses are generally limited to the [common memory used at +run-time](#Common_memory_used_at_run-time) and areas +allocated at runtime via one of the malloc calls: + +* malloc_high : Permanent high-memory zone. This area is used for + custom read/writable storage internal to SeaBIOS. The area is + located at the top of the first 4 gigabytes of ram. It is commonly + used for storing standard tables accessed by the operating system at + runtime (ACPI, SMBIOS, and MPTable) and for DMA buffers used by + hardware drivers. The area is read/writable at runtime and an entry + in the e820 memory map is used to reserve it. When running on an + emulator that has only 1 megabyte of ram this zone will be empty. + +* malloc_tmphigh : Temporary high-memory zone. This area is used for + custom read/writable storage during the SeaBIOS initialization + phase. The area generally starts after the first 1 megabyte of ram + (0x100000) and ends prior to the Permanent high-memory zone. When + running on an emulator that has only 1 megabyte of ram this zone + will be empty. The area is not reserved from the operating system, + so it must not be accessed after the SeaBIOS initialization phase. + +* malloc_tmplow : Temporary low-memory zone. This area is used for + custom read/writable storage during the SeaBIOS initialization + phase. The area resides between 0x07000-0x90000. The area is not + reserved from the operating system and by specification it is + required to be zero'd at the end of the initialization phase. + +The "tmplow" and "tmphigh" regions are only available during the +initialization phase. Any access (either read or write) after +completion of the initialization phase can result in difficult to find +errors. diff --git a/qemu/roms/seabios/docs/README b/qemu/roms/seabios/docs/README new file mode 100644 index 000000000..430e0fe47 --- /dev/null +++ b/qemu/roms/seabios/docs/README @@ -0,0 +1,5 @@ +This directory contains SeaBIOS documentation as found on the SeaBIOS +wiki. All the files in this directory (with the exclusion of this +README file) correspond to a page on the wiki. + +The documentation files use markdown syntax. diff --git a/qemu/roms/seabios/docs/Releases.md b/qemu/roms/seabios/docs/Releases.md new file mode 100644 index 000000000..6a1ecd564 --- /dev/null +++ b/qemu/roms/seabios/docs/Releases.md @@ -0,0 +1,377 @@ +History of SeaBIOS releases. Please see [download](Download) for +information on obtaining these releases. + +SeaBIOS 1.8.0 +============= + +Available on 20150218. Major changes in this release: + +* Several USB timing fixes for USB controllers on real hardware +* Initial support for USB3 hubs +* Initial support for SD cards (on QEMU only) +* Initial support for transitioning to 32bit mode using SMIs (on QEMU + TCG only) +* SeaVGABIOS improvements + * Added cursor emulation to coreboot native init vgabios (cbvga) + * Added support for read character calls when in graphics mode +* Developer documentation added to "docs/" directory in the code + repository and several documentation updates +* Several bug fixes and code cleanups + +As of the 1.8.0 release, new feature releases will modify the first +two release numbers (eg, 1.8) and stable releases will use three +numbers (eg, 1.8.1). The prior behavior of using a forth number +(eg, 1.7.5.1) for stable releases will no longer be used. + +SeaBIOS 1.7.5 +============= + +Available on 20140528. Major changes in this release: + +* Support for obtaining SMBIOS tables directly from QEMU. +* XHCI USB controller fixes for real hardware (now tested on several + boards) +* SeaVGABIOS improvements + * New driver for "coreboot native vga" support + * Improved detection of older x86emu versions with incorrect + emulation. +* Several bug fixes and code cleanups + +SeaBIOS 1.7.5.1 +--------------- + +Available on 20141113. Stable release containing only bug fixes. + +SeaBIOS 1.7.5.2 +--------------- + +Available on 20150112. Stable release containing only bug fixes. + +SeaBIOS 1.7.4 +============= + +Available on 20131223. Major changes in this release: + +* Support for obtaining ACPI tables directly from QEMU. +* Initial support for XHCI USB controllers (initially for QEMU only). +* Support for booting from "pvscsi" devices on QEMU. +* Enhanced floppy driver - improved support for real hardware. +* coreboot cbmem console support. +* Optional support for using the 9-segment instead of the e-segment + for local variables. +* Improved internal timer code and accuracy. +* SeaVGABIOS improvements + * Better support for legacy X.org releases with incomplete x86emu + emulation. + * Support for using an internal stack to reduce caller's stack + usage. + * Back port of new "bochs dispi" interface video modes. +* Several bug fixes and code cleanups + * Source code separated out into additional hardware and firmware + directories. + * Update to latest version of Kconfig + +SeaBIOS 1.7.3 +============= + +Available on 20130707. Major changes in this release: + +* Initial support for using SeaBIOS as a UEFI Compatibility Support + Module (CSM) +* Support for detecting and using ACPI reboot ports. +* By default, all 16bit entry points now use an internal stack to + reduce stack footprint. +* Floppy controller code has been rewritten to improve + compatibility. Non-standard floppy sizes now work again with recent + QEMU versions. +* Several bug fixes and code cleanups + +SeaBIOS 1.7.2 +============= + +Available on 20130118. Major changes in this release: + +* Support for ICH9 host chipset ("q35") on emulators +* Support for booting from LSI MegaRAID SAS controllers +* Support for using the ACPI PM timer on emulators +* Improved Geode VGA BIOS support. +* Several bug fixes + +SeaBIOS 1.7.2.1 +--------------- + +Available on 20130227. Stable release containing only bug fixes. + +SeaBIOS 1.7.2.2 +--------------- + +Available on 20130527. Stable release containing only bug fixes. + +SeaBIOS 1.7.1 +============= + +Available on 20120831. Major changes in this release: + +* Initial support for booting from USB attached scsi (USB UAS) drives +* USB EHCI 64bit controller support +* USB MSC multi-LUN device support +* Support for booting from LSI SCSI controllers on emulators +* Support for booting from AMD PCscsi controllers on emulators +* New PCI allocation code on emulators. Support 64bit PCI bars and + mapping them above 4G. +* Support for non-linear APIC ids on emulators. +* Stack switching for 16bit real mode irq handlers to reduce stack + footprint. +* Support for custom storage in the memory at 0xc0000-0xf0000. No + longer reserve memory for custom storage in first 640k. +* Improved code generation for 16bit segment register loads +* Boot code will now (by default) reboot after 60 seconds if no boot + device found +* CBFS and FWCFG "files" are now only scanned one time +* Several bug fixes + +SeaBIOS 1.7.0 +============= + +Available on 20120414. Major changes in this release: + +* Many enhancements to VGA BIOS code - it should now be feature + complete with LGPL vgabios. +* Support for virtio-scsi. +* Improved USB drive (usb-msc) support. +* Several USB controller bug fixes and improvements. +* Runtime ACPI AML PCI hotplug construction. +* Support for running on i386 and i486 CPUs. +* Enhancements to PCI init when running on emulators. +* Several bug fixes + +SeaBIOS 1.6.3 +============= + +Available on 20111004. Major changes in this release: + +* Initial support for Xen +* PCI init (on emulators) uses a two-phase initialization +* Fixes for AHCI so it can work on real hardware. AHCI is now enabled + by default. +* Bootsplash support for BMP files +* Several configuration options can now be configured at runtime via + CBFS files (eg, "etc/boot-menu-wait") +* PCI device scan is cached during POST phase +* Several bug fixes + +The SeaBIOS 1.6.3 release was an incremental feature release. The +first release number (1) was incremented as the project was no longer +in a beta stage, and the third release number (3) was also incremented +to indicate the release was a regular feature release. + +SeaBIOS 1.6.3.1 +--------------- + +Available on 20111124. Stable release containing only bug fixes. + +SeaBIOS 1.6.3.2 +--------------- + +Available on 20120311. Stable release containing only bug fixes. + +SeaBIOS 0.6.2 +============= + +Available on 20110228. Major changes in this release: + +* Setup code can relocate to high-memory to save space in c-f segments +* Build now configured via Kconfig +* Experimental support for AHCI controllers +* Support for run-time configuration of the boot order (via + CBFS/fw_cfg "bootorder" file) +* Support T13 EDD3.0 spec +* Improved bounds checking on PCI memory allocation +* Several bug fixes + +SeaBIOS 0.6.1 +============= + +Available on 20100913. Major changes in this release: + +* Support for virtio drives +* Add ACPI definitions for cpu hotplug support +* Support for a graphical bootsplash screen +* USB mouse support +* The PCI support for emulators is less dependent on i440 chipset +* New malloc implementation which improves memalign and free +* The build system no longer double links objects +* Several bug fixes + +SeaBIOS 0.6.1.1 +--------------- + +Available on 20101031. Stable release containing only bug fixes. + +SeaBIOS 0.6.1.2 +--------------- + +Available on 20101113. Stable release containing only bug fixes. + +SeaBIOS 0.6.1.3 +--------------- + +Available on 20101226. Stable release containing only bug fixes. + +SeaBIOS 0.6.0 +============= + +Available on 20100326. Major changes in this release: + +* USB hub support +* USB drive booting support +* USB keyboard auto-repeat support +* USB EHCI controller support +* Several improvements to compatibility of PS2 port handlers for old + code +* Support for qemu e820 interface +* Several bug fixes and code cleanups + +SeaBIOS 0.5.1 +============= + +Available on 20100108. Major changes in this release: + +* Support for 32bit PCI BIOS calls +* Support for int1589 calls +* MPTable fixes for OpenBSD +* ATA DMA and bus-mastering support +* Several bug fixes and code cleanups + +SeaBIOS 0.5.0 +============= + +Available on 20091218. Major changes in this release: + +* Several enhancements ported from the Bochs BIOS derived code in qemu + and kvm +* Support for parallel hardware initialization to reduce bootup times +* Enable PCI option rom support by default (Bochs users must now + enable CONFIG_OPTIONROMS_DEPLOYED in src/config.h). Support added + for extracting option roms from qemu "fw_cfg". +* Support USB UHCI and OHCI controllers +* Initial support for USB keyboards +* SeaBIOS can now be greater than 64K +* Support for permanent low memory allocations +* APIC "local interrupts" now enabled in SeaBIOS (on emulators) +* Several bug fixes and code cleanups + +SeaBIOS 0.4.2 +============= + +Available on 20090909. Major changes in this release: + +* Implement Post Memory Manager (PMM) support. Use equivalent "malloc" + functions for internal allocations as well. +* Refactor disk "block" interface for greater expandability +* Support CBFS based floppy images +* Allow boot menu to select either floppy to boot from +* Increase ebda size to store a CDROM harddrive/floppy emulation + buffer +* Support systems with multiple vga cards (only the card with the + legacy IO ranges mapped will have its option rom executed) +* Make option rom memory be writable during option rom execution (on + emulators) +* Compile version number into code and report on each boot +* Several bug fixes and code cleanups + +SeaBIOS 0.4.1 +============= + +Available on 20090714. Major changes in this release: + +* Support older versions of gcc that predate "-fwhole-program" (eg, + v3.x) +* Add initial port of "LGPL vga bios" code into tree in "vgasrc/" + directory +* Handle ATA drives still "spinning up" during SeaBIOS drive detect +* Add support for option rom Boot Connection Vectors (BCV) +* Enhance boot menu to support booting from any drive or any cdrom +* Support flash based Coreboot File System (CBFS) +* Support booting from a CBFS "payload" +* Support coreboot table forwarder +* Support compile time definitions for multiple root PCI buses +* New tools/readserial.py tool +* Several bug fixes and code cleanups + +SeaBIOS 0.4.0 +============= + +Available on 20090206. Major changes in this release: + +* Add Bios Boot Specification (BBS) calls; add PnP call stubs +* Support option roms stored in PCI rom BAR +* Support rebooting on ctrl+alt+delete key press +* Scan PCI devices for ATA adapters (don't assume legacy ISA ATA ports + are valid) +* Attempt to automatically determine gcc capabilities/bugs during + build +* Add script to layout 16bit sections at fixed offsets and in + compacted space +* Introduce timestamp counter based delays +* Support POST calls that are really a resume +* Use new stack in EBDA for int13 disk calls to reduce stack usage +* Support the EBDA being relocated by option roms +* Move many variables from EBDA to global variables (stored in + f-segment) +* Support for PCI bridges when iterating through PCI device list +* Initial port of several KVM specific features from their Bochs BIOS + derived code +* Access BDA using segment 0x40 and IVT using segment 0x00 (which + could be important for 16bit protected mode callers) +* Several bug fixes and code cleanups + +SeaBIOS 0.3.0 +============= + +Available on 20080817. Major changes in this release: + +* Run boot code (int18/19) in 32bit mode +* Rewrite of PS2 port handling - new code is more compatible with real + hardware +* Initial support for int155f VGA option rom calls +* Several bug fixes and code cleanups + +SeaBIOS 0.2.3 +============= + +Available on 20080702. Major changes in this release: + +* Initial support for running on real hardware with coreboot +* Support parsing coreboot tables +* Support relocating bios tables from high memory when running under + coreboot +* Dynamic e820 map generation +* Serial debug support +* New tools/checkstack.py tool +* Several bug fixes and code cleanups + +SeaBIOS 0.2.2 +============= + +Formerly known as "legacybios". Available on 20080501. Major changes +in this release: + +* Several bug fixes and code cleanups + +SeaBIOS 0.2.1 +============= + +Formerly known as "legacybios". Available on 20080406. Major changes +in this release: + +* Port of boot menu code from Bochs BIOS +* Several bug fixes and code cleanups + +SeaBIOS 0.2.0 +============= + +Formerly known as "legacybios". Available on 20080330. Major changes +in this release: + +* Completion of initial port of Bochs BIOS code to gcc. diff --git a/qemu/roms/seabios/docs/SeaBIOS.md b/qemu/roms/seabios/docs/SeaBIOS.md new file mode 100644 index 000000000..831bfced9 --- /dev/null +++ b/qemu/roms/seabios/docs/SeaBIOS.md @@ -0,0 +1,15 @@ +SeaBIOS is an open source implementation of a 16bit X86 BIOS. SeaBIOS +can run in an emulator or it can run natively on X86 hardware with the +use of [coreboot](http://www.coreboot.org/). + +SeaBIOS is the default BIOS for [qemu](http://www.qemu.org/) and +[kvm](http://www.linux-kvm.org/). + +The [coreboot SeaBIOS](http://www.coreboot.org/SeaBIOS) page has +information on using SeaBIOS in coreboot. Please see the +[releases](Releases) page for information on recent releases. See the +[download](Download) page to obtain SeaBIOS. + +Please join the [mailing list](Mailinglist) to contribute to +SeaBIOS. Information on the internals of SeaBIOS is available on the +[Developer Documentation](Developer Documentation) page. diff --git a/qemu/roms/seabios/scripts/acpi_extract.py b/qemu/roms/seabios/scripts/acpi_extract.py new file mode 100755 index 000000000..60bbac3c0 --- /dev/null +++ b/qemu/roms/seabios/scripts/acpi_extract.py @@ -0,0 +1,354 @@ +#!/usr/bin/python +# Copyright (C) 2011 Red Hat, Inc., Michael S. Tsirkin <mst@redhat.com> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +# Process mixed ASL/AML listing (.lst file) produced by iasl -l +# Locate and execute ACPI_EXTRACT directives, output offset info +# +# Documentation of ACPI_EXTRACT_* directive tags: +# +# These directive tags output offset information from AML for BIOS runtime +# table generation. +# Each directive is of the form: +# ACPI_EXTRACT_<TYPE> <array_name> <Operator> (...) +# and causes the extractor to create an array +# named <array_name> with offset, in the generated AML, +# of an object of a given type in the following <Operator>. +# +# A directive must fit on a single code line. +# +# Object type in AML is verified, a mismatch causes a build failure. +# +# Directives and operators currently supported are: +# ACPI_EXTRACT_NAME_DWORD_CONST - extract a Dword Const object from Name() +# ACPI_EXTRACT_NAME_WORD_CONST - extract a Word Const object from Name() +# ACPI_EXTRACT_NAME_BYTE_CONST - extract a Byte Const object from Name() +# ACPI_EXTRACT_METHOD_STRING - extract a NameString from Method() +# ACPI_EXTRACT_NAME_STRING - extract a NameString from Name() +# ACPI_EXTRACT_PROCESSOR_START - start of Processor() block +# ACPI_EXTRACT_PROCESSOR_STRING - extract a NameString from Processor() +# ACPI_EXTRACT_PROCESSOR_END - offset at last byte of Processor() + 1 +# ACPI_EXTRACT_DEVICE_START - start of Device() block +# ACPI_EXTRACT_DEVICE_STRING - extract a NameString from Device() +# ACPI_EXTRACT_DEVICE_END - offset at last byte of Device() + 1 +# ACPI_EXTRACT_PKG_START - start of Package block +# +# ACPI_EXTRACT_ALL_CODE - create an array storing the generated AML bytecode +# +# ACPI_EXTRACT is not allowed anywhere else in code, except in comments. + +import re +import sys +import fileinput + +aml = [] +asl = [] +output = {} +debug = "" + +class asl_line: + line = None + lineno = None + aml_offset = None + +def die(diag): + sys.stderr.write("Error: %s; %s\n" % (diag, debug)) + sys.exit(1) + +#Store an ASL command, matching AML offset, and input line (for debugging) +def add_asl(lineno, line): + l = asl_line() + l.line = line + l.lineno = lineno + l.aml_offset = len(aml) + asl.append(l) + +#Store an AML byte sequence +#Verify that offset output by iasl matches # of bytes so far +def add_aml(offset, line): + o = int(offset, 16) + # Sanity check: offset must match size of code so far + if (o != len(aml)): + die("Offset 0x%x != 0x%x" % (o, len(aml))) + # Strip any trailing dots and ASCII dump after " + line = re.sub(r'\s*\.*\s*".*$', "", line) + # Strip traling whitespace + line = re.sub(r'\s+$', "", line) + # Strip leading whitespace + line = re.sub(r'^\s+', "", line) + # Split on whitespace + code = re.split(r'\s+', line) + for c in code: + # Require a legal hex number, two digits + if (not(re.search(r'^[0-9A-Fa-f][0-9A-Fa-f]$', c))): + die("Unexpected octet %s" % c) + aml.append(int(c, 16)) + +# Process aml bytecode array, decoding AML +def aml_pkglen_bytes(offset): + # PkgLength can be multibyte. Bits 8-7 give the # of extra bytes. + pkglenbytes = aml[offset] >> 6 + return pkglenbytes + 1 + +def aml_pkglen(offset): + pkgstart = offset + pkglenbytes = aml_pkglen_bytes(offset) + pkglen = aml[offset] & 0x3F + # If multibyte, first nibble only uses bits 0-3 + if ((pkglenbytes > 1) and (pkglen & 0x30)): + die("PkgLen bytes 0x%x but first nibble 0x%x expected 0x0X" % + (pkglen, pkglen)) + offset += 1 + pkglenbytes -= 1 + for i in range(pkglenbytes): + pkglen |= aml[offset + i] << (i * 8 + 4) + if (len(aml) < pkgstart + pkglen): + die("PckgLen 0x%x at offset 0x%x exceeds AML size 0x%x" % + (pkglen, offset, len(aml))) + return pkglen + +# Given method offset, find its NameString offset +def aml_method_string(offset): + #0x14 MethodOp PkgLength NameString MethodFlags TermList + if (aml[offset] != 0x14): + die( "Method offset 0x%x: expected 0x14 actual 0x%x" % + (offset, aml[offset])) + offset += 1 + pkglenbytes = aml_pkglen_bytes(offset) + offset += pkglenbytes + return offset + +# Given name offset, find its NameString offset +def aml_name_string(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x08): + die( "Name offset 0x%x: expected 0x08 actual 0x%x" % + (offset, aml[offset])) + offset += 1 + # Block Name Modifier. Skip it. + if (aml[offset] == 0x5c or aml[offset] == 0x5e): + offset += 1 + return offset + +# Given data offset, find 8 byte buffer offset +def aml_data_buffer8(offset): + #0x08 NameOp NameString DataRef + expect = [0x11, 0x0B, 0x0A, 0x08] + if (aml[offset:offset+4] != expect): + die( "Name offset 0x%x: expected %s actual %s" % + (offset, aml[offset:offset+4], expect)) + return offset + len(expect) + +# Given data offset, find dword const offset +def aml_data_dword_const(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x0C): + die( "Name offset 0x%x: expected 0x0C actual 0x%x" % + (offset, aml[offset])) + return offset + 1 + +# Given data offset, find word const offset +def aml_data_word_const(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x0B): + die( "Name offset 0x%x: expected 0x0B actual 0x%x" % + (offset, aml[offset])) + return offset + 1 + +# Given data offset, find byte const offset +def aml_data_byte_const(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x0A): + die( "Name offset 0x%x: expected 0x0A actual 0x%x" % + (offset, aml[offset])) + return offset + 1 + +# Find name'd buffer8 +def aml_name_buffer8(offset): + return aml_data_buffer8(aml_name_string(offset) + 4) + +# Given name offset, find dword const offset +def aml_name_dword_const(offset): + return aml_data_dword_const(aml_name_string(offset) + 4) + +# Given name offset, find word const offset +def aml_name_word_const(offset): + return aml_data_word_const(aml_name_string(offset) + 4) + +# Given name offset, find byte const offset +def aml_name_byte_const(offset): + return aml_data_byte_const(aml_name_string(offset) + 4) + +def aml_device_start(offset): + #0x5B 0x82 DeviceOp PkgLength NameString + if ((aml[offset] != 0x5B) or (aml[offset + 1] != 0x82)): + die( "Name offset 0x%x: expected 0x5B 0x82 actual 0x%x 0x%x" % + (offset, aml[offset], aml[offset + 1])) + return offset + +def aml_device_string(offset): + #0x5B 0x82 DeviceOp PkgLength NameString + start = aml_device_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + offset += pkglenbytes + return offset + +def aml_device_end(offset): + start = aml_device_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + pkglen = aml_pkglen(offset) + return offset + pkglen + +def aml_processor_start(offset): + #0x5B 0x83 ProcessorOp PkgLength NameString ProcID + if ((aml[offset] != 0x5B) or (aml[offset + 1] != 0x83)): + die( "Name offset 0x%x: expected 0x5B 0x83 actual 0x%x 0x%x" % + (offset, aml[offset], aml[offset + 1])) + return offset + +def aml_processor_string(offset): + #0x5B 0x83 ProcessorOp PkgLength NameString ProcID + start = aml_processor_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + offset += pkglenbytes + return offset + +def aml_processor_end(offset): + start = aml_processor_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + pkglen = aml_pkglen(offset) + return offset + pkglen + +def aml_package_start(offset): + offset = aml_name_string(offset) + 4 + # 0x12 PkgLength NumElements PackageElementList + if (aml[offset] != 0x12): + die( "Name offset 0x%x: expected 0x12 actual 0x%x" % + (offset, aml[offset])) + offset += 1 + return offset + aml_pkglen_bytes(offset) + 1 + +lineno = 0 +for line in fileinput.input(): + # Strip trailing newline + line = line.rstrip() + # line number and debug string to output in case of errors + lineno = lineno + 1 + debug = "input line %d: %s" % (lineno, line) + #ASL listing: space, then line#, then ...., then code + pasl = re.compile('^\s+([0-9]+)(:\s\s|\.\.\.\.)\s*') + m = pasl.search(line) + if (m): + add_asl(lineno, pasl.sub("", line)) + # AML listing: offset in hex, then ...., then code + paml = re.compile('^([0-9A-Fa-f]+)(:\s\s|\.\.\.\.)\s*') + m = paml.search(line) + if (m): + add_aml(m.group(1), paml.sub("", line)) + +# Now go over code +# Track AML offset of a previous non-empty ASL command +prev_aml_offset = -1 +for i in range(len(asl)): + debug = "input line %d: %s" % (asl[i].lineno, asl[i].line) + + l = asl[i].line + + # skip if not an extract directive + a = len(re.findall(r'ACPI_EXTRACT', l)) + if (not a): + # If not empty, store AML offset. Will be used for sanity checks + # IASL seems to put {}. at random places in the listing. + # Ignore any non-words for the purpose of this test. + m = re.search(r'\w+', l) + if (m): + prev_aml_offset = asl[i].aml_offset + continue + + if (a > 1): + die("Expected at most one ACPI_EXTRACT per line, actual %d" % a) + + mext = re.search(r''' + ^\s* # leading whitespace + /\*\s* # start C comment + (ACPI_EXTRACT_\w+) # directive: group(1) + \s+ # whitspace separates directive from array name + (\w+) # array name: group(2) + \s*\*/ # end of C comment + \s*$ # trailing whitespace + ''', l, re.VERBOSE) + if (not mext): + die("Stray ACPI_EXTRACT in input") + + # previous command must have produced some AML, + # otherwise we are in a middle of a block + if (prev_aml_offset == asl[i].aml_offset): + die("ACPI_EXTRACT directive in the middle of a block") + + directive = mext.group(1) + array = mext.group(2) + offset = asl[i].aml_offset + + if (directive == "ACPI_EXTRACT_ALL_CODE"): + if array in output: + die("%s directive used more than once" % directive) + output[array] = aml + continue + if (directive == "ACPI_EXTRACT_NAME_BUFFER8"): + offset = aml_name_buffer8(offset) + elif (directive == "ACPI_EXTRACT_NAME_DWORD_CONST"): + offset = aml_name_dword_const(offset) + elif (directive == "ACPI_EXTRACT_NAME_WORD_CONST"): + offset = aml_name_word_const(offset) + elif (directive == "ACPI_EXTRACT_NAME_BYTE_CONST"): + offset = aml_name_byte_const(offset) + elif (directive == "ACPI_EXTRACT_NAME_STRING"): + offset = aml_name_string(offset) + elif (directive == "ACPI_EXTRACT_METHOD_STRING"): + offset = aml_method_string(offset) + elif (directive == "ACPI_EXTRACT_DEVICE_START"): + offset = aml_device_start(offset) + elif (directive == "ACPI_EXTRACT_DEVICE_STRING"): + offset = aml_device_string(offset) + elif (directive == "ACPI_EXTRACT_DEVICE_END"): + offset = aml_device_end(offset) + elif (directive == "ACPI_EXTRACT_PROCESSOR_START"): + offset = aml_processor_start(offset) + elif (directive == "ACPI_EXTRACT_PROCESSOR_STRING"): + offset = aml_processor_string(offset) + elif (directive == "ACPI_EXTRACT_PROCESSOR_END"): + offset = aml_processor_end(offset) + elif (directive == "ACPI_EXTRACT_PKG_START"): + offset = aml_package_start(offset) + else: + die("Unsupported directive %s" % directive) + + if array not in output: + output[array] = [] + output[array].append(offset) + +debug = "at end of file" + +def get_value_type(maxvalue): + #Use type large enough to fit the table + if (maxvalue >= 0x10000): + return "int" + elif (maxvalue >= 0x100): + return "short" + else: + return "char" + +# Pretty print output +for array in output.keys(): + otype = get_value_type(max(output[array])) + odata = [] + for value in output[array]: + odata.append("0x%x" % value) + sys.stdout.write("static unsigned %s %s[] = {\n" % (otype, array)) + sys.stdout.write(",\n".join(odata)) + sys.stdout.write('\n};\n') diff --git a/qemu/roms/seabios/scripts/acpi_extract_preprocess.py b/qemu/roms/seabios/scripts/acpi_extract_preprocess.py new file mode 100755 index 000000000..269811840 --- /dev/null +++ b/qemu/roms/seabios/scripts/acpi_extract_preprocess.py @@ -0,0 +1,41 @@ +#!/usr/bin/python +# Copyright (C) 2011 Red Hat, Inc., Michael S. Tsirkin <mst@redhat.com> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +# Read a preprocessed ASL listing and put each ACPI_EXTRACT +# directive in a comment, to make iasl skip it. +# We also put each directive on a new line, the machinery +# in scripts/acpi_extract.py requires this. + +import re +import sys +import fileinput + +def die(diag): + sys.stderr.write("Error: %s\n" % (diag)) + sys.exit(1) + +# Note: () around pattern make split return matched string as part of list +psplit = re.compile(r''' ( + \b # At word boundary + ACPI_EXTRACT_\w+ # directive + \s+ # some whitespace + \w+ # array name + )''', re.VERBOSE) + +lineno = 0 +for line in fileinput.input(): + # line number and debug string to output in case of errors + lineno = lineno + 1 + debug = "input line %d: %s" % (lineno, line.rstrip()) + + s = psplit.split(line) + # The way split works, each odd item is the matching ACPI_EXTRACT directive. + # Put each in a comment, and on a line by itself. + for i in range(len(s)): + if (i % 2): + sys.stdout.write("\n/* %s */\n" % s[i]) + else: + sys.stdout.write(s[i]) + diff --git a/qemu/roms/seabios/scripts/buildrom.py b/qemu/roms/seabios/scripts/buildrom.py new file mode 100755 index 000000000..0499049c3 --- /dev/null +++ b/qemu/roms/seabios/scripts/buildrom.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# Fill in checksum/size of an option rom, and pad it to proper length. +# +# Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys, struct + +from python23compat import as_bytes + +def alignpos(pos, alignbytes): + mask = alignbytes - 1 + return (pos + mask) & ~mask + +def checksum(data): + if (sys.version_info > (3, 0)): + cksum = sum(data) + else: + cksum = sum(map(ord, data)) + return struct.pack('<B', (0x100 - cksum) & 0xff) + +def main(): + inname = sys.argv[1] + outname = sys.argv[2] + + # Read data in + f = open(inname, 'rb') + data = f.read() + f.close() + count = len(data) + + # Pad to a 512 byte boundary + data += as_bytes("\0") * (alignpos(count, 512) - count) + count = len(data) + + # Check if a pci header is present + pcidata = ord(data[24:25]) + (ord(data[25:26]) << 8) + if pcidata != 0: + blocks = struct.pack('<H', int(count/512)) + data = data[:pcidata + 16] + blocks + data[pcidata + 18:] + + # Fill in size field; clear checksum field + blocks = struct.pack('<B', int(count/512)) + data = data[:2] + blocks + data[3:6] + as_bytes("\0") + data[7:] + + # Checksum rom + data = data[:6] + checksum(data) + data[7:] + + # Write new rom + f = open(outname, 'wb') + f.write(data) + f.close() + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/buildversion.sh b/qemu/roms/seabios/scripts/buildversion.sh new file mode 100755 index 000000000..516aff5b2 --- /dev/null +++ b/qemu/roms/seabios/scripts/buildversion.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Script to generate a C file with version information. +OUTFILE="$1" +VAR16MODE="$2" + +# Extract version info +if [ -z "$BUILD_VERSION" ]; then + if [ -d .git -o -f .git ]; then + VERSION="`git describe --tags --long --dirty`" + elif [ -f .version ]; then + VERSION="`cat .version`" + else + VERSION="?" + fi + VERSION="${VERSION}-`date +"%Y%m%d_%H%M%S"`-`hostname`" +else + VERSION="$BUILD_VERSION" +fi +echo "Version: ${VERSION}" + +# Build header file +if [ "$VAR16MODE" = "VAR16" ]; then + cat > ${OUTFILE} <<EOF +#include "types.h" +char VERSION[] VAR16 = "${VERSION}"; +EOF +else + cat > ${OUTFILE} <<EOF +char VERSION[] = "${VERSION}"; +EOF +fi diff --git a/qemu/roms/seabios/scripts/checkrom.py b/qemu/roms/seabios/scripts/checkrom.py new file mode 100755 index 000000000..377277db5 --- /dev/null +++ b/qemu/roms/seabios/scripts/checkrom.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# Script to check a bios image and report info on it. +# +# Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys, struct +import layoutrom, buildrom + +from python23compat import as_bytes + +def subst(data, offset, new): + return data[:offset] + new + data[offset + len(new):] + +def checksum(data, start, size, csum): + sumbyte = buildrom.checksum(data[start:start+size]) + return subst(data, start+csum, sumbyte) + +def main(): + # Get args + objinfo, finalsize, rawfile, outfile = sys.argv[1:] + + # Read in symbols + objinfofile = open(objinfo, 'r') + symbols = layoutrom.parseObjDump(objinfofile, 'in')[1] + + # Read in raw file + f = open(rawfile, 'rb') + rawdata = f.read() + f.close() + datasize = len(rawdata) + finalsize = int(finalsize) * 1024 + if finalsize == 0: + finalsize = 64*1024 + if datasize > 64*1024: + finalsize = 128*1024 + if datasize > 128*1024: + finalsize = 256*1024 + if datasize > finalsize: + print("Error! ROM doesn't fit (%d > %d)" % (datasize, finalsize)) + print(" You have to either increate the size (CONFIG_ROM_SIZE)") + print(" or turn off some features (such as hardware support not") + print(" needed) to make it fit. Trying a more recent gcc version") + print(" might work too.") + sys.exit(1) + + # Sanity checks + start = symbols['code32flat_start'].offset + end = symbols['code32flat_end'].offset + expend = layoutrom.BUILD_BIOS_ADDR + layoutrom.BUILD_BIOS_SIZE + if end != expend: + print("Error! Code does not end at 0x%x (got 0x%x)" % ( + expend, end)) + sys.exit(1) + if datasize > finalsize: + print("Error! Code is too big (0x%x vs 0x%x)" % ( + datasize, finalsize)) + sys.exit(1) + expdatasize = end - start + if datasize != expdatasize: + print("Error! Unknown extra data (0x%x vs 0x%x)" % ( + datasize, expdatasize)) + sys.exit(1) + + # Fix up CSM Compatibility16 table + if 'csm_compat_table' in symbols and 'entry_csm' in symbols: + # Field offsets within EFI_COMPATIBILITY16_TABLE + ENTRY_FIELD_OFS = 14 # Compatibility16CallOffset (UINT16) + SIZE_FIELD_OFS = 5 # TableLength (UINT8) + CSUM_FIELD_OFS = 4 # TableChecksum (UINT8) + + tableofs = symbols['csm_compat_table'].offset - symbols['code32flat_start'].offset + entry_addr = symbols['entry_csm'].offset - layoutrom.BUILD_BIOS_ADDR + entry_addr = struct.pack('<H', entry_addr) + rawdata = subst(rawdata, tableofs+ENTRY_FIELD_OFS, entry_addr) + + tsfield = tableofs+SIZE_FIELD_OFS + tablesize = ord(rawdata[tsfield:tsfield+1]) + rawdata = checksum(rawdata, tableofs, tablesize, CSUM_FIELD_OFS) + + # Print statistics + runtimesize = end - symbols['code32init_end'].offset + print("Total size: %d Fixed: %d Free: %d (used %.1f%% of %dKiB rom)" % ( + datasize, runtimesize, finalsize - datasize + , (datasize / float(finalsize)) * 100.0 + , int(finalsize / 1024))) + + # Write final file + f = open(outfile, 'wb') + f.write((as_bytes("\0") * (finalsize - datasize)) + rawdata) + f.close() + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/checkstack.py b/qemu/roms/seabios/scripts/checkstack.py new file mode 100755 index 000000000..b49b6c8cc --- /dev/null +++ b/qemu/roms/seabios/scripts/checkstack.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python +# Script that tries to find how much stack space each function in an +# object is using. +# +# Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +# Usage: +# objdump -m i386 -M i8086 -M suffix -d out/rom16.o | scripts/checkstack.py + +import sys +import re + +# Functions that change stacks +STACKHOP = ['stack_hop', 'stack_hop_back'] +# List of functions we can assume are never called. +#IGNORE = ['panic', '__dprintf'] +IGNORE = ['panic'] + +OUTPUTDESC = """ +#funcname1[preamble_stack_usage,max_usage_with_callers]: +# insn_addr:called_function [usage_at_call_point+caller_preamble,total_usage] +# +#funcname2[p,m,max_usage_to_yield_point]: +# insn_addr:called_function [u+c,t,usage_to_yield_point] +""" + +# Find out maximum stack usage for a function +def calcmaxstack(funcs, funcaddr): + info = funcs[funcaddr] + # Find max of all nested calls. + maxusage = info[1] + maxyieldusage = doesyield = 0 + if info[3] is not None: + maxyieldusage = info[3] + doesyield = 1 + info[2] = maxusage + info[4] = info[3] + seenbefore = {} + totcalls = 0 + for insnaddr, calladdr, usage in info[6]: + callinfo = funcs.get(calladdr) + if callinfo is None: + continue + if callinfo[2] is None: + calcmaxstack(funcs, calladdr) + if callinfo[0] not in seenbefore: + seenbefore[callinfo[0]] = 1 + totcalls += 1 + callinfo[5] + funcnameroot = callinfo[0].split('.')[0] + if funcnameroot in IGNORE: + # This called function is ignored - don't contribute it to + # the max stack. + continue + if funcnameroot in STACKHOP: + if usage > maxusage: + maxusage = usage + if callinfo[4] is not None: + doesyield = 1 + if usage > maxyieldusage: + maxyieldusage = usage + continue + totusage = usage + callinfo[2] + if totusage > maxusage: + maxusage = totusage + if callinfo[4] is not None: + doesyield = 1 + totyieldusage = usage + callinfo[4] + if totyieldusage > maxyieldusage: + maxyieldusage = totyieldusage + info[2] = maxusage + if doesyield: + info[4] = maxyieldusage + info[5] = totcalls + +# Try to arrange output so that functions that call each other are +# near each other. +def orderfuncs(funcaddrs, availfuncs): + l = [(availfuncs[funcaddr][5], availfuncs[funcaddr][0], funcaddr) + for funcaddr in funcaddrs if funcaddr in availfuncs] + l.sort() + l.reverse() + out = [] + while l: + count, name, funcaddr = l.pop(0) + if funcaddr not in availfuncs: + continue + calladdrs = [calls[1] for calls in availfuncs[funcaddr][6]] + del availfuncs[funcaddr] + out = out + orderfuncs(calladdrs, availfuncs) + [funcaddr] + return out + +# Update function info with a found "yield" point. +def noteYield(info, stackusage): + prevyield = info[3] + if prevyield is None or prevyield < stackusage: + info[3] = stackusage + +# Update function info with a found "call" point. +def noteCall(info, subfuncs, insnaddr, calladdr, stackusage): + if (calladdr, stackusage) in subfuncs: + # Already noted a nearly identical call - ignore this one. + return + info[6].append((insnaddr, calladdr, stackusage)) + subfuncs[(calladdr, stackusage)] = 1 + +hex_s = r'[0-9a-f]+' +re_func = re.compile(r'^(?P<funcaddr>' + hex_s + r') <(?P<func>.*)>:$') +re_asm = re.compile( + r'^[ ]*(?P<insnaddr>' + hex_s + + r'):\t.*\t(addr32 )?(?P<insn>.+?)[ ]*((?P<calladdr>' + hex_s + + r') <(?P<ref>.*)>)?$') +re_usestack = re.compile( + r'^(push[f]?[lw])|(sub.* [$](?P<num>0x' + hex_s + r'),%esp)$') + +def calc(): + # funcs[funcaddr] = [funcname, basicstackusage, maxstackusage + # , yieldusage, maxyieldusage, totalcalls + # , [(insnaddr, calladdr, stackusage), ...]] + funcs = {-1: ['<indirect>', 0, 0, None, None, 0, []]} + cur = None + atstart = 0 + stackusage = 0 + + # Parse input lines + for line in sys.stdin.readlines(): + m = re_func.match(line) + if m is not None: + # Found function + funcaddr = int(m.group('funcaddr'), 16) + funcs[funcaddr] = cur = [m.group('func'), 0, None, None, None, 0, []] + stackusage = 0 + atstart = 1 + subfuncs = {} + continue + m = re_asm.match(line) + if m is not None: + insn = m.group('insn') + + im = re_usestack.match(insn) + if im is not None: + if insn.startswith('pushl') or insn.startswith('pushfl'): + stackusage += 4 + continue + elif insn.startswith('pushw') or insn.startswith('pushfw'): + stackusage += 2 + continue + stackusage += int(im.group('num'), 16) + + if atstart: + if '%esp' in insn or insn.startswith('leal'): + # Still part of initial header + continue + cur[1] = stackusage + atstart = 0 + + insnaddr = m.group('insnaddr') + calladdr = m.group('calladdr') + if calladdr is None: + if insn.startswith('lcallw'): + noteCall(cur, subfuncs, insnaddr, -1, stackusage + 4) + noteYield(cur, stackusage + 4) + elif insn.startswith('int'): + noteCall(cur, subfuncs, insnaddr, -1, stackusage + 6) + noteYield(cur, stackusage + 6) + elif insn.startswith('sti'): + noteYield(cur, stackusage) + else: + # misc instruction + continue + else: + # Jump or call insn + calladdr = int(calladdr, 16) + ref = m.group('ref') + if '+' in ref: + # Inter-function jump. + pass + elif insn.startswith('j'): + # Tail call + noteCall(cur, subfuncs, insnaddr, calladdr, 0) + elif insn.startswith('calll'): + noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 4) + elif insn.startswith('callw'): + noteCall(cur, subfuncs, insnaddr, calladdr, stackusage + 2) + else: + print("unknown call", ref) + noteCall(cur, subfuncs, insnaddr, calladdr, stackusage) + # Reset stack usage to preamble usage + stackusage = cur[1] + + #print("other", repr(line)) + + # Calculate maxstackusage + for funcaddr, info in funcs.items(): + if info[2] is not None: + continue + calcmaxstack(funcs, funcaddr) + + # Sort functions for output + funcaddrs = orderfuncs(funcs.keys(), funcs.copy()) + + # Show all functions + print(OUTPUTDESC) + for funcaddr in funcaddrs: + name, basicusage, maxusage, yieldusage, maxyieldusage, count, calls = \ + funcs[funcaddr] + if maxusage == 0 and maxyieldusage is None: + continue + yieldstr = "" + if maxyieldusage is not None: + yieldstr = ",%d" % maxyieldusage + print("\n%s[%d,%d%s]:" % (name, basicusage, maxusage, yieldstr)) + for insnaddr, calladdr, stackusage in calls: + callinfo = funcs.get(calladdr, ("<unknown>", 0, 0, 0, None)) + yieldstr = "" + if callinfo[4] is not None: + yieldstr = ",%d" % (stackusage + callinfo[4]) + print(" %04s:%-40s [%d+%d,%d%s]" % ( + insnaddr, callinfo[0], stackusage, callinfo[1] + , stackusage+callinfo[2], yieldstr)) + +def main(): + calc() + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/checksum.py b/qemu/roms/seabios/scripts/checksum.py new file mode 100755 index 000000000..773fa7aa9 --- /dev/null +++ b/qemu/roms/seabios/scripts/checksum.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# Script to report the checksum of a file. +# +# Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys + +def main(): + data = sys.stdin.read() + ords = map(ord, data) + print("sum=%x\n" % sum(ords)) + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/encodeint.py b/qemu/roms/seabios/scripts/encodeint.py new file mode 100755 index 000000000..0d34aee07 --- /dev/null +++ b/qemu/roms/seabios/scripts/encodeint.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# Encode an integer in little endian format in a file. +# +# Copyright (C) 2011 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys +import struct + +def main(): + filename = sys.argv[1] + value = int(sys.argv[2], 0) + + outval = struct.pack('<Q', value) + f = open(filename, 'wb') + f.write(outval) + f.close() + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/gen-offsets.sh b/qemu/roms/seabios/scripts/gen-offsets.sh new file mode 100755 index 000000000..73dede82b --- /dev/null +++ b/qemu/roms/seabios/scripts/gen-offsets.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Extract definitions from an assembler file. This is based on code +# from the Linux Kernel. +INFILE=$1 +OUTFILE=$2 +cat > "$OUTFILE" <<EOF +// This is an auto-generated file. DO NOT EDIT! +// Generated with "$0 $@" +#ifndef __ASM_OFFSETS_H +#define __ASM_OFFSETS_H +EOF +sed -ne "/^->/{s:->#\(.*\):/* \1 */:; \ + s:^->\([^ ]*\) [\$\#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \ + s:->::; p;}" < "$INFILE" >> "$OUTFILE" +cat >> "$OUTFILE" <<EOF +#endif // asm-offsets.h +EOF diff --git a/qemu/roms/seabios/scripts/kconfig/.gitignore b/qemu/roms/seabios/scripts/kconfig/.gitignore new file mode 100644 index 000000000..be603c4fe --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/.gitignore @@ -0,0 +1,22 @@ +# +# Generated files +# +config* +*.lex.c +*.tab.c +*.tab.h +zconf.hash.c +*.moc +gconf.glade.h +*.pot +*.mo + +# +# configuration programs +# +conf +mconf +nconf +qconf +gconf +kxgettext diff --git a/qemu/roms/seabios/scripts/kconfig/Makefile b/qemu/roms/seabios/scripts/kconfig/Makefile new file mode 100644 index 000000000..1c1293618 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/Makefile @@ -0,0 +1,331 @@ +# =========================================================================== +# Kernel configuration targets +# These targets are used from top-level makefile + +PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config \ + localmodconfig localyesconfig + +ifdef KBUILD_KCONFIG +Kconfig := $(KBUILD_KCONFIG) +else +Kconfig := Kconfig +endif + +# We need this, in case the user has it in its environment +unexport CONFIG_ + +xconfig: $(obj)/qconf + $< $(Kconfig) + +gconfig: $(obj)/gconf + $< $(Kconfig) + +menuconfig: $(obj)/mconf + $< $(Kconfig) + +config: $(obj)/conf + $< --oldaskconfig $(Kconfig) + +nconfig: $(obj)/nconf + $< $(Kconfig) + +oldconfig: $(obj)/conf + $< --$@ $(Kconfig) + +silentoldconfig: $(obj)/conf + @echo " Build Kconfig config file" + $(Q)mkdir -p include/config include/generated + $(Q)$< --$@ $(Kconfig) + +localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf + $(Q)mkdir -p include/config include/generated + $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config + $(Q)if [ -f .config ]; then \ + cmp -s .tmp.config .config || \ + (mv -f .config .config.old.1; \ + mv -f .tmp.config .config; \ + $(obj)/conf --silentoldconfig $(Kconfig); \ + mv -f .config.old.1 .config.old) \ + else \ + mv -f .tmp.config .config; \ + $(obj)/conf --silentoldconfig $(Kconfig); \ + fi + $(Q)rm -f .tmp.config + +# Create new linux.pot file +# Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files +update-po-config: $(obj)/kxgettext $(obj)/gconf.glade.h + $(Q)echo " GEN config.pot" + $(Q)xgettext --default-domain=linux \ + --add-comments --keyword=_ --keyword=N_ \ + --from-code=UTF-8 \ + --files-from=$(srctree)/scripts/kconfig/POTFILES.in \ + --directory=$(srctree) --directory=$(objtree) \ + --output $(obj)/config.pot + $(Q)sed -i s/CHARSET/UTF-8/ $(obj)/config.pot + $(Q)(for i in `ls $(srctree)/arch/*/Kconfig \ + $(srctree)/arch/*/um/Kconfig`; \ + do \ + echo " GEN $$i"; \ + $(obj)/kxgettext $$i \ + >> $(obj)/config.pot; \ + done ) + $(Q)echo " GEN linux.pot" + $(Q)msguniq --sort-by-file --to-code=UTF-8 $(obj)/config.pot \ + --output $(obj)/linux.pot + $(Q)rm -f $(obj)/config.pot + +PHONY += allnoconfig allyesconfig allmodconfig alldefconfig randconfig + +allnoconfig allyesconfig allmodconfig alldefconfig randconfig: $(obj)/conf + $< --$@ $(Kconfig) + +PHONY += listnewconfig olddefconfig oldnoconfig savedefconfig defconfig + +listnewconfig olddefconfig: $(obj)/conf + $< --$@ $(Kconfig) + +# oldnoconfig is an alias of olddefconfig, because people already are dependent +# on its behavior(sets new symbols to their default value but not 'n') with the +# counter-intuitive name. +oldnoconfig: $(obj)/conf + $< --olddefconfig $(Kconfig) + +savedefconfig: $(obj)/conf + $< --$@=defconfig $(Kconfig) + +defconfig: $(obj)/conf + @echo " Build default config" + $(Q)$< --defconfig=/dev/null $(Kconfig) + +%_defconfig: $(obj)/conf + $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) + +# Help text used by make help +help: + @echo ' config - Update current config utilising a line-oriented program' + @echo ' nconfig - Update current config utilising a ncurses menu based program' + @echo ' menuconfig - Update current config utilising a menu based program' + @echo ' xconfig - Update current config utilising a QT based front-end' + @echo ' gconfig - Update current config utilising a GTK based front-end' + @echo ' oldconfig - Update current config utilising a provided .config as base' + @echo ' localmodconfig - Update current config disabling modules not loaded' + @echo ' localyesconfig - Update current config converting local mods to core' + @echo ' silentoldconfig - Same as oldconfig, but quietly, additionally update deps' + @echo ' defconfig - New config with default from ARCH supplied defconfig' + @echo ' savedefconfig - Save current config as ./defconfig (minimal config)' + @echo ' allnoconfig - New config where all options are answered with no' + @echo ' allyesconfig - New config where all options are accepted with yes' + @echo ' allmodconfig - New config selecting modules when possible' + @echo ' alldefconfig - New config with all symbols set to default' + @echo ' randconfig - New config with random answer to all options' + @echo ' listnewconfig - List new options' + @echo ' olddefconfig - Same as silentoldconfig but sets new symbols to their default value' + +# lxdialog stuff +check-lxdialog := $(srctree)/$(src)/lxdialog/check-lxdialog.sh + +# Use recursively expanded variables so we do not call gcc unless +# we really need to do so. (Do not call gcc as part of make mrproper) +HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) \ + -DLOCALE + +# =========================================================================== +# Shared Makefile for the various kconfig executables: +# conf: Used for defconfig, oldconfig and related targets +# nconf: Used for the nconfig target. +# Utilizes ncurses +# mconf: Used for the menuconfig target +# Utilizes the lxdialog package +# qconf: Used for the xconfig target +# Based on QT which needs to be installed to compile it +# gconf: Used for the gconfig target +# Based on GTK which needs to be installed to compile it +# object files used by all kconfig flavours + +lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o +lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o + +conf-objs := conf.o zconf.tab.o +mconf-objs := mconf.o zconf.tab.o $(lxdialog) +nconf-objs := nconf.o zconf.tab.o nconf.gui.o +kxgettext-objs := kxgettext.o zconf.tab.o +qconf-cxxobjs := qconf.o +qconf-objs := zconf.tab.o +gconf-objs := gconf.o zconf.tab.o + +hostprogs-y := conf + +ifeq ($(MAKECMDGOALS),nconfig) + hostprogs-y += nconf +endif + +ifeq ($(MAKECMDGOALS),menuconfig) + hostprogs-y += mconf +endif + +ifeq ($(MAKECMDGOALS),update-po-config) + hostprogs-y += kxgettext +endif + +ifeq ($(MAKECMDGOALS),xconfig) + qconf-target := 1 +endif +ifeq ($(MAKECMDGOALS),gconfig) + gconf-target := 1 +endif + + +ifeq ($(qconf-target),1) + hostprogs-y += qconf +endif + +ifeq ($(gconf-target),1) + hostprogs-y += gconf +endif + +clean-files := qconf.moc .tmp_qtcheck .tmp_gtkcheck +clean-files += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h +clean-files += mconf qconf gconf nconf +clean-files += config.pot linux.pot + +# Check that we have the required ncurses stuff installed for lxdialog (menuconfig) +PHONY += $(obj)/dochecklxdialog +$(addprefix $(obj)/,$(lxdialog)): $(obj)/dochecklxdialog +$(obj)/dochecklxdialog: + $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOSTLOADLIBES_mconf) + +always := dochecklxdialog + +# Add environment specific flags +HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(srctree)/$(src)/check.sh $(HOSTCC) $(HOSTCFLAGS)) + +# generated files seem to need this to find local include files +HOSTCFLAGS_zconf.lex.o := -I$(src) +HOSTCFLAGS_zconf.tab.o := -I$(src) + +LEX_PREFIX_zconf := zconf +YACC_PREFIX_zconf := zconf + +HOSTLOADLIBES_qconf = $(KC_QT_LIBS) +HOSTCXXFLAGS_qconf.o = $(KC_QT_CFLAGS) + +HOSTLOADLIBES_gconf = `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0` +HOSTCFLAGS_gconf.o = `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \ + -Wno-missing-prototypes + +HOSTLOADLIBES_mconf = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC)) + +HOSTLOADLIBES_nconf = $(shell \ + pkg-config --libs menu panel ncurses 2>/dev/null \ + || echo "-lmenu -lpanel -lncurses" ) +$(obj)/qconf.o: $(obj)/.tmp_qtcheck + +ifeq ($(qconf-target),1) +$(obj)/.tmp_qtcheck: $(src)/Makefile +-include $(obj)/.tmp_qtcheck + +# QT needs some extra effort... +$(obj)/.tmp_qtcheck: + @set -e; echo " CHECK qt"; dir=""; pkg=""; \ + if ! pkg-config --exists QtCore 2> /dev/null; then \ + echo "* Unable to find the QT4 tool qmake. Trying to use QT3"; \ + pkg-config --exists qt 2> /dev/null && pkg=qt; \ + pkg-config --exists qt-mt 2> /dev/null && pkg=qt-mt; \ + if [ -n "$$pkg" ]; then \ + cflags="\$$(shell pkg-config $$pkg --cflags)"; \ + libs="\$$(shell pkg-config $$pkg --libs)"; \ + moc="\$$(shell pkg-config $$pkg --variable=prefix)/bin/moc"; \ + dir="$$(pkg-config $$pkg --variable=prefix)"; \ + else \ + for d in $$QTDIR /usr/share/qt* /usr/lib/qt*; do \ + if [ -f $$d/include/qconfig.h ]; then dir=$$d; break; fi; \ + done; \ + if [ -z "$$dir" ]; then \ + echo >&2 "*"; \ + echo >&2 "* Unable to find any QT installation. Please make sure that"; \ + echo >&2 "* the QT4 or QT3 development package is correctly installed and"; \ + echo >&2 "* either qmake can be found or install pkg-config or set"; \ + echo >&2 "* the QTDIR environment variable to the correct location."; \ + echo >&2 "*"; \ + false; \ + fi; \ + libpath=$$dir/lib; lib=qt; osdir=""; \ + $(HOSTCXX) -print-multi-os-directory > /dev/null 2>&1 && \ + osdir=x$$($(HOSTCXX) -print-multi-os-directory); \ + test -d $$libpath/$$osdir && libpath=$$libpath/$$osdir; \ + test -f $$libpath/libqt-mt.so && lib=qt-mt; \ + cflags="-I$$dir/include"; \ + libs="-L$$libpath -Wl,-rpath,$$libpath -l$$lib"; \ + moc="$$dir/bin/moc"; \ + fi; \ + if [ ! -x $$dir/bin/moc -a -x /usr/bin/moc ]; then \ + echo "*"; \ + echo "* Unable to find $$dir/bin/moc, using /usr/bin/moc instead."; \ + echo "*"; \ + moc="/usr/bin/moc"; \ + fi; \ + else \ + cflags="\$$(shell pkg-config QtCore QtGui Qt3Support --cflags)"; \ + libs="\$$(shell pkg-config QtCore QtGui Qt3Support --libs)"; \ + moc="\$$(shell pkg-config QtCore --variable=moc_location)"; \ + [ -n "$$moc" ] || moc="\$$(shell pkg-config QtCore --variable=prefix)/bin/moc"; \ + fi; \ + echo "KC_QT_CFLAGS=$$cflags" > $@; \ + echo "KC_QT_LIBS=$$libs" >> $@; \ + echo "KC_QT_MOC=$$moc" >> $@ +endif + +$(obj)/gconf.o: $(obj)/.tmp_gtkcheck + +ifeq ($(gconf-target),1) +-include $(obj)/.tmp_gtkcheck + +# GTK needs some extra effort, too... +$(obj)/.tmp_gtkcheck: + @if `pkg-config --exists gtk+-2.0 gmodule-2.0 libglade-2.0`; then \ + if `pkg-config --atleast-version=2.0.0 gtk+-2.0`; then \ + touch $@; \ + else \ + echo >&2 "*"; \ + echo >&2 "* GTK+ is present but version >= 2.0.0 is required."; \ + echo >&2 "*"; \ + false; \ + fi \ + else \ + echo >&2 "*"; \ + echo >&2 "* Unable to find the GTK+ installation. Please make sure that"; \ + echo >&2 "* the GTK+ 2.0 development package is correctly installed..."; \ + echo >&2 "* You need gtk+-2.0, glib-2.0 and libglade-2.0."; \ + echo >&2 "*"; \ + false; \ + fi +endif + +$(obj)/zconf.tab.o: $(obj)/zconf.lex.c $(obj)/zconf.hash.c + +$(obj)/qconf.o: $(obj)/qconf.moc + +quiet_cmd_moc = MOC $@ + cmd_moc = $(KC_QT_MOC) -i $< -o $@ + +$(obj)/%.moc: $(src)/%.h $(obj)/.tmp_qtcheck + $(call cmd,moc) + +# Extract gconf menu items for I18N support +$(obj)/gconf.glade.h: $(obj)/gconf.glade + $(Q)intltool-extract --type=gettext/glade --srcdir=$(srctree) \ + $(obj)/gconf.glade + +VPATH := $(srctree) + +$(obj)/%:: $(src)/%_shipped + $(Q)cat $< > $@ + +host-cobjs := $(sort $(foreach m,$(hostprogs-y),$($(m)-objs))) +host-cobjs := $(addprefix $(obj)/,$(host-cobjs)) +hostprogs-y := $(addprefix $(obj)/,$(hostprogs-y)) +$(host-cobjs) : $(obj)/%.o : $(src)/%.c + $(Q)$(HOSTCC) -I$(obj) -I$(srctree)/$(src) $(HOSTCFLAGS) $(HOSTCFLAGS_$(@F)) $(HOST_EXTRACFLAGS) -c -o $@ $< +$(hostprogs-y) : $(obj)/% : $(host-cobjs) + $(Q)$(HOSTCC) $(HOSTLDFLAGS) -o $@ $(addprefix $(obj)/,$($(@F)-objs)) $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F)) diff --git a/qemu/roms/seabios/scripts/kconfig/POTFILES.in b/qemu/roms/seabios/scripts/kconfig/POTFILES.in new file mode 100644 index 000000000..967457396 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/POTFILES.in @@ -0,0 +1,12 @@ +scripts/kconfig/lxdialog/checklist.c +scripts/kconfig/lxdialog/inputbox.c +scripts/kconfig/lxdialog/menubox.c +scripts/kconfig/lxdialog/textbox.c +scripts/kconfig/lxdialog/util.c +scripts/kconfig/lxdialog/yesno.c +scripts/kconfig/mconf.c +scripts/kconfig/conf.c +scripts/kconfig/confdata.c +scripts/kconfig/gconf.c +scripts/kconfig/gconf.glade.h +scripts/kconfig/qconf.cc diff --git a/qemu/roms/seabios/scripts/kconfig/check.sh b/qemu/roms/seabios/scripts/kconfig/check.sh new file mode 100755 index 000000000..55b79ba1b --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/check.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Needed for systems without gettext +$* -x c -o /dev/null - > /dev/null 2>&1 << EOF +#include <libintl.h> +int main() +{ + gettext(""); + return 0; +} +EOF +if [ ! "$?" -eq "0" ]; then + echo -DKBUILD_NO_NLS; +fi diff --git a/qemu/roms/seabios/scripts/kconfig/conf.c b/qemu/roms/seabios/scripts/kconfig/conf.c new file mode 100644 index 000000000..fef75fc75 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/conf.c @@ -0,0 +1,718 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <locale.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <getopt.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <errno.h> + +#include "lkc.h" + +static void conf(struct menu *menu); +static void check_conf(struct menu *menu); +static void xfgets(char *str, int size, FILE *in); + +enum input_mode { + oldaskconfig, + silentoldconfig, + oldconfig, + allnoconfig, + allyesconfig, + allmodconfig, + alldefconfig, + randconfig, + defconfig, + savedefconfig, + listnewconfig, + olddefconfig, +} input_mode = oldaskconfig; + +static int indent = 1; +static int tty_stdio; +static int valid_stdin = 1; +static int sync_kconfig; +static int conf_cnt; +static char line[128]; +static struct menu *rootEntry; + +static void print_help(struct menu *menu) +{ + struct gstr help = str_new(); + + menu_get_ext_help(menu, &help); + + printf("\n%s\n", str_get(&help)); + str_free(&help); +} + +static void strip(char *str) +{ + char *p = str; + int l; + + while ((isspace(*p))) + p++; + l = strlen(p); + if (p != str) + memmove(str, p, l + 1); + if (!l) + return; + p = str + l - 1; + while ((isspace(*p))) + *p-- = 0; +} + +static void check_stdin(void) +{ + if (!valid_stdin) { + printf(_("aborted!\n\n")); + printf(_("Console input/output is redirected. ")); + printf(_("Run 'make oldconfig' to update configuration.\n\n")); + exit(1); + } +} + +static int conf_askvalue(struct symbol *sym, const char *def) +{ + enum symbol_type type = sym_get_type(sym); + + if (!sym_has_value(sym)) + printf(_("(NEW) ")); + + line[0] = '\n'; + line[1] = 0; + + if (!sym_is_changable(sym)) { + printf("%s\n", def); + line[0] = '\n'; + line[1] = 0; + return 0; + } + + switch (input_mode) { + case oldconfig: + case silentoldconfig: + if (sym_has_value(sym)) { + printf("%s\n", def); + return 0; + } + check_stdin(); + /* fall through */ + case oldaskconfig: + fflush(stdout); + xfgets(line, 128, stdin); + if (!tty_stdio) + printf("\n"); + return 1; + default: + break; + } + + switch (type) { + case S_INT: + case S_HEX: + case S_STRING: + printf("%s\n", def); + return 1; + default: + ; + } + printf("%s", line); + return 1; +} + +static int conf_string(struct menu *menu) +{ + struct symbol *sym = menu->sym; + const char *def; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + printf("(%s) ", sym->name); + def = sym_get_string_value(sym); + if (sym_get_string_value(sym)) + printf("[%s] ", def); + if (!conf_askvalue(sym, def)) + return 0; + switch (line[0]) { + case '\n': + break; + case '?': + /* print help */ + if (line[1] == '\n') { + print_help(menu); + def = NULL; + break; + } + /* fall through */ + default: + line[strlen(line)-1] = 0; + def = line; + } + if (def && sym_set_string_value(sym, def)) + return 0; + } +} + +static int conf_sym(struct menu *menu) +{ + struct symbol *sym = menu->sym; + tristate oldval, newval; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + if (sym->name) + printf("(%s) ", sym->name); + putchar('['); + oldval = sym_get_tristate_value(sym); + switch (oldval) { + case no: + putchar('N'); + break; + case mod: + putchar('M'); + break; + case yes: + putchar('Y'); + break; + } + if (oldval != no && sym_tristate_within_range(sym, no)) + printf("/n"); + if (oldval != mod && sym_tristate_within_range(sym, mod)) + printf("/m"); + if (oldval != yes && sym_tristate_within_range(sym, yes)) + printf("/y"); + if (menu_has_help(menu)) + printf("/?"); + printf("] "); + if (!conf_askvalue(sym, sym_get_string_value(sym))) + return 0; + strip(line); + + switch (line[0]) { + case 'n': + case 'N': + newval = no; + if (!line[1] || !strcmp(&line[1], "o")) + break; + continue; + case 'm': + case 'M': + newval = mod; + if (!line[1]) + break; + continue; + case 'y': + case 'Y': + newval = yes; + if (!line[1] || !strcmp(&line[1], "es")) + break; + continue; + case 0: + newval = oldval; + break; + case '?': + goto help; + default: + continue; + } + if (sym_set_tristate_value(sym, newval)) + return 0; +help: + print_help(menu); + } +} + +static int conf_choice(struct menu *menu) +{ + struct symbol *sym, *def_sym; + struct menu *child; + bool is_new; + + sym = menu->sym; + is_new = !sym_has_value(sym); + if (sym_is_changable(sym)) { + conf_sym(menu); + sym_calc_value(sym); + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + return 0; + case yes: + break; + } + } else { + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + return 0; + case yes: + break; + } + } + + while (1) { + int cnt, def; + + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + def_sym = sym_get_choice_value(sym); + cnt = def = 0; + line[0] = 0; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (!child->sym) { + printf("%*c %s\n", indent, '*', _(menu_get_prompt(child))); + continue; + } + cnt++; + if (child->sym == def_sym) { + def = cnt; + printf("%*c", indent, '>'); + } else + printf("%*c", indent, ' '); + printf(" %d. %s", cnt, _(menu_get_prompt(child))); + if (child->sym->name) + printf(" (%s)", child->sym->name); + if (!sym_has_value(child->sym)) + printf(_(" (NEW)")); + printf("\n"); + } + printf(_("%*schoice"), indent - 1, ""); + if (cnt == 1) { + printf("[1]: 1\n"); + goto conf_childs; + } + printf("[1-%d", cnt); + if (menu_has_help(menu)) + printf("?"); + printf("]: "); + switch (input_mode) { + case oldconfig: + case silentoldconfig: + if (!is_new) { + cnt = def; + printf("%d\n", cnt); + break; + } + check_stdin(); + /* fall through */ + case oldaskconfig: + fflush(stdout); + xfgets(line, 128, stdin); + strip(line); + if (line[0] == '?') { + print_help(menu); + continue; + } + if (!line[0]) + cnt = def; + else if (isdigit(line[0])) + cnt = atoi(line); + else + continue; + break; + default: + break; + } + + conf_childs: + for (child = menu->list; child; child = child->next) { + if (!child->sym || !menu_is_visible(child)) + continue; + if (!--cnt) + break; + } + if (!child) + continue; + if (line[0] && line[strlen(line) - 1] == '?') { + print_help(child); + continue; + } + sym_set_choice_value(sym, child->sym); + for (child = child->list; child; child = child->next) { + indent += 2; + conf(child); + indent -= 2; + } + return 1; + } +} + +static void conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (prop) { + const char *prompt; + + switch (prop->type) { + case P_MENU: + if ((input_mode == silentoldconfig || + input_mode == listnewconfig || + input_mode == olddefconfig) && + rootEntry != menu) { + check_conf(menu); + return; + } + /* fall through */ + case P_COMMENT: + prompt = menu_get_prompt(menu); + if (prompt) + printf("%*c\n%*c %s\n%*c\n", + indent, '*', + indent, '*', _(prompt), + indent, '*'); + default: + ; + } + } + + if (!sym) + goto conf_childs; + + if (sym_is_choice(sym)) { + conf_choice(menu); + if (sym->curr.tri != mod) + return; + goto conf_childs; + } + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + conf_string(menu); + break; + default: + conf_sym(menu); + break; + } + +conf_childs: + if (sym) + indent += 2; + for (child = menu->list; child; child = child->next) + conf(child); + if (sym) + indent -= 2; +} + +static void check_conf(struct menu *menu) +{ + struct symbol *sym; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + if (sym && !sym_has_value(sym)) { + if (sym_is_changable(sym) || + (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) { + if (input_mode == listnewconfig) { + if (sym->name && !sym_is_choice_value(sym)) { + printf("%s%s\n", CONFIG_, sym->name); + } + } else if (input_mode != olddefconfig) { + if (!conf_cnt++) + printf(_("*\n* Restart config...\n*\n")); + rootEntry = menu_get_parent_menu(menu); + conf(rootEntry); + } + } + } + + for (child = menu->list; child; child = child->next) + check_conf(child); +} + +static struct option long_opts[] = { + {"oldaskconfig", no_argument, NULL, oldaskconfig}, + {"oldconfig", no_argument, NULL, oldconfig}, + {"silentoldconfig", no_argument, NULL, silentoldconfig}, + {"defconfig", optional_argument, NULL, defconfig}, + {"savedefconfig", required_argument, NULL, savedefconfig}, + {"allnoconfig", no_argument, NULL, allnoconfig}, + {"allyesconfig", no_argument, NULL, allyesconfig}, + {"allmodconfig", no_argument, NULL, allmodconfig}, + {"alldefconfig", no_argument, NULL, alldefconfig}, + {"randconfig", no_argument, NULL, randconfig}, + {"listnewconfig", no_argument, NULL, listnewconfig}, + {"olddefconfig", no_argument, NULL, olddefconfig}, + /* + * oldnoconfig is an alias of olddefconfig, because people already + * are dependent on its behavior(sets new symbols to their default + * value but not 'n') with the counter-intuitive name. + */ + {"oldnoconfig", no_argument, NULL, olddefconfig}, + {NULL, 0, NULL, 0} +}; + +static void conf_usage(const char *progname) +{ + + printf("Usage: %s [option] <kconfig-file>\n", progname); + printf("[option] is _one_ of the following:\n"); + printf(" --listnewconfig List new options\n"); + printf(" --oldaskconfig Start a new configuration using a line-oriented program\n"); + printf(" --oldconfig Update a configuration using a provided .config as base\n"); + printf(" --silentoldconfig Same as oldconfig, but quietly, additionally update deps\n"); + printf(" --olddefconfig Same as silentoldconfig but sets new symbols to their default value\n"); + printf(" --oldnoconfig An alias of olddefconfig\n"); + printf(" --defconfig <file> New config with default defined in <file>\n"); + printf(" --savedefconfig <file> Save the minimal current configuration to <file>\n"); + printf(" --allnoconfig New config where all options are answered with no\n"); + printf(" --allyesconfig New config where all options are answered with yes\n"); + printf(" --allmodconfig New config where all options are answered with mod\n"); + printf(" --alldefconfig New config with all symbols set to default\n"); + printf(" --randconfig New config with random answer to all options\n"); +} + +int main(int ac, char **av) +{ + const char *progname = av[0]; + int opt; + const char *name, *defconfig_file = NULL /* gcc uninit */; + struct stat tmpstat; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + tty_stdio = isatty(0) && isatty(1) && isatty(2); + + while ((opt = getopt_long(ac, av, "", long_opts, NULL)) != -1) { + input_mode = (enum input_mode)opt; + switch (opt) { + case silentoldconfig: + sync_kconfig = 1; + break; + case defconfig: + case savedefconfig: + defconfig_file = optarg; + break; + case randconfig: + { + struct timeval now; + unsigned int seed; + char *seed_env; + + /* + * Use microseconds derived seed, + * compensate for systems where it may be zero + */ + gettimeofday(&now, NULL); + seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1)); + + seed_env = getenv("KCONFIG_SEED"); + if( seed_env && *seed_env ) { + char *endp; + int tmp = (int)strtol(seed_env, &endp, 0); + if (*endp == '\0') { + seed = tmp; + } + } + fprintf( stderr, "KCONFIG_SEED=0x%X\n", seed ); + srand(seed); + break; + } + case oldaskconfig: + case oldconfig: + case allnoconfig: + case allyesconfig: + case allmodconfig: + case alldefconfig: + case listnewconfig: + case olddefconfig: + break; + case '?': + conf_usage(progname); + exit(1); + break; + } + } + if (ac == optind) { + printf(_("%s: Kconfig file missing\n"), av[0]); + conf_usage(progname); + exit(1); + } + name = av[optind]; + conf_parse(name); + //zconfdump(stdout); + if (sync_kconfig) { + name = conf_get_configname(); + if (stat(name, &tmpstat)) { + fprintf(stderr, _("***\n" + "*** Configuration file \"%s\" not found!\n" + "***\n" + "*** Please run some configurator (e.g. \"make oldconfig\" or\n" + "*** \"make menuconfig\" or \"make xconfig\").\n" + "***\n"), name); + exit(1); + } + } + + switch (input_mode) { + case defconfig: + if (!defconfig_file) + defconfig_file = conf_get_default_confname(); + if (conf_read(defconfig_file)) { + printf(_("***\n" + "*** Can't find default configuration \"%s\"!\n" + "***\n"), defconfig_file); + exit(1); + } + break; + case savedefconfig: + case silentoldconfig: + case oldaskconfig: + case oldconfig: + case listnewconfig: + case olddefconfig: + conf_read(NULL); + break; + case allnoconfig: + case allyesconfig: + case allmodconfig: + case alldefconfig: + case randconfig: + name = getenv("KCONFIG_ALLCONFIG"); + if (!name) + break; + if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) { + if (conf_read_simple(name, S_DEF_USER)) { + fprintf(stderr, + _("*** Can't read seed configuration \"%s\"!\n"), + name); + exit(1); + } + break; + } + switch (input_mode) { + case allnoconfig: name = "allno.config"; break; + case allyesconfig: name = "allyes.config"; break; + case allmodconfig: name = "allmod.config"; break; + case alldefconfig: name = "alldef.config"; break; + case randconfig: name = "allrandom.config"; break; + default: break; + } + if (conf_read_simple(name, S_DEF_USER) && + conf_read_simple("all.config", S_DEF_USER)) { + fprintf(stderr, + _("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"), + name); + exit(1); + } + break; + default: + break; + } + + if (sync_kconfig) { + if (conf_get_changed()) { + name = getenv("KCONFIG_NOSILENTUPDATE"); + if (name && *name) { + fprintf(stderr, + _("\n*** The configuration requires explicit update.\n\n")); + return 1; + } + } + valid_stdin = tty_stdio; + } + + switch (input_mode) { + case allnoconfig: + conf_set_all_new_symbols(def_no); + break; + case allyesconfig: + conf_set_all_new_symbols(def_yes); + break; + case allmodconfig: + conf_set_all_new_symbols(def_mod); + break; + case alldefconfig: + conf_set_all_new_symbols(def_default); + break; + case randconfig: + /* Really nothing to do in this loop */ + while (conf_set_all_new_symbols(def_random)) ; + break; + case defconfig: + conf_set_all_new_symbols(def_default); + break; + case savedefconfig: + break; + case oldaskconfig: + rootEntry = &rootmenu; + conf(&rootmenu); + input_mode = silentoldconfig; + /* fall through */ + case oldconfig: + case listnewconfig: + case olddefconfig: + case silentoldconfig: + /* Update until a loop caused no more changes */ + do { + conf_cnt = 0; + check_conf(&rootmenu); + } while (conf_cnt && + (input_mode != listnewconfig && + input_mode != olddefconfig)); + break; + } + + if (sync_kconfig) { + /* silentoldconfig is used during the build so we shall update autoconf. + * All other commands are only used to generate a config. + */ + if (conf_get_changed() && conf_write(NULL)) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + exit(1); + } + if (conf_write_autoconf()) { + fprintf(stderr, _("\n*** Error during update of the configuration.\n\n")); + return 1; + } + } else if (input_mode == savedefconfig) { + if (conf_write_defconfig(defconfig_file)) { + fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"), + defconfig_file); + return 1; + } + } else if (input_mode != listnewconfig) { + if (conf_write(NULL)) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + exit(1); + } + } + return 0; +} + +/* + * Helper function to facilitate fgets() by Jean Sacren. + */ +void xfgets(char *str, int size, FILE *in) +{ + if (fgets(str, size, in) == NULL) + fprintf(stderr, "\nError in reading or end of file.\n"); +} diff --git a/qemu/roms/seabios/scripts/kconfig/confdata.c b/qemu/roms/seabios/scripts/kconfig/confdata.c new file mode 100644 index 000000000..08e75599d --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/confdata.c @@ -0,0 +1,1250 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <sys/stat.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "lkc.h" + +static void conf_warning(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static void conf_message(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static const char *conf_filename; +static int conf_lineno, conf_warnings, conf_unsaved; + +const char conf_defname[] = "arch/$ARCH/defconfig"; + +static void conf_warning(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + conf_warnings++; +} + +static void conf_default_message_callback(const char *fmt, va_list ap) +{ + printf("#\n# "); + vprintf(fmt, ap); + printf("\n#\n"); +} + +static void (*conf_message_callback) (const char *fmt, va_list ap) = + conf_default_message_callback; +void conf_set_message_callback(void (*fn) (const char *fmt, va_list ap)) +{ + conf_message_callback = fn; +} + +static void conf_message(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (conf_message_callback) + conf_message_callback(fmt, ap); +} + +const char *conf_get_configname(void) +{ + char *name = getenv("KCONFIG_CONFIG"); + + return name ? name : ".config"; +} + +const char *conf_get_autoconfig_name(void) +{ + char *name = getenv("KCONFIG_AUTOCONFIG"); + + return name ? name : "include/config/auto.conf"; +} + +static char *conf_expand_value(const char *in) +{ + struct symbol *sym; + const char *src; + static char res_value[SYMBOL_MAXLENGTH]; + char *dst, name[SYMBOL_MAXLENGTH]; + + res_value[0] = 0; + dst = name; + while ((src = strchr(in, '$'))) { + strncat(res_value, in, src - in); + src++; + dst = name; + while (isalnum(*src) || *src == '_') + *dst++ = *src++; + *dst = 0; + sym = sym_lookup(name, 0); + sym_calc_value(sym); + strcat(res_value, sym_get_string_value(sym)); + in = src; + } + strcat(res_value, in); + + return res_value; +} + +char *conf_get_default_confname(void) +{ + struct stat buf; + static char fullname[PATH_MAX+1]; + char *env, *name; + + name = conf_expand_value(conf_defname); + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + if (!stat(fullname, &buf)) + return fullname; + } + return name; +} + +static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) +{ + char *p2; + + switch (sym->type) { + case S_TRISTATE: + if (p[0] == 'm') { + sym->def[def].tri = mod; + sym->flags |= def_flags; + break; + } + /* fall through */ + case S_BOOLEAN: + if (p[0] == 'y') { + sym->def[def].tri = yes; + sym->flags |= def_flags; + break; + } + if (p[0] == 'n') { + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + } + if (def != S_DEF_AUTO) + conf_warning("symbol value '%s' invalid for %s", + p, sym->name); + return 1; + case S_OTHER: + if (*p != '"') { + for (p2 = p; *p2 && !isspace(*p2); p2++) + ; + sym->type = S_STRING; + goto done; + } + /* fall through */ + case S_STRING: + if (*p++ != '"') + break; + for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { + if (*p2 == '"') { + *p2 = 0; + break; + } + memmove(p2, p2 + 1, strlen(p2)); + } + if (!p2) { + if (def != S_DEF_AUTO) + conf_warning("invalid string found"); + return 1; + } + /* fall through */ + case S_INT: + case S_HEX: + done: + if (sym_string_valid(sym, p)) { + sym->def[def].val = strdup(p); + sym->flags |= def_flags; + } else { + if (def != S_DEF_AUTO) + conf_warning("symbol value '%s' invalid for %s", + p, sym->name); + return 1; + } + break; + default: + ; + } + return 0; +} + +#define LINE_GROWTH 16 +static int add_byte(int c, char **lineptr, size_t slen, size_t *n) +{ + char *nline; + size_t new_size = slen + 1; + if (new_size > *n) { + new_size += LINE_GROWTH - 1; + new_size *= 2; + nline = realloc(*lineptr, new_size); + if (!nline) + return -1; + + *lineptr = nline; + *n = new_size; + } + + (*lineptr)[slen] = c; + + return 0; +} + +static ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream) +{ + char *line = *lineptr; + size_t slen = 0; + + for (;;) { + int c = getc(stream); + + switch (c) { + case '\n': + if (add_byte(c, &line, slen, n) < 0) + goto e_out; + slen++; + /* fall through */ + case EOF: + if (add_byte('\0', &line, slen, n) < 0) + goto e_out; + *lineptr = line; + if (slen == 0) + return -1; + return slen; + default: + if (add_byte(c, &line, slen, n) < 0) + goto e_out; + slen++; + } + } + +e_out: + line[slen-1] = '\0'; + *lineptr = line; + return -1; +} + +int conf_read_simple(const char *name, int def) +{ + FILE *in = NULL; + char *line = NULL; + size_t line_asize = 0; + char *p, *p2; + struct symbol *sym; + int i, def_flags; + + if (name) { + in = zconf_fopen(name); + } else { + struct property *prop; + + name = conf_get_configname(); + in = zconf_fopen(name); + if (in) + goto load; + sym_add_change_count(1); + if (!sym_defconfig_list) { + if (modules_sym) + sym_calc_value(modules_sym); + return 1; + } + + for_all_defaults(sym_defconfig_list, prop) { + if (expr_calc_value(prop->visible.expr) == no || + prop->expr->type != E_SYMBOL) + continue; + name = conf_expand_value(prop->expr->left.sym->name); + in = zconf_fopen(name); + if (in) { + conf_message(_("using defaults found in %s"), + name); + goto load; + } + } + } + if (!in) + return 1; + +load: + conf_filename = name; + conf_lineno = 0; + conf_warnings = 0; + conf_unsaved = 0; + + def_flags = SYMBOL_DEF << def; + for_all_symbols(i, sym) { + sym->flags |= SYMBOL_CHANGED; + sym->flags &= ~(def_flags|SYMBOL_VALID); + if (sym_is_choice(sym)) + sym->flags |= def_flags; + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + if (sym->def[def].val) + free(sym->def[def].val); + /* fall through */ + default: + sym->def[def].val = NULL; + sym->def[def].tri = no; + } + } + + while (compat_getline(&line, &line_asize, in) != -1) { + conf_lineno++; + sym = NULL; + if (line[0] == '#') { + if (memcmp(line + 2, CONFIG_, strlen(CONFIG_))) + continue; + p = strchr(line + 2 + strlen(CONFIG_), ' '); + if (!p) + continue; + *p++ = 0; + if (strncmp(p, "is not set", 10)) + continue; + if (def == S_DEF_USER) { + sym = sym_find(line + 2 + strlen(CONFIG_)); + if (!sym) { + sym_add_change_count(1); + goto setsym; + } + } else { + sym = sym_lookup(line + 2 + strlen(CONFIG_), 0); + if (sym->type == S_UNKNOWN) + sym->type = S_BOOLEAN; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + default: + ; + } + } else if (memcmp(line, CONFIG_, strlen(CONFIG_)) == 0) { + p = strchr(line + strlen(CONFIG_), '='); + if (!p) + continue; + *p++ = 0; + p2 = strchr(p, '\n'); + if (p2) { + *p2-- = 0; + if (*p2 == '\r') + *p2 = 0; + } + if (def == S_DEF_USER) { + sym = sym_find(line + strlen(CONFIG_)); + if (!sym) { + sym_add_change_count(1); + goto setsym; + } + } else { + sym = sym_lookup(line + strlen(CONFIG_), 0); + if (sym->type == S_UNKNOWN) + sym->type = S_OTHER; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + if (conf_set_sym_val(sym, def, def_flags, p)) + continue; + } else { + if (line[0] != '\r' && line[0] != '\n') + conf_warning("unexpected data"); + continue; + } +setsym: + if (sym && sym_is_choice_value(sym)) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + switch (sym->def[def].tri) { + case no: + break; + case mod: + if (cs->def[def].tri == yes) { + conf_warning("%s creates inconsistent choice state", sym->name); + cs->flags &= ~def_flags; + } + break; + case yes: + if (cs->def[def].tri != no) + conf_warning("override: %s changes choice state", sym->name); + cs->def[def].val = sym; + break; + } + cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); + } + } + free(line); + fclose(in); + + if (modules_sym) + sym_calc_value(modules_sym); + return 0; +} + +int conf_read(const char *name) +{ + struct symbol *sym; + int i; + + sym_set_change_count(0); + + if (conf_read_simple(name, S_DEF_USER)) + return 1; + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO)) + continue; + if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) { + /* check that calculated value agrees with saved value */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym)) + break; + if (!sym_is_choice(sym)) + continue; + /* fall through */ + default: + if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val)) + continue; + break; + } + } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) + /* no previous value and not saved */ + continue; + conf_unsaved++; + /* maybe print value in verbose mode... */ + } + + for_all_symbols(i, sym) { + if (sym_has_value(sym) && !sym_is_choice_value(sym)) { + /* Reset values of generates values, so they'll appear + * as new, if they should become visible, but that + * doesn't quite work if the Kconfig and the saved + * configuration disagree. + */ + if (sym->visible == no && !conf_unsaved) + sym->flags &= ~SYMBOL_DEF_USER; + switch (sym->type) { + case S_STRING: + case S_INT: + case S_HEX: + /* Reset a string value if it's out of range */ + if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) + break; + sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER); + conf_unsaved++; + break; + default: + break; + } + } + } + + sym_add_change_count(conf_warnings || conf_unsaved); + + return 0; +} + +/* + * Kconfig configuration printer + * + * This printer is used when generating the resulting configuration after + * kconfig invocation and `defconfig' files. Unset symbol might be omitted by + * passing a non-NULL argument to the printer. + * + */ +static void +kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (*value == 'n') { + bool skip_unset = (arg != NULL); + + if (!skip_unset) + fprintf(fp, "# %s%s is not set\n", + CONFIG_, sym->name); + return; + } + break; + default: + break; + } + + fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value); +} + +static void +kconfig_print_comment(FILE *fp, const char *value, void *arg) +{ + const char *p = value; + size_t l; + + for (;;) { + l = strcspn(p, "\n"); + fprintf(fp, "#"); + if (l) { + fprintf(fp, " "); + xfwrite(p, l, 1, fp); + p += l; + } + fprintf(fp, "\n"); + if (*p++ == '\0') + break; + } +} + +static struct conf_printer kconfig_printer_cb = +{ + .print_symbol = kconfig_print_symbol, + .print_comment = kconfig_print_comment, +}; + +/* + * Header printer + * + * This printer is used when generating the `include/generated/autoconf.h' file. + */ +static void +header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: { + const char *suffix = ""; + + switch (*value) { + case 'n': + fprintf(fp, "#define %s%s%s 0\n", + CONFIG_, sym->name, suffix); + break; + case 'm': + suffix = "_MODULE"; + /* fall through */ + default: + fprintf(fp, "#define %s%s%s 1\n", + CONFIG_, sym->name, suffix); + } + break; + } + case S_HEX: { + const char *prefix = ""; + + if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) + prefix = "0x"; + if (!value[0]) + prefix = "0"; + fprintf(fp, "#define %s%s %s%s\n", + CONFIG_, sym->name, prefix, value); + break; + } + case S_STRING: + case S_INT: + fprintf(fp, "#define %s%s %s\n", + CONFIG_, sym->name, value); + break; + default: + break; + } + +} + +static void +header_print_comment(FILE *fp, const char *value, void *arg) +{ + const char *p = value; + size_t l; + + fprintf(fp, "/*\n"); + for (;;) { + l = strcspn(p, "\n"); + fprintf(fp, " *"); + if (l) { + fprintf(fp, " "); + xfwrite(p, l, 1, fp); + p += l; + } + fprintf(fp, "\n"); + if (*p++ == '\0') + break; + } + fprintf(fp, " */\n"); +} + +static struct conf_printer header_printer_cb = +{ + .print_symbol = header_print_symbol, + .print_comment = header_print_comment, +}; + +/* + * Tristate printer + * + * This printer is used when generating the `include/config/tristate.conf' file. + */ +static void +tristate_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + if (sym->type == S_TRISTATE && *value != 'n') + fprintf(fp, "%s%s=%c\n", CONFIG_, sym->name, (char)toupper(*value)); +} + +static struct conf_printer tristate_printer_cb = +{ + .print_symbol = tristate_print_symbol, + .print_comment = kconfig_print_comment, +}; + +static void conf_write_symbol(FILE *fp, struct symbol *sym, + struct conf_printer *printer, void *printer_arg) +{ + const char *str; + + switch (sym->type) { + case S_OTHER: + case S_UNKNOWN: + break; + case S_STRING: + str = sym_get_string_value(sym); + str = sym_escape_string_value(str); + printer->print_symbol(fp, sym, str, printer_arg); + free((void *)str); + break; + default: + str = sym_get_string_value(sym); + printer->print_symbol(fp, sym, str, printer_arg); + } +} + +static void +conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), + "\n" + "Automatically generated file; DO NOT EDIT.\n" + "%s\n", + rootmenu.prompt->text); + + printer->print_comment(fp, buf, printer_arg); +} + +/* + * Write out a minimal config. + * All values that has default values are skipped as this is redundant. + */ +int conf_write_defconfig(const char *filename) +{ + struct symbol *sym; + struct menu *menu; + FILE *out; + + out = fopen(filename, "w"); + if (!out) + return 1; + + sym_clear_all_valid(); + + /* Traverse all menus to find all relevant symbols */ + menu = rootmenu.list; + + while (menu != NULL) + { + sym = menu->sym; + if (sym == NULL) { + if (!menu_is_visible(menu)) + goto next_menu; + } else if (!sym_is_choice(sym)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next_menu; + sym->flags &= ~SYMBOL_WRITE; + /* If we cannot change the symbol - skip */ + if (!sym_is_changable(sym)) + goto next_menu; + /* If symbol equals to default value - skip */ + if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0) + goto next_menu; + + /* + * If symbol is a choice value and equals to the + * default for a choice - skip. + * But only if value is bool and equal to "y" and + * choice is not "optional". + * (If choice is "optional" then all values can be "n") + */ + if (sym_is_choice_value(sym)) { + struct symbol *cs; + struct symbol *ds; + + cs = prop_get_symbol(sym_get_choice_prop(sym)); + ds = sym_choice_default(cs); + if (!sym_is_optional(cs) && sym == ds) { + if ((sym->type == S_BOOLEAN) && + sym_get_tristate_value(sym) == yes) + goto next_menu; + } + } + conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + } +next_menu: + if (menu->list != NULL) { + menu = menu->list; + } + else if (menu->next != NULL) { + menu = menu->next; + } else { + while ((menu = menu->parent)) { + if (menu->next != NULL) { + menu = menu->next; + break; + } + } + } + } + fclose(out); + return 0; +} + +int conf_write(const char *name) +{ + FILE *out; + struct symbol *sym; + struct menu *menu; + const char *basename; + const char *str; + char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1]; + char *env; + + dirname[0] = 0; + if (name && name[0]) { + struct stat st; + char *slash; + + if (!stat(name, &st) && S_ISDIR(st.st_mode)) { + strcpy(dirname, name); + strcat(dirname, "/"); + basename = conf_get_configname(); + } else if ((slash = strrchr(name, '/'))) { + int size = slash - name + 1; + memcpy(dirname, name, size); + dirname[size] = 0; + if (slash[1]) + basename = slash + 1; + else + basename = conf_get_configname(); + } else + basename = name; + } else + basename = conf_get_configname(); + + sprintf(newname, "%s%s", dirname, basename); + env = getenv("KCONFIG_OVERWRITECONFIG"); + if (!env || !*env) { + sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid()); + out = fopen(tmpname, "w"); + } else { + *tmpname = 0; + out = fopen(newname, "w"); + } + if (!out) + return 1; + + conf_write_heading(out, &kconfig_printer_cb, NULL); + + if (!conf_get_changed()) + sym_clear_all_valid(); + + menu = rootmenu.list; + while (menu) { + sym = menu->sym; + if (!sym) { + if (!menu_is_visible(menu)) + goto next; + str = menu_get_prompt(menu); + fprintf(out, "\n" + "#\n" + "# %s\n" + "#\n", str); + } else if (!(sym->flags & SYMBOL_CHOICE)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next; + sym->flags &= ~SYMBOL_WRITE; + + conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + } + +next: + if (menu->list) { + menu = menu->list; + continue; + } + if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->next) { + menu = menu->next; + break; + } + } + } + fclose(out); + + if (*tmpname) { + strcat(dirname, basename); + strcat(dirname, ".old"); + rename(newname, dirname); + if (rename(tmpname, newname)) + return 1; + } + + conf_message(_("configuration written to %s"), newname); + + sym_set_change_count(0); + + return 0; +} + +static int conf_split_config(void) +{ + const char *name; + char path[PATH_MAX+1]; + char *s, *d, c; + struct symbol *sym; + struct stat sb; + int res, i, fd; + + name = conf_get_autoconfig_name(); + conf_read_simple(name, S_DEF_AUTO); + + if (chdir("include/config")) + return 1; + + res = 0; + for_all_symbols(i, sym) { + sym_calc_value(sym); + if ((sym->flags & SYMBOL_AUTO) || !sym->name) + continue; + if (sym->flags & SYMBOL_WRITE) { + if (sym->flags & SYMBOL_DEF_AUTO) { + /* + * symbol has old and new value, + * so compare them... + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == + sym->def[S_DEF_AUTO].tri) + continue; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (!strcmp(sym_get_string_value(sym), + sym->def[S_DEF_AUTO].val)) + continue; + break; + default: + break; + } + } else { + /* + * If there is no old value, only 'no' (unset) + * is allowed as new value. + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == no) + continue; + break; + default: + break; + } + } + } else if (!(sym->flags & SYMBOL_DEF_AUTO)) + /* There is neither an old nor a new value. */ + continue; + /* else + * There is an old value, but no new value ('no' (unset) + * isn't saved in auto.conf, so the old value is always + * different from 'no'). + */ + + /* Replace all '_' and append ".h" */ + s = sym->name; + d = path; + while ((c = *s++)) { + c = tolower(c); + *d++ = (c == '_') ? '/' : c; + } + strcpy(d, ".h"); + + /* Assume directory path already exists. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + if (errno != ENOENT) { + res = 1; + break; + } + /* + * Create directory components, + * unless they exist already. + */ + d = path; + while ((d = strchr(d, '/'))) { + *d = 0; + if (stat(path, &sb) && mkdir(path, 0755)) { + res = 1; + goto out; + } + *d++ = '/'; + } + /* Try it again. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + res = 1; + break; + } + } + close(fd); + } +out: + if (chdir("../..")) + return 1; + + return res; +} + +int conf_write_autoconf(void) +{ + struct symbol *sym; + const char *name; + FILE *out, *tristate, *out_h; + int i; + + sym_clear_all_valid(); + + file_write_dep("include/config/auto.conf.cmd"); + + if (conf_split_config()) + return 1; + + out = fopen(".tmpconfig", "w"); + if (!out) + return 1; + + tristate = fopen(".tmpconfig_tristate", "w"); + if (!tristate) { + fclose(out); + return 1; + } + + out_h = fopen(".tmpconfig.h", "w"); + if (!out_h) { + fclose(out); + fclose(tristate); + return 1; + } + + conf_write_heading(out, &kconfig_printer_cb, NULL); + + conf_write_heading(tristate, &tristate_printer_cb, NULL); + + conf_write_heading(out_h, &header_printer_cb, NULL); + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (!sym->name) + continue; + if (!(sym->flags & SYMBOL_WRITE)) { + conf_write_symbol(out_h, sym, &header_printer_cb, NULL); + continue; + } + + /* write symbol to auto.conf, tristate and header files */ + conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); + + conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1); + + conf_write_symbol(out_h, sym, &header_printer_cb, NULL); + } + fclose(out); + fclose(tristate); + fclose(out_h); + + name = getenv("KCONFIG_AUTOHEADER"); + if (!name) + name = "include/generated/autoconf.h"; + if (rename(".tmpconfig.h", name)) + return 1; + name = getenv("KCONFIG_TRISTATE"); + if (!name) + name = "include/config/tristate.conf"; + if (rename(".tmpconfig_tristate", name)) + return 1; + name = conf_get_autoconfig_name(); + /* + * This must be the last step, kbuild has a dependency on auto.conf + * and this marks the successful completion of the previous steps. + */ + if (rename(".tmpconfig", name)) + return 1; + + return 0; +} + +static int sym_change_count; +static void (*conf_changed_callback)(void); + +void sym_set_change_count(int count) +{ + int _sym_change_count = sym_change_count; + sym_change_count = count; + if (conf_changed_callback && + (bool)_sym_change_count != (bool)count) + conf_changed_callback(); +} + +void sym_add_change_count(int count) +{ + sym_set_change_count(count + sym_change_count); +} + +bool conf_get_changed(void) +{ + return sym_change_count; +} + +void conf_set_changed_callback(void (*fn)(void)) +{ + conf_changed_callback = fn; +} + +static bool randomize_choice_values(struct symbol *csym) +{ + struct property *prop; + struct symbol *sym; + struct expr *e; + int cnt, def; + + /* + * If choice is mod then we may have more items selected + * and if no then no-one. + * In both cases stop. + */ + if (csym->curr.tri != yes) + return false; + + prop = sym_get_choice_prop(csym); + + /* count entries in choice block */ + cnt = 0; + expr_list_for_each_sym(prop->expr, e, sym) + cnt++; + + /* + * find a random value and set it to yes, + * set the rest to no so we have only one set + */ + def = (rand() % cnt); + + cnt = 0; + expr_list_for_each_sym(prop->expr, e, sym) { + if (def == cnt++) { + sym->def[S_DEF_USER].tri = yes; + csym->def[S_DEF_USER].val = sym; + } + else { + sym->def[S_DEF_USER].tri = no; + } + sym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + sym->flags &= ~SYMBOL_VALID; + } + csym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + csym->flags &= ~(SYMBOL_VALID); + + return true; +} + +void set_all_choice_values(struct symbol *csym) +{ + struct property *prop; + struct symbol *sym; + struct expr *e; + + prop = sym_get_choice_prop(csym); + + /* + * Set all non-assinged choice values to no + */ + expr_list_for_each_sym(prop->expr, e, sym) { + if (!sym_has_value(sym)) + sym->def[S_DEF_USER].tri = no; + } + csym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); +} + +bool conf_set_all_new_symbols(enum conf_def_mode mode) +{ + struct symbol *sym, *csym; + int i, cnt, pby, pty, ptm; /* pby: probability of boolean = y + * pty: probability of tristate = y + * ptm: probability of tristate = m + */ + + pby = 50; pty = ptm = 33; /* can't go as the default in switch-case + * below, otherwise gcc whines about + * -Wmaybe-uninitialized */ + if (mode == def_random) { + int n, p[3]; + char *env = getenv("KCONFIG_PROBABILITY"); + n = 0; + while( env && *env ) { + char *endp; + int tmp = strtol( env, &endp, 10 ); + if( tmp >= 0 && tmp <= 100 ) { + p[n++] = tmp; + } else { + errno = ERANGE; + perror( "KCONFIG_PROBABILITY" ); + exit( 1 ); + } + env = (*endp == ':') ? endp+1 : endp; + if( n >=3 ) { + break; + } + } + switch( n ) { + case 1: + pby = p[0]; ptm = pby/2; pty = pby-ptm; + break; + case 2: + pty = p[0]; ptm = p[1]; pby = pty + ptm; + break; + case 3: + pby = p[0]; pty = p[1]; ptm = p[2]; + break; + } + + if( pty+ptm > 100 ) { + errno = ERANGE; + perror( "KCONFIG_PROBABILITY" ); + exit( 1 ); + } + } + bool has_changed = false; + + for_all_symbols(i, sym) { + if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID)) + continue; + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + has_changed = true; + switch (mode) { + case def_yes: + sym->def[S_DEF_USER].tri = yes; + break; + case def_mod: + sym->def[S_DEF_USER].tri = mod; + break; + case def_no: + if (sym->flags & SYMBOL_ALLNOCONFIG_Y) + sym->def[S_DEF_USER].tri = yes; + else + sym->def[S_DEF_USER].tri = no; + break; + case def_random: + sym->def[S_DEF_USER].tri = no; + cnt = rand() % 100; + if (sym->type == S_TRISTATE) { + if (cnt < pty) + sym->def[S_DEF_USER].tri = yes; + else if (cnt < (pty+ptm)) + sym->def[S_DEF_USER].tri = mod; + } else if (cnt < pby) + sym->def[S_DEF_USER].tri = yes; + break; + default: + continue; + } + if (!(sym_is_choice(sym) && mode == def_random)) + sym->flags |= SYMBOL_DEF_USER; + break; + default: + break; + } + + } + + sym_clear_all_valid(); + + /* + * We have different type of choice blocks. + * If curr.tri equals to mod then we can select several + * choice symbols in one block. + * In this case we do nothing. + * If curr.tri equals yes then only one symbol can be + * selected in a choice block and we set it to yes, + * and the rest to no. + */ + if (mode != def_random) { + for_all_symbols(i, csym) { + if ((sym_is_choice(csym) && !sym_has_value(csym)) || + sym_is_choice_value(csym)) + csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES; + } + } + + for_all_symbols(i, csym) { + if (sym_has_value(csym) || !sym_is_choice(csym)) + continue; + + sym_calc_value(csym); + if (mode == def_random) + has_changed = randomize_choice_values(csym); + else { + set_all_choice_values(csym); + has_changed = true; + } + } + + return has_changed; +} diff --git a/qemu/roms/seabios/scripts/kconfig/expr.c b/qemu/roms/seabios/scripts/kconfig/expr.c new file mode 100644 index 000000000..d6626521f --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/expr.c @@ -0,0 +1,1168 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lkc.h" + +#define DEBUG_EXPR 0 + +struct expr *expr_alloc_symbol(struct symbol *sym) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = E_SYMBOL; + e->left.sym = sym; + return e; +} + +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = type; + e->left.expr = ce; + return e; +} + +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = type; + e->left.expr = e1; + e->right.expr = e2; + return e; +} + +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = type; + e->left.sym = s1; + e->right.sym = s2; + return e; +} + +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; +} + +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; +} + +struct expr *expr_copy(const struct expr *org) +{ + struct expr *e; + + if (!org) + return NULL; + + e = xmalloc(sizeof(*org)); + memcpy(e, org, sizeof(*org)); + switch (org->type) { + case E_SYMBOL: + e->left = org->left; + break; + case E_NOT: + e->left.expr = expr_copy(org->left.expr); + break; + case E_EQUAL: + case E_UNEQUAL: + e->left.sym = org->left.sym; + e->right.sym = org->right.sym; + break; + case E_AND: + case E_OR: + case E_LIST: + e->left.expr = expr_copy(org->left.expr); + e->right.expr = expr_copy(org->right.expr); + break; + default: + printf("can't copy type %d\n", e->type); + free(e); + e = NULL; + break; + } + + return e; +} + +void expr_free(struct expr *e) +{ + if (!e) + return; + + switch (e->type) { + case E_SYMBOL: + break; + case E_NOT: + expr_free(e->left.expr); + return; + case E_EQUAL: + case E_UNEQUAL: + break; + case E_OR: + case E_AND: + expr_free(e->left.expr); + expr_free(e->right.expr); + break; + default: + printf("how to free type %d?\n", e->type); + break; + } + free(e); +} + +static int trans_count; + +#define e1 (*ep1) +#define e2 (*ep2) + +static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ + if (e1->type == type) { + __expr_eliminate_eq(type, &e1->left.expr, &e2); + __expr_eliminate_eq(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + __expr_eliminate_eq(type, &e1, &e2->left.expr); + __expr_eliminate_eq(type, &e1, &e2->right.expr); + return; + } + if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym && + (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) + return; + if (!expr_eq(e1, e2)) + return; + trans_count++; + expr_free(e1); expr_free(e2); + switch (type) { + case E_OR: + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + break; + case E_AND: + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + break; + default: + ; + } +} + +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) +{ + if (!e1 || !e2) + return; + switch (e1->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e1->type, ep1, ep2); + default: + ; + } + if (e1->type != e2->type) switch (e2->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e2->type, ep1, ep2); + default: + ; + } + e1 = expr_eliminate_yn(e1); + e2 = expr_eliminate_yn(e2); +} + +#undef e1 +#undef e2 + +int expr_eq(struct expr *e1, struct expr *e2) +{ + int res, old_count; + + if (e1->type != e2->type) + return 0; + switch (e1->type) { + case E_EQUAL: + case E_UNEQUAL: + return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; + case E_SYMBOL: + return e1->left.sym == e2->left.sym; + case E_NOT: + return expr_eq(e1->left.expr, e2->left.expr); + case E_AND: + case E_OR: + e1 = expr_copy(e1); + e2 = expr_copy(e2); + old_count = trans_count; + expr_eliminate_eq(&e1, &e2); + res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym); + expr_free(e1); + expr_free(e2); + trans_count = old_count; + return res; + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + + if (DEBUG_EXPR) { + expr_fprint(e1, stdout); + printf(" = "); + expr_fprint(e2, stdout); + printf(" ?\n"); + } + + return 0; +} + +struct expr *expr_eliminate_yn(struct expr *e) +{ + struct expr *tmp; + + if (e) switch (e->type) { + case E_AND: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } + } + break; + case E_OR: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + break; + default: + ; + } + return e; +} + +/* + * bool FOO!=n => FOO + */ +struct expr *expr_trans_bool(struct expr *e) +{ + if (!e) + return NULL; + switch (e->type) { + case E_AND: + case E_OR: + case E_NOT: + e->left.expr = expr_trans_bool(e->left.expr); + e->right.expr = expr_trans_bool(e->right.expr); + break; + case E_UNEQUAL: + // FOO!=n -> FOO + if (e->left.sym->type == S_TRISTATE) { + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + } + } + break; + default: + ; + } + return e; +} + +/* + * e1 || e2 -> ? + */ +static struct expr *expr_join_or(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='m') -> (a!='n') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='n') -> (a!='m') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { + // (a='m') || (a='n') -> (a!='y') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); + } + } + if (sym1->type == S_BOOLEAN && sym1 == sym2) { + if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || + (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) + return expr_alloc_symbol(&symbol_yes); + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") || ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static struct expr *expr_join_and(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) + // (a) && (a='y') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) + // (a) && (a!='n') -> (a) + return expr_alloc_symbol(sym1); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) + // (a) && (a!='m') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e1->right.sym; + if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e2->right.sym; + if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='m') -> (a='n') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) + // (a!='m') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || + (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) + return NULL; + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") && ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp; + + if (e1->type == type) { + expr_eliminate_dups1(type, &e1->left.expr, &e2); + expr_eliminate_dups1(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups1(type, &e1, &e2->left.expr); + expr_eliminate_dups1(type, &e1, &e2->right.expr); + return; + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e1->type, &e1, &e1); + default: + ; + } + + switch (type) { + case E_OR: + tmp = expr_join_or(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_no); + e2 = tmp; + trans_count++; + } + break; + case E_AND: + tmp = expr_join_and(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_yes); + e2 = tmp; + trans_count++; + } + break; + default: + ; + } +#undef e1 +#undef e2 +} + +static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp, *tmp1, *tmp2; + + if (e1->type == type) { + expr_eliminate_dups2(type, &e1->left.expr, &e2); + expr_eliminate_dups2(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups2(type, &e1, &e2->left.expr); + expr_eliminate_dups2(type, &e1, &e2->right.expr); + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO || BAR) && (!FOO && !BAR) -> n + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_and(&tmp1, &tmp2); + if (expr_is_yes(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_no); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + case E_AND: + expr_eliminate_dups2(e1->type, &e1, &e1); + // (FOO && BAR) || (!FOO || !BAR) -> y + tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); + tmp2 = expr_copy(e2); + tmp = expr_extract_eq_or(&tmp1, &tmp2); + if (expr_is_no(tmp1)) { + expr_free(e1); + e1 = expr_alloc_symbol(&symbol_yes); + trans_count++; + } + expr_free(tmp2); + expr_free(tmp1); + expr_free(tmp); + break; + default: + ; + } +#undef e1 +#undef e2 +} + +struct expr *expr_eliminate_dups(struct expr *e) +{ + int oldcount; + if (!e) + return e; + + oldcount = trans_count; + while (1) { + trans_count = 0; + switch (e->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e->type, &e, &e); + expr_eliminate_dups2(e->type, &e, &e); + default: + ; + } + if (!trans_count) + break; + e = expr_eliminate_yn(e); + } + trans_count = oldcount; + return e; +} + +struct expr *expr_transform(struct expr *e) +{ + struct expr *tmp; + + if (!e) + return NULL; + switch (e->type) { + case E_EQUAL: + case E_UNEQUAL: + case E_SYMBOL: + case E_LIST: + break; + default: + e->left.expr = expr_transform(e->left.expr); + e->right.expr = expr_transform(e->right.expr); + } + + switch (e->type) { + case E_EQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + break; + case E_UNEQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + break; + case E_NOT: + switch (e->left.expr->type) { + case E_NOT: + // !!a -> a + tmp = e->left.expr->left.expr; + free(e->left.expr); + free(e); + e = tmp; + e = expr_transform(e); + break; + case E_EQUAL: + case E_UNEQUAL: + // !a='x' -> a!='x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; + break; + case E_OR: + // !(a || b) -> !a && !b + tmp = e->left.expr; + e->type = E_AND; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_AND: + // !(a && b) -> !a || !b + tmp = e->left.expr; + e->type = E_OR; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_SYMBOL: + if (e->left.expr->left.sym == &symbol_yes) { + // !'y' -> 'n' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + break; + } + if (e->left.expr->left.sym == &symbol_mod) { + // !'m' -> 'm' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_mod; + break; + } + if (e->left.expr->left.sym == &symbol_no) { + // !'n' -> 'y' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + break; + } + break; + default: + ; + } + break; + default: + ; + } + return e; +} + +int expr_contains_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return 0; + + switch (dep->type) { + case E_AND: + case E_OR: + return expr_contains_symbol(dep->left.expr, sym) || + expr_contains_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + case E_UNEQUAL: + return dep->left.sym == sym || + dep->right.sym == sym; + case E_NOT: + return expr_contains_symbol(dep->left.expr, sym); + default: + ; + } + return 0; +} + +bool expr_depends_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return false; + + switch (dep->type) { + case E_AND: + return expr_depends_symbol(dep->left.expr, sym) || + expr_depends_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) + return true; + } + break; + case E_UNEQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_no) + return true; + } + break; + default: + ; + } + return false; +} + +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_AND, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) +{ + struct expr *tmp = NULL; + expr_extract_eq(E_OR, &tmp, ep1, ep2); + if (tmp) { + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); + } + return tmp; +} + +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + if (e1->type == type) { + expr_extract_eq(type, ep, &e1->left.expr, &e2); + expr_extract_eq(type, ep, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_extract_eq(type, ep, ep1, &e2->left.expr); + expr_extract_eq(type, ep, ep1, &e2->right.expr); + return; + } + if (expr_eq(e1, e2)) { + *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1; + expr_free(e2); + if (type == E_AND) { + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + } else if (type == E_OR) { + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + } + } +#undef e1 +#undef e2 +} + +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) +{ + struct expr *e1, *e2; + + if (!e) { + e = expr_alloc_symbol(sym); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + } + switch (e->type) { + case E_AND: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_AND, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_OR, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_OR: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_OR, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_AND, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_NOT: + return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); + case E_UNEQUAL: + case E_EQUAL: + if (type == E_EQUAL) { + if (sym == &symbol_yes) + return expr_copy(e); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_no); + if (sym == &symbol_no) + return expr_alloc_one(E_NOT, expr_copy(e)); + } else { + if (sym == &symbol_yes) + return expr_alloc_one(E_NOT, expr_copy(e)); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_yes); + if (sym == &symbol_no) + return expr_copy(e); + } + break; + case E_SYMBOL: + return expr_alloc_comp(type, e->left.sym, sym); + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + return NULL; +} + +tristate expr_calc_value(struct expr *e) +{ + tristate val1, val2; + const char *str1, *str2; + + if (!e) + return yes; + + switch (e->type) { + case E_SYMBOL: + sym_calc_value(e->left.sym); + return e->left.sym->curr.tri; + case E_AND: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_AND(val1, val2); + case E_OR: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_OR(val1, val2); + case E_NOT: + val1 = expr_calc_value(e->left.expr); + return EXPR_NOT(val1); + case E_EQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? yes : no; + case E_UNEQUAL: + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + return !strcmp(str1, str2) ? no : yes; + default: + printf("expr_calc_value: %d?\n", e->type); + return no; + } +} + +int expr_compare_type(enum expr_type t1, enum expr_type t2) +{ +#if 0 + return 1; +#else + if (t1 == t2) + return 0; + switch (t1) { + case E_EQUAL: + case E_UNEQUAL: + if (t2 == E_NOT) + return 1; + case E_NOT: + if (t2 == E_AND) + return 1; + case E_AND: + if (t2 == E_OR) + return 1; + case E_OR: + if (t2 == E_LIST) + return 1; + case E_LIST: + if (t2 == 0) + return 1; + default: + return -1; + } + printf("[%dgt%d?]", t1, t2); + return 0; +#endif +} + +static inline struct expr * +expr_get_leftmost_symbol(const struct expr *e) +{ + + if (e == NULL) + return NULL; + + while (e->type != E_SYMBOL) + e = e->left.expr; + + return expr_copy(e); +} + +/* + * Given expression `e1' and `e2', returns the leaf of the longest + * sub-expression of `e1' not containing 'e2. + */ +struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2) +{ + struct expr *ret; + + switch (e1->type) { + case E_OR: + return expr_alloc_and( + expr_simplify_unmet_dep(e1->left.expr, e2), + expr_simplify_unmet_dep(e1->right.expr, e2)); + case E_AND: { + struct expr *e; + e = expr_alloc_and(expr_copy(e1), expr_copy(e2)); + e = expr_eliminate_dups(e); + ret = (!expr_eq(e, e1)) ? e1 : NULL; + expr_free(e); + break; + } + default: + ret = e1; + break; + } + + return expr_get_leftmost_symbol(ret); +} + +void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) +{ + if (!e) { + fn(data, NULL, "y"); + return; + } + + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, "("); + switch (e->type) { + case E_SYMBOL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + break; + case E_NOT: + fn(data, NULL, "!"); + expr_print(e->left.expr, fn, data, E_NOT); + break; + case E_EQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + fn(data, NULL, "="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_UNEQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, "<choice>"); + fn(data, NULL, "!="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_OR: + expr_print(e->left.expr, fn, data, E_OR); + fn(data, NULL, " || "); + expr_print(e->right.expr, fn, data, E_OR); + break; + case E_AND: + expr_print(e->left.expr, fn, data, E_AND); + fn(data, NULL, " && "); + expr_print(e->right.expr, fn, data, E_AND); + break; + case E_LIST: + fn(data, e->right.sym, e->right.sym->name); + if (e->left.expr) { + fn(data, NULL, " ^ "); + expr_print(e->left.expr, fn, data, E_LIST); + } + break; + case E_RANGE: + fn(data, NULL, "["); + fn(data, e->left.sym, e->left.sym->name); + fn(data, NULL, " "); + fn(data, e->right.sym, e->right.sym->name); + fn(data, NULL, "]"); + break; + default: + { + char buf[32]; + sprintf(buf, "<unknown type %d>", e->type); + fn(data, NULL, buf); + break; + } + } + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, ")"); +} + +static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) +{ + xfwrite(str, strlen(str), 1, data); +} + +void expr_fprint(struct expr *e, FILE *out) +{ + expr_print(e, expr_print_file_helper, out, E_NONE); +} + +static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str) +{ + struct gstr *gs = (struct gstr*)data; + const char *sym_str = NULL; + + if (sym) + sym_str = sym_get_string_value(sym); + + if (gs->max_width) { + unsigned extra_length = strlen(str); + const char *last_cr = strrchr(gs->s, '\n'); + unsigned last_line_length; + + if (sym_str) + extra_length += 4 + strlen(sym_str); + + if (!last_cr) + last_cr = gs->s; + + last_line_length = strlen(gs->s) - (last_cr - gs->s); + + if ((last_line_length + extra_length) > gs->max_width) + str_append(gs, "\\\n"); + } + + str_append(gs, str); + if (sym && sym->type != S_UNKNOWN) + str_printf(gs, " [=%s]", sym_str); +} + +void expr_gstr_print(struct expr *e, struct gstr *gs) +{ + expr_print(e, expr_print_gstr_helper, gs, E_NONE); +} diff --git a/qemu/roms/seabios/scripts/kconfig/expr.h b/qemu/roms/seabios/scripts/kconfig/expr.h new file mode 100644 index 000000000..412ea8a2a --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/expr.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef EXPR_H +#define EXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <assert.h> +#include <stdio.h> +#include "list.h" +#ifndef __cplusplus +#include <stdbool.h> +#endif + +struct file { + struct file *next; + struct file *parent; + const char *name; + int lineno; +}; + +typedef enum tristate { + no, mod, yes +} tristate; + +enum expr_type { + E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LIST, E_SYMBOL, E_RANGE +}; + +union expr_data { + struct expr *expr; + struct symbol *sym; +}; + +struct expr { + enum expr_type type; + union expr_data left, right; +}; + +#define EXPR_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) +#define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) +#define EXPR_NOT(dep) (2-(dep)) + +#define expr_list_for_each_sym(l, e, s) \ + for (e = (l); e && (s = e->right.sym); e = e->left.expr) + +struct expr_value { + struct expr *expr; + tristate tri; +}; + +struct symbol_value { + void *val; + tristate tri; +}; + +enum symbol_type { + S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER +}; + +/* enum values are used as index to symbol.def[] */ +enum { + S_DEF_USER, /* main user value */ + S_DEF_AUTO, /* values read from auto.conf */ + S_DEF_DEF3, /* Reserved for UI usage */ + S_DEF_DEF4, /* Reserved for UI usage */ + S_DEF_COUNT +}; + +struct symbol { + struct symbol *next; + char *name; + enum symbol_type type; + struct symbol_value curr; + struct symbol_value def[S_DEF_COUNT]; + tristate visible; + int flags; + struct property *prop; + struct expr_value dir_dep; + struct expr_value rev_dep; +}; + +#define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) + +#define SYMBOL_CONST 0x0001 /* symbol is const */ +#define SYMBOL_CHECK 0x0008 /* used during dependency checking */ +#define SYMBOL_CHOICE 0x0010 /* start of a choice block (null name) */ +#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ +#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ +#define SYMBOL_OPTIONAL 0x0100 /* choice is optional - values can be 'n' */ +#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ +#define SYMBOL_CHANGED 0x0400 /* ? */ +#define SYMBOL_AUTO 0x1000 /* value from environment variable */ +#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ +#define SYMBOL_WARNED 0x8000 /* warning has been issued */ + +/* Set when symbol.def[] is used */ +#define SYMBOL_DEF 0x10000 /* First bit of SYMBOL_DEF */ +#define SYMBOL_DEF_USER 0x10000 /* symbol.def[S_DEF_USER] is valid */ +#define SYMBOL_DEF_AUTO 0x20000 /* symbol.def[S_DEF_AUTO] is valid */ +#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */ +#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */ + +/* choice values need to be set before calculating this symbol value */ +#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000 + +/* Set symbol to y if allnoconfig; used for symbols that hide others */ +#define SYMBOL_ALLNOCONFIG_Y 0x200000 + +#define SYMBOL_MAXLENGTH 256 +#define SYMBOL_HASHSIZE 9973 + +/* A property represent the config options that can be associated + * with a config "symbol". + * Sample: + * config FOO + * default y + * prompt "foo prompt" + * select BAR + * config BAZ + * int "BAZ Value" + * range 1..255 + */ +enum prop_type { + P_UNKNOWN, + P_PROMPT, /* prompt "foo prompt" or "BAZ Value" */ + P_COMMENT, /* text associated with a comment */ + P_MENU, /* prompt associated with a menuconfig option */ + P_DEFAULT, /* default y */ + P_CHOICE, /* choice value */ + P_SELECT, /* select BAR */ + P_RANGE, /* range 7..100 (for a symbol) */ + P_ENV, /* value from environment variable */ + P_SYMBOL, /* where a symbol is defined */ +}; + +struct property { + struct property *next; /* next property - null if last */ + struct symbol *sym; /* the symbol for which the property is associated */ + enum prop_type type; /* type of property */ + const char *text; /* the prompt value - P_PROMPT, P_MENU, P_COMMENT */ + struct expr_value visible; + struct expr *expr; /* the optional conditional part of the property */ + struct menu *menu; /* the menu the property are associated with + * valid for: P_SELECT, P_RANGE, P_CHOICE, + * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */ + struct file *file; /* what file was this property defined */ + int lineno; /* what lineno was this property defined */ +}; + +#define for_all_properties(sym, st, tok) \ + for (st = sym->prop; st; st = st->next) \ + if (st->type == (tok)) +#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) +#define for_all_prompts(sym, st) \ + for (st = sym->prop; st; st = st->next) \ + if (st->text) + +struct menu { + struct menu *next; + struct menu *parent; + struct menu *list; + struct symbol *sym; + struct property *prompt; + struct expr *visibility; + struct expr *dep; + unsigned int flags; + char *help; + struct file *file; + int lineno; + void *data; +}; + +#define MENU_CHANGED 0x0001 +#define MENU_ROOT 0x0002 + +struct jump_key { + struct list_head entries; + size_t offset; + struct menu *target; + int index; +}; + +#define JUMP_NB 9 + +extern struct file *file_list; +extern struct file *current_file; +struct file *lookup_file(const char *name); + +extern struct symbol symbol_yes, symbol_no, symbol_mod; +extern struct symbol *modules_sym; +extern struct symbol *sym_defconfig_list; +extern int cdebug; +struct expr *expr_alloc_symbol(struct symbol *sym); +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); +struct expr *expr_copy(const struct expr *org); +void expr_free(struct expr *e); +int expr_eq(struct expr *e1, struct expr *e2); +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); +tristate expr_calc_value(struct expr *e); +struct expr *expr_eliminate_yn(struct expr *e); +struct expr *expr_trans_bool(struct expr *e); +struct expr *expr_eliminate_dups(struct expr *e); +struct expr *expr_transform(struct expr *e); +int expr_contains_symbol(struct expr *dep, struct symbol *sym); +bool expr_depends_symbol(struct expr *dep, struct symbol *sym); +struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); +struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); +void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); +struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2); + +void expr_fprint(struct expr *e, FILE *out); +struct gstr; /* forward */ +void expr_gstr_print(struct expr *e, struct gstr *gs); + +static inline int expr_is_yes(struct expr *e) +{ + return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); +} + +static inline int expr_is_no(struct expr *e) +{ + return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); +} + +#ifdef __cplusplus +} +#endif + +#endif /* EXPR_H */ diff --git a/qemu/roms/seabios/scripts/kconfig/gconf.c b/qemu/roms/seabios/scripts/kconfig/gconf.c new file mode 100644 index 000000000..d0a35b21f --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/gconf.c @@ -0,0 +1,1542 @@ +/* Hey EMACS -*- linux-c -*- */ +/* + * + * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info> + * Released under the terms of the GNU GPL v2.0. + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include "lkc.h" +#include "images.c" + +#include <glade/glade.h> +#include <gtk/gtk.h> +#include <glib.h> +#include <gdk/gdkkeysyms.h> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +//#define DEBUG + +enum { + SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW +}; + +enum { + OPT_NORMAL, OPT_ALL, OPT_PROMPT +}; + +static gint view_mode = FULL_VIEW; +static gboolean show_name = TRUE; +static gboolean show_range = TRUE; +static gboolean show_value = TRUE; +static gboolean resizeable = FALSE; +static int opt_mode = OPT_NORMAL; + +GtkWidget *main_wnd = NULL; +GtkWidget *tree1_w = NULL; // left frame +GtkWidget *tree2_w = NULL; // right frame +GtkWidget *text_w = NULL; +GtkWidget *hpaned = NULL; +GtkWidget *vpaned = NULL; +GtkWidget *back_btn = NULL; +GtkWidget *save_btn = NULL; +GtkWidget *save_menu_item = NULL; + +GtkTextTag *tag1, *tag2; +GdkColor color; + +GtkTreeStore *tree1, *tree2, *tree; +GtkTreeModel *model1, *model2; +static GtkTreeIter *parents[256]; +static gint indent; + +static struct menu *current; // current node for SINGLE view +static struct menu *browsed; // browsed node for SPLIT view + +enum { + COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, + COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF, + COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD, + COL_NUMBER +}; + +static void display_list(void); +static void display_tree(struct menu *menu); +static void display_tree_part(void); +static void update_tree(struct menu *src, GtkTreeIter * dst); +static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row); +static gchar **fill_row(struct menu *menu); +static void conf_changed(void); + +/* Helping/Debugging Functions */ + +const char *dbg_sym_flags(int val) +{ + static char buf[256]; + + bzero(buf, 256); + + if (val & SYMBOL_CONST) + strcat(buf, "const/"); + if (val & SYMBOL_CHECK) + strcat(buf, "check/"); + if (val & SYMBOL_CHOICE) + strcat(buf, "choice/"); + if (val & SYMBOL_CHOICEVAL) + strcat(buf, "choiceval/"); + if (val & SYMBOL_VALID) + strcat(buf, "valid/"); + if (val & SYMBOL_OPTIONAL) + strcat(buf, "optional/"); + if (val & SYMBOL_WRITE) + strcat(buf, "write/"); + if (val & SYMBOL_CHANGED) + strcat(buf, "changed/"); + if (val & SYMBOL_AUTO) + strcat(buf, "auto/"); + + buf[strlen(buf) - 1] = '\0'; + + return buf; +} + +void replace_button_icon(GladeXML * xml, GdkDrawable * window, + GtkStyle * style, gchar * btn_name, gchar ** xpm) +{ + GdkPixmap *pixmap; + GdkBitmap *mask; + GtkToolButton *button; + GtkWidget *image; + + pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, + &style->bg[GTK_STATE_NORMAL], + xpm); + + button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); + image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_widget_show(image); + gtk_tool_button_set_icon_widget(button, image); +} + +/* Main Window Initialization */ +void init_main_window(const gchar * glade_file) +{ + GladeXML *xml; + GtkWidget *widget; + GtkTextBuffer *txtbuf; + GtkStyle *style; + + xml = glade_xml_new(glade_file, "window1", NULL); + if (!xml) + g_error(_("GUI loading failed !\n")); + glade_xml_signal_autoconnect(xml); + + main_wnd = glade_xml_get_widget(xml, "window1"); + hpaned = glade_xml_get_widget(xml, "hpaned1"); + vpaned = glade_xml_get_widget(xml, "vpaned1"); + tree1_w = glade_xml_get_widget(xml, "treeview1"); + tree2_w = glade_xml_get_widget(xml, "treeview2"); + text_w = glade_xml_get_widget(xml, "textview3"); + + back_btn = glade_xml_get_widget(xml, "button1"); + gtk_widget_set_sensitive(back_btn, FALSE); + + widget = glade_xml_get_widget(xml, "show_name1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_name); + + widget = glade_xml_get_widget(xml, "show_range1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_range); + + widget = glade_xml_get_widget(xml, "show_data1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_value); + + save_btn = glade_xml_get_widget(xml, "button3"); + save_menu_item = glade_xml_get_widget(xml, "save1"); + conf_set_changed_callback(conf_changed); + + style = gtk_widget_get_style(main_wnd); + widget = glade_xml_get_widget(xml, "toolbar1"); + +#if 0 /* Use stock Gtk icons instead */ + replace_button_icon(xml, main_wnd->window, style, + "button1", (gchar **) xpm_back); + replace_button_icon(xml, main_wnd->window, style, + "button2", (gchar **) xpm_load); + replace_button_icon(xml, main_wnd->window, style, + "button3", (gchar **) xpm_save); +#endif + replace_button_icon(xml, main_wnd->window, style, + "button4", (gchar **) xpm_single_view); + replace_button_icon(xml, main_wnd->window, style, + "button5", (gchar **) xpm_split_view); + replace_button_icon(xml, main_wnd->window, style, + "button6", (gchar **) xpm_tree_view); + +#if 0 + switch (view_mode) { + case SINGLE_VIEW: + widget = glade_xml_get_widget(xml, "button4"); + g_signal_emit_by_name(widget, "clicked"); + break; + case SPLIT_VIEW: + widget = glade_xml_get_widget(xml, "button5"); + g_signal_emit_by_name(widget, "clicked"); + break; + case FULL_VIEW: + widget = glade_xml_get_widget(xml, "button6"); + g_signal_emit_by_name(widget, "clicked"); + break; + } +#endif + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", + "foreground", "red", + "weight", PANGO_WEIGHT_BOLD, + NULL); + tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", + /*"style", PANGO_STYLE_OBLIQUE, */ + NULL); + + gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); + + gtk_widget_show(main_wnd); +} + +void init_tree_model(void) +{ + gint i; + + tree = tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model2 = GTK_TREE_MODEL(tree2); + + for (parents[0] = NULL, i = 1; i < 256; i++) + parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter)); + + tree1 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model1 = GTK_TREE_MODEL(tree1); +} + +void init_left_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + + gtk_tree_view_set_model(view, model1); + gtk_tree_view_set_headers_visible(view, TRUE); + gtk_tree_view_set_rules_hint(view, TRUE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, _("Options")); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); + gtk_widget_realize(tree1_w); +} + +static void renderer_edited(GtkCellRendererText * cell, + const gchar * path_string, + const gchar * new_text, gpointer user_data); + +void init_right_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree2_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + gint i; + + gtk_tree_view_set_model(view, model2); + gtk_tree_view_set_headers_visible(view, TRUE); + gtk_tree_view_set_rules_hint(view, TRUE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, _("Options")); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "pixbuf", COL_PIXBUF, + "visible", COL_PIXVIS, NULL); + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + _("Name"), renderer, + "text", COL_NAME, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "N", renderer, + "text", COL_NO, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "M", renderer, + "text", COL_MOD, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Y", renderer, + "text", COL_YES, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + _("Value"), renderer, + "text", COL_VALUE, + "editable", + COL_EDIT, + "foreground-gdk", + COL_COLOR, NULL); + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(renderer_edited), NULL); + + column = gtk_tree_view_get_column(view, COL_NAME); + gtk_tree_view_column_set_visible(column, show_name); + column = gtk_tree_view_get_column(view, COL_NO); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_MOD); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_YES); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_VALUE); + gtk_tree_view_column_set_visible(column, show_value); + + if (resizeable) { + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); + } + } + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} + + +/* Utility Functions */ + + +static void text_insert_help(struct menu *menu) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + const char *prompt = _(menu_get_prompt(menu)); + struct gstr help = str_new(); + + menu_get_ext_help(menu, &help); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1, + NULL); + gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2, + NULL); + str_free(&help); +} + + +static void text_insert_msg(const char *title, const char *message) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + const char *msg = message; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, + NULL); + gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, + NULL); +} + + +/* Main Windows Callbacks */ + +void on_save_activate(GtkMenuItem * menuitem, gpointer user_data); +gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, + gpointer user_data) +{ + GtkWidget *dialog, *label; + gint result; + + if (!conf_get_changed()) + return FALSE; + + dialog = gtk_dialog_new_with_buttons(_("Warning !"), + GTK_WINDOW(main_wnd), + (GtkDialogFlags) + (GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_OK, + GTK_RESPONSE_YES, + GTK_STOCK_NO, + GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CANCEL); + + label = gtk_label_new(_("\nSave configuration ?\n")); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + gtk_widget_show(label); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + on_save_activate(NULL, NULL); + return FALSE; + case GTK_RESPONSE_NO: + return FALSE; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + default: + gtk_widget_destroy(dialog); + return TRUE; + } + + return FALSE; +} + + +void on_window1_destroy(GtkObject * object, gpointer user_data) +{ + gtk_main_quit(); +} + + +void +on_window1_size_request(GtkWidget * widget, + GtkRequisition * requisition, gpointer user_data) +{ + static gint old_h; + gint w, h; + + if (widget->window == NULL) + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + else + gdk_window_get_size(widget->window, &w, &h); + + if (h == old_h) + return; + old_h = h; + + gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); +} + + +/* Menu & Toolbar Callbacks */ + + +static void +load_filename(GtkFileSelection * file_selector, gpointer user_data) +{ + const gchar *fn; + + fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION + (user_data)); + + if (conf_read(fn)) + text_insert_msg(_("Error"), _("Unable to load configuration !")); + else + display_tree(&rootmenu); +} + +void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *fs; + + fs = gtk_file_selection_new(_("Load file...")); + g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), + "clicked", + G_CALLBACK(load_filename), (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->ok_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->cancel_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + gtk_widget_show(fs); +} + + +void on_save_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + if (conf_write(NULL)) + text_insert_msg(_("Error"), _("Unable to save configuration !")); +} + + +static void +store_filename(GtkFileSelection * file_selector, gpointer user_data) +{ + const gchar *fn; + + fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION + (user_data)); + + if (conf_write(fn)) + text_insert_msg(_("Error"), _("Unable to save configuration !")); + + gtk_widget_destroy(GTK_WIDGET(user_data)); +} + +void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *fs; + + fs = gtk_file_selection_new(_("Save file as...")); + g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), + "clicked", + G_CALLBACK(store_filename), (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->ok_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->cancel_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + gtk_widget_show(fs); +} + + +void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + if (!on_window1_delete_event(NULL, NULL, NULL)) + gtk_widget_destroy(GTK_WIDGET(main_wnd)); +} + + +void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_name = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME); + if (col) + gtk_tree_view_column_set_visible(col, show_name); +} + + +void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_range = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + +} + + +void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_value = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE); + if (col) + gtk_tree_view_column_set_visible(col, show_value); +} + + +void +on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + opt_mode = OPT_NORMAL; + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); /* instead of update_tree to speed-up */ +} + + +void +on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + opt_mode = OPT_ALL; + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); /* instead of update_tree to speed-up */ +} + + +void +on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + opt_mode = OPT_PROMPT; + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); /* instead of update_tree to speed-up */ +} + + +void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *intro_text = _( + "Welcome to gkc, the GTK+ graphical configuration tool\n" + "For each option, a blank box indicates the feature is disabled, a\n" + "check indicates it is enabled, and a dot indicates that it is to\n" + "be compiled as a module. Clicking on the box will cycle through the three states.\n" + "\n" + "If you do not see an option (e.g., a device driver) that you\n" + "believe should be present, try turning on Show All Options\n" + "under the Options menu.\n" + "Although there is no cross reference yet to help you figure out\n" + "what other options must be enabled to support the option you\n" + "are interested in, you can still view the help of a grayed-out\n" + "option.\n" + "\n" + "Toggling Show Debug Info under the Options menu will show \n" + "the dependencies, which you can then match by examining other options."); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, "%s", intro_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *about_text = + _("gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n" + "Based on the source code from Roman Zippel.\n"); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, "%s", about_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *license_text = + _("gkc is released under the terms of the GNU GPL v2.\n" + "For more information, please see the source code or\n" + "visit http://www.fsf.org/licenses/licenses.html\n"); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, "%s", license_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_back_clicked(GtkButton * button, gpointer user_data) +{ + enum prop_type ptype; + + current = current->parent; + ptype = current->prompt ? current->prompt->type : P_UNKNOWN; + if (ptype != P_MENU) + current = current->parent; + display_tree_part(); + + if (current == &rootmenu) + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_load_clicked(GtkButton * button, gpointer user_data) +{ + on_load1_activate(NULL, user_data); +} + + +void on_single_clicked(GtkButton * button, gpointer user_data) +{ + view_mode = SINGLE_VIEW; + gtk_widget_hide(tree1_w); + current = &rootmenu; + display_tree_part(); +} + + +void on_split_clicked(GtkButton * button, gpointer user_data) +{ + gint w, h; + view_mode = SPLIT_VIEW; + gtk_widget_show(tree1_w); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + gtk_paned_set_position(GTK_PANED(hpaned), w / 2); + if (tree2) + gtk_tree_store_clear(tree2); + display_list(); + + /* Disable back btn, like in full mode. */ + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_full_clicked(GtkButton * button, gpointer user_data) +{ + view_mode = FULL_VIEW; + gtk_widget_hide(tree1_w); + if (tree2) + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_collapse_clicked(GtkButton * button, gpointer user_data) +{ + gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w)); +} + + +void on_expand_clicked(GtkButton * button, gpointer user_data) +{ + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); +} + + +/* CTree Callbacks */ + +/* Change hex/int/string value in the cell */ +static void renderer_edited(GtkCellRendererText * cell, + const gchar * path_string, + const gchar * new_text, gpointer user_data) +{ + GtkTreePath *path = gtk_tree_path_new_from_string(path_string); + GtkTreeIter iter; + const char *old_def, *new_def; + struct menu *menu; + struct symbol *sym; + + if (!gtk_tree_model_get_iter(model2, &iter, path)) + return; + + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + sym = menu->sym; + + gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1); + new_def = new_text; + + sym_set_string_value(sym, new_def); + + update_tree(&rootmenu, NULL); + + gtk_tree_path_free(path); +} + +/* Change the value of a symbol and update the tree */ +static void change_sym_value(struct menu *menu, gint col) +{ + struct symbol *sym = menu->sym; + tristate newval; + + if (!sym) + return; + + if (col == COL_NO) + newval = no; + else if (col == COL_MOD) + newval = mod; + else if (col == COL_YES) + newval = yes; + else + return; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (!sym_tristate_within_range(sym, newval)) + newval = yes; + sym_set_tristate_value(sym, newval); + if (view_mode == FULL_VIEW) + update_tree(&rootmenu, NULL); + else if (view_mode == SPLIT_VIEW) { + update_tree(browsed, NULL); + display_list(); + } + else if (view_mode == SINGLE_VIEW) + display_tree_part(); //fixme: keep exp/coll + break; + case S_INT: + case S_HEX: + case S_STRING: + default: + break; + } +} + +static void toggle_sym_value(struct menu *menu) +{ + if (!menu->sym) + return; + + sym_toggle_tristate_value(menu->sym); + if (view_mode == FULL_VIEW) + update_tree(&rootmenu, NULL); + else if (view_mode == SPLIT_VIEW) { + update_tree(browsed, NULL); + display_list(); + } + else if (view_mode == SINGLE_VIEW) + display_tree_part(); //fixme: keep exp/coll +} + +static gint column2index(GtkTreeViewColumn * column) +{ + gint i; + + for (i = 0; i < COL_NUMBER; i++) { + GtkTreeViewColumn *col; + + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i); + if (col == column) + return i; + } + + return -1; +} + + +/* User click: update choice (full) or goes down (single) */ +gboolean +on_treeview2_button_press_event(GtkWidget * widget, + GdkEventButton * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + gint col; + +#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK + gint tx = (gint) event->x; + gint ty = (gint) event->y; + gint cx, cy; + + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, + &cy); +#else + gtk_tree_view_get_cursor(view, &path, &column); +#endif + if (path == NULL) + return FALSE; + + if (!gtk_tree_model_get_iter(model2, &iter, path)) + return FALSE; + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + + col = column2index(column); + if (event->type == GDK_2BUTTON_PRESS) { + enum prop_type ptype; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + + if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { + // goes down into menu + current = menu; + display_tree_part(); + gtk_widget_set_sensitive(back_btn, TRUE); + } else if ((col == COL_OPTION)) { + toggle_sym_value(menu); + gtk_tree_view_expand_row(view, path, TRUE); + } + } else { + if (col == COL_VALUE) { + toggle_sym_value(menu); + gtk_tree_view_expand_row(view, path, TRUE); + } else if (col == COL_NO || col == COL_MOD + || col == COL_YES) { + change_sym_value(menu, col); + gtk_tree_view_expand_row(view, path, TRUE); + } + } + + return FALSE; +} + +/* Key pressed: update choice */ +gboolean +on_treeview2_key_press_event(GtkWidget * widget, + GdkEventKey * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + gint col; + + gtk_tree_view_get_cursor(view, &path, &column); + if (path == NULL) + return FALSE; + + if (event->keyval == GDK_space) { + if (gtk_tree_view_row_expanded(view, path)) + gtk_tree_view_collapse_row(view, path); + else + gtk_tree_view_expand_row(view, path, FALSE); + return TRUE; + } + if (event->keyval == GDK_KP_Enter) { + } + if (widget == tree1_w) + return FALSE; + + gtk_tree_model_get_iter(model2, &iter, path); + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + + if (!strcasecmp(event->string, "n")) + col = COL_NO; + else if (!strcasecmp(event->string, "m")) + col = COL_MOD; + else if (!strcasecmp(event->string, "y")) + col = COL_YES; + else + col = -1; + change_sym_value(menu, col); + + return FALSE; +} + + +/* Row selection changed: update help */ +void +on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + struct menu *menu; + + selection = gtk_tree_view_get_selection(treeview); + if (gtk_tree_selection_get_selected(selection, &model2, &iter)) { + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + text_insert_help(menu); + } +} + + +/* User click: display sub-tree in the right frame. */ +gboolean +on_treeview1_button_press_event(GtkWidget * widget, + GdkEventButton * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + + gint tx = (gint) event->x; + gint ty = (gint) event->y; + gint cx, cy; + + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, + &cy); + if (path == NULL) + return FALSE; + + gtk_tree_model_get_iter(model1, &iter, path); + gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1); + + if (event->type == GDK_2BUTTON_PRESS) { + toggle_sym_value(menu); + current = menu; + display_tree_part(); + } else { + browsed = menu; + display_tree_part(); + } + + gtk_widget_realize(tree2_w); + gtk_tree_view_set_cursor(view, path, NULL, FALSE); + gtk_widget_grab_focus(tree2_w); + + return FALSE; +} + + +/* Fill a row of strings */ +static gchar **fill_row(struct menu *menu) +{ + static gchar *row[COL_NUMBER]; + struct symbol *sym = menu->sym; + const char *def; + int stype; + tristate val; + enum prop_type ptype; + int i; + + for (i = COL_OPTION; i <= COL_COLOR; i++) + g_free(row[i]); + bzero(row, sizeof(row)); + + row[COL_OPTION] = + g_strdup_printf("%s %s", _(menu_get_prompt(menu)), + sym && !sym_has_value(sym) ? "(NEW)" : ""); + + if (opt_mode == OPT_ALL && !menu_is_visible(menu)) + row[COL_COLOR] = g_strdup("DarkGray"); + else if (opt_mode == OPT_PROMPT && + menu_has_prompt(menu) && !menu_is_visible(menu)) + row[COL_COLOR] = g_strdup("DarkGray"); + else + row[COL_COLOR] = g_strdup("Black"); + + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + switch (ptype) { + case P_MENU: + row[COL_PIXBUF] = (gchar *) xpm_menu; + if (view_mode == SINGLE_VIEW) + row[COL_PIXVIS] = GINT_TO_POINTER(TRUE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + case P_COMMENT: + row[COL_PIXBUF] = (gchar *) xpm_void; + row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + default: + row[COL_PIXBUF] = (gchar *) xpm_void; + row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); + row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); + break; + } + + if (!sym) + return row; + row[COL_NAME] = g_strdup(sym->name); + + sym_calc_value(sym); + sym->flags &= ~SYMBOL_CHANGED; + + if (sym_is_choice(sym)) { // parse childs for getting final value + struct menu *child; + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) + && child->sym == def_sym) + def_menu = child; + } + + if (def_menu) + row[COL_VALUE] = + g_strdup(_(menu_get_prompt(def_menu))); + } + if (sym->flags & SYMBOL_CHOICEVAL) + row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); + + stype = sym_get_type(sym); + switch (stype) { + case S_BOOLEAN: + if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE) + row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); + if (sym_is_choice(sym)) + break; + /* fall through */ + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + row[COL_NO] = g_strdup("N"); + row[COL_VALUE] = g_strdup("N"); + row[COL_BTNACT] = GINT_TO_POINTER(FALSE); + row[COL_BTNINC] = GINT_TO_POINTER(FALSE); + break; + case mod: + row[COL_MOD] = g_strdup("M"); + row[COL_VALUE] = g_strdup("M"); + row[COL_BTNINC] = GINT_TO_POINTER(TRUE); + break; + case yes: + row[COL_YES] = g_strdup("Y"); + row[COL_VALUE] = g_strdup("Y"); + row[COL_BTNACT] = GINT_TO_POINTER(TRUE); + row[COL_BTNINC] = GINT_TO_POINTER(FALSE); + break; + } + + if (val != no && sym_tristate_within_range(sym, no)) + row[COL_NO] = g_strdup("_"); + if (val != mod && sym_tristate_within_range(sym, mod)) + row[COL_MOD] = g_strdup("_"); + if (val != yes && sym_tristate_within_range(sym, yes)) + row[COL_YES] = g_strdup("_"); + break; + case S_INT: + case S_HEX: + case S_STRING: + def = sym_get_string_value(sym); + row[COL_VALUE] = g_strdup(def); + row[COL_EDIT] = GINT_TO_POINTER(TRUE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + } + + return row; +} + + +/* Set the node content with a row of strings */ +static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) +{ + GdkColor color; + gboolean success; + GdkPixbuf *pix; + + pix = gdk_pixbuf_new_from_xpm_data((const char **) + row[COL_PIXBUF]); + + gdk_color_parse(row[COL_COLOR], &color); + gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1, + FALSE, FALSE, &success); + + gtk_tree_store_set(tree, node, + COL_OPTION, row[COL_OPTION], + COL_NAME, row[COL_NAME], + COL_NO, row[COL_NO], + COL_MOD, row[COL_MOD], + COL_YES, row[COL_YES], + COL_VALUE, row[COL_VALUE], + COL_MENU, (gpointer) menu, + COL_COLOR, &color, + COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]), + COL_PIXBUF, pix, + COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]), + COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]), + COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]), + COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]), + COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]), + -1); + + g_object_unref(pix); +} + + +/* Add a node to the tree */ +static void place_node(struct menu *menu, char **row) +{ + GtkTreeIter *parent = parents[indent - 1]; + GtkTreeIter *node = parents[indent]; + + gtk_tree_store_append(tree, node, parent); + set_node(node, menu, row); +} + + +/* Find a node in the GTK+ tree */ +static GtkTreeIter found; + +/* + * Find a menu in the GtkTree starting at parent. + */ +GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent, + struct menu *tofind) +{ + GtkTreeIter iter; + GtkTreeIter *child = &iter; + gboolean valid; + GtkTreeIter *ret; + + valid = gtk_tree_model_iter_children(model2, child, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model2, child, 6, &menu, -1); + + if (menu == tofind) { + memcpy(&found, child, sizeof(GtkTreeIter)); + return &found; + } + + ret = gtktree_iter_find_node(child, tofind); + if (ret) + return ret; + + valid = gtk_tree_model_iter_next(model2, child); + } + + return NULL; +} + + +/* + * Update the tree by adding/removing entries + * Does not change other nodes + */ +static void update_tree(struct menu *src, GtkTreeIter * dst) +{ + struct menu *child1; + GtkTreeIter iter, tmp; + GtkTreeIter *child2 = &iter; + gboolean valid; + GtkTreeIter *sibling; + struct symbol *sym; + struct menu *menu1, *menu2; + + if (src == &rootmenu) + indent = 1; + + valid = gtk_tree_model_iter_children(model2, child2, dst); + for (child1 = src->list; child1; child1 = child1->next) { + + sym = child1->sym; + + reparse: + menu1 = child1; + if (valid) + gtk_tree_model_get(model2, child2, COL_MENU, + &menu2, -1); + else + menu2 = NULL; // force adding of a first child + +#ifdef DEBUG + printf("%*c%s | %s\n", indent, ' ', + menu1 ? menu_get_prompt(menu1) : "nil", + menu2 ? menu_get_prompt(menu2) : "nil"); +#endif + + if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) || + (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) || + (opt_mode == OPT_ALL && !menu_get_prompt(child1))) { + + /* remove node */ + if (gtktree_iter_find_node(dst, menu1) != NULL) { + memcpy(&tmp, child2, sizeof(GtkTreeIter)); + valid = gtk_tree_model_iter_next(model2, + child2); + gtk_tree_store_remove(tree2, &tmp); + if (!valid) + return; /* next parent */ + else + goto reparse; /* next child */ + } else + continue; + } + + if (menu1 != menu2) { + if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node + if (!valid && !menu2) + sibling = NULL; + else + sibling = child2; + gtk_tree_store_insert_before(tree2, + child2, + dst, sibling); + set_node(child2, menu1, fill_row(menu1)); + if (menu2 == NULL) + valid = TRUE; + } else { // remove node + memcpy(&tmp, child2, sizeof(GtkTreeIter)); + valid = gtk_tree_model_iter_next(model2, + child2); + gtk_tree_store_remove(tree2, &tmp); + if (!valid) + return; // next parent + else + goto reparse; // next child + } + } else if (sym && (sym->flags & SYMBOL_CHANGED)) { + set_node(child2, menu1, fill_row(menu1)); + } + + indent++; + update_tree(child1, child2); + indent--; + + valid = gtk_tree_model_iter_next(model2, child2); + } +} + + +/* Display the whole tree (single/split/full view) */ +static void display_tree(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + enum prop_type ptype; + + if (menu == &rootmenu) { + indent = 1; + current = &rootmenu; + } + + for (child = menu->list; child; child = child->next) { + prop = child->prompt; + sym = child->sym; + ptype = prop ? prop->type : P_UNKNOWN; + + if (sym) + sym->flags &= ~SYMBOL_CHANGED; + + if ((view_mode == SPLIT_VIEW) + && !(child->flags & MENU_ROOT) && (tree == tree1)) + continue; + + if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT) + && (tree == tree2)) + continue; + + if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) || + (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || + (opt_mode == OPT_ALL && menu_get_prompt(child))) + place_node(child, fill_row(child)); +#ifdef DEBUG + printf("%*c%s: ", indent, ' ', menu_get_prompt(child)); + printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : ""); + printf("%s", prop_get_type_name(ptype)); + printf(" | "); + if (sym) { + printf("%s", sym_type_name(sym->type)); + printf(" | "); + printf("%s", dbg_sym_flags(sym->flags)); + printf("\n"); + } else + printf("\n"); +#endif + if ((view_mode != FULL_VIEW) && (ptype == P_MENU) + && (tree == tree2)) + continue; +/* + if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) + || (view_mode == FULL_VIEW) + || (view_mode == SPLIT_VIEW))*/ + + /* Change paned position if the view is not in 'split mode' */ + if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) { + gtk_paned_set_position(GTK_PANED(hpaned), 0); + } + + if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) + || (view_mode == FULL_VIEW) + || (view_mode == SPLIT_VIEW)) { + indent++; + display_tree(child); + indent--; + } + } +} + +/* Display a part of the tree starting at current node (single/split view) */ +static void display_tree_part(void) +{ + if (tree2) + gtk_tree_store_clear(tree2); + if (view_mode == SINGLE_VIEW) + display_tree(current); + else if (view_mode == SPLIT_VIEW) + display_tree(browsed); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); +} + +/* Display the list in the left frame (split view) */ +static void display_list(void) +{ + if (tree1) + gtk_tree_store_clear(tree1); + + tree = tree1; + display_tree(&rootmenu); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); + tree = tree2; +} + +void fixup_rootmenu(struct menu *menu) +{ + struct menu *child; + static int menu_cnt = 0; + + menu->flags |= MENU_ROOT; + for (child = menu->list; child; child = child->next) { + if (child->prompt && child->prompt->type == P_MENU) { + menu_cnt++; + fixup_rootmenu(child); + menu_cnt--; + } else if (!menu_cnt) + fixup_rootmenu(child); + } +} + + +/* Main */ +int main(int ac, char *av[]) +{ + const char *name; + char *env; + gchar *glade_file; + + bindtextdomain(PACKAGE, LOCALEDIR); + bind_textdomain_codeset(PACKAGE, "UTF-8"); + textdomain(PACKAGE); + + /* GTK stuffs */ + gtk_set_locale(); + gtk_init(&ac, &av); + glade_init(); + + //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); + //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps"); + + /* Determine GUI path */ + env = getenv(SRCTREE); + if (env) + glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL); + else if (av[0][0] == '/') + glade_file = g_strconcat(av[0], ".glade", NULL); + else + glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL); + + /* Conf stuffs */ + if (ac > 1 && av[1][0] == '-') { + switch (av[1][1]) { + case 'a': + //showAll = 1; + break; + case 'h': + case '?': + printf("%s <config>\n", av[0]); + exit(0); + } + name = av[2]; + } else + name = av[1]; + + conf_parse(name); + fixup_rootmenu(&rootmenu); + conf_read(NULL); + + /* Load the interface and connect signals */ + init_main_window(glade_file); + init_tree_model(); + init_left_tree(); + init_right_tree(); + + switch (view_mode) { + case SINGLE_VIEW: + display_tree_part(); + break; + case SPLIT_VIEW: + display_list(); + break; + case FULL_VIEW: + display_tree(&rootmenu); + break; + } + + gtk_main(); + + return 0; +} + +static void conf_changed(void) +{ + bool changed = conf_get_changed(); + gtk_widget_set_sensitive(save_btn, changed); + gtk_widget_set_sensitive(save_menu_item, changed); +} diff --git a/qemu/roms/seabios/scripts/kconfig/gconf.glade b/qemu/roms/seabios/scripts/kconfig/gconf.glade new file mode 100644 index 000000000..aa483cb32 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/gconf.glade @@ -0,0 +1,661 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> + +<glade-interface> + +<widget class="GtkWindow" id="window1"> + <property name="visible">True</property> + <property name="title" translatable="yes">Gtk Kernel Configurator</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="default_width">640</property> + <property name="default_height">480</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <signal name="destroy" handler="on_window1_destroy" object="window1"/> + <signal name="size_request" handler="on_window1_size_request" object="vpaned1" last_modification_time="Fri, 11 Jan 2002 16:17:11 GMT"/> + <signal name="delete_event" handler="on_window1_delete_event" object="window1" last_modification_time="Sun, 09 Mar 2003 19:42:46 GMT"/> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkMenuBar" id="menubar1"> + <property name="visible">True</property> + + <child> + <widget class="GtkMenuItem" id="file1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="file1_menu"> + + <child> + <widget class="GtkImageMenuItem" id="load1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Load a config file</property> + <property name="label" translatable="yes">_Load</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_load1_activate"/> + <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image39"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="save1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save the config in .config</property> + <property name="label" translatable="yes">_Save</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_save_activate"/> + <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image40"> + <property name="visible">True</property> + <property name="stock">gtk-save</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="save_as1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save the config in a file</property> + <property name="label" translatable="yes">Save _as</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_save_as1_activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image41"> + <property name="visible">True</property> + <property name="stock">gtk-save-as</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator1"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="quit1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Quit</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_quit1_activate"/> + <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image42"> + <property name="visible">True</property> + <property name="stock">gtk-quit</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="options1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Options</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="options1_menu"> + + <child> + <widget class="GtkCheckMenuItem" id="show_name1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show name</property> + <property name="label" translatable="yes">Show _name</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_name1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkCheckMenuItem" id="show_range1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show range (Y/M/N)</property> + <property name="label" translatable="yes">Show _range</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_range1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkCheckMenuItem" id="show_data1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show value of the option</property> + <property name="label" translatable="yes">Show _data</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_show_data1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkSeparatorMenuItem" id="separator2"> + <property name="visible">True</property> + </widget> + </child> + + <child> + <widget class="GtkRadioMenuItem" id="set_option_mode1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show normal options</property> + <property name="label" translatable="yes">Show normal options</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <signal name="activate" handler="on_set_option_mode1_activate"/> + </widget> + </child> + + <child> + <widget class="GtkRadioMenuItem" id="set_option_mode2"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show all options</property> + <property name="label" translatable="yes">Show all _options</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">set_option_mode1</property> + <signal name="activate" handler="on_set_option_mode2_activate"/> + </widget> + </child> + + <child> + <widget class="GtkRadioMenuItem" id="set_option_mode3"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Show all options with prompts</property> + <property name="label" translatable="yes">Show all prompt options</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <property name="group">set_option_mode1</property> + <signal name="activate" handler="on_set_option_mode3_activate"/> + </widget> + </child> + + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="help1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="help1_menu"> + + <child> + <widget class="GtkImageMenuItem" id="introduction1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Introduction</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_introduction1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/> + <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image43"> + <property name="visible">True</property> + <property name="stock">gtk-dialog-question</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="about1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_About</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_about1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/> + <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image44"> + <property name="visible">True</property> + <property name="stock">gtk-properties</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkImageMenuItem" id="license1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_License</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_license1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/> + + <child internal-child="image"> + <widget class="GtkImage" id="image45"> + <property name="visible">True</property> + <property name="stock">gtk-justify-fill</property> + <property name="icon_size">1</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHandleBox" id="handlebox1"> + <property name="visible">True</property> + <property name="shadow_type">GTK_SHADOW_OUT</property> + <property name="handle_position">GTK_POS_LEFT</property> + <property name="snap_edge">GTK_POS_TOP</property> + + <child> + <widget class="GtkToolbar" id="toolbar1"> + <property name="visible">True</property> + <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH</property> + <property name="tooltips">True</property> + <property name="show_arrow">True</property> + + <child> + <widget class="GtkToolButton" id="button1"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Goes up of one level (single view)</property> + <property name="label" translatable="yes">Back</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-undo</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_back_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolItem" id="toolitem1"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkVSeparator" id="vseparator1"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button2"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Load a config file</property> + <property name="label" translatable="yes">Load</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-open</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_load_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button3"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save a config file</property> + <property name="label" translatable="yes">Save</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-save</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_save_activate"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolItem" id="toolitem2"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkVSeparator" id="vseparator2"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button4"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Single view</property> + <property name="label" translatable="yes">Single</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-missing-image</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_single_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:39 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button5"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Split view</property> + <property name="label" translatable="yes">Split</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-missing-image</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_split_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:45 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button6"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Full view</property> + <property name="label" translatable="yes">Full</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-missing-image</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_full_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:50 GMT"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolItem" id="toolitem3"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkVSeparator" id="vseparator3"> + <property name="visible">True</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button7"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Collapse the whole tree in the right frame</property> + <property name="label" translatable="yes">Collapse</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-remove</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_collapse_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + + <child> + <widget class="GtkToolButton" id="button8"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Expand the whole tree in the right frame</property> + <property name="label" translatable="yes">Expand</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-add</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + <signal name="clicked" handler="on_expand_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHPaned" id="hpaned1"> + <property name="width_request">1</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">0</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">False</property> + <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:58:22 GMT"/> + <signal name="button_press_event" handler="on_treeview1_button_press_event" last_modification_time="Sun, 12 Jan 2003 16:03:52 GMT"/> + <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 16:11:44 GMT"/> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkVPaned" id="vpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="position">0</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow2"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">False</property> + <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:57:55 GMT"/> + <signal name="button_press_event" handler="on_treeview2_button_press_event" last_modification_time="Sun, 12 Jan 2003 15:57:58 GMT"/> + <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 15:58:01 GMT"/> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow3"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="textview3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_WORD</property> + <property name="cursor_visible">True</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes">Sorry, no help available for this option yet.</property> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/qemu/roms/seabios/scripts/kconfig/images.c b/qemu/roms/seabios/scripts/kconfig/images.c new file mode 100644 index 000000000..d4f84bd4a --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/images.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +static const char *xpm_load[] = { +"22 22 5 1", +". c None", +"# c #000000", +"c c #838100", +"a c #ffff00", +"b c #ffffff", +"......................", +"......................", +"......................", +"............####....#.", +"...........#....##.##.", +"..................###.", +".................####.", +".####...........#####.", +"#abab##########.......", +"#babababababab#.......", +"#ababababababa#.......", +"#babababababab#.......", +"#ababab###############", +"#babab##cccccccccccc##", +"#abab##cccccccccccc##.", +"#bab##cccccccccccc##..", +"#ab##cccccccccccc##...", +"#b##cccccccccccc##....", +"###cccccccccccc##.....", +"##cccccccccccc##......", +"###############.......", +"......................"}; + +static const char *xpm_save[] = { +"22 22 5 1", +". c None", +"# c #000000", +"a c #838100", +"b c #c5c2c5", +"c c #cdb6d5", +"......................", +".####################.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbcbb####.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aaa############aaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaa#############aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +"..##################..", +"......................"}; + +static const char *xpm_back[] = { +"22 22 3 1", +". c None", +"# c #000083", +"a c #838183", +"......................", +"......................", +"......................", +"......................", +"......................", +"...........######a....", +"..#......##########...", +"..##...####......##a..", +"..###.###.........##..", +"..######..........##..", +"..#####...........##..", +"..######..........##..", +"..#######.........##..", +"..########.......##a..", +"...............a###...", +"...............###....", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................"}; + +static const char *xpm_tree_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......................", +"......................"}; + +static const char *xpm_single_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"......................", +"......................"}; + +static const char *xpm_split_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......................", +"......................"}; + +static const char *xpm_symbol_no[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_symbol_mod[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . .. . ", +" . .... . ", +" . .... . ", +" . .. . ", +" . . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_symbol_yes[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . . . ", +" . .. . ", +" . . .. . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_choice_no[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .... ", +" .. .. ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .. .. ", +" .... ", +" "}; + +static const char *xpm_choice_yes[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .... ", +" .. .. ", +" . . ", +" . .. . ", +" . .... . ", +" . .... . ", +" . .. . ", +" . . ", +" .. .. ", +" .... ", +" "}; + +static const char *xpm_menu[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . .. . ", +" . .... . ", +" . ...... . ", +" . ...... . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_menu_inv[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" .......... ", +" .. ...... ", +" .. .... ", +" .. .. ", +" .. .. ", +" .. .... ", +" .. ...... ", +" .......... ", +" .......... ", +" "}; + +static const char *xpm_menuback[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . .. . ", +" . .... . ", +" . ...... . ", +" . ...... . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_void[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/qemu/roms/seabios/scripts/kconfig/kxgettext.c b/qemu/roms/seabios/scripts/kconfig/kxgettext.c new file mode 100644 index 000000000..2858738b2 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/kxgettext.c @@ -0,0 +1,235 @@ +/* + * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2005 + * + * Released under the terms of the GNU GPL v2.0 + */ + +#include <stdlib.h> +#include <string.h> + +#include "lkc.h" + +static char *escape(const char* text, char *bf, int len) +{ + char *bfp = bf; + int multiline = strchr(text, '\n') != NULL; + int eol = 0; + int textlen = strlen(text); + + if ((textlen > 0) && (text[textlen-1] == '\n')) + eol = 1; + + *bfp++ = '"'; + --len; + + if (multiline) { + *bfp++ = '"'; + *bfp++ = '\n'; + *bfp++ = '"'; + len -= 3; + } + + while (*text != '\0' && len > 1) { + if (*text == '"') + *bfp++ = '\\'; + else if (*text == '\n') { + *bfp++ = '\\'; + *bfp++ = 'n'; + *bfp++ = '"'; + *bfp++ = '\n'; + *bfp++ = '"'; + len -= 5; + ++text; + goto next; + } + else if (*text == '\\') { + *bfp++ = '\\'; + len--; + } + *bfp++ = *text++; +next: + --len; + } + + if (multiline && eol) + bfp -= 3; + + *bfp++ = '"'; + *bfp = '\0'; + + return bf; +} + +struct file_line { + struct file_line *next; + const char *file; + int lineno; +}; + +static struct file_line *file_line__new(const char *file, int lineno) +{ + struct file_line *self = malloc(sizeof(*self)); + + if (self == NULL) + goto out; + + self->file = file; + self->lineno = lineno; + self->next = NULL; +out: + return self; +} + +struct message { + const char *msg; + const char *option; + struct message *next; + struct file_line *files; +}; + +static struct message *message__list; + +static struct message *message__new(const char *msg, char *option, + const char *file, int lineno) +{ + struct message *self = malloc(sizeof(*self)); + + if (self == NULL) + goto out; + + self->files = file_line__new(file, lineno); + if (self->files == NULL) + goto out_fail; + + self->msg = strdup(msg); + if (self->msg == NULL) + goto out_fail_msg; + + self->option = option; + self->next = NULL; +out: + return self; +out_fail_msg: + free(self->files); +out_fail: + free(self); + self = NULL; + goto out; +} + +static struct message *mesage__find(const char *msg) +{ + struct message *m = message__list; + + while (m != NULL) { + if (strcmp(m->msg, msg) == 0) + break; + m = m->next; + } + + return m; +} + +static int message__add_file_line(struct message *self, const char *file, + int lineno) +{ + int rc = -1; + struct file_line *fl = file_line__new(file, lineno); + + if (fl == NULL) + goto out; + + fl->next = self->files; + self->files = fl; + rc = 0; +out: + return rc; +} + +static int message__add(const char *msg, char *option, const char *file, + int lineno) +{ + int rc = 0; + char bf[16384]; + char *escaped = escape(msg, bf, sizeof(bf)); + struct message *m = mesage__find(escaped); + + if (m != NULL) + rc = message__add_file_line(m, file, lineno); + else { + m = message__new(escaped, option, file, lineno); + + if (m != NULL) { + m->next = message__list; + message__list = m; + } else + rc = -1; + } + return rc; +} + +static void menu_build_message_list(struct menu *menu) +{ + struct menu *child; + + message__add(menu_get_prompt(menu), NULL, + menu->file == NULL ? "Root Menu" : menu->file->name, + menu->lineno); + + if (menu->sym != NULL && menu_has_help(menu)) + message__add(menu_get_help(menu), menu->sym->name, + menu->file == NULL ? "Root Menu" : menu->file->name, + menu->lineno); + + for (child = menu->list; child != NULL; child = child->next) + if (child->prompt != NULL) + menu_build_message_list(child); +} + +static void message__print_file_lineno(struct message *self) +{ + struct file_line *fl = self->files; + + putchar('\n'); + if (self->option != NULL) + printf("# %s:00000\n", self->option); + + printf("#: %s:%d", fl->file, fl->lineno); + fl = fl->next; + + while (fl != NULL) { + printf(", %s:%d", fl->file, fl->lineno); + fl = fl->next; + } + + putchar('\n'); +} + +static void message__print_gettext_msgid_msgstr(struct message *self) +{ + message__print_file_lineno(self); + + printf("msgid %s\n" + "msgstr \"\"\n", self->msg); +} + +static void menu__xgettext(void) +{ + struct message *m = message__list; + + while (m != NULL) { + /* skip empty lines ("") */ + if (strlen(m->msg) > sizeof("\"\"")) + message__print_gettext_msgid_msgstr(m); + m = m->next; + } +} + +int main(int ac, char **av) +{ + conf_parse(av[1]); + + menu_build_message_list(menu_get_root_menu(NULL)); + menu__xgettext(); + return 0; +} diff --git a/qemu/roms/seabios/scripts/kconfig/lex.zconf.c b/qemu/roms/seabios/scripts/kconfig/lex.zconf.c new file mode 100644 index 000000000..6eb039718 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lex.zconf.c @@ -0,0 +1,2430 @@ + +#line 3 "scripts/kconfig/lex.zconf.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer zconf_create_buffer +#define yy_delete_buffer zconf_delete_buffer +#define yy_flex_debug zconf_flex_debug +#define yy_init_buffer zconf_init_buffer +#define yy_flush_buffer zconf_flush_buffer +#define yy_load_buffer_state zconf_load_buffer_state +#define yy_switch_to_buffer zconf_switch_to_buffer +#define yyin zconfin +#define yyleng zconfleng +#define yylex zconflex +#define yylineno zconflineno +#define yyout zconfout +#define yyrestart zconfrestart +#define yytext zconftext +#define yywrap zconfwrap +#define yyalloc zconfalloc +#define yyrealloc zconfrealloc +#define yyfree zconffree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE zconfrestart(zconfin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int zconfleng; + +extern FILE *zconfin, *zconfout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up zconftext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via zconfrestart()), so that the user can continue scanning by + * just pointing zconfin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when zconftext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int zconfleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow zconfwrap()'s to do buffer switches + * instead of setting up a fresh zconfin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void zconfrestart (FILE *input_file ); +void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size ); +void zconf_delete_buffer (YY_BUFFER_STATE b ); +void zconf_flush_buffer (YY_BUFFER_STATE b ); +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void zconfpop_buffer_state (void ); + +static void zconfensure_buffer_stack (void ); +static void zconf_load_buffer_state (void ); +static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len ); + +void *zconfalloc (yy_size_t ); +void *zconfrealloc (void *,yy_size_t ); +void zconffree (void * ); + +#define yy_new_buffer zconf_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define zconfwrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0; + +typedef int yy_state_type; + +extern int zconflineno; + +int zconflineno = 1; + +extern char *zconftext; +#define yytext_ptr zconftext +static yyconst flex_int16_t yy_nxt[][17] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 16 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 16 + + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, + 33, 34, 35, 35, 36, 37, 38 + + }, + + { + 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, + 33, 34, 35, 35, 36, 37, 38 + }, + + { + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11 + }, + + { + 11, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12 + }, + + { + 11, -13, 39, 40, -13, -13, 41, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13 + }, + + { + 11, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14 + + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16 + }, + + { + 11, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17 + }, + + { + 11, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, -18, -18, 44, -18, -18, -18 + }, + + { + 11, 45, 45, -19, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45 + + }, + + { + 11, -20, 46, 47, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20 + }, + + { + 11, 48, -21, -21, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48 + }, + + { + 11, 49, 49, 50, 49, -22, 49, 49, -22, 49, + 49, 49, 49, 49, 49, -22, 49 + }, + + { + 11, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23 + }, + + { + 11, -24, -24, -24, -24, -24, -24, -24, -24, -24, + -24, -24, -24, -24, -24, -24, -24 + + }, + + { + 11, 51, 51, 52, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51 + }, + + { + 11, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26 + }, + + { + 11, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27 + }, + + { + 11, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, 53, -28, -28 + }, + + { + 11, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29 + + }, + + { + 11, 54, 54, -30, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54 + }, + + { + 11, -31, -31, -31, -31, -31, -31, 55, -31, -31, + -31, -31, -31, -31, -31, -31, -31 + }, + + { + 11, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32 + }, + + { + 11, -33, -33, -33, -33, -33, -33, -33, -33, -33, + -33, -33, -33, -33, -33, -33, -33 + }, + + { + 11, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, 56, 57, 57, -34, -34, -34 + + }, + + { + 11, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, 57, 57, 57, -35, -35, -35 + }, + + { + 11, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36 + }, + + { + 11, -37, -37, 58, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37 + }, + + { + 11, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, 59 + }, + + { + 11, -39, 39, 40, -39, -39, 41, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39 + + }, + + { + 11, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40 + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, -43, -43, -43, -43, -43, -43, -43, -43, -43, + -43, -43, -43, -43, -43, -43, -43 + }, + + { + 11, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, 44, -44, -44, -44 + + }, + + { + 11, 45, 45, -45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45 + }, + + { + 11, -46, 46, 47, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46 + }, + + { + 11, 48, -47, -47, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48 + }, + + { + 11, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48 + }, + + { + 11, 49, 49, 50, 49, -49, 49, 49, -49, 49, + 49, 49, 49, 49, 49, -49, 49 + + }, + + { + 11, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50 + }, + + { + 11, -51, -51, 52, -51, -51, -51, -51, -51, -51, + -51, -51, -51, -51, -51, -51, -51 + }, + + { + 11, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52 + }, + + { + 11, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53 + }, + + { + 11, 54, 54, -54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54 + + }, + + { + 11, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55 + }, + + { + 11, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, 60, 57, 57, -56, -56, -56 + }, + + { + 11, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, 57, 57, 57, -57, -57, -57 + }, + + { + 11, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58 + }, + + { + 11, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59 + + }, + + { + 11, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, 57, 57, 57, -60, -60, -60 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up zconftext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + zconfleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 33 +#define YY_END_OF_BUFFER 34 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[61] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 34, 5, 4, 2, 3, 7, 8, 6, 32, 29, + 31, 24, 28, 27, 26, 22, 17, 13, 16, 20, + 22, 11, 12, 19, 19, 14, 22, 22, 4, 2, + 3, 3, 1, 6, 32, 29, 31, 30, 24, 23, + 26, 25, 15, 20, 9, 19, 19, 21, 10, 18 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 7, 8, 9, + 10, 1, 1, 1, 11, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, + 14, 1, 1, 1, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 15, 1, 1, 13, 1, 13, 13, 13, 13, + + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 1, 16, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int zconf_flex_debug; +int zconf_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *zconftext; +#define YY_NO_INPUT 1 + +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define LKC_DIRECT_LINK +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +static void new_string(void) +{ + text = malloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +static void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +static void alloc_string(const char *str, int size) +{ + text = malloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} + +#define INITIAL 0 +#define COMMAND 1 +#define HELP 2 +#define STRING 3 +#define PARAM 4 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int zconflex_destroy (void ); + +int zconfget_debug (void ); + +void zconfset_debug (int debug_flag ); + +YY_EXTRA_TYPE zconfget_extra (void ); + +void zconfset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *zconfget_in (void ); + +void zconfset_in (FILE * in_str ); + +FILE *zconfget_out (void ); + +void zconfset_out (FILE * out_str ); + +int zconfget_leng (void ); + +char *zconfget_text (void ); + +int zconfget_lineno (void ); + +void zconfset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int zconfwrap (void ); +#else +extern int zconfwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( zconftext, zconfleng, 1, zconfout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(zconfin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int zconflex (void); + +#define YY_DECL int zconflex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after zconftext and zconfleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + int str = 0; + int ts, i; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! zconfin ) + zconfin = stdin; + + if ( ! zconfout ) + zconfout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of zconftext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +/* rule 1 can match eol */ +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + return T_EOL; +} + YY_BREAK +case 3: +YY_RULE_SETUP + + YY_BREAK +case 4: +YY_RULE_SETUP +{ + BEGIN(COMMAND); +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ + unput(zconftext[0]); + BEGIN(COMMAND); +} + YY_BREAK + +case 6: +YY_RULE_SETUP +{ + struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 7: +YY_RULE_SETUP + + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +{ + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } + YY_BREAK + +case 9: +YY_RULE_SETUP +return T_AND; + YY_BREAK +case 10: +YY_RULE_SETUP +return T_OR; + YY_BREAK +case 11: +YY_RULE_SETUP +return T_OPEN_PAREN; + YY_BREAK +case 12: +YY_RULE_SETUP +return T_CLOSE_PAREN; + YY_BREAK +case 13: +YY_RULE_SETUP +return T_NOT; + YY_BREAK +case 14: +YY_RULE_SETUP +return T_EQUAL; + YY_BREAK +case 15: +YY_RULE_SETUP +return T_UNEQUAL; + YY_BREAK +case 16: +YY_RULE_SETUP +{ + str = zconftext[0]; + new_string(); + BEGIN(STRING); + } + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +BEGIN(INITIAL); current_file->lineno++; return T_EOL; + YY_BREAK +case 18: +YY_RULE_SETUP +/* ignore */ + YY_BREAK +case 19: +YY_RULE_SETUP +{ + struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 20: +YY_RULE_SETUP +/* comment */ + YY_BREAK +case 21: +/* rule 21 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 22: +YY_RULE_SETUP + + YY_BREAK +case YY_STATE_EOF(PARAM): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 23: +/* rule 23 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 24: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + } + YY_BREAK +case 25: +/* rule 25 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 26: +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + } + YY_BREAK +case 27: +YY_RULE_SETUP +{ + if (str == zconftext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(zconftext, 1); + } + YY_BREAK +case 28: +/* rule 28 can match eol */ +YY_RULE_SETUP +{ + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + YY_BREAK +case YY_STATE_EOF(STRING): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 29: +YY_RULE_SETUP +{ + ts = 0; + for (i = 0; i < zconfleng; i++) { + if (zconftext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + YY_BREAK +case 30: +/* rule 30 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK +case 31: +/* rule 31 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + append_string("\n", 1); + } + YY_BREAK +case 32: +YY_RULE_SETUP +{ + while (zconfleng) { + if ((zconftext[zconfleng-1] != ' ') && (zconftext[zconfleng-1] != '\t')) + break; + zconfleng--; + } + append_string(zconftext, zconfleng); + if (!first_ts) + first_ts = last_ts; + } + YY_BREAK +case YY_STATE_EOF(HELP): +{ + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK + +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(COMMAND): +{ + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(zconfin); + yyterminate(); +} + YY_BREAK +case 33: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed zconfin at a new source and called + * zconflex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( zconfwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * zconftext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of zconflex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + zconfrestart(zconfin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) zconfrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up zconftext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + zconfrestart(zconfin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( zconfwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve zconftext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void zconfrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_init_buffer(YY_CURRENT_BUFFER,input_file ); + zconf_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * zconfpop_buffer_state(); + * zconfpush_buffer_state(new_buffer); + */ + zconfensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + zconf_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (zconfwrap()) processing, but the only time this flag + * is looked at is after zconfwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void zconf_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + zconf_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with zconf_create_buffer() + * + */ + void zconf_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + zconffree((void *) b->yy_ch_buf ); + + zconffree((void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a zconfrestart() or at EOF. + */ + static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + zconf_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then zconf_init_buffer was _probably_ + * called from zconfrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void zconf_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + zconf_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + zconfensure_buffer_stack(); + + /* This block is copied from zconf_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from zconf_switch_to_buffer. */ + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void zconfpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void zconfensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + zconf_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to zconflex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * zconf_scan_bytes() instead. + */ +YY_BUFFER_STATE zconf_scan_string (yyconst char * yystr ) +{ + + return zconf_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) zconfalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = zconf_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + zconftext[zconfleng] = (yy_hold_char); \ + (yy_c_buf_p) = zconftext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + zconfleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int zconfget_lineno (void) +{ + + return zconflineno; +} + +/** Get the input stream. + * + */ +FILE *zconfget_in (void) +{ + return zconfin; +} + +/** Get the output stream. + * + */ +FILE *zconfget_out (void) +{ + return zconfout; +} + +/** Get the length of the current token. + * + */ +int zconfget_leng (void) +{ + return zconfleng; +} + +/** Get the current token. + * + */ + +char *zconfget_text (void) +{ + return zconftext; +} + +/** Set the current line number. + * @param line_number + * + */ +void zconfset_lineno (int line_number ) +{ + + zconflineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see zconf_switch_to_buffer + */ +void zconfset_in (FILE * in_str ) +{ + zconfin = in_str ; +} + +void zconfset_out (FILE * out_str ) +{ + zconfout = out_str ; +} + +int zconfget_debug (void) +{ + return zconf_flex_debug; +} + +void zconfset_debug (int bdebug ) +{ + zconf_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from zconflex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + zconfin = stdin; + zconfout = stdout; +#else + zconfin = (FILE *) 0; + zconfout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * zconflex_init() + */ + return 0; +} + +/* zconflex_destroy is for both reentrant and non-reentrant scanners. */ +int zconflex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + zconfpop_buffer_state(); + } + + /* Destroy the stack itself. */ + zconffree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * zconflex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *zconfalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *zconfrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void zconffree (void * ptr ) +{ + free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = malloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; + current_file->flags = FILE_BUSY; +} + +void zconf_nextfile(const char *name) +{ + struct file *file = file_lookup(name); + struct buffer *buf = malloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + zconfin = zconf_fopen(file->name); + if (!zconfin) { + printf("%s:%d: can't open file \"%s\"\n", + zconf_curname(), zconf_lineno(), file->name); + exit(1); + } + zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + if (file->flags & FILE_BUSY) { + printf("%s:%d: do not source '%s' from itself\n", + zconf_curname(), zconf_lineno(), name); + exit(1); + } + if (file->flags & FILE_SCANNED) { + printf("%s:%d: file '%s' is already sourced from '%s'\n", + zconf_curname(), zconf_lineno(), name, + file->parent->name); + exit(1); + } + file->flags |= FILE_BUSY; + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file->flags |= FILE_SCANNED; + current_file->flags &= ~FILE_BUSY; + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(zconfin); + zconf_delete_buffer(YY_CURRENT_BUFFER); + zconf_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +const char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : "<none>"; +} + diff --git a/qemu/roms/seabios/scripts/kconfig/list.h b/qemu/roms/seabios/scripts/kconfig/list.h new file mode 100644 index 000000000..685d80e1b --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/list.h @@ -0,0 +1,131 @@ +#ifndef LIST_H +#define LIST_H + +/* + * Copied from include/linux/... + */ + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + + +struct list_head { + struct list_head *next, *prev; +}; + + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *_new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *_new, struct list_head *head) +{ + __list_add(_new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = (struct list_head*)LIST_POISON1; + entry->prev = (struct list_head*)LIST_POISON2; +} +#endif diff --git a/qemu/roms/seabios/scripts/kconfig/lkc.h b/qemu/roms/seabios/scripts/kconfig/lkc.h new file mode 100644 index 000000000..d5daa7af8 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lkc.h @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef LKC_H +#define LKC_H + +#include "expr.h" + +#ifndef KBUILD_NO_NLS +# include <libintl.h> +#else +static inline const char *gettext(const char *txt) { return txt; } +static inline void textdomain(const char *domainname) {} +static inline void bindtextdomain(const char *name, const char *dir) {} +static inline char *bind_textdomain_codeset(const char *dn, char *c) { return c; } +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define P(name,type,arg) extern type name arg +#include "lkc_proto.h" +#undef P + +#define SRCTREE "srctree" + +#ifndef PACKAGE +#define PACKAGE "linux" +#endif + +#define LOCALEDIR "/usr/share/locale" + +#define _(text) gettext(text) +#define N_(text) (text) + +#ifndef CONFIG_ +#define CONFIG_ "CONFIG_" +#endif +static inline const char *CONFIG_prefix(void) +{ + return getenv( "CONFIG_" ) ?: CONFIG_; +} +#undef CONFIG_ +#define CONFIG_ CONFIG_prefix() + +#define TF_COMMAND 0x0001 +#define TF_PARAM 0x0002 +#define TF_OPTION 0x0004 + +enum conf_def_mode { + def_default, + def_yes, + def_mod, + def_no, + def_random +}; + +#define T_OPT_MODULES 1 +#define T_OPT_DEFCONFIG_LIST 2 +#define T_OPT_ENV 3 +#define T_OPT_ALLNOCONFIG_Y 4 + +struct kconf_id { + int name; + int token; + unsigned int flags; + enum symbol_type stype; +}; + +extern int zconfdebug; + +int zconfparse(void); +void zconfdump(FILE *out); +void zconf_starthelp(void); +FILE *zconf_fopen(const char *name); +void zconf_initscan(const char *name); +void zconf_nextfile(const char *name); +int zconf_lineno(void); +const char *zconf_curname(void); + +/* confdata.c */ +const char *conf_get_configname(void); +const char *conf_get_autoconfig_name(void); +char *conf_get_default_confname(void); +void sym_set_change_count(int count); +void sym_add_change_count(int count); +bool conf_set_all_new_symbols(enum conf_def_mode mode); +void set_all_choice_values(struct symbol *csym); + +struct conf_printer { + void (*print_symbol)(FILE *, struct symbol *, const char *, void *); + void (*print_comment)(FILE *, const char *, void *); +}; + +/* confdata.c and expr.c */ +static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out) +{ + assert(len != 0); + + if (fwrite(str, len, count, out) != count) + fprintf(stderr, "Error in writing or end of file.\n"); +} + +/* menu.c */ +void _menu_init(void); +void menu_warn(struct menu *menu, const char *fmt, ...); +struct menu *menu_add_menu(void); +void menu_end_menu(void); +void menu_add_entry(struct symbol *sym); +void menu_end_entry(void); +void menu_add_dep(struct expr *dep); +void menu_add_visibility(struct expr *dep); +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep); +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); +void menu_add_option(int token, char *arg); +void menu_finalize(struct menu *parent); +void menu_set_type(int type); + +/* util.c */ +struct file *file_lookup(const char *name); +int file_write_dep(const char *name); +void *xmalloc(size_t size); +void *xcalloc(size_t nmemb, size_t size); + +struct gstr { + size_t len; + char *s; + /* + * when max_width is not zero long lines in string s (if any) get + * wrapped not to exceed the max_width value + */ + int max_width; +}; +struct gstr str_new(void); +struct gstr str_assign(const char *s); +void str_free(struct gstr *gs); +void str_append(struct gstr *gs, const char *s); +void str_printf(struct gstr *gs, const char *fmt, ...); +const char *str_get(struct gstr *gs); + +/* symbol.c */ +extern struct expr *sym_env_list; + +void sym_init(void); +void sym_clear_all_valid(void); +void sym_set_all_changed(void); +void sym_set_changed(struct symbol *sym); +struct symbol *sym_choice_default(struct symbol *sym); +const char *sym_get_string_default(struct symbol *sym); +struct symbol *sym_check_deps(struct symbol *sym); +struct property *prop_alloc(enum prop_type type, struct symbol *sym); +struct symbol *prop_get_symbol(struct property *prop); +struct property *sym_get_env_prop(struct symbol *sym); + +static inline tristate sym_get_tristate_value(struct symbol *sym) +{ + return sym->curr.tri; +} + + +static inline struct symbol *sym_get_choice_value(struct symbol *sym) +{ + return (struct symbol *)sym->curr.val; +} + +static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) +{ + return sym_set_tristate_value(chval, yes); +} + +static inline bool sym_is_choice(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICE ? true : false; +} + +static inline bool sym_is_choice_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICEVAL ? true : false; +} + +static inline bool sym_is_optional(struct symbol *sym) +{ + return sym->flags & SYMBOL_OPTIONAL ? true : false; +} + +static inline bool sym_has_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_DEF_USER ? true : false; +} + +#ifdef __cplusplus +} +#endif + +#endif /* LKC_H */ diff --git a/qemu/roms/seabios/scripts/kconfig/lkc_proto.h b/qemu/roms/seabios/scripts/kconfig/lkc_proto.h new file mode 100644 index 000000000..ecdb9659b --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lkc_proto.h @@ -0,0 +1,57 @@ +#include <stdarg.h> + +/* confdata.c */ +P(conf_parse,void,(const char *name)); +P(conf_read,int,(const char *name)); +P(conf_read_simple,int,(const char *name, int)); +P(conf_write_defconfig,int,(const char *name)); +P(conf_write,int,(const char *name)); +P(conf_write_autoconf,int,(void)); +P(conf_get_changed,bool,(void)); +P(conf_set_changed_callback, void,(void (*fn)(void))); +P(conf_set_message_callback, void,(void (*fn)(const char *fmt, va_list ap))); + +/* menu.c */ +P(rootmenu,struct menu,); + +P(menu_is_empty, bool, (struct menu *menu)); +P(menu_is_visible, bool, (struct menu *menu)); +P(menu_has_prompt, bool, (struct menu *menu)); +P(menu_get_prompt,const char *,(struct menu *menu)); +P(menu_get_root_menu,struct menu *,(struct menu *menu)); +P(menu_get_parent_menu,struct menu *,(struct menu *menu)); +P(menu_has_help,bool,(struct menu *menu)); +P(menu_get_help,const char *,(struct menu *menu)); +P(get_symbol_str, void, (struct gstr *r, struct symbol *sym, struct list_head + *head)); +P(get_relations_str, struct gstr, (struct symbol **sym_arr, struct list_head + *head)); +P(menu_get_ext_help,void,(struct menu *menu, struct gstr *help)); + +/* symbol.c */ +P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]); + +P(sym_lookup,struct symbol *,(const char *name, int flags)); +P(sym_find,struct symbol *,(const char *name)); +P(sym_expand_string_value,const char *,(const char *in)); +P(sym_escape_string_value, const char *,(const char *in)); +P(sym_re_search,struct symbol **,(const char *pattern)); +P(sym_type_name,const char *,(enum symbol_type type)); +P(sym_calc_value,void,(struct symbol *sym)); +P(sym_get_type,enum symbol_type,(struct symbol *sym)); +P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri)); +P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri)); +P(sym_toggle_tristate_value,tristate,(struct symbol *sym)); +P(sym_string_valid,bool,(struct symbol *sym, const char *newval)); +P(sym_string_within_range,bool,(struct symbol *sym, const char *str)); +P(sym_set_string_value,bool,(struct symbol *sym, const char *newval)); +P(sym_is_changable,bool,(struct symbol *sym)); +P(sym_get_choice_prop,struct property *,(struct symbol *sym)); +P(sym_get_default_prop,struct property *,(struct symbol *sym)); +P(sym_get_string_value,const char *,(struct symbol *sym)); + +P(prop_get_type_name,const char *,(enum prop_type type)); + +/* expr.c */ +P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); +P(expr_print,void,(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken)); diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/.gitignore b/qemu/roms/seabios/scripts/kconfig/lxdialog/.gitignore new file mode 100644 index 000000000..90b08ff02 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/.gitignore @@ -0,0 +1,4 @@ +# +# Generated files +# +lxdialog diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/BIG.FAT.WARNING b/qemu/roms/seabios/scripts/kconfig/lxdialog/BIG.FAT.WARNING new file mode 100644 index 000000000..a8999d82b --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/BIG.FAT.WARNING @@ -0,0 +1,4 @@ +This is NOT the official version of dialog. This version has been +significantly modified from the original. It is for use by the Linux +kernel configuration script. Please do not bother Savio Lam with +questions about this program. diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/check-lxdialog.sh b/qemu/roms/seabios/scripts/kconfig/lxdialog/check-lxdialog.sh new file mode 100644 index 000000000..9d2a4c585 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/check-lxdialog.sh @@ -0,0 +1,87 @@ +#!/bin/sh +# Check ncurses compatibility + +# What library to link +ldflags() +{ + pkg-config --libs ncursesw 2>/dev/null && exit + pkg-config --libs ncurses 2>/dev/null && exit + for ext in so a dll.a dylib ; do + for lib in ncursesw ncurses curses ; do + $cc -print-file-name=lib${lib}.${ext} | grep -q / + if [ $? -eq 0 ]; then + echo "-l${lib}" + exit + fi + done + done + exit 1 +} + +# Where is ncurses.h? +ccflags() +{ + if [ -f /usr/include/ncursesw/curses.h ]; then + echo '-I/usr/include/ncursesw -DCURSES_LOC="<curses.h>"' + echo ' -DNCURSES_WIDECHAR=1' + elif [ -f /usr/include/ncurses/ncurses.h ]; then + echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"' + elif [ -f /usr/include/ncurses/curses.h ]; then + echo '-I/usr/include/ncurses -DCURSES_LOC="<curses.h>"' + elif [ -f /usr/include/ncurses.h ]; then + echo '-DCURSES_LOC="<ncurses.h>"' + else + echo '-DCURSES_LOC="<curses.h>"' + fi +} + +# Temp file, try to clean up after us +tmp=.lxdialog.tmp +trap "rm -f $tmp" 0 1 2 3 15 + +# Check if we can link to ncurses +check() { + $cc -x c - -o $tmp 2>/dev/null <<'EOF' +#include CURSES_LOC +main() {} +EOF + if [ $? != 0 ]; then + echo " *** Unable to find the ncurses libraries or the" 1>&2 + echo " *** required header files." 1>&2 + echo " *** 'make menuconfig' requires the ncurses libraries." 1>&2 + echo " *** " 1>&2 + echo " *** Install ncurses (ncurses-devel) and try again." 1>&2 + echo " *** " 1>&2 + exit 1 + fi +} + +usage() { + printf "Usage: $0 [-check compiler options|-ccflags|-ldflags compiler options]\n" +} + +if [ $# -eq 0 ]; then + usage + exit 1 +fi + +cc="" +case "$1" in + "-check") + shift + cc="$@" + check + ;; + "-ccflags") + ccflags + ;; + "-ldflags") + shift + cc="$@" + ldflags + ;; + "*") + usage + exit 1 + ;; +esac diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/checklist.c b/qemu/roms/seabios/scripts/kconfig/lxdialog/checklist.c new file mode 100644 index 000000000..8d016faa2 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/checklist.c @@ -0,0 +1,332 @@ +/* + * checklist.c -- implements the checklist box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension + * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static int list_width, check_x, item_x; + +/* + * Print list item + */ +static void print_item(WINDOW * win, int choice, int selected) +{ + int i; + char *list_item = malloc(list_width + 1); + + strncpy(list_item, item_str(), list_width - item_x); + list_item[list_width - item_x] = '\0'; + + /* Clear 'residue' of last item */ + wattrset(win, dlg.menubox.atr); + wmove(win, choice, 0); + for (i = 0; i < list_width; i++) + waddch(win, ' '); + + wmove(win, choice, check_x); + wattrset(win, selected ? dlg.check_selected.atr + : dlg.check.atr); + if (!item_is_tag(':')) + wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' '); + + wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr); + mvwaddch(win, choice, item_x, list_item[0]); + wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr); + waddstr(win, list_item + 1); + if (selected) { + wmove(win, choice, check_x + 1); + wrefresh(win); + } + free(list_item); +} + +/* + * Print the scroll indicators. + */ +static void print_arrows(WINDOW * win, int choice, int item_no, int scroll, + int y, int x, int height) +{ + wmove(win, y, x); + + if (scroll > 0) { + wattrset(win, dlg.uarrow.atr); + waddch(win, ACS_UARROW); + waddstr(win, "(-)"); + } else { + wattrset(win, dlg.menubox.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + choice < item_no - 1)) { + wattrset(win, dlg.darrow.atr); + waddch(win, ACS_DARROW); + waddstr(win, "(+)"); + } else { + wattrset(win, dlg.menubox_border.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } +} + +/* + * Display the termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button(dialog, gettext("Select"), y, x, selected == 0); + print_button(dialog, gettext(" Help "), y, x + 14, selected == 1); + + wmove(dialog, y, x + 1 + 14 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box with a list of options that can be turned on or off + * in the style of radiolist (only one option turned on at a time). + */ +int dialog_checklist(const char *title, const char *prompt, int height, + int width, int list_height) +{ + int i, x, y, box_x, box_y; + int key = 0, button = 0, choice = 0, scroll = 0, max_choice; + WINDOW *dialog, *list; + + /* which item to highlight */ + item_foreach() { + if (item_is_tag('X')) + choice = item_n(); + if (item_is_selected()) { + choice = item_n(); + break; + } + } + +do_resize: + if (getmaxy(stdscr) < (height + CHECKLIST_HEIGTH_MIN)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) < (width + CHECKLIST_WIDTH_MIN)) + return -ERRDISPLAYTOOSMALL; + + max_choice = MIN(list_height, item_count()); + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + list_width = width - 6; + box_y = height - list_height - 5; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin(dialog, list_height, list_width, y + box_y + 1, + x + box_x + 1); + + keypad(list, TRUE); + + /* draw a box around the list items */ + draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, + dlg.menubox_border.atr, dlg.menubox.atr); + + /* Find length of longest item in order to center checklist */ + check_x = 0; + item_foreach() + check_x = MAX(check_x, strlen(item_str()) + 4); + check_x = MIN(check_x, list_width); + + check_x = (list_width - check_x) / 2; + item_x = check_x + 4; + + if (choice >= list_height) { + scroll = choice - list_height + 1; + choice -= scroll; + } + + /* Print the list */ + for (i = 0; i < max_choice; i++) { + item_set(scroll + i); + print_item(list, i, i == choice); + } + + print_arrows(dialog, choice, item_count(), scroll, + box_y, box_x + check_x + 5, list_height); + + print_buttons(dialog, height, width, 0); + + wnoutrefresh(dialog); + wnoutrefresh(list); + doupdate(); + + while (key != KEY_ESC) { + key = wgetch(dialog); + + for (i = 0; i < max_choice; i++) { + item_set(i + scroll); + if (toupper(key) == toupper(item_str()[0])) + break; + } + + if (i < max_choice || key == KEY_UP || key == KEY_DOWN || + key == '+' || key == '-') { + if (key == KEY_UP || key == '-') { + if (!choice) { + if (!scroll) + continue; + /* Scroll list down */ + if (list_height > 1) { + /* De-highlight current first item */ + item_set(scroll); + print_item(list, 0, FALSE); + scrollok(list, TRUE); + wscrl(list, -1); + scrollok(list, FALSE); + } + scroll--; + item_set(scroll); + print_item(list, 0, TRUE); + print_arrows(dialog, choice, item_count(), + scroll, box_y, box_x + check_x + 5, list_height); + + wnoutrefresh(dialog); + wrefresh(list); + + continue; /* wait for another key press */ + } else + i = choice - 1; + } else if (key == KEY_DOWN || key == '+') { + if (choice == max_choice - 1) { + if (scroll + choice >= item_count() - 1) + continue; + /* Scroll list up */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + item_set(scroll + max_choice - 1); + print_item(list, + max_choice - 1, + FALSE); + scrollok(list, TRUE); + wscrl(list, 1); + scrollok(list, FALSE); + } + scroll++; + item_set(scroll + max_choice - 1); + print_item(list, max_choice - 1, TRUE); + + print_arrows(dialog, choice, item_count(), + scroll, box_y, box_x + check_x + 5, list_height); + + wnoutrefresh(dialog); + wrefresh(list); + + continue; /* wait for another key press */ + } else + i = choice + 1; + } + if (i != choice) { + /* De-highlight current item */ + item_set(scroll + choice); + print_item(list, choice, FALSE); + /* Highlight new item */ + choice = i; + item_set(scroll + choice); + print_item(list, choice, TRUE); + wnoutrefresh(dialog); + wrefresh(list); + } + continue; /* wait for another key press */ + } + switch (key) { + case 'H': + case 'h': + case '?': + button = 1; + /* fall-through */ + case 'S': + case 's': + case ' ': + case '\n': + item_foreach() + item_set_selected(0); + item_set(scroll + choice); + item_set_selected(1); + delwin(list); + delwin(dialog); + return button; + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(dialog); + break; + case 'X': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(list); + delwin(dialog); + on_key_resize(); + goto do_resize; + } + + /* Now, update everything... */ + doupdate(); + } + delwin(list); + delwin(dialog); + return key; /* ESC pressed */ +} diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/dialog.h b/qemu/roms/seabios/scripts/kconfig/lxdialog/dialog.h new file mode 100644 index 000000000..b4343d384 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/dialog.h @@ -0,0 +1,257 @@ +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#ifndef KBUILD_NO_NLS +# include <libintl.h> +#else +# define gettext(Msgid) ((const char *) (Msgid)) +#endif + +#ifdef __sun__ +#define CURS_MACROS +#endif +#include CURSES_LOC + +/* + * Colors in ncurses 1.9.9e do not work properly since foreground and + * background colors are OR'd rather than separately masked. This version + * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible + * with standard curses. The simplest fix (to make this work with standard + * curses) uses the wbkgdset() function, not used in the original hack. + * Turn it off if we're building with 1.9.9e, since it just confuses things. + */ +#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE) +#define OLD_NCURSES 1 +#undef wbkgdset +#define wbkgdset(w,p) /*nothing */ +#else +#define OLD_NCURSES 0 +#endif + +#define TR(params) _tracef params + +#define KEY_ESC 27 +#define TAB 9 +#define MAX_LEN 2048 +#define BUF_SIZE (10*1024) +#define MIN(x,y) (x < y ? x : y) +#define MAX(x,y) (x > y ? x : y) + +#ifndef ACS_ULCORNER +#define ACS_ULCORNER '+' +#endif +#ifndef ACS_LLCORNER +#define ACS_LLCORNER '+' +#endif +#ifndef ACS_URCORNER +#define ACS_URCORNER '+' +#endif +#ifndef ACS_LRCORNER +#define ACS_LRCORNER '+' +#endif +#ifndef ACS_HLINE +#define ACS_HLINE '-' +#endif +#ifndef ACS_VLINE +#define ACS_VLINE '|' +#endif +#ifndef ACS_LTEE +#define ACS_LTEE '+' +#endif +#ifndef ACS_RTEE +#define ACS_RTEE '+' +#endif +#ifndef ACS_UARROW +#define ACS_UARROW '^' +#endif +#ifndef ACS_DARROW +#define ACS_DARROW 'v' +#endif + +/* error return codes */ +#define ERRDISPLAYTOOSMALL (KEY_MAX + 1) + +/* + * Color definitions + */ +struct dialog_color { + chtype atr; /* Color attribute */ + int fg; /* foreground */ + int bg; /* background */ + int hl; /* highlight this item */ +}; + +struct subtitle_list { + struct subtitle_list *next; + const char *text; +}; + +struct dialog_info { + const char *backtitle; + struct subtitle_list *subtitles; + struct dialog_color screen; + struct dialog_color shadow; + struct dialog_color dialog; + struct dialog_color title; + struct dialog_color border; + struct dialog_color button_active; + struct dialog_color button_inactive; + struct dialog_color button_key_active; + struct dialog_color button_key_inactive; + struct dialog_color button_label_active; + struct dialog_color button_label_inactive; + struct dialog_color inputbox; + struct dialog_color inputbox_border; + struct dialog_color searchbox; + struct dialog_color searchbox_title; + struct dialog_color searchbox_border; + struct dialog_color position_indicator; + struct dialog_color menubox; + struct dialog_color menubox_border; + struct dialog_color item; + struct dialog_color item_selected; + struct dialog_color tag; + struct dialog_color tag_selected; + struct dialog_color tag_key; + struct dialog_color tag_key_selected; + struct dialog_color check; + struct dialog_color check_selected; + struct dialog_color uarrow; + struct dialog_color darrow; +}; + +/* + * Global variables + */ +extern struct dialog_info dlg; +extern char dialog_input_result[]; +extern int saved_x, saved_y; /* Needed in signal handler in mconf.c */ + +/* + * Function prototypes + */ + +/* item list as used by checklist and menubox */ +void item_reset(void); +void item_make(const char *fmt, ...); +void item_add_str(const char *fmt, ...); +void item_set_tag(char tag); +void item_set_data(void *p); +void item_set_selected(int val); +int item_activate_selected(void); +void *item_data(void); +char item_tag(void); + +/* item list manipulation for lxdialog use */ +#define MAXITEMSTR 200 +struct dialog_item { + char str[MAXITEMSTR]; /* promtp displayed */ + char tag; + void *data; /* pointer to menu item - used by menubox+checklist */ + int selected; /* Set to 1 by dialog_*() function if selected. */ +}; + +/* list of lialog_items */ +struct dialog_list { + struct dialog_item node; + struct dialog_list *next; +}; + +extern struct dialog_list *item_cur; +extern struct dialog_list item_nil; +extern struct dialog_list *item_head; + +int item_count(void); +void item_set(int n); +int item_n(void); +const char *item_str(void); +int item_is_selected(void); +int item_is_tag(char tag); +#define item_foreach() \ + for (item_cur = item_head ? item_head: item_cur; \ + item_cur && (item_cur != &item_nil); item_cur = item_cur->next) + +/* generic key handlers */ +int on_key_esc(WINDOW *win); +int on_key_resize(void); + +/* minimum (re)size values */ +#define CHECKLIST_HEIGTH_MIN 6 /* For dialog_checklist() */ +#define CHECKLIST_WIDTH_MIN 6 +#define INPUTBOX_HEIGTH_MIN 2 /* For dialog_inputbox() */ +#define INPUTBOX_WIDTH_MIN 2 +#define MENUBOX_HEIGTH_MIN 15 /* For dialog_menu() */ +#define MENUBOX_WIDTH_MIN 65 +#define TEXTBOX_HEIGTH_MIN 8 /* For dialog_textbox() */ +#define TEXTBOX_WIDTH_MIN 8 +#define YESNO_HEIGTH_MIN 4 /* For dialog_yesno() */ +#define YESNO_WIDTH_MIN 4 +#define WINDOW_HEIGTH_MIN 19 /* For init_dialog() */ +#define WINDOW_WIDTH_MIN 80 + +int init_dialog(const char *backtitle); +void set_dialog_backtitle(const char *backtitle); +void set_dialog_subtitles(struct subtitle_list *subtitles); +void end_dialog(int x, int y); +void attr_clear(WINDOW * win, int height, int width, chtype attr); +void dialog_clear(void); +void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x); +void print_button(WINDOW * win, const char *label, int y, int x, int selected); +void print_title(WINDOW *dialog, const char *title, int width); +void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box, + chtype border); +void draw_shadow(WINDOW * win, int y, int x, int height, int width); + +int first_alpha(const char *string, const char *exempt); +int dialog_yesno(const char *title, const char *prompt, int height, int width); +int dialog_msgbox(const char *title, const char *prompt, int height, + int width, int pause); + + +typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void + *_data); +int dialog_textbox(const char *title, char *tbuf, int initial_height, + int initial_width, int *keys, int *_vscroll, int *_hscroll, + update_text_fn update_text, void *data); +int dialog_menu(const char *title, const char *prompt, + const void *selected, int *s_scroll); +int dialog_checklist(const char *title, const char *prompt, int height, + int width, int list_height); +int dialog_inputbox(const char *title, const char *prompt, int height, + int width, const char *init); + +/* + * This is the base for fictitious keys, which activate + * the buttons. + * + * Mouse-generated keys are the following: + * -- the first 32 are used as numbers, in addition to '0'-'9' + * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o') + * -- uppercase chars are used to invoke the button (M_EVENT + 'O') + */ +#define M_EVENT (KEY_MAX+1) diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/inputbox.c b/qemu/roms/seabios/scripts/kconfig/lxdialog/inputbox.c new file mode 100644 index 000000000..d58de1dc5 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/inputbox.c @@ -0,0 +1,301 @@ +/* + * inputbox.c -- implements the input box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +char dialog_input_result[MAX_LEN + 1]; + +/* + * Print the termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button(dialog, gettext(" Ok "), y, x, selected == 0); + print_button(dialog, gettext(" Help "), y, x + 14, selected == 1); + + wmove(dialog, y, x + 1 + 14 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box for inputing a string + */ +int dialog_inputbox(const char *title, const char *prompt, int height, int width, + const char *init) +{ + int i, x, y, box_y, box_x, box_width; + int input_x = 0, key = 0, button = -1; + int show_x, len, pos; + char *instr = dialog_input_result; + WINDOW *dialog; + + if (!init) + instr[0] = '\0'; + else + strcpy(instr, init); + +do_resize: + if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGTH_MIN)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) <= (width - INPUTBOX_WIDTH_MIN)) + return -ERRDISPLAYTOOSMALL; + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + /* Draw the input field box */ + box_width = width - 6; + getyx(dialog, y, x); + box_y = y + 2; + box_x = (width - box_width) / 2; + draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2, + dlg.dialog.atr, dlg.border.atr); + + print_buttons(dialog, height, width, 0); + + /* Set up the initial value */ + wmove(dialog, box_y, box_x); + wattrset(dialog, dlg.inputbox.atr); + + len = strlen(instr); + pos = len; + + if (len >= box_width) { + show_x = len - box_width + 1; + input_x = box_width - 1; + for (i = 0; i < box_width - 1; i++) + waddch(dialog, instr[show_x + i]); + } else { + show_x = 0; + input_x = len; + waddstr(dialog, instr); + } + + wmove(dialog, box_y, box_x + input_x); + + wrefresh(dialog); + + while (key != KEY_ESC) { + key = wgetch(dialog); + + if (button == -1) { /* Input box selected */ + switch (key) { + case TAB: + case KEY_UP: + case KEY_DOWN: + break; + case KEY_BACKSPACE: + case 127: + if (pos) { + wattrset(dialog, dlg.inputbox.atr); + if (input_x == 0) { + show_x--; + } else + input_x--; + + if (pos < len) { + for (i = pos - 1; i < len; i++) { + instr[i] = instr[i+1]; + } + } + + pos--; + len--; + instr[len] = '\0'; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); + wrefresh(dialog); + } + continue; + case KEY_LEFT: + if (pos > 0) { + if (input_x > 0) { + wmove(dialog, box_y, --input_x + box_x); + } else if (input_x == 0) { + show_x--; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, box_x); + } + pos--; + } + continue; + case KEY_RIGHT: + if (pos < len) { + if (input_x < box_width - 1) { + wmove(dialog, box_y, ++input_x + box_x); + } else if (input_x == box_width - 1) { + show_x++; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); + } + pos++; + } + continue; + default: + if (key < 0x100 && isprint(key)) { + if (len < MAX_LEN) { + wattrset(dialog, dlg.inputbox.atr); + if (pos < len) { + for (i = len; i > pos; i--) + instr[i] = instr[i-1]; + instr[pos] = key; + } else { + instr[len] = key; + } + pos++; + len++; + instr[len] = '\0'; + + if (input_x == box_width - 1) { + show_x++; + } else { + input_x++; + } + + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); + wrefresh(dialog); + } else + flash(); /* Alarm user about overflow */ + continue; + } + } + } + switch (key) { + case 'O': + case 'o': + delwin(dialog); + return 0; + case 'H': + case 'h': + delwin(dialog); + return 1; + case KEY_UP: + case KEY_LEFT: + switch (button) { + case -1: + button = 1; /* Indicates "Help" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 0: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove(dialog, box_y, box_x + input_x); + wrefresh(dialog); + break; + case 1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + } + break; + case TAB: + case KEY_DOWN: + case KEY_RIGHT: + switch (button) { + case -1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + case 0: + button = 1; /* Indicates "Help" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 1: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove(dialog, box_y, box_x + input_x); + wrefresh(dialog); + break; + } + break; + case ' ': + case '\n': + delwin(dialog); + return (button == -1 ? 0 : button); + case 'X': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(dialog); + on_key_resize(); + goto do_resize; + } + } + + delwin(dialog); + return KEY_ESC; /* ESC pressed */ +} diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/menubox.c b/qemu/roms/seabios/scripts/kconfig/lxdialog/menubox.c new file mode 100644 index 000000000..11ae9ad7a --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/menubox.c @@ -0,0 +1,437 @@ +/* + * menubox.c -- implements the menu box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Changes by Clifford Wolf (god@clifford.at) + * + * [ 1998-06-13 ] + * + * *) A bugfix for the Page-Down problem + * + * *) Formerly when I used Page Down and Page Up, the cursor would be set + * to the first position in the menu box. Now lxdialog is a bit + * smarter and works more like other menu systems (just have a look at + * it). + * + * *) Formerly if I selected something my scrolling would be broken because + * lxdialog is re-invoked by the Menuconfig shell script, can't + * remember the last scrolling position, and just sets it so that the + * cursor is at the bottom of the box. Now it writes the temporary file + * lxdialog.scrltmp which contains this information. The file is + * deleted by lxdialog if the user leaves a submenu or enters a new + * one, but it would be nice if Menuconfig could make another "rm -f" + * just to be sure. Just try it out - you will recognise a difference! + * + * [ 1998-06-14 ] + * + * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files + * and menus change their size on the fly. + * + * *) If for some reason the last scrolling position is not saved by + * lxdialog, it sets the scrolling so that the selected item is in the + * middle of the menu box, not at the bottom. + * + * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net) + * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus. + * This fixes a bug in Menuconfig where using ' ' to descend into menus + * would leave mis-synchronized lxdialog.scrltmp files lying around, + * fscanf would read in 'scroll', and eventually that value would get used. + */ + +#include "dialog.h" + +static int menu_width, item_x; + +/* + * Print menu item + */ +static void do_print_item(WINDOW * win, const char *item, int line_y, + int selected, int hotkey) +{ + int j; + char *menu_item = malloc(menu_width + 1); + + strncpy(menu_item, item, menu_width - item_x); + menu_item[menu_width - item_x] = '\0'; + j = first_alpha(menu_item, "YyNnMmHh"); + + /* Clear 'residue' of last item */ + wattrset(win, dlg.menubox.atr); + wmove(win, line_y, 0); +#if OLD_NCURSES + { + int i; + for (i = 0; i < menu_width; i++) + waddch(win, ' '); + } +#else + wclrtoeol(win); +#endif + wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr); + mvwaddstr(win, line_y, item_x, menu_item); + if (hotkey) { + wattrset(win, selected ? dlg.tag_key_selected.atr + : dlg.tag_key.atr); + mvwaddch(win, line_y, item_x + j, menu_item[j]); + } + if (selected) { + wmove(win, line_y, item_x + 1); + } + free(menu_item); + wrefresh(win); +} + +#define print_item(index, choice, selected) \ +do { \ + item_set(index); \ + do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \ +} while (0) + +/* + * Print the scroll indicators. + */ +static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x, + int height) +{ + int cur_y, cur_x; + + getyx(win, cur_y, cur_x); + + wmove(win, y, x); + + if (scroll > 0) { + wattrset(win, dlg.uarrow.atr); + waddch(win, ACS_UARROW); + waddstr(win, "(-)"); + } else { + wattrset(win, dlg.menubox.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + wrefresh(win); + + if ((height < item_no) && (scroll + height < item_no)) { + wattrset(win, dlg.darrow.atr); + waddch(win, ACS_DARROW); + waddstr(win, "(+)"); + } else { + wattrset(win, dlg.menubox_border.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + wmove(win, cur_y, cur_x); + wrefresh(win); +} + +/* + * Display the termination buttons. + */ +static void print_buttons(WINDOW * win, int height, int width, int selected) +{ + int x = width / 2 - 28; + int y = height - 2; + + print_button(win, gettext("Select"), y, x, selected == 0); + print_button(win, gettext(" Exit "), y, x + 12, selected == 1); + print_button(win, gettext(" Help "), y, x + 24, selected == 2); + print_button(win, gettext(" Save "), y, x + 36, selected == 3); + print_button(win, gettext(" Load "), y, x + 48, selected == 4); + + wmove(win, y, x + 1 + 12 * selected); + wrefresh(win); +} + +/* scroll up n lines (n may be negative) */ +static void do_scroll(WINDOW *win, int *scroll, int n) +{ + /* Scroll menu up */ + scrollok(win, TRUE); + wscrl(win, n); + scrollok(win, FALSE); + *scroll = *scroll + n; + wrefresh(win); +} + +/* + * Display a menu for choosing among a number of options + */ +int dialog_menu(const char *title, const char *prompt, + const void *selected, int *s_scroll) +{ + int i, j, x, y, box_x, box_y; + int height, width, menu_height; + int key = 0, button = 0, scroll = 0, choice = 0; + int first_item = 0, max_choice; + WINDOW *dialog, *menu; + +do_resize: + height = getmaxy(stdscr); + width = getmaxx(stdscr); + if (height < MENUBOX_HEIGTH_MIN || width < MENUBOX_WIDTH_MIN) + return -ERRDISPLAYTOOSMALL; + + height -= 4; + width -= 5; + menu_height = height - 10; + + max_choice = MIN(menu_height, item_count()); + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + wbkgdset(dialog, dlg.dialog.atr & A_COLOR); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + menu_width = width - 6; + box_y = height - menu_height - 5; + box_x = (width - menu_width) / 2 - 1; + + /* create new window for the menu */ + menu = subwin(dialog, menu_height, menu_width, + y + box_y + 1, x + box_x + 1); + keypad(menu, TRUE); + + /* draw a box around the menu items */ + draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, + dlg.menubox_border.atr, dlg.menubox.atr); + + if (menu_width >= 80) + item_x = (menu_width - 70) / 2; + else + item_x = 4; + + /* Set choice to default item */ + item_foreach() + if (selected && (selected == item_data())) + choice = item_n(); + /* get the saved scroll info */ + scroll = *s_scroll; + if ((scroll <= choice) && (scroll + max_choice > choice) && + (scroll >= 0) && (scroll + max_choice <= item_count())) { + first_item = scroll; + choice = choice - scroll; + } else { + scroll = 0; + } + if ((choice >= max_choice)) { + if (choice >= item_count() - max_choice / 2) + scroll = first_item = item_count() - max_choice; + else + scroll = first_item = choice - max_choice / 2; + choice = choice - scroll; + } + + /* Print the menu */ + for (i = 0; i < max_choice; i++) { + print_item(first_item + i, i, i == choice); + } + + wnoutrefresh(menu); + + print_arrows(dialog, item_count(), scroll, + box_y, box_x + item_x + 1, menu_height); + + print_buttons(dialog, height, width, 0); + wmove(menu, choice, item_x + 1); + wrefresh(menu); + + while (key != KEY_ESC) { + key = wgetch(menu); + + if (key < 256 && isalpha(key)) + key = tolower(key); + + if (strchr("ynmh", key)) + i = max_choice; + else { + for (i = choice + 1; i < max_choice; i++) { + item_set(scroll + i); + j = first_alpha(item_str(), "YyNnMmHh"); + if (key == tolower(item_str()[j])) + break; + } + if (i == max_choice) + for (i = 0; i < max_choice; i++) { + item_set(scroll + i); + j = first_alpha(item_str(), "YyNnMmHh"); + if (key == tolower(item_str()[j])) + break; + } + } + + if (item_count() != 0 && + (i < max_choice || + key == KEY_UP || key == KEY_DOWN || + key == '-' || key == '+' || + key == KEY_PPAGE || key == KEY_NPAGE)) { + /* Remove highligt of current item */ + print_item(scroll + choice, choice, FALSE); + + if (key == KEY_UP || key == '-') { + if (choice < 2 && scroll) { + /* Scroll menu down */ + do_scroll(menu, &scroll, -1); + + print_item(scroll, 0, FALSE); + } else + choice = MAX(choice - 1, 0); + + } else if (key == KEY_DOWN || key == '+') { + print_item(scroll+choice, choice, FALSE); + + if ((choice > max_choice - 3) && + (scroll + max_choice < item_count())) { + /* Scroll menu up */ + do_scroll(menu, &scroll, 1); + + print_item(scroll+max_choice - 1, + max_choice - 1, FALSE); + } else + choice = MIN(choice + 1, max_choice - 1); + + } else if (key == KEY_PPAGE) { + scrollok(menu, TRUE); + for (i = 0; (i < max_choice); i++) { + if (scroll > 0) { + do_scroll(menu, &scroll, -1); + print_item(scroll, 0, FALSE); + } else { + if (choice > 0) + choice--; + } + } + + } else if (key == KEY_NPAGE) { + for (i = 0; (i < max_choice); i++) { + if (scroll + max_choice < item_count()) { + do_scroll(menu, &scroll, 1); + print_item(scroll+max_choice-1, + max_choice - 1, FALSE); + } else { + if (choice + 1 < max_choice) + choice++; + } + } + } else + choice = i; + + print_item(scroll + choice, choice, TRUE); + + print_arrows(dialog, item_count(), scroll, + box_y, box_x + item_x + 1, menu_height); + + wnoutrefresh(dialog); + wrefresh(menu); + + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_LEFT: + case TAB: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 4 : (button > 4 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(menu); + break; + case ' ': + case 's': + case 'y': + case 'n': + case 'm': + case '/': + case 'h': + case '?': + case 'z': + case '\n': + /* save scroll info */ + *s_scroll = scroll; + delwin(menu); + delwin(dialog); + item_set(scroll + choice); + item_set_selected(1); + switch (key) { + case 'h': + case '?': + return 2; + case 's': + case 'y': + return 5; + case 'n': + return 6; + case 'm': + return 7; + case ' ': + return 8; + case '/': + return 9; + case 'z': + return 10; + case '\n': + return button; + } + return 0; + case 'e': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(menu); + break; + case KEY_RESIZE: + on_key_resize(); + delwin(menu); + delwin(dialog); + goto do_resize; + } + } + delwin(menu); + delwin(dialog); + return key; /* ESC pressed */ +} diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/textbox.c b/qemu/roms/seabios/scripts/kconfig/lxdialog/textbox.c new file mode 100644 index 000000000..1773319b9 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/textbox.c @@ -0,0 +1,408 @@ +/* + * textbox.c -- implements the text box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static void back_lines(int n); +static void print_page(WINDOW *win, int height, int width, update_text_fn + update_text, void *data); +static void print_line(WINDOW *win, int row, int width); +static char *get_line(void); +static void print_position(WINDOW * win); + +static int hscroll; +static int begin_reached, end_reached, page_length; +static char *buf; +static char *page; + +/* + * refresh window content + */ +static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, + int cur_y, int cur_x, update_text_fn update_text, + void *data) +{ + print_page(box, boxh, boxw, update_text, data); + print_position(dialog); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); +} + + +/* + * Display text from a file in a dialog box. + * + * keys is a null-terminated array + * update_text() may not add or remove any '\n' or '\0' in tbuf + */ +int dialog_textbox(const char *title, char *tbuf, int initial_height, + int initial_width, int *keys, int *_vscroll, int *_hscroll, + update_text_fn update_text, void *data) +{ + int i, x, y, cur_x, cur_y, key = 0; + int height, width, boxh, boxw; + WINDOW *dialog, *box; + bool done = false; + + begin_reached = 1; + end_reached = 0; + page_length = 0; + hscroll = 0; + buf = tbuf; + page = buf; /* page is pointer to start of page to be displayed */ + + if (_vscroll && *_vscroll) { + begin_reached = 0; + + for (i = 0; i < *_vscroll; i++) + get_line(); + } + if (_hscroll) + hscroll = *_hscroll; + +do_resize: + getmaxyx(stdscr, height, width); + if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN) + return -ERRDISPLAYTOOSMALL; + if (initial_height != 0) + height = initial_height; + else + if (height > 4) + height -= 4; + else + height = 0; + if (initial_width != 0) + width = initial_width; + else + if (width > 5) + width -= 5; + else + width = 0; + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + /* Create window for box region, used for scrolling text */ + boxh = height - 4; + boxw = width - 2; + box = subwin(dialog, boxh, boxw, y + 1, x + 1); + wattrset(box, dlg.dialog.atr); + wbkgdset(box, dlg.dialog.atr & A_COLOR); + + keypad(box, TRUE); + + /* register the new window, along with its borders */ + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + wbkgdset(dialog, dlg.dialog.atr & A_COLOR); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + print_button(dialog, gettext(" Exit "), height - 2, width / 2 - 4, TRUE); + wnoutrefresh(dialog); + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + + /* Print first page of text */ + attr_clear(box, boxh, boxw, dlg.dialog.atr); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text, + data); + + while (!done) { + key = wgetch(dialog); + switch (key) { + case 'E': /* Exit */ + case 'e': + case 'X': + case 'x': + case 'q': + case '\n': + done = true; + break; + case 'g': /* First page */ + case KEY_HOME: + if (!begin_reached) { + begin_reached = 1; + page = buf; + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x, update_text, + data); + } + break; + case 'G': /* Last page */ + case KEY_END: + + end_reached = 1; + /* point to last char in buf */ + page = buf + strlen(buf); + back_lines(boxh); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'K': /* Previous line */ + case 'k': + case KEY_UP: + if (begin_reached) + break; + + back_lines(page_length + 1); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'B': /* Previous page */ + case 'b': + case 'u': + case KEY_PPAGE: + if (begin_reached) + break; + back_lines(page_length + boxh); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'J': /* Next line */ + case 'j': + case KEY_DOWN: + if (end_reached) + break; + + back_lines(page_length - 1); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case KEY_NPAGE: /* Next page */ + case ' ': + case 'd': + if (end_reached) + break; + + begin_reached = 0; + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case '0': /* Beginning of line */ + case 'H': /* Scroll left */ + case 'h': + case KEY_LEFT: + if (hscroll <= 0) + break; + + if (key == '0') + hscroll = 0; + else + hscroll--; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'L': /* Scroll right */ + case 'l': + case KEY_RIGHT: + if (hscroll >= MAX_LEN) + break; + hscroll++; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case KEY_ESC: + if (on_key_esc(dialog) == KEY_ESC) + done = true; + break; + case KEY_RESIZE: + back_lines(height); + delwin(box); + delwin(dialog); + on_key_resize(); + goto do_resize; + default: + for (i = 0; keys[i]; i++) { + if (key == keys[i]) { + done = true; + break; + } + } + } + } + delwin(box); + delwin(dialog); + if (_vscroll) { + const char *s; + + s = buf; + *_vscroll = 0; + back_lines(page_length); + while (s < page && (s = strchr(s, '\n'))) { + (*_vscroll)++; + s++; + } + } + if (_hscroll) + *_hscroll = hscroll; + return key; +} + +/* + * Go back 'n' lines in text. Called by dialog_textbox(). + * 'page' will be updated to point to the desired line in 'buf'. + */ +static void back_lines(int n) +{ + int i; + + begin_reached = 0; + /* Go back 'n' lines */ + for (i = 0; i < n; i++) { + if (*page == '\0') { + if (end_reached) { + end_reached = 0; + continue; + } + } + if (page == buf) { + begin_reached = 1; + return; + } + page--; + do { + if (page == buf) { + begin_reached = 1; + return; + } + page--; + } while (*page != '\n'); + page++; + } +} + +/* + * Print a new page of text. + */ +static void print_page(WINDOW *win, int height, int width, update_text_fn + update_text, void *data) +{ + int i, passed_end = 0; + + if (update_text) { + char *end; + + for (i = 0; i < height; i++) + get_line(); + end = page; + back_lines(height); + update_text(buf, page - buf, end - buf, data); + } + + page_length = 0; + for (i = 0; i < height; i++) { + print_line(win, i, width); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + wnoutrefresh(win); +} + +/* + * Print a new line of text. + */ +static void print_line(WINDOW * win, int row, int width) +{ + char *line; + + line = get_line(); + line += MIN(strlen(line), hscroll); /* Scroll horizontally */ + wmove(win, row, 0); /* move cursor to correct line */ + waddch(win, ' '); + waddnstr(win, line, MIN(strlen(line), width - 2)); + + /* Clear 'residue' of previous line */ +#if OLD_NCURSES + { + int x = getcurx(win); + int i; + for (i = 0; i < width - x; i++) + waddch(win, ' '); + } +#else + wclrtoeol(win); +#endif +} + +/* + * Return current line of text. Called by dialog_textbox() and print_line(). + * 'page' should point to start of current line before calling, and will be + * updated to point to start of next line. + */ +static char *get_line(void) +{ + int i = 0; + static char line[MAX_LEN + 1]; + + end_reached = 0; + while (*page != '\n') { + if (*page == '\0') { + end_reached = 1; + break; + } else if (i < MAX_LEN) + line[i++] = *(page++); + else { + /* Truncate lines longer than MAX_LEN characters */ + if (i == MAX_LEN) + line[i++] = '\0'; + page++; + } + } + if (i <= MAX_LEN) + line[i] = '\0'; + if (!end_reached) + page++; /* move past '\n' */ + + return line; +} + +/* + * Print current position + */ +static void print_position(WINDOW * win) +{ + int percent; + + wattrset(win, dlg.position_indicator.atr); + wbkgdset(win, dlg.position_indicator.atr & A_COLOR); + percent = (page - buf) * 100 / strlen(buf); + wmove(win, getmaxy(win) - 3, getmaxx(win) - 9); + wprintw(win, "(%3d%%)", percent); +} diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/util.c b/qemu/roms/seabios/scripts/kconfig/lxdialog/util.c new file mode 100644 index 000000000..f7abdeb92 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/util.c @@ -0,0 +1,713 @@ +/* + * util.c + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdarg.h> + +#include "dialog.h" + +/* Needed in signal handler in mconf.c */ +int saved_x, saved_y; + +struct dialog_info dlg; + +static void set_mono_theme(void) +{ + dlg.screen.atr = A_NORMAL; + dlg.shadow.atr = A_NORMAL; + dlg.dialog.atr = A_NORMAL; + dlg.title.atr = A_BOLD; + dlg.border.atr = A_NORMAL; + dlg.button_active.atr = A_REVERSE; + dlg.button_inactive.atr = A_DIM; + dlg.button_key_active.atr = A_REVERSE; + dlg.button_key_inactive.atr = A_BOLD; + dlg.button_label_active.atr = A_REVERSE; + dlg.button_label_inactive.atr = A_NORMAL; + dlg.inputbox.atr = A_NORMAL; + dlg.inputbox_border.atr = A_NORMAL; + dlg.searchbox.atr = A_NORMAL; + dlg.searchbox_title.atr = A_BOLD; + dlg.searchbox_border.atr = A_NORMAL; + dlg.position_indicator.atr = A_BOLD; + dlg.menubox.atr = A_NORMAL; + dlg.menubox_border.atr = A_NORMAL; + dlg.item.atr = A_NORMAL; + dlg.item_selected.atr = A_REVERSE; + dlg.tag.atr = A_BOLD; + dlg.tag_selected.atr = A_REVERSE; + dlg.tag_key.atr = A_BOLD; + dlg.tag_key_selected.atr = A_REVERSE; + dlg.check.atr = A_BOLD; + dlg.check_selected.atr = A_REVERSE; + dlg.uarrow.atr = A_BOLD; + dlg.darrow.atr = A_BOLD; +} + +#define DLG_COLOR(dialog, f, b, h) \ +do { \ + dlg.dialog.fg = (f); \ + dlg.dialog.bg = (b); \ + dlg.dialog.hl = (h); \ +} while (0) + +static void set_classic_theme(void) +{ + DLG_COLOR(screen, COLOR_CYAN, COLOR_BLUE, true); + DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, true); + DLG_COLOR(dialog, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(title, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(button_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(button_inactive, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(button_key_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_WHITE, false); + DLG_COLOR(button_label_active, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_WHITE, true); + DLG_COLOR(inputbox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(inputbox_border, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(searchbox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(searchbox_border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(position_indicator, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(menubox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(menubox_border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(item, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(item_selected, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(tag, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(tag_key, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(check, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(check_selected, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(uarrow, COLOR_GREEN, COLOR_WHITE, true); + DLG_COLOR(darrow, COLOR_GREEN, COLOR_WHITE, true); +} + +static void set_blackbg_theme(void) +{ + DLG_COLOR(screen, COLOR_RED, COLOR_BLACK, true); + DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false); + DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false); + DLG_COLOR(title, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(button_active, COLOR_YELLOW, COLOR_RED, false); + DLG_COLOR(button_inactive, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_RED, true); + DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_RED, false); + DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(inputbox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(inputbox_border, COLOR_YELLOW, COLOR_BLACK, false); + + DLG_COLOR(searchbox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_BLACK, true); + DLG_COLOR(searchbox_border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK, false); + + DLG_COLOR(menubox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(menubox_border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(item, COLOR_WHITE, COLOR_BLACK, false); + DLG_COLOR(item_selected, COLOR_WHITE, COLOR_RED, false); + + DLG_COLOR(tag, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_RED, true); + DLG_COLOR(tag_key, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED, true); + + DLG_COLOR(check, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(check_selected, COLOR_YELLOW, COLOR_RED, true); + + DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false); +} + +static void set_bluetitle_theme(void) +{ + set_classic_theme(); + DLG_COLOR(title, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(searchbox_title, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(position_indicator, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(tag, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(tag_key, COLOR_BLUE, COLOR_WHITE, true); + +} + +/* + * Select color theme + */ +static int set_theme(const char *theme) +{ + int use_color = 1; + if (!theme) + set_bluetitle_theme(); + else if (strcmp(theme, "classic") == 0) + set_classic_theme(); + else if (strcmp(theme, "bluetitle") == 0) + set_bluetitle_theme(); + else if (strcmp(theme, "blackbg") == 0) + set_blackbg_theme(); + else if (strcmp(theme, "mono") == 0) + use_color = 0; + + return use_color; +} + +static void init_one_color(struct dialog_color *color) +{ + static int pair = 0; + + pair++; + init_pair(pair, color->fg, color->bg); + if (color->hl) + color->atr = A_BOLD | COLOR_PAIR(pair); + else + color->atr = COLOR_PAIR(pair); +} + +static void init_dialog_colors(void) +{ + init_one_color(&dlg.screen); + init_one_color(&dlg.shadow); + init_one_color(&dlg.dialog); + init_one_color(&dlg.title); + init_one_color(&dlg.border); + init_one_color(&dlg.button_active); + init_one_color(&dlg.button_inactive); + init_one_color(&dlg.button_key_active); + init_one_color(&dlg.button_key_inactive); + init_one_color(&dlg.button_label_active); + init_one_color(&dlg.button_label_inactive); + init_one_color(&dlg.inputbox); + init_one_color(&dlg.inputbox_border); + init_one_color(&dlg.searchbox); + init_one_color(&dlg.searchbox_title); + init_one_color(&dlg.searchbox_border); + init_one_color(&dlg.position_indicator); + init_one_color(&dlg.menubox); + init_one_color(&dlg.menubox_border); + init_one_color(&dlg.item); + init_one_color(&dlg.item_selected); + init_one_color(&dlg.tag); + init_one_color(&dlg.tag_selected); + init_one_color(&dlg.tag_key); + init_one_color(&dlg.tag_key_selected); + init_one_color(&dlg.check); + init_one_color(&dlg.check_selected); + init_one_color(&dlg.uarrow); + init_one_color(&dlg.darrow); +} + +/* + * Setup for color display + */ +static void color_setup(const char *theme) +{ + int use_color; + + use_color = set_theme(theme); + if (use_color && has_colors()) { + start_color(); + init_dialog_colors(); + } else + set_mono_theme(); +} + +/* + * Set window to attribute 'attr' + */ +void attr_clear(WINDOW * win, int height, int width, chtype attr) +{ + int i, j; + + wattrset(win, attr); + for (i = 0; i < height; i++) { + wmove(win, i, 0); + for (j = 0; j < width; j++) + waddch(win, ' '); + } + touchwin(win); +} + +void dialog_clear(void) +{ + int lines, columns; + + lines = getmaxy(stdscr); + columns = getmaxx(stdscr); + + attr_clear(stdscr, lines, columns, dlg.screen.atr); + /* Display background title if it exists ... - SLH */ + if (dlg.backtitle != NULL) { + int i, len = 0, skip = 0; + struct subtitle_list *pos; + + wattrset(stdscr, dlg.screen.atr); + mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle); + + for (pos = dlg.subtitles; pos != NULL; pos = pos->next) { + /* 3 is for the arrow and spaces */ + len += strlen(pos->text) + 3; + } + + wmove(stdscr, 1, 1); + if (len > columns - 2) { + const char *ellipsis = "[...] "; + waddstr(stdscr, ellipsis); + skip = len - (columns - 2 - strlen(ellipsis)); + } + + for (pos = dlg.subtitles; pos != NULL; pos = pos->next) { + if (skip == 0) + waddch(stdscr, ACS_RARROW); + else + skip--; + + if (skip == 0) + waddch(stdscr, ' '); + else + skip--; + + if (skip < strlen(pos->text)) { + waddstr(stdscr, pos->text + skip); + skip = 0; + } else + skip -= strlen(pos->text); + + if (skip == 0) + waddch(stdscr, ' '); + else + skip--; + } + + for (i = len + 1; i < columns - 1; i++) + waddch(stdscr, ACS_HLINE); + } + wnoutrefresh(stdscr); +} + +/* + * Do some initialization for dialog + */ +int init_dialog(const char *backtitle) +{ + int height, width; + + initscr(); /* Init curses */ + + /* Get current cursor position for signal handler in mconf.c */ + getyx(stdscr, saved_y, saved_x); + + getmaxyx(stdscr, height, width); + if (height < WINDOW_HEIGTH_MIN || width < WINDOW_WIDTH_MIN) { + endwin(); + return -ERRDISPLAYTOOSMALL; + } + + dlg.backtitle = backtitle; + color_setup(getenv("MENUCONFIG_COLOR")); + + keypad(stdscr, TRUE); + cbreak(); + noecho(); + dialog_clear(); + + return 0; +} + +void set_dialog_backtitle(const char *backtitle) +{ + dlg.backtitle = backtitle; +} + +void set_dialog_subtitles(struct subtitle_list *subtitles) +{ + dlg.subtitles = subtitles; +} + +/* + * End using dialog functions. + */ +void end_dialog(int x, int y) +{ + /* move cursor back to original position */ + move(y, x); + refresh(); + endwin(); +} + +/* Print the title of the dialog. Center the title and truncate + * tile if wider than dialog (- 2 chars). + **/ +void print_title(WINDOW *dialog, const char *title, int width) +{ + if (title) { + int tlen = MIN(width - 2, strlen(title)); + wattrset(dialog, dlg.title.atr); + mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' '); + mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen); + waddch(dialog, ' '); + } +} + +/* + * Print a string of text in a window, automatically wrap around to the + * next line if the string is too long to fit on one line. Newline + * characters '\n' are propperly processed. We start on a new line + * if there is no room for at least 4 nonblanks following a double-space. + */ +void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) +{ + int newl, cur_x, cur_y; + int prompt_len, room, wlen; + char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0; + + strcpy(tempstr, prompt); + + prompt_len = strlen(tempstr); + + if (prompt_len <= width - x * 2) { /* If prompt is short */ + wmove(win, y, (width - prompt_len) / 2); + waddstr(win, tempstr); + } else { + cur_x = x; + cur_y = y; + newl = 1; + word = tempstr; + while (word && *word) { + sp = strpbrk(word, "\n "); + if (sp && *sp == '\n') + newline_separator = sp; + + if (sp) + *sp++ = 0; + + /* Wrap to next line if either the word does not fit, + or it is the first word of a new sentence, and it is + short, and the next word does not fit. */ + room = width - cur_x; + wlen = strlen(word); + if (wlen > room || + (newl && wlen < 4 && sp + && wlen + 1 + strlen(sp) > room + && (!(sp2 = strpbrk(sp, "\n ")) + || wlen + 1 + (sp2 - sp) > room))) { + cur_y++; + cur_x = x; + } + wmove(win, cur_y, cur_x); + waddstr(win, word); + getyx(win, cur_y, cur_x); + + /* Move to the next line if the word separator was a newline */ + if (newline_separator) { + cur_y++; + cur_x = x; + newline_separator = 0; + } else + cur_x++; + + if (sp && *sp == ' ') { + cur_x++; /* double space */ + while (*++sp == ' ') ; + newl = 1; + } else + newl = 0; + word = sp; + } + } +} + +/* + * Print a button + */ +void print_button(WINDOW * win, const char *label, int y, int x, int selected) +{ + int i, temp; + + wmove(win, y, x); + wattrset(win, selected ? dlg.button_active.atr + : dlg.button_inactive.atr); + waddstr(win, "<"); + temp = strspn(label, " "); + label += temp; + wattrset(win, selected ? dlg.button_label_active.atr + : dlg.button_label_inactive.atr); + for (i = 0; i < temp; i++) + waddch(win, ' '); + wattrset(win, selected ? dlg.button_key_active.atr + : dlg.button_key_inactive.atr); + waddch(win, label[0]); + wattrset(win, selected ? dlg.button_label_active.atr + : dlg.button_label_inactive.atr); + waddstr(win, (char *)label + 1); + wattrset(win, selected ? dlg.button_active.atr + : dlg.button_inactive.atr); + waddstr(win, ">"); + wmove(win, y, x + temp + 1); +} + +/* + * Draw a rectangular box with line drawing characters + */ +void +draw_box(WINDOW * win, int y, int x, int height, int width, + chtype box, chtype border) +{ + int i, j; + + wattrset(win, 0); + for (i = 0; i < height; i++) { + wmove(win, y + i, x); + for (j = 0; j < width; j++) + if (!i && !j) + waddch(win, border | ACS_ULCORNER); + else if (i == height - 1 && !j) + waddch(win, border | ACS_LLCORNER); + else if (!i && j == width - 1) + waddch(win, box | ACS_URCORNER); + else if (i == height - 1 && j == width - 1) + waddch(win, box | ACS_LRCORNER); + else if (!i) + waddch(win, border | ACS_HLINE); + else if (i == height - 1) + waddch(win, box | ACS_HLINE); + else if (!j) + waddch(win, border | ACS_VLINE); + else if (j == width - 1) + waddch(win, box | ACS_VLINE); + else + waddch(win, box | ' '); + } +} + +/* + * Draw shadows along the right and bottom edge to give a more 3D look + * to the boxes + */ +void draw_shadow(WINDOW * win, int y, int x, int height, int width) +{ + int i; + + if (has_colors()) { /* Whether terminal supports color? */ + wattrset(win, dlg.shadow.atr); + wmove(win, y + height, x + 2); + for (i = 0; i < width; i++) + waddch(win, winch(win) & A_CHARTEXT); + for (i = y + 1; i < y + height + 1; i++) { + wmove(win, i, x + width); + waddch(win, winch(win) & A_CHARTEXT); + waddch(win, winch(win) & A_CHARTEXT); + } + wnoutrefresh(win); + } +} + +/* + * Return the position of the first alphabetic character in a string. + */ +int first_alpha(const char *string, const char *exempt) +{ + int i, in_paren = 0, c; + + for (i = 0; i < strlen(string); i++) { + c = tolower(string[i]); + + if (strchr("<[(", c)) + ++in_paren; + if (strchr(">])", c) && in_paren > 0) + --in_paren; + + if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0) + return i; + } + + return 0; +} + +/* + * ncurses uses ESC to detect escaped char sequences. This resutl in + * a small timeout before ESC is actually delivered to the application. + * lxdialog suggest <ESC> <ESC> which is correctly translated to two + * times esc. But then we need to ignore the second esc to avoid stepping + * out one menu too much. Filter away all escaped key sequences since + * keypad(FALSE) turn off ncurses support for escape sequences - and thats + * needed to make notimeout() do as expected. + */ +int on_key_esc(WINDOW *win) +{ + int key; + int key2; + int key3; + + nodelay(win, TRUE); + keypad(win, FALSE); + key = wgetch(win); + key2 = wgetch(win); + do { + key3 = wgetch(win); + } while (key3 != ERR); + nodelay(win, FALSE); + keypad(win, TRUE); + if (key == KEY_ESC && key2 == ERR) + return KEY_ESC; + else if (key != ERR && key != KEY_ESC && key2 == ERR) + ungetch(key); + + return -1; +} + +/* redraw screen in new size */ +int on_key_resize(void) +{ + dialog_clear(); + return KEY_RESIZE; +} + +struct dialog_list *item_cur; +struct dialog_list item_nil; +struct dialog_list *item_head; + +void item_reset(void) +{ + struct dialog_list *p, *next; + + for (p = item_head; p; p = next) { + next = p->next; + free(p); + } + item_head = NULL; + item_cur = &item_nil; +} + +void item_make(const char *fmt, ...) +{ + va_list ap; + struct dialog_list *p = malloc(sizeof(*p)); + + if (item_head) + item_cur->next = p; + else + item_head = p; + item_cur = p; + memset(p, 0, sizeof(*p)); + + va_start(ap, fmt); + vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap); + va_end(ap); +} + +void item_add_str(const char *fmt, ...) +{ + va_list ap; + size_t avail; + + avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str); + + va_start(ap, fmt); + vsnprintf(item_cur->node.str + strlen(item_cur->node.str), + avail, fmt, ap); + item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0'; + va_end(ap); +} + +void item_set_tag(char tag) +{ + item_cur->node.tag = tag; +} +void item_set_data(void *ptr) +{ + item_cur->node.data = ptr; +} + +void item_set_selected(int val) +{ + item_cur->node.selected = val; +} + +int item_activate_selected(void) +{ + item_foreach() + if (item_is_selected()) + return 1; + return 0; +} + +void *item_data(void) +{ + return item_cur->node.data; +} + +char item_tag(void) +{ + return item_cur->node.tag; +} + +int item_count(void) +{ + int n = 0; + struct dialog_list *p; + + for (p = item_head; p; p = p->next) + n++; + return n; +} + +void item_set(int n) +{ + int i = 0; + item_foreach() + if (i++ == n) + return; +} + +int item_n(void) +{ + int n = 0; + struct dialog_list *p; + + for (p = item_head; p; p = p->next) { + if (p == item_cur) + return n; + n++; + } + return 0; +} + +const char *item_str(void) +{ + return item_cur->node.str; +} + +int item_is_selected(void) +{ + return (item_cur->node.selected != 0); +} + +int item_is_tag(char tag) +{ + return (item_cur->node.tag == tag); +} diff --git a/qemu/roms/seabios/scripts/kconfig/lxdialog/yesno.c b/qemu/roms/seabios/scripts/kconfig/lxdialog/yesno.c new file mode 100644 index 000000000..676fb2f82 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/lxdialog/yesno.c @@ -0,0 +1,114 @@ +/* + * yesno.c -- implements the yes/no box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +/* + * Display termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 10; + int y = height - 2; + + print_button(dialog, gettext(" Yes "), y, x, selected == 0); + print_button(dialog, gettext(" No "), y, x + 13, selected == 1); + + wmove(dialog, y, x + 1 + 13 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box with two buttons - Yes and No + */ +int dialog_yesno(const char *title, const char *prompt, int height, int width) +{ + int i, x, y, key = 0, button = 0; + WINDOW *dialog; + +do_resize: + if (getmaxy(stdscr) < (height + YESNO_HEIGTH_MIN)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) < (width + YESNO_WIDTH_MIN)) + return -ERRDISPLAYTOOSMALL; + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + print_buttons(dialog, height, width, 0); + + while (key != KEY_ESC) { + key = wgetch(dialog); + switch (key) { + case 'Y': + case 'y': + delwin(dialog); + return 0; + case 'N': + case 'n': + delwin(dialog); + return 1; + + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(dialog); + break; + case ' ': + case '\n': + delwin(dialog); + return button; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(dialog); + on_key_resize(); + goto do_resize; + } + } + + delwin(dialog); + return key; /* ESC pressed */ +} diff --git a/qemu/roms/seabios/scripts/kconfig/mconf.c b/qemu/roms/seabios/scripts/kconfig/mconf.c new file mode 100644 index 000000000..14cea7463 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/mconf.c @@ -0,0 +1,1036 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + * + * Introduced single menu mode (show all sub-menus in one large tree). + * 2002-11-06 Petr Baudis <pasky@ucw.cz> + * + * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br> + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <locale.h> + +#include "lkc.h" +#include "lxdialog/dialog.h" + +static const char mconf_readme[] = N_( +"Overview\n" +"--------\n" +"This interface lets you select features and parameters for the build.\n" +"Features can either be built-in, modularized, or ignored. Parameters\n" +"must be entered in as decimal or hexadecimal numbers or text.\n" +"\n" +"Menu items beginning with following braces represent features that\n" +" [ ] can be built in or removed\n" +" < > can be built in, modularized or removed\n" +" { } can be built in or modularized (selected by other feature)\n" +" - - are selected by other feature,\n" +"while *, M or whitespace inside braces means to build in, build as\n" +"a module or to exclude the feature respectively.\n" +"\n" +"To change any of these features, highlight it with the cursor\n" +"keys and press <Y> to build it in, <M> to make it a module or\n" +"<N> to remove it. You may also press the <Space Bar> to cycle\n" +"through the available options (i.e. Y->N->M->Y).\n" +"\n" +"Some additional keyboard hints:\n" +"\n" +"Menus\n" +"----------\n" +"o Use the Up/Down arrow keys (cursor keys) to highlight the item you\n" +" wish to change or the submenu you wish to select and press <Enter>.\n" +" Submenus are designated by \"--->\", empty ones by \"----\".\n" +"\n" +" Shortcut: Press the option's highlighted letter (hotkey).\n" +" Pressing a hotkey more than once will sequence\n" +" through all visible items which use that hotkey.\n" +"\n" +" You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n" +" unseen options into view.\n" +"\n" +"o To exit a menu use the cursor keys to highlight the <Exit> button\n" +" and press <ENTER>.\n" +"\n" +" Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n" +" using those letters. You may press a single <ESC>, but\n" +" there is a delayed response which you may find annoying.\n" +"\n" +" Also, the <TAB> and cursor keys will cycle between <Select>,\n" +" <Exit>, <Help>, <Save>, and <Load>.\n" +"\n" +"o To get help with an item, use the cursor keys to highlight <Help>\n" +" and press <ENTER>.\n" +"\n" +" Shortcut: Press <H> or <?>.\n" +"\n" +"o To toggle the display of hidden options, press <Z>.\n" +"\n" +"\n" +"Radiolists (Choice lists)\n" +"-----------\n" +"o Use the cursor keys to select the option you wish to set and press\n" +" <S> or the <SPACE BAR>.\n" +"\n" +" Shortcut: Press the first letter of the option you wish to set then\n" +" press <S> or <SPACE BAR>.\n" +"\n" +"o To see available help for the item, use the cursor keys to highlight\n" +" <Help> and Press <ENTER>.\n" +"\n" +" Shortcut: Press <H> or <?>.\n" +"\n" +" Also, the <TAB> and cursor keys will cycle between <Select> and\n" +" <Help>\n" +"\n" +"\n" +"Data Entry\n" +"-----------\n" +"o Enter the requested information and press <ENTER>\n" +" If you are entering hexadecimal values, it is not necessary to\n" +" add the '0x' prefix to the entry.\n" +"\n" +"o For help, use the <TAB> or cursor keys to highlight the help option\n" +" and press <ENTER>. You can try <TAB><H> as well.\n" +"\n" +"\n" +"Text Box (Help Window)\n" +"--------\n" +"o Use the cursor keys to scroll up/down/left/right. The VI editor\n" +" keys h,j,k,l function here as do <u>, <d>, <SPACE BAR> and <B> for\n" +" those who are familiar with less and lynx.\n" +"\n" +"o Press <E>, <X>, <q>, <Enter> or <Esc><Esc> to exit.\n" +"\n" +"\n" +"Alternate Configuration Files\n" +"-----------------------------\n" +"Menuconfig supports the use of alternate configuration files for\n" +"those who, for various reasons, find it necessary to switch\n" +"between different configurations.\n" +"\n" +"The <Save> button will let you save the current configuration to\n" +"a file of your choosing. Use the <Load> button to load a previously\n" +"saved alternate configuration.\n" +"\n" +"Even if you don't use alternate configuration files, but you find\n" +"during a Menuconfig session that you have completely messed up your\n" +"settings, you may use the <Load> button to restore your previously\n" +"saved settings from \".config\" without restarting Menuconfig.\n" +"\n" +"Other information\n" +"-----------------\n" +"If you use Menuconfig in an XTERM window, make sure you have your\n" +"$TERM variable set to point to an xterm definition which supports\n" +"color. Otherwise, Menuconfig will look rather bad. Menuconfig will\n" +"not display correctly in an RXVT window because rxvt displays only one\n" +"intensity of color, bright.\n" +"\n" +"Menuconfig will display larger menus on screens or xterms which are\n" +"set to display more than the standard 25 row by 80 column geometry.\n" +"In order for this to work, the \"stty size\" command must be able to\n" +"display the screen's current row and column geometry. I STRONGLY\n" +"RECOMMEND that you make sure you do NOT have the shell variables\n" +"LINES and COLUMNS exported into your environment. Some distributions\n" +"export those variables via /etc/profile. Some ncurses programs can\n" +"become confused when those variables (LINES & COLUMNS) don't reflect\n" +"the true screen size.\n" +"\n" +"Optional personality available\n" +"------------------------------\n" +"If you prefer to have all of the options listed in a single menu,\n" +"rather than the default multimenu hierarchy, run the menuconfig with\n" +"MENUCONFIG_MODE environment variable set to single_menu. Example:\n" +"\n" +"make MENUCONFIG_MODE=single_menu menuconfig\n" +"\n" +"<Enter> will then unroll the appropriate category, or enfold it if it\n" +"is already unrolled.\n" +"\n" +"Note that this mode can eventually be a little more CPU expensive\n" +"(especially with a larger number of unrolled categories) than the\n" +"default mode.\n" +"\n" +"Different color themes available\n" +"--------------------------------\n" +"It is possible to select different color themes using the variable\n" +"MENUCONFIG_COLOR. To select a theme use:\n" +"\n" +"make MENUCONFIG_COLOR=<theme> menuconfig\n" +"\n" +"Available themes are\n" +" mono => selects colors suitable for monochrome displays\n" +" blackbg => selects a color scheme with black background\n" +" classic => theme with blue background. The classic look\n" +" bluetitle => an LCD friendly version of classic. (default)\n" +"\n"), +menu_instructions[] = N_( + "Arrow keys navigate the menu. " + "<Enter> selects submenus ---> (or empty submenus ----). " + "Highlighted letters are hotkeys. " + "Pressing <Y> includes, <N> excludes, <M> modularizes features. " + "Press <Esc><Esc> to exit, <?> for Help, </> for Search. " + "Legend: [*] built-in [ ] excluded <M> module < > module capable"), +radiolist_instructions[] = N_( + "Use the arrow keys to navigate this window or " + "press the hotkey of the item you wish to select " + "followed by the <SPACE BAR>. " + "Press <?> for additional information about this option."), +inputbox_instructions_int[] = N_( + "Please enter a decimal value. " + "Fractions will not be accepted. " + "Use the <TAB> key to move from the input field to the buttons below it."), +inputbox_instructions_hex[] = N_( + "Please enter a hexadecimal value. " + "Use the <TAB> key to move from the input field to the buttons below it."), +inputbox_instructions_string[] = N_( + "Please enter a string value. " + "Use the <TAB> key to move from the input field to the buttons below it."), +setmod_text[] = N_( + "This feature depends on another which has been configured as a module.\n" + "As a result, this feature will be built as a module."), +load_config_text[] = N_( + "Enter the name of the configuration file you wish to load. " + "Accept the name shown to restore the configuration you " + "last retrieved. Leave blank to abort."), +load_config_help[] = N_( + "\n" + "For various reasons, one may wish to keep several different\n" + "configurations available on a single machine.\n" + "\n" + "If you have saved a previous configuration in a file other than the\n" + "default one, entering its name here will allow you to modify that\n" + "configuration.\n" + "\n" + "If you are uncertain, then you have probably never used alternate\n" + "configuration files. You should therefore leave this blank to abort.\n"), +save_config_text[] = N_( + "Enter a filename to which this configuration should be saved " + "as an alternate. Leave blank to abort."), +save_config_help[] = N_( + "\n" + "For various reasons, one may wish to keep different configurations\n" + "available on a single machine.\n" + "\n" + "Entering a file name here will allow you to later retrieve, modify\n" + "and use the current configuration as an alternate to whatever\n" + "configuration options you have selected at that time.\n" + "\n" + "If you are uncertain what all this means then you should probably\n" + "leave this blank.\n"), +search_help[] = N_( + "\n" + "Search for symbols and display their relations.\n" + "Regular expressions are allowed.\n" + "Example: search for \"^FOO\"\n" + "Result:\n" + "-----------------------------------------------------------------\n" + "Symbol: FOO [=m]\n" + "Type : tristate\n" + "Prompt: Foo bus is used to drive the bar HW\n" + " Location:\n" + " -> Bus options (PCI, PCMCIA, EISA, ISA)\n" + " -> PCI support (PCI [=y])\n" + "(1) -> PCI access mode (<choice> [=y])\n" + " Defined at drivers/pci/Kconfig:47\n" + " Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" + " Selects: LIBCRC32\n" + " Selected by: BAR [=n]\n" + "-----------------------------------------------------------------\n" + "o The line 'Type:' shows the type of the configuration option for\n" + " this symbol (boolean, tristate, string, ...)\n" + "o The line 'Prompt:' shows the text used in the menu structure for\n" + " this symbol\n" + "o The 'Defined at' line tells at what file / line number the symbol\n" + " is defined\n" + "o The 'Depends on:' line tells what symbols need to be defined for\n" + " this symbol to be visible in the menu (selectable)\n" + "o The 'Location:' lines tells where in the menu structure this symbol\n" + " is located\n" + " A location followed by a [=y] indicates that this is a\n" + " selectable menu item - and the current value is displayed inside\n" + " brackets.\n" + " Press the key in the (#) prefix to jump directly to that\n" + " location. You will be returned to the current search results\n" + " after exiting this new menu.\n" + "o The 'Selects:' line tells what symbols will be automatically\n" + " selected if this symbol is selected (y or m)\n" + "o The 'Selected by' line tells what symbol has selected this symbol\n" + "\n" + "Only relevant lines are shown.\n" + "\n\n" + "Search examples:\n" + "Examples: USB => find all symbols containing USB\n" + " ^USB => find all symbols starting with USB\n" + " USB$ => find all symbols ending with USB\n" + "\n"); + +static int indent; +static struct menu *current_menu; +static int child_count; +static int single_menu_mode; +static int show_all_options; +static int save_and_exit; + +static void conf(struct menu *menu, struct menu *active_menu); +static void conf_choice(struct menu *menu); +static void conf_string(struct menu *menu); +static void conf_load(void); +static void conf_save(void); +static int show_textbox_ext(const char *title, char *text, int r, int c, + int *keys, int *vscroll, int *hscroll, + update_text_fn update_text, void *data); +static void show_textbox(const char *title, const char *text, int r, int c); +static void show_helptext(const char *title, const char *text); +static void show_help(struct menu *menu); + +static char filename[PATH_MAX+1]; +static void set_config_filename(const char *config_filename) +{ + static char menu_backtitle[PATH_MAX+128]; + int size; + + size = snprintf(menu_backtitle, sizeof(menu_backtitle), + "%s - %s", config_filename, rootmenu.prompt->text); + if (size >= sizeof(menu_backtitle)) + menu_backtitle[sizeof(menu_backtitle)-1] = '\0'; + set_dialog_backtitle(menu_backtitle); + + size = snprintf(filename, sizeof(filename), "%s", config_filename); + if (size >= sizeof(filename)) + filename[sizeof(filename)-1] = '\0'; +} + +struct subtitle_part { + struct list_head entries; + const char *text; +}; +static LIST_HEAD(trail); + +static struct subtitle_list *subtitles; +static void set_subtitle(void) +{ + struct subtitle_part *sp; + struct subtitle_list *pos, *tmp; + + for (pos = subtitles; pos != NULL; pos = tmp) { + tmp = pos->next; + free(pos); + } + + subtitles = NULL; + list_for_each_entry(sp, &trail, entries) { + if (sp->text) { + if (pos) { + pos->next = xcalloc(sizeof(*pos), 1); + pos = pos->next; + } else { + subtitles = pos = xcalloc(sizeof(*pos), 1); + } + pos->text = sp->text; + } + } + + set_dialog_subtitles(subtitles); +} + +static void reset_subtitle(void) +{ + struct subtitle_list *pos, *tmp; + + for (pos = subtitles; pos != NULL; pos = tmp) { + tmp = pos->next; + free(pos); + } + subtitles = NULL; + set_dialog_subtitles(subtitles); +} + +struct search_data { + struct list_head *head; + struct menu **targets; + int *keys; +}; + +static void update_text(char *buf, size_t start, size_t end, void *_data) +{ + struct search_data *data = _data; + struct jump_key *pos; + int k = 0; + + list_for_each_entry(pos, data->head, entries) { + if (pos->offset >= start && pos->offset < end) { + char header[4]; + + if (k < JUMP_NB) { + int key = '0' + (pos->index % JUMP_NB) + 1; + + sprintf(header, "(%c)", key); + data->keys[k] = key; + data->targets[k] = pos->target; + k++; + } else { + sprintf(header, " "); + } + + memcpy(buf + pos->offset, header, sizeof(header) - 1); + } + } + data->keys[k] = 0; +} + +static void search_conf(void) +{ + struct symbol **sym_arr; + struct gstr res; + struct gstr title; + char *dialog_input; + int dres, vscroll = 0, hscroll = 0; + bool again; + struct gstr sttext; + struct subtitle_part stpart; + + title = str_new(); + str_printf( &title, _("Enter (sub)string or regexp to search for " + "(with or without \"%s\")"), CONFIG_); + +again: + dialog_clear(); + dres = dialog_inputbox(_("Search Configuration Parameter"), + str_get(&title), + 10, 75, ""); + switch (dres) { + case 0: + break; + case 1: + show_helptext(_("Search Configuration"), search_help); + goto again; + default: + str_free(&title); + return; + } + + /* strip the prefix if necessary */ + dialog_input = dialog_input_result; + if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0) + dialog_input += strlen(CONFIG_); + + sttext = str_new(); + str_printf(&sttext, "Search (%s)", dialog_input_result); + stpart.text = str_get(&sttext); + list_add_tail(&stpart.entries, &trail); + + sym_arr = sym_re_search(dialog_input); + do { + LIST_HEAD(head); + struct menu *targets[JUMP_NB]; + int keys[JUMP_NB + 1], i; + struct search_data data = { + .head = &head, + .targets = targets, + .keys = keys, + }; + struct jump_key *pos, *tmp; + + res = get_relations_str(sym_arr, &head); + set_subtitle(); + dres = show_textbox_ext(_("Search Results"), (char *) + str_get(&res), 0, 0, keys, &vscroll, + &hscroll, &update_text, (void *) + &data); + again = false; + for (i = 0; i < JUMP_NB && keys[i]; i++) + if (dres == keys[i]) { + conf(targets[i]->parent, targets[i]); + again = true; + } + str_free(&res); + list_for_each_entry_safe(pos, tmp, &head, entries) + free(pos); + } while (again); + free(sym_arr); + str_free(&title); + list_del(trail.prev); + str_free(&sttext); +} + +static void build_conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + int type, tmp, doint = 2; + tristate val; + char ch; + bool visible; + + /* + * note: menu_is_visible() has side effect that it will + * recalc the value of the symbol. + */ + visible = menu_is_visible(menu); + if (show_all_options && !menu_has_prompt(menu)) + return; + else if (!show_all_options && !visible) + return; + + sym = menu->sym; + prop = menu->prompt; + if (!sym) { + if (prop && menu != current_menu) { + const char *prompt = menu_get_prompt(menu); + switch (prop->type) { + case P_MENU: + child_count++; + prompt = _(prompt); + if (single_menu_mode) { + item_make("%s%*c%s", + menu->data ? "-->" : "++>", + indent + 1, ' ', prompt); + } else + item_make(" %*c%s %s", + indent + 1, ' ', prompt, + menu_is_empty(menu) ? "----" : "--->"); + item_set_tag('m'); + item_set_data(menu); + if (single_menu_mode && menu->data) + goto conf_childs; + return; + case P_COMMENT: + if (prompt) { + child_count++; + item_make(" %*c*** %s ***", indent + 1, ' ', _(prompt)); + item_set_tag(':'); + item_set_data(menu); + } + break; + default: + if (prompt) { + child_count++; + item_make("---%*c%s", indent + 1, ' ', _(prompt)); + item_set_tag(':'); + item_set_data(menu); + } + } + } else + doint = 0; + goto conf_childs; + } + + type = sym_get_type(sym); + if (sym_is_choice(sym)) { + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + child_count++; + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + val = sym_get_tristate_value(sym); + if (sym_is_changable(sym)) { + switch (type) { + case S_BOOLEAN: + item_make("[%c]", val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + item_make("<%c>", ch); + break; + } + item_set_tag('t'); + item_set_data(menu); + } else { + item_make(" "); + item_set_tag(def_menu ? 't' : ':'); + item_set_data(menu); + } + + item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu))); + if (val == yes) { + if (def_menu) { + item_add_str(" (%s)", _(menu_get_prompt(def_menu))); + item_add_str(" --->"); + if (def_menu->list) { + indent += 2; + build_conf(def_menu); + indent -= 2; + } + } + return; + } + } else { + if (menu == current_menu) { + item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu))); + item_set_tag(':'); + item_set_data(menu); + goto conf_childs; + } + child_count++; + val = sym_get_tristate_value(sym); + if (sym_is_choice_value(sym) && val == yes) { + item_make(" "); + item_set_tag(':'); + item_set_data(menu); + } else { + switch (type) { + case S_BOOLEAN: + if (sym_is_changable(sym)) + item_make("[%c]", val == no ? ' ' : '*'); + else + item_make("-%c-", val == no ? ' ' : '*'); + item_set_tag('t'); + item_set_data(menu); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + if (sym_is_changable(sym)) { + if (sym->rev_dep.tri == mod) + item_make("{%c}", ch); + else + item_make("<%c>", ch); + } else + item_make("-%c-", ch); + item_set_tag('t'); + item_set_data(menu); + break; + default: + tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */ + item_make("(%s)", sym_get_string_value(sym)); + tmp = indent - tmp + 4; + if (tmp < 0) + tmp = 0; + item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + item_set_tag('s'); + item_set_data(menu); + goto conf_childs; + } + } + item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + if (menu->prompt->type == P_MENU) { + item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->"); + return; + } + } + +conf_childs: + indent += doint; + for (child = menu->list; child; child = child->next) + build_conf(child); + indent -= doint; +} + +static void conf(struct menu *menu, struct menu *active_menu) +{ + struct menu *submenu; + const char *prompt = menu_get_prompt(menu); + struct subtitle_part stpart; + struct symbol *sym; + int res; + int s_scroll = 0; + + if (menu != &rootmenu) + stpart.text = menu_get_prompt(menu); + else + stpart.text = NULL; + list_add_tail(&stpart.entries, &trail); + + while (1) { + item_reset(); + current_menu = menu; + build_conf(menu); + if (!child_count) + break; + set_subtitle(); + dialog_clear(); + res = dialog_menu(prompt ? _(prompt) : _("Main Menu"), + _(menu_instructions), + active_menu, &s_scroll); + if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL) + break; + if (item_count() != 0) { + if (!item_activate_selected()) + continue; + if (!item_tag()) + continue; + } + submenu = item_data(); + active_menu = item_data(); + if (submenu) + sym = submenu->sym; + else + sym = NULL; + + switch (res) { + case 0: + switch (item_tag()) { + case 'm': + if (single_menu_mode) + submenu->data = (void *) (long) !submenu->data; + else + conf(submenu, NULL); + break; + case 't': + if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) + conf_choice(submenu); + else if (submenu->prompt->type == P_MENU) + conf(submenu, NULL); + break; + case 's': + conf_string(submenu); + break; + } + break; + case 2: + if (sym) + show_help(submenu); + else { + reset_subtitle(); + show_helptext(_("README"), _(mconf_readme)); + } + break; + case 3: + reset_subtitle(); + conf_save(); + break; + case 4: + reset_subtitle(); + conf_load(); + break; + case 5: + if (item_is_tag('t')) { + if (sym_set_tristate_value(sym, yes)) + break; + if (sym_set_tristate_value(sym, mod)) + show_textbox(NULL, setmod_text, 6, 74); + } + break; + case 6: + if (item_is_tag('t')) + sym_set_tristate_value(sym, no); + break; + case 7: + if (item_is_tag('t')) + sym_set_tristate_value(sym, mod); + break; + case 8: + if (item_is_tag('t')) + sym_toggle_tristate_value(sym); + else if (item_is_tag('m')) + conf(submenu, NULL); + break; + case 9: + search_conf(); + break; + case 10: + show_all_options = !show_all_options; + break; + } + } + + list_del(trail.prev); +} + +static int show_textbox_ext(const char *title, char *text, int r, int c, int + *keys, int *vscroll, int *hscroll, update_text_fn + update_text, void *data) +{ + dialog_clear(); + return dialog_textbox(title, text, r, c, keys, vscroll, hscroll, + update_text, data); +} + +static void show_textbox(const char *title, const char *text, int r, int c) +{ + show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL, + NULL, NULL); +} + +static void show_helptext(const char *title, const char *text) +{ + show_textbox(title, text, 0, 0); +} + +static void conf_message_callback(const char *fmt, va_list ap) +{ + char buf[PATH_MAX+1]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + if (save_and_exit) + printf("%s", buf); + else + show_textbox(NULL, buf, 6, 60); +} + +static void show_help(struct menu *menu) +{ + struct gstr help = str_new(); + + help.max_width = getmaxx(stdscr) - 10; + menu_get_ext_help(menu, &help); + + show_helptext(_(menu_get_prompt(menu)), str_get(&help)); + str_free(&help); +} + +static void conf_choice(struct menu *menu) +{ + const char *prompt = _(menu_get_prompt(menu)); + struct menu *child; + struct symbol *active; + + active = sym_get_choice_value(menu->sym); + while (1) { + int res; + int selected; + item_reset(); + + current_menu = menu; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (child->sym) + item_make("%s", _(menu_get_prompt(child))); + else { + item_make("*** %s ***", _(menu_get_prompt(child))); + item_set_tag(':'); + } + item_set_data(child); + if (child->sym == active) + item_set_selected(1); + if (child->sym == sym_get_choice_value(menu->sym)) + item_set_tag('X'); + } + dialog_clear(); + res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"), + _(radiolist_instructions), + MENUBOX_HEIGTH_MIN, + MENUBOX_WIDTH_MIN, + CHECKLIST_HEIGTH_MIN); + selected = item_activate_selected(); + switch (res) { + case 0: + if (selected) { + child = item_data(); + if (!child->sym) + break; + + sym_set_tristate_value(child->sym, yes); + } + return; + case 1: + if (selected) { + child = item_data(); + show_help(child); + active = child->sym; + } else + show_help(menu); + break; + case KEY_ESC: + return; + case -ERRDISPLAYTOOSMALL: + return; + } + } +} + +static void conf_string(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + + while (1) { + int res; + const char *heading; + + switch (sym_get_type(menu->sym)) { + case S_INT: + heading = _(inputbox_instructions_int); + break; + case S_HEX: + heading = _(inputbox_instructions_hex); + break; + case S_STRING: + heading = _(inputbox_instructions_string); + break; + default: + heading = _("Internal mconf error!"); + } + dialog_clear(); + res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"), + heading, 10, 75, + sym_get_string_value(menu->sym)); + switch (res) { + case 0: + if (sym_set_string_value(menu->sym, dialog_input_result)) + return; + show_textbox(NULL, _("You have made an invalid entry."), 5, 43); + break; + case 1: + show_help(menu); + break; + case KEY_ESC: + return; + } + } +} + +static void conf_load(void) +{ + + while (1) { + int res; + dialog_clear(); + res = dialog_inputbox(NULL, load_config_text, + 11, 55, filename); + switch(res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_read(dialog_input_result)) { + set_config_filename(dialog_input_result); + sym_set_change_count(1); + return; + } + show_textbox(NULL, _("File does not exist!"), 5, 38); + break; + case 1: + show_helptext(_("Load Alternate Configuration"), load_config_help); + break; + case KEY_ESC: + return; + } + } +} + +static void conf_save(void) +{ + while (1) { + int res; + dialog_clear(); + res = dialog_inputbox(NULL, save_config_text, + 11, 55, filename); + switch(res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_write(dialog_input_result)) { + set_config_filename(dialog_input_result); + return; + } + show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60); + break; + case 1: + show_helptext(_("Save Alternate Configuration"), save_config_help); + break; + case KEY_ESC: + return; + } + } +} + +static int handle_exit(void) +{ + int res; + + save_and_exit = 1; + reset_subtitle(); + dialog_clear(); + if (conf_get_changed()) + res = dialog_yesno(NULL, + _("Do you wish to save your new configuration?\n" + "(Press <ESC><ESC> to continue kernel configuration.)"), + 6, 60); + else + res = -1; + + end_dialog(saved_x, saved_y); + + switch (res) { + case 0: + if (conf_write(filename)) { + fprintf(stderr, _("\n\n" + "Error while writing of the configuration.\n" + "Your configuration changes were NOT saved." + "\n\n")); + return 1; + } + /* fall through */ + case -1: + printf(_("\n\n" + "*** End of the configuration.\n" + "*** Execute 'make' to start the build or try 'make help'." + "\n\n")); + res = 0; + break; + default: + fprintf(stderr, _("\n\n" + "Your configuration changes were NOT saved." + "\n\n")); + if (res != KEY_ESC) + res = 0; + } + + return res; +} + +static void sig_handler(int signo) +{ + exit(handle_exit()); +} + +int main(int ac, char **av) +{ + char *mode; + int res; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + signal(SIGINT, sig_handler); + + conf_parse(av[1]); + conf_read(NULL); + + mode = getenv("MENUCONFIG_MODE"); + if (mode) { + if (!strcasecmp(mode, "single_menu")) + single_menu_mode = 1; + } + + if (init_dialog(NULL)) { + fprintf(stderr, N_("Your display is too small to run Menuconfig!\n")); + fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n")); + return 1; + } + + set_config_filename(conf_get_configname()); + conf_set_message_callback(conf_message_callback); + do { + conf(&rootmenu, NULL); + res = handle_exit(); + } while (res == KEY_ESC); + + return res; +} diff --git a/qemu/roms/seabios/scripts/kconfig/menu.c b/qemu/roms/seabios/scripts/kconfig/menu.c new file mode 100644 index 000000000..a26cc5d2a --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/menu.c @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include "lkc.h" + +static const char nohelp_text[] = "There is no help available for this option."; + +struct menu rootmenu; +static struct menu **last_entry_ptr; + +struct file *file_list; +struct file *current_file; + +void menu_warn(struct menu *menu, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +static void prop_warn(struct property *prop, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +void _menu_init(void) +{ + current_entry = current_menu = &rootmenu; + last_entry_ptr = &rootmenu.list; +} + +void menu_add_entry(struct symbol *sym) +{ + struct menu *menu; + + menu = xmalloc(sizeof(*menu)); + memset(menu, 0, sizeof(*menu)); + menu->sym = sym; + menu->parent = current_menu; + menu->file = current_file; + menu->lineno = zconf_lineno(); + + *last_entry_ptr = menu; + last_entry_ptr = &menu->next; + current_entry = menu; + if (sym) + menu_add_symbol(P_SYMBOL, sym, NULL); +} + +void menu_end_entry(void) +{ +} + +struct menu *menu_add_menu(void) +{ + menu_end_entry(); + last_entry_ptr = ¤t_entry->list; + return current_menu = current_entry; +} + +void menu_end_menu(void) +{ + last_entry_ptr = ¤t_menu->next; + current_menu = current_menu->parent; +} + +static struct expr *menu_check_dep(struct expr *e) +{ + if (!e) + return e; + + switch (e->type) { + case E_NOT: + e->left.expr = menu_check_dep(e->left.expr); + break; + case E_OR: + case E_AND: + e->left.expr = menu_check_dep(e->left.expr); + e->right.expr = menu_check_dep(e->right.expr); + break; + case E_SYMBOL: + /* change 'm' into 'm' && MODULES */ + if (e->left.sym == &symbol_mod) + return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); + break; + default: + break; + } + return e; +} + +void menu_add_dep(struct expr *dep) +{ + current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); +} + +void menu_set_type(int type) +{ + struct symbol *sym = current_entry->sym; + + if (sym->type == type) + return; + if (sym->type == S_UNKNOWN) { + sym->type = type; + return; + } + menu_warn(current_entry, + "ignoring type redefinition of '%s' from '%s' to '%s'", + sym->name ? sym->name : "<choice>", + sym_type_name(sym->type), sym_type_name(type)); +} + +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) +{ + struct property *prop = prop_alloc(type, current_entry->sym); + + prop->menu = current_entry; + prop->expr = expr; + prop->visible.expr = menu_check_dep(dep); + + if (prompt) { + if (isspace(*prompt)) { + prop_warn(prop, "leading whitespace ignored"); + while (isspace(*prompt)) + prompt++; + } + if (current_entry->prompt && current_entry != &rootmenu) + prop_warn(prop, "prompt redefined"); + + /* Apply all upper menus' visibilities to actual prompts. */ + if(type == P_PROMPT) { + struct menu *menu = current_entry; + + while ((menu = menu->parent) != NULL) { + struct expr *dup_expr; + + if (!menu->visibility) + continue; + /* + * Do not add a reference to the + * menu's visibility expression but + * use a copy of it. Otherwise the + * expression reduction functions + * will modify expressions that have + * multiple references which can + * cause unwanted side effects. + */ + dup_expr = expr_copy(menu->visibility); + + prop->visible.expr + = expr_alloc_and(prop->visible.expr, + dup_expr); + } + } + + current_entry->prompt = prop; + } + prop->text = prompt; + + return prop; +} + +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) +{ + return menu_add_prop(type, prompt, NULL, dep); +} + +void menu_add_visibility(struct expr *expr) +{ + current_entry->visibility = expr_alloc_and(current_entry->visibility, + expr); +} + +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) +{ + menu_add_prop(type, NULL, expr, dep); +} + +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) +{ + menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); +} + +void menu_add_option(int token, char *arg) +{ + switch (token) { + case T_OPT_MODULES: + if (modules_sym) + zconf_error("symbol '%s' redefines option 'modules'" + " already defined by symbol '%s'", + current_entry->sym->name, + modules_sym->name + ); + modules_sym = current_entry->sym; + break; + case T_OPT_DEFCONFIG_LIST: + if (!sym_defconfig_list) + sym_defconfig_list = current_entry->sym; + else if (sym_defconfig_list != current_entry->sym) + zconf_error("trying to redefine defconfig symbol"); + break; + case T_OPT_ENV: + prop_add_env(arg); + break; + case T_OPT_ALLNOCONFIG_Y: + current_entry->sym->flags |= SYMBOL_ALLNOCONFIG_Y; + break; + } +} + +static int menu_validate_number(struct symbol *sym, struct symbol *sym2) +{ + return sym2->type == S_INT || sym2->type == S_HEX || + (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); +} + +static void sym_check_prop(struct symbol *sym) +{ + struct property *prop; + struct symbol *sym2; + for (prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_DEFAULT: + if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && + prop->expr->type != E_SYMBOL) + prop_warn(prop, + "default for config symbol '%s'" + " must be a single symbol", sym->name); + if (prop->expr->type != E_SYMBOL) + break; + sym2 = prop_get_symbol(prop); + if (sym->type == S_HEX || sym->type == S_INT) { + if (!menu_validate_number(sym, sym2)) + prop_warn(prop, + "'%s': number is invalid", + sym->name); + } + break; + case P_SELECT: + sym2 = prop_get_symbol(prop); + if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) + prop_warn(prop, + "config symbol '%s' uses select, but is " + "not boolean or tristate", sym->name); + else if (sym2->type != S_UNKNOWN && + sym2->type != S_BOOLEAN && + sym2->type != S_TRISTATE) + prop_warn(prop, + "'%s' has wrong type. 'select' only " + "accept arguments of boolean and " + "tristate type", sym2->name); + break; + case P_RANGE: + if (sym->type != S_INT && sym->type != S_HEX) + prop_warn(prop, "range is only allowed " + "for int or hex symbols"); + if (!menu_validate_number(sym, prop->expr->left.sym) || + !menu_validate_number(sym, prop->expr->right.sym)) + prop_warn(prop, "range is invalid"); + break; + default: + ; + } + } +} + +void menu_finalize(struct menu *parent) +{ + struct menu *menu, *last_menu; + struct symbol *sym; + struct property *prop; + struct expr *parentdep, *basedep, *dep, *dep2, **ep; + + sym = parent->sym; + if (parent->list) { + if (sym && sym_is_choice(sym)) { + if (sym->type == S_UNKNOWN) { + /* find the first choice value to find out choice type */ + current_entry = parent; + for (menu = parent->list; menu; menu = menu->next) { + if (menu->sym && menu->sym->type != S_UNKNOWN) { + menu_set_type(menu->sym->type); + break; + } + } + } + /* set the type of the remaining choice values */ + for (menu = parent->list; menu; menu = menu->next) { + current_entry = menu; + if (menu->sym && menu->sym->type == S_UNKNOWN) + menu_set_type(sym->type); + } + parentdep = expr_alloc_symbol(sym); + } else if (parent->prompt) + parentdep = parent->prompt->visible.expr; + else + parentdep = parent->dep; + + for (menu = parent->list; menu; menu = menu->next) { + basedep = expr_transform(menu->dep); + basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_eliminate_dups(basedep); + menu->dep = basedep; + if (menu->sym) + prop = menu->sym->prop; + else + prop = menu->prompt; + for (; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + dep = expr_transform(prop->visible.expr); + dep = expr_alloc_and(expr_copy(basedep), dep); + dep = expr_eliminate_dups(dep); + if (menu->sym && menu->sym->type != S_TRISTATE) + dep = expr_trans_bool(dep); + prop->visible.expr = dep; + if (prop->type == P_SELECT) { + struct symbol *es = prop_get_symbol(prop); + es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); + } + } + } + for (menu = parent->list; menu; menu = menu->next) + menu_finalize(menu); + } else if (sym) { + basedep = parent->prompt ? parent->prompt->visible.expr : NULL; + basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); + basedep = expr_eliminate_dups(expr_transform(basedep)); + last_menu = NULL; + for (menu = parent->next; menu; menu = menu->next) { + dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; + if (!expr_contains_symbol(dep, sym)) + break; + if (expr_depends_symbol(dep, sym)) + goto next; + dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); + dep = expr_eliminate_dups(expr_transform(dep)); + dep2 = expr_copy(basedep); + expr_eliminate_eq(&dep, &dep2); + expr_free(dep); + if (!expr_is_yes(dep2)) { + expr_free(dep2); + break; + } + expr_free(dep2); + next: + menu_finalize(menu); + menu->parent = parent; + last_menu = menu; + } + if (last_menu) { + parent->list = parent->next; + parent->next = last_menu->next; + last_menu->next = NULL; + } + + sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); + } + for (menu = parent->list; menu; menu = menu->next) { + if (sym && sym_is_choice(sym) && + menu->sym && !sym_is_choice_value(menu->sym)) { + current_entry = menu; + menu->sym->flags |= SYMBOL_CHOICEVAL; + if (!menu->prompt) + menu_warn(menu, "choice value must have a prompt"); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type == P_DEFAULT) + prop_warn(prop, "defaults for choice " + "values not supported"); + if (prop->menu == menu) + continue; + if (prop->type == P_PROMPT && + prop->menu->parent->sym != sym) + prop_warn(prop, "choice value used outside its choice group"); + } + /* Non-tristate choice values of tristate choices must + * depend on the choice being set to Y. The choice + * values' dependencies were propagated to their + * properties above, so the change here must be re- + * propagated. + */ + if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { + basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); + menu->dep = expr_alloc_and(basedep, menu->dep); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + prop->visible.expr = expr_alloc_and(expr_copy(basedep), + prop->visible.expr); + } + } + menu_add_symbol(P_CHOICE, sym, NULL); + prop = sym_get_choice_prop(sym); + for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) + ; + *ep = expr_alloc_one(E_LIST, NULL); + (*ep)->right.sym = menu->sym; + } + if (menu->list && (!menu->prompt || !menu->prompt->text)) { + for (last_menu = menu->list; ; last_menu = last_menu->next) { + last_menu->parent = parent; + if (!last_menu->next) + break; + } + last_menu->next = menu->next; + menu->next = menu->list; + menu->list = NULL; + } + } + + if (sym && !(sym->flags & SYMBOL_WARNED)) { + if (sym->type == S_UNKNOWN) + menu_warn(parent, "config symbol defined without type"); + + if (sym_is_choice(sym) && !parent->prompt) + menu_warn(parent, "choice must have a prompt"); + + /* Check properties connected to this symbol */ + sym_check_prop(sym); + sym->flags |= SYMBOL_WARNED; + } + + if (sym && !sym_is_optional(sym) && parent->prompt) { + sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, + expr_alloc_and(parent->prompt->visible.expr, + expr_alloc_symbol(&symbol_mod))); + } +} + +bool menu_has_prompt(struct menu *menu) +{ + if (!menu->prompt) + return false; + return true; +} + +/* + * Determine if a menu is empty. + * A menu is considered empty if it contains no or only + * invisible entries. + */ +bool menu_is_empty(struct menu *menu) +{ + struct menu *child; + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child)) + return(false); + } + return(true); +} + +bool menu_is_visible(struct menu *menu) +{ + struct menu *child; + struct symbol *sym; + tristate visible; + + if (!menu->prompt) + return false; + + if (menu->visibility) { + if (expr_calc_value(menu->visibility) == no) + return no; + } + + sym = menu->sym; + if (sym) { + sym_calc_value(sym); + visible = menu->prompt->visible.tri; + } else + visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); + + if (visible != no) + return true; + + if (!sym || sym_get_tristate_value(menu->sym) == no) + return false; + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child)) { + if (sym) + sym->flags |= SYMBOL_DEF_USER; + return true; + } + } + + return false; +} + +const char *menu_get_prompt(struct menu *menu) +{ + if (menu->prompt) + return menu->prompt->text; + else if (menu->sym) + return menu->sym->name; + return NULL; +} + +struct menu *menu_get_root_menu(struct menu *menu) +{ + return &rootmenu; +} + +struct menu *menu_get_parent_menu(struct menu *menu) +{ + enum prop_type type; + + for (; menu != &rootmenu; menu = menu->parent) { + type = menu->prompt ? menu->prompt->type : 0; + if (type == P_MENU) + break; + } + return menu; +} + +bool menu_has_help(struct menu *menu) +{ + return menu->help != NULL; +} + +const char *menu_get_help(struct menu *menu) +{ + if (menu->help) + return menu->help; + else + return ""; +} + +static void get_prompt_str(struct gstr *r, struct property *prop, + struct list_head *head) +{ + int i, j; + struct menu *submenu[8], *menu, *location = NULL; + struct jump_key *jump; + + str_printf(r, _("Prompt: %s\n"), _(prop->text)); + menu = prop->menu->parent; + for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) { + bool accessible = menu_is_visible(menu); + + submenu[i++] = menu; + if (location == NULL && accessible) + location = menu; + } + if (head && location) { + jump = xmalloc(sizeof(struct jump_key)); + + if (menu_is_visible(prop->menu)) { + /* + * There is not enough room to put the hint at the + * beginning of the "Prompt" line. Put the hint on the + * last "Location" line even when it would belong on + * the former. + */ + jump->target = prop->menu; + } else + jump->target = location; + + if (list_empty(head)) + jump->index = 0; + else + jump->index = list_entry(head->prev, struct jump_key, + entries)->index + 1; + + list_add_tail(&jump->entries, head); + } + + if (i > 0) { + str_printf(r, _(" Location:\n")); + for (j = 4; --i >= 0; j += 2) { + menu = submenu[i]; + if (head && location && menu == location) + jump->offset = strlen(r->s); + str_printf(r, "%*c-> %s", j, ' ', + _(menu_get_prompt(menu))); + if (menu->sym) { + str_printf(r, " (%s [=%s])", menu->sym->name ? + menu->sym->name : _("<choice>"), + sym_get_string_value(menu->sym)); + } + str_append(r, "\n"); + } + } +} + +/* + * get property of type P_SYMBOL + */ +static struct property *get_symbol_prop(struct symbol *sym) +{ + struct property *prop = NULL; + + for_all_properties(sym, prop, P_SYMBOL) + break; + return prop; +} + +/* + * head is optional and may be NULL + */ +void get_symbol_str(struct gstr *r, struct symbol *sym, + struct list_head *head) +{ + bool hit; + struct property *prop; + + if (sym && sym->name) { + str_printf(r, "Symbol: %s [=%s]\n", sym->name, + sym_get_string_value(sym)); + str_printf(r, "Type : %s\n", sym_type_name(sym->type)); + if (sym->type == S_INT || sym->type == S_HEX) { + prop = sym_get_range_prop(sym); + if (prop) { + str_printf(r, "Range : "); + expr_gstr_print(prop->expr, r); + str_append(r, "\n"); + } + } + } + for_all_prompts(sym, prop) + get_prompt_str(r, prop, head); + + prop = get_symbol_prop(sym); + if (prop) { + str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name, + prop->menu->lineno); + if (!expr_is_yes(prop->visible.expr)) { + str_append(r, _(" Depends on: ")); + expr_gstr_print(prop->visible.expr, r); + str_append(r, "\n"); + } + } + + hit = false; + for_all_properties(sym, prop, P_SELECT) { + if (!hit) { + str_append(r, " Selects: "); + hit = true; + } else + str_printf(r, " && "); + expr_gstr_print(prop->expr, r); + } + if (hit) + str_append(r, "\n"); + if (sym->rev_dep.expr) { + str_append(r, _(" Selected by: ")); + expr_gstr_print(sym->rev_dep.expr, r); + str_append(r, "\n"); + } + str_append(r, "\n\n"); +} + +struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head) +{ + struct symbol *sym; + struct gstr res = str_new(); + int i; + + for (i = 0; sym_arr && (sym = sym_arr[i]); i++) + get_symbol_str(&res, sym, head); + if (!i) + str_append(&res, _("No matches found.\n")); + return res; +} + + +void menu_get_ext_help(struct menu *menu, struct gstr *help) +{ + struct symbol *sym = menu->sym; + const char *help_text = nohelp_text; + + if (menu_has_help(menu)) { + if (sym->name) + str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); + help_text = menu_get_help(menu); + } + str_printf(help, "%s\n", _(help_text)); + if (sym) + get_symbol_str(help, sym, NULL); +} diff --git a/qemu/roms/seabios/scripts/kconfig/merge_config.sh b/qemu/roms/seabios/scripts/kconfig/merge_config.sh new file mode 100755 index 000000000..81b0c61bb --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/merge_config.sh @@ -0,0 +1,150 @@ +#!/bin/sh +# merge_config.sh - Takes a list of config fragment values, and merges +# them one by one. Provides warnings on overridden values, and specified +# values that did not make it to the resulting .config file (due to missed +# dependencies or config symbol removal). +# +# Portions reused from kconf_check and generate_cfg: +# http://git.yoctoproject.org/cgit/cgit.cgi/yocto-kernel-tools/tree/tools/kconf_check +# http://git.yoctoproject.org/cgit/cgit.cgi/yocto-kernel-tools/tree/tools/generate_cfg +# +# Copyright (c) 2009-2010 Wind River Systems, Inc. +# Copyright 2011 Linaro +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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. + +clean_up() { + rm -f $TMP_FILE + exit +} +trap clean_up HUP INT TERM + +usage() { + echo "Usage: $0 [OPTIONS] [CONFIG [...]]" + echo " -h display this help text" + echo " -m only merge the fragments, do not execute the make command" + echo " -n use allnoconfig instead of alldefconfig" + echo " -r list redundant entries when merging fragments" + echo " -O dir to put generated output files" +} + +MAKE=true +ALLTARGET=alldefconfig +WARNREDUN=false +OUTPUT=. + +while true; do + case $1 in + "-n") + ALLTARGET=allnoconfig + shift + continue + ;; + "-m") + MAKE=false + shift + continue + ;; + "-h") + usage + exit + ;; + "-r") + WARNREDUN=true + shift + continue + ;; + "-O") + if [ -d $2 ];then + OUTPUT=$(echo $2 | sed 's/\/*$//') + else + echo "output directory $2 does not exist" 1>&2 + exit 1 + fi + shift 2 + continue + ;; + *) + break + ;; + esac +done + +INITFILE=$1 +shift; + +MERGE_LIST=$* +SED_CONFIG_EXP="s/^\(# \)\{0,1\}\(CONFIG_[a-zA-Z0-9_]*\)[= ].*/\2/p" +TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX) + +echo "Using $INITFILE as base" +cat $INITFILE > $TMP_FILE + +# Merge files, printing warnings on overrided values +for MERGE_FILE in $MERGE_LIST ; do + echo "Merging $MERGE_FILE" + CFG_LIST=$(sed -n "$SED_CONFIG_EXP" $MERGE_FILE) + + for CFG in $CFG_LIST ; do + grep -q -w $CFG $TMP_FILE + if [ $? -eq 0 ] ; then + PREV_VAL=$(grep -w $CFG $TMP_FILE) + NEW_VAL=$(grep -w $CFG $MERGE_FILE) + if [ "x$PREV_VAL" != "x$NEW_VAL" ] ; then + echo Value of $CFG is redefined by fragment $MERGE_FILE: + echo Previous value: $PREV_VAL + echo New value: $NEW_VAL + echo + elif [ "$WARNREDUN" = "true" ]; then + echo Value of $CFG is redundant by fragment $MERGE_FILE: + fi + sed -i "/$CFG[ =]/d" $TMP_FILE + fi + done + cat $MERGE_FILE >> $TMP_FILE +done + +if [ "$MAKE" = "false" ]; then + cp $TMP_FILE $OUTPUT/.config + echo "#" + echo "# merged configuration written to $OUTPUT/.config (needs make)" + echo "#" + clean_up + exit +fi + +# If we have an output dir, setup the O= argument, otherwise leave +# it blank, since O=. will create an unnecessary ./source softlink +OUTPUT_ARG="" +if [ "$OUTPUT" != "." ] ; then + OUTPUT_ARG="O=$OUTPUT" +fi + + +# Use the merged file as the starting point for: +# alldefconfig: Fills in any missing symbols with Kconfig default +# allnoconfig: Fills in any missing symbols with # CONFIG_* is not set +make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET + + +# Check all specified config values took (might have missed-dependency issues) +for CFG in $(sed -n "$SED_CONFIG_EXP" $TMP_FILE); do + + REQUESTED_VAL=$(grep -w -e "$CFG" $TMP_FILE) + ACTUAL_VAL=$(grep -w -e "$CFG" $OUTPUT/.config) + if [ "x$REQUESTED_VAL" != "x$ACTUAL_VAL" ] ; then + echo "Value requested for $CFG not in final .config" + echo "Requested value: $REQUESTED_VAL" + echo "Actual value: $ACTUAL_VAL" + echo "" + fi +done + +clean_up diff --git a/qemu/roms/seabios/scripts/kconfig/nconf.c b/qemu/roms/seabios/scripts/kconfig/nconf.c new file mode 100644 index 000000000..984489ef2 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/nconf.c @@ -0,0 +1,1556 @@ +/* + * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com? + * Released under the terms of the GNU GPL v2.0. + * + * Derived from menuconfig. + * + */ +#define _GNU_SOURCE +#include <string.h> +#include <stdlib.h> + +#include "lkc.h" +#include "nconf.h" +#include <ctype.h> + +static const char nconf_global_help[] = N_( +"Help windows\n" +"------------\n" +"o Global help: Unless in a data entry window, pressing <F1> will give \n" +" you the global help window, which you are just reading.\n" +"\n" +"o A short version of the global help is available by pressing <F3>.\n" +"\n" +"o Local help: To get help related to the current menu entry, use any\n" +" of <?> <h>, or if in a data entry window then press <F1>.\n" +"\n" +"\n" +"Menu entries\n" +"------------\n" +"This interface lets you select features and parameters for the kernel\n" +"build. Kernel features can either be built-in, modularized, or removed.\n" +"Parameters must be entered as text or decimal or hexadecimal numbers.\n" +"\n" +"Menu entries beginning with following braces represent features that\n" +" [ ] can be built in or removed\n" +" < > can be built in, modularized or removed\n" +" { } can be built in or modularized, are selected by another feature\n" +" - - are selected by another feature\n" +" XXX cannot be selected. Symbol Info <F2> tells you why.\n" +"*, M or whitespace inside braces means to build in, build as a module\n" +"or to exclude the feature respectively.\n" +"\n" +"To change any of these features, highlight it with the movement keys\n" +"listed below and press <y> to build it in, <m> to make it a module or\n" +"<n> to remove it. You may press the <Space> key to cycle through the\n" +"available options.\n" +"\n" +"A trailing \"--->\" designates a submenu, a trailing \"----\" an\n" +"empty submenu.\n" +"\n" +"Menu navigation keys\n" +"----------------------------------------------------------------------\n" +"Linewise up <Up>\n" +"Linewise down <Down>\n" +"Pagewise up <Page Up>\n" +"Pagewise down <Page Down>\n" +"First entry <Home>\n" +"Last entry <End>\n" +"Enter a submenu <Right> <Enter>\n" +"Go back to parent menu <Left> <Esc> <F5>\n" +"Close a help window <Enter> <Esc> <F5>\n" +"Close entry window, apply <Enter>\n" +"Close entry window, forget <Esc> <F5>\n" +"Start incremental, case-insensitive search for STRING in menu entries,\n" +" no regex support, STRING is displayed in upper left corner\n" +" </>STRING\n" +" Remove last character <Backspace>\n" +" Jump to next hit <Down>\n" +" Jump to previous hit <Up>\n" +"Exit menu search mode </> <Esc>\n" +"Search for configuration variables with or without leading CONFIG_\n" +" <F8>RegExpr<Enter>\n" +"Verbose search help <F8><F1>\n" +"----------------------------------------------------------------------\n" +"\n" +"Unless in a data entry window, key <1> may be used instead of <F1>,\n" +"<2> instead of <F2>, etc.\n" +"\n" +"\n" +"Radiolist (Choice list)\n" +"-----------------------\n" +"Use the movement keys listed above to select the option you wish to set\n" +"and press <Space>.\n" +"\n" +"\n" +"Data entry\n" +"----------\n" +"Enter the requested information and press <Enter>. Hexadecimal values\n" +"may be entered without the \"0x\" prefix.\n" +"\n" +"\n" +"Text Box (Help Window)\n" +"----------------------\n" +"Use movement keys as listed in table above.\n" +"\n" +"Press any of <Enter> <Esc> <q> <F5> <F9> to exit.\n" +"\n" +"\n" +"Alternate configuration files\n" +"-----------------------------\n" +"nconfig supports switching between different configurations.\n" +"Press <F6> to save your current configuration. Press <F7> and enter\n" +"a file name to load a previously saved configuration.\n" +"\n" +"\n" +"Terminal configuration\n" +"----------------------\n" +"If you use nconfig in a xterm window, make sure your TERM environment\n" +"variable specifies a terminal configuration which supports at least\n" +"16 colors. Otherwise nconfig will look rather bad.\n" +"\n" +"If the \"stty size\" command reports the current terminalsize correctly,\n" +"nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n" +"and display longer menus properly.\n" +"\n" +"\n" +"Single menu mode\n" +"----------------\n" +"If you prefer to have all of the menu entries listed in a single menu,\n" +"rather than the default multimenu hierarchy, run nconfig with\n" +"NCONFIG_MODE environment variable set to single_menu. Example:\n" +"\n" +"make NCONFIG_MODE=single_menu nconfig\n" +"\n" +"<Enter> will then unfold the appropriate category, or fold it if it\n" +"is already unfolded. Folded menu entries will be designated by a\n" +"leading \"++>\" and unfolded entries by a leading \"-->\".\n" +"\n" +"Note that this mode can eventually be a little more CPU expensive than\n" +"the default mode, especially with a larger number of unfolded submenus.\n" +"\n"), +menu_no_f_instructions[] = N_( +"Legend: [*] built-in [ ] excluded <M> module < > module capable.\n" +"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" +"\n" +"Use the following keys to navigate the menus:\n" +"Move up or down with <Up> and <Down>.\n" +"Enter a submenu with <Enter> or <Right>.\n" +"Exit a submenu to its parent menu with <Esc> or <Left>.\n" +"Pressing <y> includes, <n> excludes, <m> modularizes features.\n" +"Pressing <Space> cycles through the available options.\n" +"To search for menu entries press </>.\n" +"<Esc> always leaves the current window.\n" +"\n" +"You do not have function keys support.\n" +"Press <1> instead of <F1>, <2> instead of <F2>, etc.\n" +"For verbose global help use key <1>.\n" +"For help related to the current menu entry press <?> or <h>.\n"), +menu_instructions[] = N_( +"Legend: [*] built-in [ ] excluded <M> module < > module capable.\n" +"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" +"\n" +"Use the following keys to navigate the menus:\n" +"Move up or down with <Up> or <Down>.\n" +"Enter a submenu with <Enter> or <Right>.\n" +"Exit a submenu to its parent menu with <Esc> or <Left>.\n" +"Pressing <y> includes, <n> excludes, <m> modularizes features.\n" +"Pressing <Space> cycles through the available options.\n" +"To search for menu entries press </>.\n" +"<Esc> always leaves the current window.\n" +"\n" +"Pressing <1> may be used instead of <F1>, <2> instead of <F2>, etc.\n" +"For verbose global help press <F1>.\n" +"For help related to the current menu entry press <?> or <h>.\n"), +radiolist_instructions[] = N_( +"Press <Up>, <Down>, <Home> or <End> to navigate a radiolist, select\n" +"with <Space>.\n" +"For help related to the current entry press <?> or <h>.\n" +"For global help press <F1>.\n"), +inputbox_instructions_int[] = N_( +"Please enter a decimal value.\n" +"Fractions will not be accepted.\n" +"Press <Enter> to apply, <Esc> to cancel."), +inputbox_instructions_hex[] = N_( +"Please enter a hexadecimal value.\n" +"Press <Enter> to apply, <Esc> to cancel."), +inputbox_instructions_string[] = N_( +"Please enter a string value.\n" +"Press <Enter> to apply, <Esc> to cancel."), +setmod_text[] = N_( +"This feature depends on another feature which has been configured as a\n" +"module. As a result, the current feature will be built as a module too."), +load_config_text[] = N_( +"Enter the name of the configuration file you wish to load.\n" +"Accept the name shown to restore the configuration you last\n" +"retrieved. Leave empty to abort."), +load_config_help[] = N_( +"For various reasons, one may wish to keep several different\n" +"configurations available on a single machine.\n" +"\n" +"If you have saved a previous configuration in a file other than the\n" +"default one, entering its name here will allow you to load and modify\n" +"that configuration.\n" +"\n" +"Leave empty to abort.\n"), +save_config_text[] = N_( +"Enter a filename to which this configuration should be saved\n" +"as an alternate. Leave empty to abort."), +save_config_help[] = N_( +"For various reasons, one may wish to keep several different\n" +"configurations available on a single machine.\n" +"\n" +"Entering a file name here will allow you to later retrieve, modify\n" +"and use the current configuration as an alternate to whatever\n" +"configuration options you have selected at that time.\n" +"\n" +"Leave empty to abort.\n"), +search_help[] = N_( +"Search for symbols (configuration variable names CONFIG_*) and display\n" +"their relations. Regular expressions are supported.\n" +"Example: Search for \"^FOO\".\n" +"Result:\n" +"-----------------------------------------------------------------\n" +"Symbol: FOO [ = m]\n" +"Prompt: Foo bus is used to drive the bar HW\n" +"Defined at drivers/pci/Kconfig:47\n" +"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" +"Location:\n" +" -> Bus options (PCI, PCMCIA, EISA, ISA)\n" +" -> PCI support (PCI [ = y])\n" +" -> PCI access mode (<choice> [ = y])\n" +"Selects: LIBCRC32\n" +"Selected by: BAR\n" +"-----------------------------------------------------------------\n" +"o The line 'Prompt:' shows the text displayed for this symbol in\n" +" the menu hierarchy.\n" +"o The 'Defined at' line tells at what file / line number the symbol is\n" +" defined.\n" +"o The 'Depends on:' line lists symbols that need to be defined for\n" +" this symbol to be visible and selectable in the menu.\n" +"o The 'Location:' lines tell, where in the menu structure this symbol\n" +" is located. A location followed by a [ = y] indicates that this is\n" +" a selectable menu item, and the current value is displayed inside\n" +" brackets.\n" +"o The 'Selects:' line tells, what symbol will be automatically selected\n" +" if this symbol is selected (y or m).\n" +"o The 'Selected by' line tells what symbol has selected this symbol.\n" +"\n" +"Only relevant lines are shown.\n" +"\n\n" +"Search examples:\n" +"USB => find all symbols containing USB\n" +"^USB => find all symbols starting with USB\n" +"USB$ => find all symbols ending with USB\n" +"\n"); + +struct mitem { + char str[256]; + char tag; + void *usrptr; + int is_visible; +}; + +#define MAX_MENU_ITEMS 4096 +static int show_all_items; +static int indent; +static struct menu *current_menu; +static int child_count; +static int single_menu_mode; +/* the window in which all information appears */ +static WINDOW *main_window; +/* the largest size of the menu window */ +static int mwin_max_lines; +static int mwin_max_cols; +/* the window in which we show option buttons */ +static MENU *curses_menu; +static ITEM *curses_menu_items[MAX_MENU_ITEMS]; +static struct mitem k_menu_items[MAX_MENU_ITEMS]; +static int items_num; +static int global_exit; +/* the currently selected button */ +const char *current_instructions = menu_instructions; + +static char *dialog_input_result; +static int dialog_input_result_len; + +static void conf(struct menu *menu); +static void conf_choice(struct menu *menu); +static void conf_string(struct menu *menu); +static void conf_load(void); +static void conf_save(void); +static void show_help(struct menu *menu); +static int do_exit(void); +static void setup_windows(void); +static void search_conf(void); + +typedef void (*function_key_handler_t)(int *key, struct menu *menu); +static void handle_f1(int *key, struct menu *current_item); +static void handle_f2(int *key, struct menu *current_item); +static void handle_f3(int *key, struct menu *current_item); +static void handle_f4(int *key, struct menu *current_item); +static void handle_f5(int *key, struct menu *current_item); +static void handle_f6(int *key, struct menu *current_item); +static void handle_f7(int *key, struct menu *current_item); +static void handle_f8(int *key, struct menu *current_item); +static void handle_f9(int *key, struct menu *current_item); + +struct function_keys { + const char *key_str; + const char *func; + function_key key; + function_key_handler_t handler; +}; + +static const int function_keys_num = 9; +struct function_keys function_keys[] = { + { + .key_str = "F1", + .func = "Help", + .key = F_HELP, + .handler = handle_f1, + }, + { + .key_str = "F2", + .func = "SymInfo", + .key = F_SYMBOL, + .handler = handle_f2, + }, + { + .key_str = "F3", + .func = "Help 2", + .key = F_INSTS, + .handler = handle_f3, + }, + { + .key_str = "F4", + .func = "ShowAll", + .key = F_CONF, + .handler = handle_f4, + }, + { + .key_str = "F5", + .func = "Back", + .key = F_BACK, + .handler = handle_f5, + }, + { + .key_str = "F6", + .func = "Save", + .key = F_SAVE, + .handler = handle_f6, + }, + { + .key_str = "F7", + .func = "Load", + .key = F_LOAD, + .handler = handle_f7, + }, + { + .key_str = "F8", + .func = "SymSearch", + .key = F_SEARCH, + .handler = handle_f8, + }, + { + .key_str = "F9", + .func = "Exit", + .key = F_EXIT, + .handler = handle_f9, + }, +}; + +static void print_function_line(void) +{ + int i; + int offset = 1; + const int skip = 1; + int lines = getmaxy(stdscr); + + for (i = 0; i < function_keys_num; i++) { + (void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]); + mvwprintw(main_window, lines-3, offset, + "%s", + function_keys[i].key_str); + (void) wattrset(main_window, attributes[FUNCTION_TEXT]); + offset += strlen(function_keys[i].key_str); + mvwprintw(main_window, lines-3, + offset, "%s", + function_keys[i].func); + offset += strlen(function_keys[i].func) + skip; + } + (void) wattrset(main_window, attributes[NORMAL]); +} + +/* help */ +static void handle_f1(int *key, struct menu *current_item) +{ + show_scroll_win(main_window, + _("Global help"), _(nconf_global_help)); + return; +} + +/* symbole help */ +static void handle_f2(int *key, struct menu *current_item) +{ + show_help(current_item); + return; +} + +/* instructions */ +static void handle_f3(int *key, struct menu *current_item) +{ + show_scroll_win(main_window, + _("Short help"), + _(current_instructions)); + return; +} + +/* config */ +static void handle_f4(int *key, struct menu *current_item) +{ + int res = btn_dialog(main_window, + _("Show all symbols?"), + 2, + " <Show All> ", + "<Don't show all>"); + if (res == 0) + show_all_items = 1; + else if (res == 1) + show_all_items = 0; + + return; +} + +/* back */ +static void handle_f5(int *key, struct menu *current_item) +{ + *key = KEY_LEFT; + return; +} + +/* save */ +static void handle_f6(int *key, struct menu *current_item) +{ + conf_save(); + return; +} + +/* load */ +static void handle_f7(int *key, struct menu *current_item) +{ + conf_load(); + return; +} + +/* search */ +static void handle_f8(int *key, struct menu *current_item) +{ + search_conf(); + return; +} + +/* exit */ +static void handle_f9(int *key, struct menu *current_item) +{ + do_exit(); + return; +} + +/* return != 0 to indicate the key was handles */ +static int process_special_keys(int *key, struct menu *menu) +{ + int i; + + if (*key == KEY_RESIZE) { + setup_windows(); + return 1; + } + + for (i = 0; i < function_keys_num; i++) { + if (*key == KEY_F(function_keys[i].key) || + *key == '0' + function_keys[i].key){ + function_keys[i].handler(key, menu); + return 1; + } + } + + return 0; +} + +static void clean_items(void) +{ + int i; + for (i = 0; curses_menu_items[i]; i++) + free_item(curses_menu_items[i]); + bzero(curses_menu_items, sizeof(curses_menu_items)); + bzero(k_menu_items, sizeof(k_menu_items)); + items_num = 0; +} + +typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN, + FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f; + +/* return the index of the matched item, or -1 if no such item exists */ +static int get_mext_match(const char *match_str, match_f flag) +{ + int match_start = item_index(current_item(curses_menu)); + int index; + + if (flag == FIND_NEXT_MATCH_DOWN) + ++match_start; + else if (flag == FIND_NEXT_MATCH_UP) + --match_start; + + index = match_start; + index = (index + items_num) % items_num; + while (true) { + char *str = k_menu_items[index].str; + if (strcasestr(str, match_str) != 0) + return index; + if (flag == FIND_NEXT_MATCH_UP || + flag == MATCH_TINKER_PATTERN_UP) + --index; + else + ++index; + index = (index + items_num) % items_num; + if (index == match_start) + return -1; + } +} + +/* Make a new item. */ +static void item_make(struct menu *menu, char tag, const char *fmt, ...) +{ + va_list ap; + + if (items_num > MAX_MENU_ITEMS-1) + return; + + bzero(&k_menu_items[items_num], sizeof(k_menu_items[0])); + k_menu_items[items_num].tag = tag; + k_menu_items[items_num].usrptr = menu; + if (menu != NULL) + k_menu_items[items_num].is_visible = + menu_is_visible(menu); + else + k_menu_items[items_num].is_visible = 1; + + va_start(ap, fmt); + vsnprintf(k_menu_items[items_num].str, + sizeof(k_menu_items[items_num].str), + fmt, ap); + va_end(ap); + + if (!k_menu_items[items_num].is_visible) + memcpy(k_menu_items[items_num].str, "XXX", 3); + + curses_menu_items[items_num] = new_item( + k_menu_items[items_num].str, + k_menu_items[items_num].str); + set_item_userptr(curses_menu_items[items_num], + &k_menu_items[items_num]); + /* + if (!k_menu_items[items_num].is_visible) + item_opts_off(curses_menu_items[items_num], O_SELECTABLE); + */ + + items_num++; + curses_menu_items[items_num] = NULL; +} + +/* very hackish. adds a string to the last item added */ +static void item_add_str(const char *fmt, ...) +{ + va_list ap; + int index = items_num-1; + char new_str[256]; + char tmp_str[256]; + + if (index < 0) + return; + + va_start(ap, fmt); + vsnprintf(new_str, sizeof(new_str), fmt, ap); + va_end(ap); + snprintf(tmp_str, sizeof(tmp_str), "%s%s", + k_menu_items[index].str, new_str); + strncpy(k_menu_items[index].str, + tmp_str, + sizeof(k_menu_items[index].str)); + + free_item(curses_menu_items[index]); + curses_menu_items[index] = new_item( + k_menu_items[index].str, + k_menu_items[index].str); + set_item_userptr(curses_menu_items[index], + &k_menu_items[index]); +} + +/* get the tag of the currently selected item */ +static char item_tag(void) +{ + ITEM *cur; + struct mitem *mcur; + + cur = current_item(curses_menu); + if (cur == NULL) + return 0; + mcur = (struct mitem *) item_userptr(cur); + return mcur->tag; +} + +static int curses_item_index(void) +{ + return item_index(current_item(curses_menu)); +} + +static void *item_data(void) +{ + ITEM *cur; + struct mitem *mcur; + + cur = current_item(curses_menu); + if (!cur) + return NULL; + mcur = (struct mitem *) item_userptr(cur); + return mcur->usrptr; + +} + +static int item_is_tag(char tag) +{ + return item_tag() == tag; +} + +static char filename[PATH_MAX+1]; +static char menu_backtitle[PATH_MAX+128]; +static const char *set_config_filename(const char *config_filename) +{ + int size; + + size = snprintf(menu_backtitle, sizeof(menu_backtitle), + "%s - %s", config_filename, rootmenu.prompt->text); + if (size >= sizeof(menu_backtitle)) + menu_backtitle[sizeof(menu_backtitle)-1] = '\0'; + + size = snprintf(filename, sizeof(filename), "%s", config_filename); + if (size >= sizeof(filename)) + filename[sizeof(filename)-1] = '\0'; + return menu_backtitle; +} + +/* return = 0 means we are successful. + * -1 means go on doing what you were doing + */ +static int do_exit(void) +{ + int res; + if (!conf_get_changed()) { + global_exit = 1; + return 0; + } + res = btn_dialog(main_window, + _("Do you wish to save your new configuration?\n" + "<ESC> to cancel and resume nconfig."), + 2, + " <save> ", + "<don't save>"); + if (res == KEY_EXIT) { + global_exit = 0; + return -1; + } + + /* if we got here, the user really wants to exit */ + switch (res) { + case 0: + res = conf_write(filename); + if (res) + btn_dialog( + main_window, + _("Error during writing of configuration.\n" + "Your configuration changes were NOT saved."), + 1, + "<OK>"); + break; + default: + btn_dialog( + main_window, + _("Your configuration changes were NOT saved."), + 1, + "<OK>"); + break; + } + global_exit = 1; + return 0; +} + + +static void search_conf(void) +{ + struct symbol **sym_arr; + struct gstr res; + struct gstr title; + char *dialog_input; + int dres; + + title = str_new(); + str_printf( &title, _("Enter (sub)string or regexp to search for " + "(with or without \"%s\")"), CONFIG_); + +again: + dres = dialog_inputbox(main_window, + _("Search Configuration Parameter"), + str_get(&title), + "", &dialog_input_result, &dialog_input_result_len); + switch (dres) { + case 0: + break; + case 1: + show_scroll_win(main_window, + _("Search Configuration"), search_help); + goto again; + default: + str_free(&title); + return; + } + + /* strip the prefix if necessary */ + dialog_input = dialog_input_result; + if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0) + dialog_input += strlen(CONFIG_); + + sym_arr = sym_re_search(dialog_input); + res = get_relations_str(sym_arr, NULL); + free(sym_arr); + show_scroll_win(main_window, + _("Search Results"), str_get(&res)); + str_free(&res); + str_free(&title); +} + + +static void build_conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + int type, tmp, doint = 2; + tristate val; + char ch; + + if (!menu || (!show_all_items && !menu_is_visible(menu))) + return; + + sym = menu->sym; + prop = menu->prompt; + if (!sym) { + if (prop && menu != current_menu) { + const char *prompt = menu_get_prompt(menu); + enum prop_type ptype; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + switch (ptype) { + case P_MENU: + child_count++; + prompt = _(prompt); + if (single_menu_mode) { + item_make(menu, 'm', + "%s%*c%s", + menu->data ? "-->" : "++>", + indent + 1, ' ', prompt); + } else + item_make(menu, 'm', + " %*c%s %s", + indent + 1, ' ', prompt, + menu_is_empty(menu) ? "----" : "--->"); + + if (single_menu_mode && menu->data) + goto conf_childs; + return; + case P_COMMENT: + if (prompt) { + child_count++; + item_make(menu, ':', + " %*c*** %s ***", + indent + 1, ' ', + _(prompt)); + } + break; + default: + if (prompt) { + child_count++; + item_make(menu, ':', "---%*c%s", + indent + 1, ' ', + _(prompt)); + } + } + } else + doint = 0; + goto conf_childs; + } + + type = sym_get_type(sym); + if (sym_is_choice(sym)) { + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + child_count++; + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + val = sym_get_tristate_value(sym); + if (sym_is_changable(sym)) { + switch (type) { + case S_BOOLEAN: + item_make(menu, 't', "[%c]", + val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: + ch = '*'; + break; + case mod: + ch = 'M'; + break; + default: + ch = ' '; + break; + } + item_make(menu, 't', "<%c>", ch); + break; + } + } else { + item_make(menu, def_menu ? 't' : ':', " "); + } + + item_add_str("%*c%s", indent + 1, + ' ', _(menu_get_prompt(menu))); + if (val == yes) { + if (def_menu) { + item_add_str(" (%s)", + _(menu_get_prompt(def_menu))); + item_add_str(" --->"); + if (def_menu->list) { + indent += 2; + build_conf(def_menu); + indent -= 2; + } + } + return; + } + } else { + if (menu == current_menu) { + item_make(menu, ':', + "---%*c%s", indent + 1, + ' ', _(menu_get_prompt(menu))); + goto conf_childs; + } + child_count++; + val = sym_get_tristate_value(sym); + if (sym_is_choice_value(sym) && val == yes) { + item_make(menu, ':', " "); + } else { + switch (type) { + case S_BOOLEAN: + if (sym_is_changable(sym)) + item_make(menu, 't', "[%c]", + val == no ? ' ' : '*'); + else + item_make(menu, 't', "-%c-", + val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: + ch = '*'; + break; + case mod: + ch = 'M'; + break; + default: + ch = ' '; + break; + } + if (sym_is_changable(sym)) { + if (sym->rev_dep.tri == mod) + item_make(menu, + 't', "{%c}", ch); + else + item_make(menu, + 't', "<%c>", ch); + } else + item_make(menu, 't', "-%c-", ch); + break; + default: + tmp = 2 + strlen(sym_get_string_value(sym)); + item_make(menu, 's', " (%s)", + sym_get_string_value(sym)); + tmp = indent - tmp + 4; + if (tmp < 0) + tmp = 0; + item_add_str("%*c%s%s", tmp, ' ', + _(menu_get_prompt(menu)), + (sym_has_value(sym) || + !sym_is_changable(sym)) ? "" : + _(" (NEW)")); + goto conf_childs; + } + } + item_add_str("%*c%s%s", indent + 1, ' ', + _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + if (menu->prompt && menu->prompt->type == P_MENU) { + item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->"); + return; + } + } + +conf_childs: + indent += doint; + for (child = menu->list; child; child = child->next) + build_conf(child); + indent -= doint; +} + +static void reset_menu(void) +{ + unpost_menu(curses_menu); + clean_items(); +} + +/* adjust the menu to show this item. + * prefer not to scroll the menu if possible*/ +static void center_item(int selected_index, int *last_top_row) +{ + int toprow; + + set_top_row(curses_menu, *last_top_row); + toprow = top_row(curses_menu); + if (selected_index < toprow || + selected_index >= toprow+mwin_max_lines) { + toprow = max(selected_index-mwin_max_lines/2, 0); + if (toprow >= item_count(curses_menu)-mwin_max_lines) + toprow = item_count(curses_menu)-mwin_max_lines; + set_top_row(curses_menu, toprow); + } + set_current_item(curses_menu, + curses_menu_items[selected_index]); + *last_top_row = toprow; + post_menu(curses_menu); + refresh_all_windows(main_window); +} + +/* this function assumes reset_menu has been called before */ +static void show_menu(const char *prompt, const char *instructions, + int selected_index, int *last_top_row) +{ + int maxx, maxy; + WINDOW *menu_window; + + current_instructions = instructions; + + clear(); + (void) wattrset(main_window, attributes[NORMAL]); + print_in_middle(stdscr, 1, 0, getmaxx(stdscr), + menu_backtitle, + attributes[MAIN_HEADING]); + + (void) wattrset(main_window, attributes[MAIN_MENU_BOX]); + box(main_window, 0, 0); + (void) wattrset(main_window, attributes[MAIN_MENU_HEADING]); + mvwprintw(main_window, 0, 3, " %s ", prompt); + (void) wattrset(main_window, attributes[NORMAL]); + + set_menu_items(curses_menu, curses_menu_items); + + /* position the menu at the middle of the screen */ + scale_menu(curses_menu, &maxy, &maxx); + maxx = min(maxx, mwin_max_cols-2); + maxy = mwin_max_lines; + menu_window = derwin(main_window, + maxy, + maxx, + 2, + (mwin_max_cols-maxx)/2); + keypad(menu_window, TRUE); + set_menu_win(curses_menu, menu_window); + set_menu_sub(curses_menu, menu_window); + + /* must reassert this after changing items, otherwise returns to a + * default of 16 + */ + set_menu_format(curses_menu, maxy, 1); + center_item(selected_index, last_top_row); + set_menu_format(curses_menu, maxy, 1); + + print_function_line(); + + /* Post the menu */ + post_menu(curses_menu); + refresh_all_windows(main_window); +} + +static void adj_match_dir(match_f *match_direction) +{ + if (*match_direction == FIND_NEXT_MATCH_DOWN) + *match_direction = + MATCH_TINKER_PATTERN_DOWN; + else if (*match_direction == FIND_NEXT_MATCH_UP) + *match_direction = + MATCH_TINKER_PATTERN_UP; + /* else, do no change.. */ +} + +struct match_state +{ + int in_search; + match_f match_direction; + char pattern[256]; +}; + +/* Return 0 means I have handled the key. In such a case, ans should hold the + * item to center, or -1 otherwise. + * Else return -1 . + */ +static int do_match(int key, struct match_state *state, int *ans) +{ + char c = (char) key; + int terminate_search = 0; + *ans = -1; + if (key == '/' || (state->in_search && key == 27)) { + move(0, 0); + refresh(); + clrtoeol(); + state->in_search = 1-state->in_search; + bzero(state->pattern, sizeof(state->pattern)); + state->match_direction = MATCH_TINKER_PATTERN_DOWN; + return 0; + } else if (!state->in_search) + return 1; + + if (isalnum(c) || isgraph(c) || c == ' ') { + state->pattern[strlen(state->pattern)] = c; + state->pattern[strlen(state->pattern)] = '\0'; + adj_match_dir(&state->match_direction); + *ans = get_mext_match(state->pattern, + state->match_direction); + } else if (key == KEY_DOWN) { + state->match_direction = FIND_NEXT_MATCH_DOWN; + *ans = get_mext_match(state->pattern, + state->match_direction); + } else if (key == KEY_UP) { + state->match_direction = FIND_NEXT_MATCH_UP; + *ans = get_mext_match(state->pattern, + state->match_direction); + } else if (key == KEY_BACKSPACE || key == 127) { + state->pattern[strlen(state->pattern)-1] = '\0'; + adj_match_dir(&state->match_direction); + } else + terminate_search = 1; + + if (terminate_search) { + state->in_search = 0; + bzero(state->pattern, sizeof(state->pattern)); + move(0, 0); + refresh(); + clrtoeol(); + return -1; + } + return 0; +} + +static void conf(struct menu *menu) +{ + struct menu *submenu = 0; + const char *prompt = menu_get_prompt(menu); + struct symbol *sym; + int res; + int current_index = 0; + int last_top_row = 0; + struct match_state match_state = { + .in_search = 0, + .match_direction = MATCH_TINKER_PATTERN_DOWN, + .pattern = "", + }; + + while (!global_exit) { + reset_menu(); + current_menu = menu; + build_conf(menu); + if (!child_count) + break; + + show_menu(prompt ? _(prompt) : _("Main Menu"), + _(menu_instructions), + current_index, &last_top_row); + keypad((menu_win(curses_menu)), TRUE); + while (!global_exit) { + if (match_state.in_search) { + mvprintw(0, 0, + "searching: %s", match_state.pattern); + clrtoeol(); + } + refresh_all_windows(main_window); + res = wgetch(menu_win(curses_menu)); + if (!res) + break; + if (do_match(res, &match_state, ¤t_index) == 0) { + if (current_index != -1) + center_item(current_index, + &last_top_row); + continue; + } + if (process_special_keys(&res, + (struct menu *) item_data())) + break; + switch (res) { + case KEY_DOWN: + menu_driver(curses_menu, REQ_DOWN_ITEM); + break; + case KEY_UP: + menu_driver(curses_menu, REQ_UP_ITEM); + break; + case KEY_NPAGE: + menu_driver(curses_menu, REQ_SCR_DPAGE); + break; + case KEY_PPAGE: + menu_driver(curses_menu, REQ_SCR_UPAGE); + break; + case KEY_HOME: + menu_driver(curses_menu, REQ_FIRST_ITEM); + break; + case KEY_END: + menu_driver(curses_menu, REQ_LAST_ITEM); + break; + case 'h': + case '?': + show_help((struct menu *) item_data()); + break; + } + if (res == 10 || res == 27 || + res == 32 || res == 'n' || res == 'y' || + res == KEY_LEFT || res == KEY_RIGHT || + res == 'm') + break; + refresh_all_windows(main_window); + } + + refresh_all_windows(main_window); + /* if ESC or left*/ + if (res == 27 || (menu != &rootmenu && res == KEY_LEFT)) + break; + + /* remember location in the menu */ + last_top_row = top_row(curses_menu); + current_index = curses_item_index(); + + if (!item_tag()) + continue; + + submenu = (struct menu *) item_data(); + if (!submenu || !menu_is_visible(submenu)) + continue; + sym = submenu->sym; + + switch (res) { + case ' ': + if (item_is_tag('t')) + sym_toggle_tristate_value(sym); + else if (item_is_tag('m')) + conf(submenu); + break; + case KEY_RIGHT: + case 10: /* ENTER WAS PRESSED */ + switch (item_tag()) { + case 'm': + if (single_menu_mode) + submenu->data = + (void *) (long) !submenu->data; + else + conf(submenu); + break; + case 't': + if (sym_is_choice(sym) && + sym_get_tristate_value(sym) == yes) + conf_choice(submenu); + else if (submenu->prompt && + submenu->prompt->type == P_MENU) + conf(submenu); + else if (res == 10) + sym_toggle_tristate_value(sym); + break; + case 's': + conf_string(submenu); + break; + } + break; + case 'y': + if (item_is_tag('t')) { + if (sym_set_tristate_value(sym, yes)) + break; + if (sym_set_tristate_value(sym, mod)) + btn_dialog(main_window, setmod_text, 0); + } + break; + case 'n': + if (item_is_tag('t')) + sym_set_tristate_value(sym, no); + break; + case 'm': + if (item_is_tag('t')) + sym_set_tristate_value(sym, mod); + break; + } + } +} + +static void conf_message_callback(const char *fmt, va_list ap) +{ + char buf[1024]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + btn_dialog(main_window, buf, 1, "<OK>"); +} + +static void show_help(struct menu *menu) +{ + struct gstr help; + + if (!menu) + return; + + help = str_new(); + menu_get_ext_help(menu, &help); + show_scroll_win(main_window, _(menu_get_prompt(menu)), str_get(&help)); + str_free(&help); +} + +static void conf_choice(struct menu *menu) +{ + const char *prompt = _(menu_get_prompt(menu)); + struct menu *child = 0; + struct symbol *active; + int selected_index = 0; + int last_top_row = 0; + int res, i = 0; + struct match_state match_state = { + .in_search = 0, + .match_direction = MATCH_TINKER_PATTERN_DOWN, + .pattern = "", + }; + + active = sym_get_choice_value(menu->sym); + /* this is mostly duplicated from the conf() function. */ + while (!global_exit) { + reset_menu(); + + for (i = 0, child = menu->list; child; child = child->next) { + if (!show_all_items && !menu_is_visible(child)) + continue; + + if (child->sym == sym_get_choice_value(menu->sym)) + item_make(child, ':', "<X> %s", + _(menu_get_prompt(child))); + else if (child->sym) + item_make(child, ':', " %s", + _(menu_get_prompt(child))); + else + item_make(child, ':', "*** %s ***", + _(menu_get_prompt(child))); + + if (child->sym == active){ + last_top_row = top_row(curses_menu); + selected_index = i; + } + i++; + } + show_menu(prompt ? _(prompt) : _("Choice Menu"), + _(radiolist_instructions), + selected_index, + &last_top_row); + while (!global_exit) { + if (match_state.in_search) { + mvprintw(0, 0, "searching: %s", + match_state.pattern); + clrtoeol(); + } + refresh_all_windows(main_window); + res = wgetch(menu_win(curses_menu)); + if (!res) + break; + if (do_match(res, &match_state, &selected_index) == 0) { + if (selected_index != -1) + center_item(selected_index, + &last_top_row); + continue; + } + if (process_special_keys( + &res, + (struct menu *) item_data())) + break; + switch (res) { + case KEY_DOWN: + menu_driver(curses_menu, REQ_DOWN_ITEM); + break; + case KEY_UP: + menu_driver(curses_menu, REQ_UP_ITEM); + break; + case KEY_NPAGE: + menu_driver(curses_menu, REQ_SCR_DPAGE); + break; + case KEY_PPAGE: + menu_driver(curses_menu, REQ_SCR_UPAGE); + break; + case KEY_HOME: + menu_driver(curses_menu, REQ_FIRST_ITEM); + break; + case KEY_END: + menu_driver(curses_menu, REQ_LAST_ITEM); + break; + case 'h': + case '?': + show_help((struct menu *) item_data()); + break; + } + if (res == 10 || res == 27 || res == ' ' || + res == KEY_LEFT){ + break; + } + refresh_all_windows(main_window); + } + /* if ESC or left */ + if (res == 27 || res == KEY_LEFT) + break; + + child = item_data(); + if (!child || !menu_is_visible(child) || !child->sym) + continue; + switch (res) { + case ' ': + case 10: + case KEY_RIGHT: + sym_set_tristate_value(child->sym, yes); + return; + case 'h': + case '?': + show_help(child); + active = child->sym; + break; + case KEY_EXIT: + return; + } + } +} + +static void conf_string(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + + while (1) { + int res; + const char *heading; + + switch (sym_get_type(menu->sym)) { + case S_INT: + heading = _(inputbox_instructions_int); + break; + case S_HEX: + heading = _(inputbox_instructions_hex); + break; + case S_STRING: + heading = _(inputbox_instructions_string); + break; + default: + heading = _("Internal nconf error!"); + } + res = dialog_inputbox(main_window, + prompt ? _(prompt) : _("Main Menu"), + heading, + sym_get_string_value(menu->sym), + &dialog_input_result, + &dialog_input_result_len); + switch (res) { + case 0: + if (sym_set_string_value(menu->sym, + dialog_input_result)) + return; + btn_dialog(main_window, + _("You have made an invalid entry."), 0); + break; + case 1: + show_help(menu); + break; + case KEY_EXIT: + return; + } + } +} + +static void conf_load(void) +{ + while (1) { + int res; + res = dialog_inputbox(main_window, + NULL, load_config_text, + filename, + &dialog_input_result, + &dialog_input_result_len); + switch (res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_read(dialog_input_result)) { + set_config_filename(dialog_input_result); + sym_set_change_count(1); + return; + } + btn_dialog(main_window, _("File does not exist!"), 0); + break; + case 1: + show_scroll_win(main_window, + _("Load Alternate Configuration"), + load_config_help); + break; + case KEY_EXIT: + return; + } + } +} + +static void conf_save(void) +{ + while (1) { + int res; + res = dialog_inputbox(main_window, + NULL, save_config_text, + filename, + &dialog_input_result, + &dialog_input_result_len); + switch (res) { + case 0: + if (!dialog_input_result[0]) + return; + res = conf_write(dialog_input_result); + if (!res) { + set_config_filename(dialog_input_result); + return; + } + btn_dialog(main_window, _("Can't create file! " + "Probably a nonexistent directory."), + 1, "<OK>"); + break; + case 1: + show_scroll_win(main_window, + _("Save Alternate Configuration"), + save_config_help); + break; + case KEY_EXIT: + return; + } + } +} + +void setup_windows(void) +{ + int lines, columns; + + getmaxyx(stdscr, lines, columns); + + if (main_window != NULL) + delwin(main_window); + + /* set up the menu and menu window */ + main_window = newwin(lines-2, columns-2, 2, 1); + keypad(main_window, TRUE); + mwin_max_lines = lines-7; + mwin_max_cols = columns-6; + + /* panels order is from bottom to top */ + new_panel(main_window); +} + +int main(int ac, char **av) +{ + int lines, columns; + char *mode; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + conf_parse(av[1]); + conf_read(NULL); + + mode = getenv("NCONFIG_MODE"); + if (mode) { + if (!strcasecmp(mode, "single_menu")) + single_menu_mode = 1; + } + + /* Initialize curses */ + initscr(); + /* set color theme */ + set_colors(); + + cbreak(); + noecho(); + keypad(stdscr, TRUE); + curs_set(0); + + getmaxyx(stdscr, lines, columns); + if (columns < 75 || lines < 20) { + endwin(); + printf("Your terminal should have at " + "least 20 lines and 75 columns\n"); + return 1; + } + + notimeout(stdscr, FALSE); +#if NCURSES_REENTRANT + set_escdelay(1); +#else + ESCDELAY = 1; +#endif + + /* set btns menu */ + curses_menu = new_menu(curses_menu_items); + menu_opts_off(curses_menu, O_SHOWDESC); + menu_opts_on(curses_menu, O_SHOWMATCH); + menu_opts_on(curses_menu, O_ONEVALUE); + menu_opts_on(curses_menu, O_NONCYCLIC); + menu_opts_on(curses_menu, O_IGNORECASE); + set_menu_mark(curses_menu, " "); + set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]); + set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]); + set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]); + + set_config_filename(conf_get_configname()); + setup_windows(); + + /* check for KEY_FUNC(1) */ + if (has_key(KEY_F(1)) == FALSE) { + show_scroll_win(main_window, + _("Instructions"), + _(menu_no_f_instructions)); + } + + conf_set_message_callback(conf_message_callback); + /* do the work */ + while (!global_exit) { + conf(&rootmenu); + if (!global_exit && do_exit() == 0) + break; + } + /* ok, we are done */ + unpost_menu(curses_menu); + free_menu(curses_menu); + delwin(main_window); + clear(); + refresh(); + endwin(); + return 0; +} diff --git a/qemu/roms/seabios/scripts/kconfig/nconf.gui.c b/qemu/roms/seabios/scripts/kconfig/nconf.gui.c new file mode 100644 index 000000000..8275f0e55 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/nconf.gui.c @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com? + * Released under the terms of the GNU GPL v2.0. + * + * Derived from menuconfig. + * + */ +#include "nconf.h" + +/* a list of all the different widgets we use */ +attributes_t attributes[ATTR_MAX+1] = {0}; + +/* available colors: + COLOR_BLACK 0 + COLOR_RED 1 + COLOR_GREEN 2 + COLOR_YELLOW 3 + COLOR_BLUE 4 + COLOR_MAGENTA 5 + COLOR_CYAN 6 + COLOR_WHITE 7 + */ +static void set_normal_colors(void) +{ + init_pair(NORMAL, -1, -1); + init_pair(MAIN_HEADING, COLOR_MAGENTA, -1); + + /* FORE is for the selected item */ + init_pair(MAIN_MENU_FORE, -1, -1); + /* BACK for all the rest */ + init_pair(MAIN_MENU_BACK, -1, -1); + init_pair(MAIN_MENU_GREY, -1, -1); + init_pair(MAIN_MENU_HEADING, COLOR_GREEN, -1); + init_pair(MAIN_MENU_BOX, COLOR_YELLOW, -1); + + init_pair(SCROLLWIN_TEXT, -1, -1); + init_pair(SCROLLWIN_HEADING, COLOR_GREEN, -1); + init_pair(SCROLLWIN_BOX, COLOR_YELLOW, -1); + + init_pair(DIALOG_TEXT, -1, -1); + init_pair(DIALOG_BOX, COLOR_YELLOW, -1); + init_pair(DIALOG_MENU_BACK, COLOR_YELLOW, -1); + init_pair(DIALOG_MENU_FORE, COLOR_RED, -1); + + init_pair(INPUT_BOX, COLOR_YELLOW, -1); + init_pair(INPUT_HEADING, COLOR_GREEN, -1); + init_pair(INPUT_TEXT, -1, -1); + init_pair(INPUT_FIELD, -1, -1); + + init_pair(FUNCTION_HIGHLIGHT, -1, -1); + init_pair(FUNCTION_TEXT, COLOR_YELLOW, -1); +} + +/* available attributes: + A_NORMAL Normal display (no highlight) + A_STANDOUT Best highlighting mode of the terminal. + A_UNDERLINE Underlining + A_REVERSE Reverse video + A_BLINK Blinking + A_DIM Half bright + A_BOLD Extra bright or bold + A_PROTECT Protected mode + A_INVIS Invisible or blank mode + A_ALTCHARSET Alternate character set + A_CHARTEXT Bit-mask to extract a character + COLOR_PAIR(n) Color-pair number n + */ +static void normal_color_theme(void) +{ + /* automatically add color... */ +#define mkattr(name, attr) do { \ +attributes[name] = attr | COLOR_PAIR(name); } while (0) + mkattr(NORMAL, NORMAL); + mkattr(MAIN_HEADING, A_BOLD | A_UNDERLINE); + + mkattr(MAIN_MENU_FORE, A_REVERSE); + mkattr(MAIN_MENU_BACK, A_NORMAL); + mkattr(MAIN_MENU_GREY, A_NORMAL); + mkattr(MAIN_MENU_HEADING, A_BOLD); + mkattr(MAIN_MENU_BOX, A_NORMAL); + + mkattr(SCROLLWIN_TEXT, A_NORMAL); + mkattr(SCROLLWIN_HEADING, A_BOLD); + mkattr(SCROLLWIN_BOX, A_BOLD); + + mkattr(DIALOG_TEXT, A_BOLD); + mkattr(DIALOG_BOX, A_BOLD); + mkattr(DIALOG_MENU_FORE, A_STANDOUT); + mkattr(DIALOG_MENU_BACK, A_NORMAL); + + mkattr(INPUT_BOX, A_NORMAL); + mkattr(INPUT_HEADING, A_BOLD); + mkattr(INPUT_TEXT, A_NORMAL); + mkattr(INPUT_FIELD, A_UNDERLINE); + + mkattr(FUNCTION_HIGHLIGHT, A_BOLD); + mkattr(FUNCTION_TEXT, A_REVERSE); +} + +static void no_colors_theme(void) +{ + /* automatically add highlight, no color */ +#define mkattrn(name, attr) { attributes[name] = attr; } + + mkattrn(NORMAL, NORMAL); + mkattrn(MAIN_HEADING, A_BOLD | A_UNDERLINE); + + mkattrn(MAIN_MENU_FORE, A_STANDOUT); + mkattrn(MAIN_MENU_BACK, A_NORMAL); + mkattrn(MAIN_MENU_GREY, A_NORMAL); + mkattrn(MAIN_MENU_HEADING, A_BOLD); + mkattrn(MAIN_MENU_BOX, A_NORMAL); + + mkattrn(SCROLLWIN_TEXT, A_NORMAL); + mkattrn(SCROLLWIN_HEADING, A_BOLD); + mkattrn(SCROLLWIN_BOX, A_BOLD); + + mkattrn(DIALOG_TEXT, A_NORMAL); + mkattrn(DIALOG_BOX, A_BOLD); + mkattrn(DIALOG_MENU_FORE, A_STANDOUT); + mkattrn(DIALOG_MENU_BACK, A_NORMAL); + + mkattrn(INPUT_BOX, A_BOLD); + mkattrn(INPUT_HEADING, A_BOLD); + mkattrn(INPUT_TEXT, A_NORMAL); + mkattrn(INPUT_FIELD, A_UNDERLINE); + + mkattrn(FUNCTION_HIGHLIGHT, A_BOLD); + mkattrn(FUNCTION_TEXT, A_REVERSE); +} + +void set_colors() +{ + start_color(); + use_default_colors(); + set_normal_colors(); + if (has_colors()) { + normal_color_theme(); + } else { + /* give defaults */ + no_colors_theme(); + } +} + + +/* this changes the windows attributes !!! */ +void print_in_middle(WINDOW *win, + int starty, + int startx, + int width, + const char *string, + chtype color) +{ int length, x, y; + float temp; + + + if (win == NULL) + win = stdscr; + getyx(win, y, x); + if (startx != 0) + x = startx; + if (starty != 0) + y = starty; + if (width == 0) + width = 80; + + length = strlen(string); + temp = (width - length) / 2; + x = startx + (int)temp; + (void) wattrset(win, color); + mvwprintw(win, y, x, "%s", string); + refresh(); +} + +int get_line_no(const char *text) +{ + int i; + int total = 1; + + if (!text) + return 0; + + for (i = 0; text[i] != '\0'; i++) + if (text[i] == '\n') + total++; + return total; +} + +const char *get_line(const char *text, int line_no) +{ + int i; + int lines = 0; + + if (!text) + return 0; + + for (i = 0; text[i] != '\0' && lines < line_no; i++) + if (text[i] == '\n') + lines++; + return text+i; +} + +int get_line_length(const char *line) +{ + int res = 0; + while (*line != '\0' && *line != '\n') { + line++; + res++; + } + return res; +} + +/* print all lines to the window. */ +void fill_window(WINDOW *win, const char *text) +{ + int x, y; + int total_lines = get_line_no(text); + int i; + + getmaxyx(win, y, x); + /* do not go over end of line */ + total_lines = min(total_lines, y); + for (i = 0; i < total_lines; i++) { + char tmp[x+10]; + const char *line = get_line(text, i); + int len = get_line_length(line); + strncpy(tmp, line, min(len, x)); + tmp[len] = '\0'; + mvwprintw(win, i, 0, "%s", tmp); + } +} + +/* get the message, and buttons. + * each button must be a char* + * return the selected button + * + * this dialog is used for 2 different things: + * 1) show a text box, no buttons. + * 2) show a dialog, with horizontal buttons + */ +int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...) +{ + va_list ap; + char *btn; + int btns_width = 0; + int msg_lines = 0; + int msg_width = 0; + int total_width; + int win_rows = 0; + WINDOW *win; + WINDOW *msg_win; + WINDOW *menu_win; + MENU *menu; + ITEM *btns[btn_num+1]; + int i, x, y; + int res = -1; + + + va_start(ap, btn_num); + for (i = 0; i < btn_num; i++) { + btn = va_arg(ap, char *); + btns[i] = new_item(btn, ""); + btns_width += strlen(btn)+1; + } + va_end(ap); + btns[btn_num] = NULL; + + /* find the widest line of msg: */ + msg_lines = get_line_no(msg); + for (i = 0; i < msg_lines; i++) { + const char *line = get_line(msg, i); + int len = get_line_length(line); + if (msg_width < len) + msg_width = len; + } + + total_width = max(msg_width, btns_width); + /* place dialog in middle of screen */ + y = (getmaxy(stdscr)-(msg_lines+4))/2; + x = (getmaxx(stdscr)-(total_width+4))/2; + + + /* create the windows */ + if (btn_num > 0) + win_rows = msg_lines+4; + else + win_rows = msg_lines+2; + + win = newwin(win_rows, total_width+4, y, x); + keypad(win, TRUE); + menu_win = derwin(win, 1, btns_width, win_rows-2, + 1+(total_width+2-btns_width)/2); + menu = new_menu(btns); + msg_win = derwin(win, win_rows-2, msg_width, 1, + 1+(total_width+2-msg_width)/2); + + set_menu_fore(menu, attributes[DIALOG_MENU_FORE]); + set_menu_back(menu, attributes[DIALOG_MENU_BACK]); + + (void) wattrset(win, attributes[DIALOG_BOX]); + box(win, 0, 0); + + /* print message */ + (void) wattrset(msg_win, attributes[DIALOG_TEXT]); + fill_window(msg_win, msg); + + set_menu_win(menu, win); + set_menu_sub(menu, menu_win); + set_menu_format(menu, 1, btn_num); + menu_opts_off(menu, O_SHOWDESC); + menu_opts_off(menu, O_SHOWMATCH); + menu_opts_on(menu, O_ONEVALUE); + menu_opts_on(menu, O_NONCYCLIC); + set_menu_mark(menu, ""); + post_menu(menu); + + + touchwin(win); + refresh_all_windows(main_window); + while ((res = wgetch(win))) { + switch (res) { + case KEY_LEFT: + menu_driver(menu, REQ_LEFT_ITEM); + break; + case KEY_RIGHT: + menu_driver(menu, REQ_RIGHT_ITEM); + break; + case 10: /* ENTER */ + case 27: /* ESCAPE */ + case ' ': + case KEY_F(F_BACK): + case KEY_F(F_EXIT): + break; + } + touchwin(win); + refresh_all_windows(main_window); + + if (res == 10 || res == ' ') { + res = item_index(current_item(menu)); + break; + } else if (res == 27 || res == KEY_F(F_BACK) || + res == KEY_F(F_EXIT)) { + res = KEY_EXIT; + break; + } + } + + unpost_menu(menu); + free_menu(menu); + for (i = 0; i < btn_num; i++) + free_item(btns[i]); + + delwin(win); + return res; +} + +int dialog_inputbox(WINDOW *main_window, + const char *title, const char *prompt, + const char *init, char **resultp, int *result_len) +{ + int prompt_lines = 0; + int prompt_width = 0; + WINDOW *win; + WINDOW *prompt_win; + WINDOW *form_win; + PANEL *panel; + int i, x, y; + int res = -1; + int cursor_position = strlen(init); + int cursor_form_win; + char *result = *resultp; + + if (strlen(init)+1 > *result_len) { + *result_len = strlen(init)+1; + *resultp = result = realloc(result, *result_len); + } + + /* find the widest line of msg: */ + prompt_lines = get_line_no(prompt); + for (i = 0; i < prompt_lines; i++) { + const char *line = get_line(prompt, i); + int len = get_line_length(line); + prompt_width = max(prompt_width, len); + } + + if (title) + prompt_width = max(prompt_width, strlen(title)); + + /* place dialog in middle of screen */ + y = (getmaxy(stdscr)-(prompt_lines+4))/2; + x = (getmaxx(stdscr)-(prompt_width+4))/2; + + strncpy(result, init, *result_len); + + /* create the windows */ + win = newwin(prompt_lines+6, prompt_width+7, y, x); + prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2); + form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2); + keypad(form_win, TRUE); + + (void) wattrset(form_win, attributes[INPUT_FIELD]); + + (void) wattrset(win, attributes[INPUT_BOX]); + box(win, 0, 0); + (void) wattrset(win, attributes[INPUT_HEADING]); + if (title) + mvwprintw(win, 0, 3, "%s", title); + + /* print message */ + (void) wattrset(prompt_win, attributes[INPUT_TEXT]); + fill_window(prompt_win, prompt); + + mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); + cursor_form_win = min(cursor_position, prompt_width-1); + mvwprintw(form_win, 0, 0, "%s", + result + cursor_position-cursor_form_win); + + /* create panels */ + panel = new_panel(win); + + /* show the cursor */ + curs_set(1); + + touchwin(win); + refresh_all_windows(main_window); + while ((res = wgetch(form_win))) { + int len = strlen(result); + switch (res) { + case 10: /* ENTER */ + case 27: /* ESCAPE */ + case KEY_F(F_HELP): + case KEY_F(F_EXIT): + case KEY_F(F_BACK): + break; + case 127: + case KEY_BACKSPACE: + if (cursor_position > 0) { + memmove(&result[cursor_position-1], + &result[cursor_position], + len-cursor_position+1); + cursor_position--; + cursor_form_win--; + len--; + } + break; + case KEY_DC: + if (cursor_position >= 0 && cursor_position < len) { + memmove(&result[cursor_position], + &result[cursor_position+1], + len-cursor_position+1); + len--; + } + break; + case KEY_UP: + case KEY_RIGHT: + if (cursor_position < len) { + cursor_position++; + cursor_form_win++; + } + break; + case KEY_DOWN: + case KEY_LEFT: + if (cursor_position > 0) { + cursor_position--; + cursor_form_win--; + } + break; + case KEY_HOME: + cursor_position = 0; + cursor_form_win = 0; + break; + case KEY_END: + cursor_position = len; + cursor_form_win = min(cursor_position, prompt_width-1); + break; + default: + if ((isgraph(res) || isspace(res))) { + /* one for new char, one for '\0' */ + if (len+2 > *result_len) { + *result_len = len+2; + *resultp = result = realloc(result, + *result_len); + } + /* insert the char at the proper position */ + memmove(&result[cursor_position+1], + &result[cursor_position], + len-cursor_position+1); + result[cursor_position] = res; + cursor_position++; + cursor_form_win++; + len++; + } else { + mvprintw(0, 0, "unknown key: %d\n", res); + } + break; + } + if (cursor_form_win < 0) + cursor_form_win = 0; + else if (cursor_form_win > prompt_width-1) + cursor_form_win = prompt_width-1; + + wmove(form_win, 0, 0); + wclrtoeol(form_win); + mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); + mvwprintw(form_win, 0, 0, "%s", + result + cursor_position-cursor_form_win); + wmove(form_win, 0, cursor_form_win); + touchwin(win); + refresh_all_windows(main_window); + + if (res == 10) { + res = 0; + break; + } else if (res == 27 || res == KEY_F(F_BACK) || + res == KEY_F(F_EXIT)) { + res = KEY_EXIT; + break; + } else if (res == KEY_F(F_HELP)) { + res = 1; + break; + } + } + + /* hide the cursor */ + curs_set(0); + del_panel(panel); + delwin(prompt_win); + delwin(form_win); + delwin(win); + return res; +} + +/* refresh all windows in the correct order */ +void refresh_all_windows(WINDOW *main_window) +{ + update_panels(); + touchwin(main_window); + refresh(); +} + +/* layman's scrollable window... */ +void show_scroll_win(WINDOW *main_window, + const char *title, + const char *text) +{ + int res; + int total_lines = get_line_no(text); + int x, y, lines, columns; + int start_x = 0, start_y = 0; + int text_lines = 0, text_cols = 0; + int total_cols = 0; + int win_cols = 0; + int win_lines = 0; + int i = 0; + WINDOW *win; + WINDOW *pad; + PANEL *panel; + + getmaxyx(stdscr, lines, columns); + + /* find the widest line of msg: */ + total_lines = get_line_no(text); + for (i = 0; i < total_lines; i++) { + const char *line = get_line(text, i); + int len = get_line_length(line); + total_cols = max(total_cols, len+2); + } + + /* create the pad */ + pad = newpad(total_lines+10, total_cols+10); + (void) wattrset(pad, attributes[SCROLLWIN_TEXT]); + fill_window(pad, text); + + win_lines = min(total_lines+4, lines-2); + win_cols = min(total_cols+2, columns-2); + text_lines = max(win_lines-4, 0); + text_cols = max(win_cols-2, 0); + + /* place window in middle of screen */ + y = (lines-win_lines)/2; + x = (columns-win_cols)/2; + + win = newwin(win_lines, win_cols, y, x); + keypad(win, TRUE); + /* show the help in the help window, and show the help panel */ + (void) wattrset(win, attributes[SCROLLWIN_BOX]); + box(win, 0, 0); + (void) wattrset(win, attributes[SCROLLWIN_HEADING]); + mvwprintw(win, 0, 3, " %s ", title); + panel = new_panel(win); + + /* handle scrolling */ + do { + + copywin(pad, win, start_y, start_x, 2, 2, text_lines, + text_cols, 0); + print_in_middle(win, + text_lines+2, + 0, + text_cols, + "<OK>", + attributes[DIALOG_MENU_FORE]); + wrefresh(win); + + res = wgetch(win); + switch (res) { + case KEY_NPAGE: + case ' ': + case 'd': + start_y += text_lines-2; + break; + case KEY_PPAGE: + case 'u': + start_y -= text_lines+2; + break; + case KEY_HOME: + start_y = 0; + break; + case KEY_END: + start_y = total_lines-text_lines; + break; + case KEY_DOWN: + case 'j': + start_y++; + break; + case KEY_UP: + case 'k': + start_y--; + break; + case KEY_LEFT: + case 'h': + start_x--; + break; + case KEY_RIGHT: + case 'l': + start_x++; + break; + } + if (res == 10 || res == 27 || res == 'q' || + res == KEY_F(F_HELP) || res == KEY_F(F_BACK) || + res == KEY_F(F_EXIT)) + break; + if (start_y < 0) + start_y = 0; + if (start_y >= total_lines-text_lines) + start_y = total_lines-text_lines; + if (start_x < 0) + start_x = 0; + if (start_x >= total_cols-text_cols) + start_x = total_cols-text_cols; + } while (res); + + del_panel(panel); + delwin(win); + refresh_all_windows(main_window); +} diff --git a/qemu/roms/seabios/scripts/kconfig/nconf.h b/qemu/roms/seabios/scripts/kconfig/nconf.h new file mode 100644 index 000000000..0d5261705 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/nconf.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com? + * Released under the terms of the GNU GPL v2.0. + * + * Derived from menuconfig. + * + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <locale.h> +#include <curses.h> +#include <menu.h> +#include <panel.h> +#include <form.h> + +#include <stdio.h> +#include <time.h> +#include <sys/time.h> + +#include "ncurses.h" + +#define max(a, b) ({\ + typeof(a) _a = a;\ + typeof(b) _b = b;\ + _a > _b ? _a : _b; }) + +#define min(a, b) ({\ + typeof(a) _a = a;\ + typeof(b) _b = b;\ + _a < _b ? _a : _b; }) + +typedef enum { + NORMAL = 1, + MAIN_HEADING, + MAIN_MENU_BOX, + MAIN_MENU_FORE, + MAIN_MENU_BACK, + MAIN_MENU_GREY, + MAIN_MENU_HEADING, + SCROLLWIN_TEXT, + SCROLLWIN_HEADING, + SCROLLWIN_BOX, + DIALOG_TEXT, + DIALOG_MENU_FORE, + DIALOG_MENU_BACK, + DIALOG_BOX, + INPUT_BOX, + INPUT_HEADING, + INPUT_TEXT, + INPUT_FIELD, + FUNCTION_TEXT, + FUNCTION_HIGHLIGHT, + ATTR_MAX +} attributes_t; +extern attributes_t attributes[]; + +typedef enum { + F_HELP = 1, + F_SYMBOL = 2, + F_INSTS = 3, + F_CONF = 4, + F_BACK = 5, + F_SAVE = 6, + F_LOAD = 7, + F_SEARCH = 8, + F_EXIT = 9, +} function_key; + +void set_colors(void); + +/* this changes the windows attributes !!! */ +void print_in_middle(WINDOW *win, + int starty, + int startx, + int width, + const char *string, + chtype color); +int get_line_length(const char *line); +int get_line_no(const char *text); +const char *get_line(const char *text, int line_no); +void fill_window(WINDOW *win, const char *text); +int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...); +int dialog_inputbox(WINDOW *main_window, + const char *title, const char *prompt, + const char *init, char **resultp, int *result_len); +void refresh_all_windows(WINDOW *main_window); +void show_scroll_win(WINDOW *main_window, + const char *title, + const char *text); diff --git a/qemu/roms/seabios/scripts/kconfig/qconf.cc b/qemu/roms/seabios/scripts/kconfig/qconf.cc new file mode 100644 index 000000000..9d3b04b07 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/qconf.cc @@ -0,0 +1,1795 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <qglobal.h> + +#if QT_VERSION < 0x040000 +#include <stddef.h> +#include <qmainwindow.h> +#include <qvbox.h> +#include <qvaluelist.h> +#include <qtextbrowser.h> +#include <qaction.h> +#include <qheader.h> +#include <qfiledialog.h> +#include <qdragobject.h> +#include <qpopupmenu.h> +#else +#include <q3mainwindow.h> +#include <q3vbox.h> +#include <q3valuelist.h> +#include <q3textbrowser.h> +#include <q3action.h> +#include <q3header.h> +#include <q3filedialog.h> +#include <q3dragobject.h> +#include <q3popupmenu.h> +#endif + +#include <qapplication.h> +#include <qdesktopwidget.h> +#include <qtoolbar.h> +#include <qlayout.h> +#include <qsplitter.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qmenubar.h> +#include <qmessagebox.h> +#include <qregexp.h> +#include <qevent.h> + +#include <stdlib.h> + +#include "lkc.h" +#include "qconf.h" + +#include "qconf.moc" +#include "images.c" + +#ifdef _ +# undef _ +# define _ qgettext +#endif + +static QApplication *configApp; +static ConfigSettings *configSettings; + +Q3Action *ConfigMainWindow::saveAction; + +static inline QString qgettext(const char* str) +{ + return QString::fromLocal8Bit(gettext(str)); +} + +static inline QString qgettext(const QString& str) +{ + return QString::fromLocal8Bit(gettext(str.latin1())); +} + +ConfigSettings::ConfigSettings() + : QSettings("kernel.org", "qconf") +{ +} + +/** + * Reads a list of integer values from the application settings. + */ +Q3ValueList<int> ConfigSettings::readSizes(const QString& key, bool *ok) +{ + Q3ValueList<int> result; + QStringList entryList = readListEntry(key, ok); + QStringList::Iterator it; + + for (it = entryList.begin(); it != entryList.end(); ++it) + result.push_back((*it).toInt()); + + return result; +} + +/** + * Writes a list of integer values to the application settings. + */ +bool ConfigSettings::writeSizes(const QString& key, const Q3ValueList<int>& value) +{ + QStringList stringList; + Q3ValueList<int>::ConstIterator it; + + for (it = value.begin(); it != value.end(); ++it) + stringList.push_back(QString::number(*it)); + return writeEntry(key, stringList); +} + + +/* + * set the new data + * TODO check the value + */ +void ConfigItem::okRename(int col) +{ + Parent::okRename(col); + sym_set_string_value(menu->sym, text(dataColIdx).latin1()); + listView()->updateList(this); +} + +/* + * update the displayed of a menu entry + */ +void ConfigItem::updateMenu(void) +{ + ConfigList* list; + struct symbol* sym; + struct property *prop; + QString prompt; + int type; + tristate expr; + + list = listView(); + if (goParent) { + setPixmap(promptColIdx, list->menuBackPix); + prompt = ".."; + goto set_prompt; + } + + sym = menu->sym; + prop = menu->prompt; + prompt = _(menu_get_prompt(menu)); + + if (prop) switch (prop->type) { + case P_MENU: + if (list->mode == singleMode || list->mode == symbolMode) { + /* a menuconfig entry is displayed differently + * depending whether it's at the view root or a child. + */ + if (sym && list->rootEntry == menu) + break; + setPixmap(promptColIdx, list->menuPix); + } else { + if (sym) + break; + setPixmap(promptColIdx, 0); + } + goto set_prompt; + case P_COMMENT: + setPixmap(promptColIdx, 0); + goto set_prompt; + default: + ; + } + if (!sym) + goto set_prompt; + + setText(nameColIdx, QString::fromLocal8Bit(sym->name)); + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + char ch; + + if (!sym_is_changable(sym) && list->optMode == normalOpt) { + setPixmap(promptColIdx, 0); + setText(noColIdx, QString::null); + setText(modColIdx, QString::null); + setText(yesColIdx, QString::null); + break; + } + expr = sym_get_tristate_value(sym); + switch (expr) { + case yes: + if (sym_is_choice_value(sym) && type == S_BOOLEAN) + setPixmap(promptColIdx, list->choiceYesPix); + else + setPixmap(promptColIdx, list->symbolYesPix); + setText(yesColIdx, "Y"); + ch = 'Y'; + break; + case mod: + setPixmap(promptColIdx, list->symbolModPix); + setText(modColIdx, "M"); + ch = 'M'; + break; + default: + if (sym_is_choice_value(sym) && type == S_BOOLEAN) + setPixmap(promptColIdx, list->choiceNoPix); + else + setPixmap(promptColIdx, list->symbolNoPix); + setText(noColIdx, "N"); + ch = 'N'; + break; + } + if (expr != no) + setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0); + if (expr != mod) + setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0); + if (expr != yes) + setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0); + + setText(dataColIdx, QChar(ch)); + break; + case S_INT: + case S_HEX: + case S_STRING: + const char* data; + + data = sym_get_string_value(sym); + + int i = list->mapIdx(dataColIdx); + if (i >= 0) + setRenameEnabled(i, TRUE); + setText(dataColIdx, data); + if (type == S_STRING) + prompt = QString("%1: %2").arg(prompt).arg(data); + else + prompt = QString("(%2) %1").arg(prompt).arg(data); + break; + } + if (!sym_has_value(sym) && visible) + prompt += _(" (NEW)"); +set_prompt: + setText(promptColIdx, prompt); +} + +void ConfigItem::testUpdateMenu(bool v) +{ + ConfigItem* i; + + visible = v; + if (!menu) + return; + + sym_calc_value(menu->sym); + if (menu->flags & MENU_CHANGED) { + /* the menu entry changed, so update all list items */ + menu->flags &= ~MENU_CHANGED; + for (i = (ConfigItem*)menu->data; i; i = i->nextItem) + i->updateMenu(); + } else if (listView()->updateAll) + updateMenu(); +} + +void ConfigItem::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align) +{ + ConfigList* list = listView(); + + if (visible) { + if (isSelected() && !list->hasFocus() && list->mode == menuMode) + Parent::paintCell(p, list->inactivedColorGroup, column, width, align); + else + Parent::paintCell(p, cg, column, width, align); + } else + Parent::paintCell(p, list->disabledColorGroup, column, width, align); +} + +/* + * construct a menu entry + */ +void ConfigItem::init(void) +{ + if (menu) { + ConfigList* list = listView(); + nextItem = (ConfigItem*)menu->data; + menu->data = this; + + if (list->mode != fullMode) + setOpen(TRUE); + sym_calc_value(menu->sym); + } + updateMenu(); +} + +/* + * destruct a menu entry + */ +ConfigItem::~ConfigItem(void) +{ + if (menu) { + ConfigItem** ip = (ConfigItem**)&menu->data; + for (; *ip; ip = &(*ip)->nextItem) { + if (*ip == this) { + *ip = nextItem; + break; + } + } + } +} + +ConfigLineEdit::ConfigLineEdit(ConfigView* parent) + : Parent(parent) +{ + connect(this, SIGNAL(lostFocus()), SLOT(hide())); +} + +void ConfigLineEdit::show(ConfigItem* i) +{ + item = i; + if (sym_get_string_value(item->menu->sym)) + setText(QString::fromLocal8Bit(sym_get_string_value(item->menu->sym))); + else + setText(QString::null); + Parent::show(); + setFocus(); +} + +void ConfigLineEdit::keyPressEvent(QKeyEvent* e) +{ + switch (e->key()) { + case Qt::Key_Escape: + break; + case Qt::Key_Return: + case Qt::Key_Enter: + sym_set_string_value(item->menu->sym, text().latin1()); + parent()->updateList(item); + break; + default: + Parent::keyPressEvent(e); + return; + } + e->accept(); + parent()->list->setFocus(); + hide(); +} + +ConfigList::ConfigList(ConfigView* p, const char *name) + : Parent(p, name), + updateAll(false), + symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no), + choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no), + menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void), + showName(false), showRange(false), showData(false), optMode(normalOpt), + rootEntry(0), headerPopup(0) +{ + int i; + + setSorting(-1); + setRootIsDecorated(TRUE); + disabledColorGroup = palette().active(); + disabledColorGroup.setColor(QColorGroup::Text, palette().disabled().text()); + inactivedColorGroup = palette().active(); + inactivedColorGroup.setColor(QColorGroup::Highlight, palette().disabled().highlight()); + + connect(this, SIGNAL(selectionChanged(void)), + SLOT(updateSelection(void))); + + if (name) { + configSettings->beginGroup(name); + showName = configSettings->readBoolEntry("/showName", false); + showRange = configSettings->readBoolEntry("/showRange", false); + showData = configSettings->readBoolEntry("/showData", false); + optMode = (enum optionMode)configSettings->readNumEntry("/optionMode", false); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } + + for (i = 0; i < colNr; i++) + colMap[i] = colRevMap[i] = -1; + addColumn(promptColIdx, _("Option")); + + reinit(); +} + +bool ConfigList::menuSkip(struct menu *menu) +{ + if (optMode == normalOpt && menu_is_visible(menu)) + return false; + if (optMode == promptOpt && menu_has_prompt(menu)) + return false; + if (optMode == allOpt) + return false; + return true; +} + +void ConfigList::reinit(void) +{ + removeColumn(dataColIdx); + removeColumn(yesColIdx); + removeColumn(modColIdx); + removeColumn(noColIdx); + removeColumn(nameColIdx); + + if (showName) + addColumn(nameColIdx, _("Name")); + if (showRange) { + addColumn(noColIdx, "N"); + addColumn(modColIdx, "M"); + addColumn(yesColIdx, "Y"); + } + if (showData) + addColumn(dataColIdx, _("Value")); + + updateListAll(); +} + +void ConfigList::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/showName", showName); + configSettings->writeEntry("/showRange", showRange); + configSettings->writeEntry("/showData", showData); + configSettings->writeEntry("/optionMode", (int)optMode); + configSettings->endGroup(); + } +} + +ConfigItem* ConfigList::findConfigItem(struct menu *menu) +{ + ConfigItem* item = (ConfigItem*)menu->data; + + for (; item; item = item->nextItem) { + if (this == item->listView()) + break; + } + + return item; +} + +void ConfigList::updateSelection(void) +{ + struct menu *menu; + enum prop_type type; + + ConfigItem* item = (ConfigItem*)selectedItem(); + if (!item) + return; + + menu = item->menu; + emit menuChanged(menu); + if (!menu) + return; + type = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (mode == menuMode && type == P_MENU) + emit menuSelected(menu); +} + +void ConfigList::updateList(ConfigItem* item) +{ + ConfigItem* last = 0; + + if (!rootEntry) { + if (mode != listMode) + goto update; + Q3ListViewItemIterator it(this); + ConfigItem* item; + + for (; it.current(); ++it) { + item = (ConfigItem*)it.current(); + if (!item->menu) + continue; + item->testUpdateMenu(menu_is_visible(item->menu)); + } + return; + } + + if (rootEntry != &rootmenu && (mode == singleMode || + (mode == symbolMode && rootEntry->parent != &rootmenu))) { + item = firstChild(); + if (!item) + item = new ConfigItem(this, 0, true); + last = item; + } + if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) && + rootEntry->sym && rootEntry->prompt) { + item = last ? last->nextSibling() : firstChild(); + if (!item) + item = new ConfigItem(this, last, rootEntry, true); + else + item->testUpdateMenu(true); + + updateMenuList(item, rootEntry); + triggerUpdate(); + return; + } +update: + updateMenuList(this, rootEntry); + triggerUpdate(); +} + +void ConfigList::setValue(ConfigItem* item, tristate val) +{ + struct symbol* sym; + int type; + tristate oldval; + + sym = item->menu ? item->menu->sym : 0; + if (!sym) + return; + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + oldval = sym_get_tristate_value(sym); + + if (!sym_set_tristate_value(sym, val)) + return; + if (oldval == no && item->menu->list) + item->setOpen(TRUE); + parent()->updateList(item); + break; + } +} + +void ConfigList::changeValue(ConfigItem* item) +{ + struct symbol* sym; + struct menu* menu; + int type, oldexpr, newexpr; + + menu = item->menu; + if (!menu) + return; + sym = menu->sym; + if (!sym) { + if (item->menu->list) + item->setOpen(!item->isOpen()); + return; + } + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + oldexpr = sym_get_tristate_value(sym); + newexpr = sym_toggle_tristate_value(sym); + if (item->menu->list) { + if (oldexpr == newexpr) + item->setOpen(!item->isOpen()); + else if (oldexpr == no) + item->setOpen(TRUE); + } + if (oldexpr != newexpr) + parent()->updateList(item); + break; + case S_INT: + case S_HEX: + case S_STRING: + if (colMap[dataColIdx] >= 0) + item->startRename(colMap[dataColIdx]); + else + parent()->lineEdit->show(item); + break; + } +} + +void ConfigList::setRootMenu(struct menu *menu) +{ + enum prop_type type; + + if (rootEntry == menu) + return; + type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (type != P_MENU) + return; + updateMenuList(this, 0); + rootEntry = menu; + updateListAll(); + setSelected(currentItem(), hasFocus()); + ensureItemVisible(currentItem()); +} + +void ConfigList::setParentMenu(void) +{ + ConfigItem* item; + struct menu *oldroot; + + oldroot = rootEntry; + if (rootEntry == &rootmenu) + return; + setRootMenu(menu_get_parent_menu(rootEntry->parent)); + + Q3ListViewItemIterator it(this); + for (; (item = (ConfigItem*)it.current()); it++) { + if (item->menu == oldroot) { + setCurrentItem(item); + ensureItemVisible(item); + break; + } + } +} + +/* + * update all the children of a menu entry + * removes/adds the entries from the parent widget as necessary + * + * parent: either the menu list widget or a menu entry widget + * menu: entry to be updated + */ +template <class P> +void ConfigList::updateMenuList(P* parent, struct menu* menu) +{ + struct menu* child; + ConfigItem* item; + ConfigItem* last; + bool visible; + enum prop_type type; + + if (!menu) { + while ((item = parent->firstChild())) + delete item; + return; + } + + last = parent->firstChild(); + if (last && !last->goParent) + last = 0; + for (child = menu->list; child; child = child->next) { + item = last ? last->nextSibling() : parent->firstChild(); + type = child->prompt ? child->prompt->type : P_UNKNOWN; + + switch (mode) { + case menuMode: + if (!(child->flags & MENU_ROOT)) + goto hide; + break; + case symbolMode: + if (child->flags & MENU_ROOT) + goto hide; + break; + default: + break; + } + + visible = menu_is_visible(child); + if (!menuSkip(child)) { + if (!child->sym && !child->list && !child->prompt) + continue; + if (!item || item->menu != child) + item = new ConfigItem(parent, last, child, visible); + else + item->testUpdateMenu(visible); + + if (mode == fullMode || mode == menuMode || type != P_MENU) + updateMenuList(item, child); + else + updateMenuList(item, 0); + last = item; + continue; + } + hide: + if (item && item->menu == child) { + last = parent->firstChild(); + if (last == item) + last = 0; + else while (last->nextSibling() != item) + last = last->nextSibling(); + delete item; + } + } +} + +void ConfigList::keyPressEvent(QKeyEvent* ev) +{ + Q3ListViewItem* i = currentItem(); + ConfigItem* item; + struct menu *menu; + enum prop_type type; + + if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) { + emit parentSelected(); + ev->accept(); + return; + } + + if (!i) { + Parent::keyPressEvent(ev); + return; + } + item = (ConfigItem*)i; + + switch (ev->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + if (item->goParent) { + emit parentSelected(); + break; + } + menu = item->menu; + if (!menu) + break; + type = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (type == P_MENU && rootEntry != menu && + mode != fullMode && mode != menuMode) { + emit menuSelected(menu); + break; + } + case Qt::Key_Space: + changeValue(item); + break; + case Qt::Key_N: + setValue(item, no); + break; + case Qt::Key_M: + setValue(item, mod); + break; + case Qt::Key_Y: + setValue(item, yes); + break; + default: + Parent::keyPressEvent(ev); + return; + } + ev->accept(); +} + +void ConfigList::contentsMousePressEvent(QMouseEvent* e) +{ + //QPoint p(contentsToViewport(e->pos())); + //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMousePressEvent(e); +} + +void ConfigList::contentsMouseReleaseEvent(QMouseEvent* e) +{ + QPoint p(contentsToViewport(e->pos())); + ConfigItem* item = (ConfigItem*)itemAt(p); + struct menu *menu; + enum prop_type ptype; + const QPixmap* pm; + int idx, x; + + if (!item) + goto skip; + + menu = item->menu; + x = header()->offset() + p.x(); + idx = colRevMap[header()->sectionAt(x)]; + switch (idx) { + case promptColIdx: + pm = item->pixmap(promptColIdx); + if (pm) { + int off = header()->sectionPos(0) + itemMargin() + + treeStepSize() * (item->depth() + (rootIsDecorated() ? 1 : 0)); + if (x >= off && x < off + pm->width()) { + if (item->goParent) { + emit parentSelected(); + break; + } else if (!menu) + break; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (ptype == P_MENU && rootEntry != menu && + mode != fullMode && mode != menuMode) + emit menuSelected(menu); + else + changeValue(item); + } + } + break; + case noColIdx: + setValue(item, no); + break; + case modColIdx: + setValue(item, mod); + break; + case yesColIdx: + setValue(item, yes); + break; + case dataColIdx: + changeValue(item); + break; + } + +skip: + //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMouseReleaseEvent(e); +} + +void ConfigList::contentsMouseMoveEvent(QMouseEvent* e) +{ + //QPoint p(contentsToViewport(e->pos())); + //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMouseMoveEvent(e); +} + +void ConfigList::contentsMouseDoubleClickEvent(QMouseEvent* e) +{ + QPoint p(contentsToViewport(e->pos())); + ConfigItem* item = (ConfigItem*)itemAt(p); + struct menu *menu; + enum prop_type ptype; + + if (!item) + goto skip; + if (item->goParent) { + emit parentSelected(); + goto skip; + } + menu = item->menu; + if (!menu) + goto skip; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (ptype == P_MENU && (mode == singleMode || mode == symbolMode)) + emit menuSelected(menu); + else if (menu->sym) + changeValue(item); + +skip: + //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y()); + Parent::contentsMouseDoubleClickEvent(e); +} + +void ConfigList::focusInEvent(QFocusEvent *e) +{ + struct menu *menu = NULL; + + Parent::focusInEvent(e); + + ConfigItem* item = (ConfigItem *)currentItem(); + if (item) { + setSelected(item, TRUE); + menu = item->menu; + } + emit gotFocus(menu); +} + +void ConfigList::contextMenuEvent(QContextMenuEvent *e) +{ + if (e->y() <= header()->geometry().bottom()) { + if (!headerPopup) { + Q3Action *action; + + headerPopup = new Q3PopupMenu(this); + action = new Q3Action(NULL, _("Show Name"), 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowName(bool))); + connect(parent(), SIGNAL(showNameChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showName); + action->addTo(headerPopup); + action = new Q3Action(NULL, _("Show Range"), 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowRange(bool))); + connect(parent(), SIGNAL(showRangeChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showRange); + action->addTo(headerPopup); + action = new Q3Action(NULL, _("Show Data"), 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowData(bool))); + connect(parent(), SIGNAL(showDataChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showData); + action->addTo(headerPopup); + } + headerPopup->exec(e->globalPos()); + e->accept(); + } else + e->ignore(); +} + +ConfigView*ConfigView::viewList; +QAction *ConfigView::showNormalAction; +QAction *ConfigView::showAllAction; +QAction *ConfigView::showPromptAction; + +ConfigView::ConfigView(QWidget* parent, const char *name) + : Parent(parent, name) +{ + list = new ConfigList(this, name); + lineEdit = new ConfigLineEdit(this); + lineEdit->hide(); + + this->nextView = viewList; + viewList = this; +} + +ConfigView::~ConfigView(void) +{ + ConfigView** vp; + + for (vp = &viewList; *vp; vp = &(*vp)->nextView) { + if (*vp == this) { + *vp = nextView; + break; + } + } +} + +void ConfigView::setOptionMode(QAction *act) +{ + if (act == showNormalAction) + list->optMode = normalOpt; + else if (act == showAllAction) + list->optMode = allOpt; + else + list->optMode = promptOpt; + + list->updateListAll(); +} + +void ConfigView::setShowName(bool b) +{ + if (list->showName != b) { + list->showName = b; + list->reinit(); + emit showNameChanged(b); + } +} + +void ConfigView::setShowRange(bool b) +{ + if (list->showRange != b) { + list->showRange = b; + list->reinit(); + emit showRangeChanged(b); + } +} + +void ConfigView::setShowData(bool b) +{ + if (list->showData != b) { + list->showData = b; + list->reinit(); + emit showDataChanged(b); + } +} + +void ConfigList::setAllOpen(bool open) +{ + Q3ListViewItemIterator it(this); + + for (; it.current(); it++) + it.current()->setOpen(open); +} + +void ConfigView::updateList(ConfigItem* item) +{ + ConfigView* v; + + for (v = viewList; v; v = v->nextView) + v->list->updateList(item); +} + +void ConfigView::updateListAll(void) +{ + ConfigView* v; + + for (v = viewList; v; v = v->nextView) + v->list->updateListAll(); +} + +ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name) + : Parent(parent, name), sym(0), _menu(0) +{ + if (name) { + configSettings->beginGroup(name); + _showDebug = configSettings->readBoolEntry("/showDebug", false); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigInfoView::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/showDebug", showDebug()); + configSettings->endGroup(); + } +} + +void ConfigInfoView::setShowDebug(bool b) +{ + if (_showDebug != b) { + _showDebug = b; + if (_menu) + menuInfo(); + else if (sym) + symbolInfo(); + emit showDebugChanged(b); + } +} + +void ConfigInfoView::setInfo(struct menu *m) +{ + if (_menu == m) + return; + _menu = m; + sym = NULL; + if (!_menu) + clear(); + else + menuInfo(); +} + +void ConfigInfoView::symbolInfo(void) +{ + QString str; + + str += "<big>Symbol: <b>"; + str += print_filter(sym->name); + str += "</b></big><br><br>value: "; + str += print_filter(sym_get_string_value(sym)); + str += "<br>visibility: "; + str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n"; + str += "<br>"; + str += debug_info(sym); + + setText(str); +} + +void ConfigInfoView::menuInfo(void) +{ + struct symbol* sym; + QString head, debug, help; + + sym = _menu->sym; + if (sym) { + if (_menu->prompt) { + head += "<big><b>"; + head += print_filter(_(_menu->prompt->text)); + head += "</b></big>"; + if (sym->name) { + head += " ("; + if (showDebug()) + head += QString().sprintf("<a href=\"s%p\">", sym); + head += print_filter(sym->name); + if (showDebug()) + head += "</a>"; + head += ")"; + } + } else if (sym->name) { + head += "<big><b>"; + if (showDebug()) + head += QString().sprintf("<a href=\"s%p\">", sym); + head += print_filter(sym->name); + if (showDebug()) + head += "</a>"; + head += "</b></big>"; + } + head += "<br><br>"; + + if (showDebug()) + debug = debug_info(sym); + + struct gstr help_gstr = str_new(); + menu_get_ext_help(_menu, &help_gstr); + help = print_filter(str_get(&help_gstr)); + str_free(&help_gstr); + } else if (_menu->prompt) { + head += "<big><b>"; + head += print_filter(_(_menu->prompt->text)); + head += "</b></big><br><br>"; + if (showDebug()) { + if (_menu->prompt->visible.expr) { + debug += " dep: "; + expr_print(_menu->prompt->visible.expr, expr_print_help, &debug, E_NONE); + debug += "<br><br>"; + } + } + } + if (showDebug()) + debug += QString().sprintf("defined at %s:%d<br><br>", _menu->file->name, _menu->lineno); + + setText(head + debug + help); +} + +QString ConfigInfoView::debug_info(struct symbol *sym) +{ + QString debug; + + debug += "type: "; + debug += print_filter(sym_type_name(sym->type)); + if (sym_is_choice(sym)) + debug += " (choice)"; + debug += "<br>"; + if (sym->rev_dep.expr) { + debug += "reverse dep: "; + expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + } + for (struct property *prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_PROMPT: + case P_MENU: + debug += QString().sprintf("prompt: <a href=\"m%p\">", prop->menu); + debug += print_filter(_(prop->text)); + debug += "</a><br>"; + break; + case P_DEFAULT: + case P_SELECT: + case P_RANGE: + case P_ENV: + debug += prop_get_type_name(prop->type); + debug += ": "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + break; + case P_CHOICE: + if (sym_is_choice(sym)) { + debug += "choice: "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + } + break; + default: + debug += "unknown property: "; + debug += prop_get_type_name(prop->type); + debug += "<br>"; + } + if (prop->visible.expr) { + debug += " dep: "; + expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE); + debug += "<br>"; + } + } + debug += "<br>"; + + return debug; +} + +QString ConfigInfoView::print_filter(const QString &str) +{ + QRegExp re("[<>&\"\\n]"); + QString res = str; + for (int i = 0; (i = res.find(re, i)) >= 0;) { + switch (res[i].latin1()) { + case '<': + res.replace(i, 1, "<"); + i += 4; + break; + case '>': + res.replace(i, 1, ">"); + i += 4; + break; + case '&': + res.replace(i, 1, "&"); + i += 5; + break; + case '"': + res.replace(i, 1, """); + i += 6; + break; + case '\n': + res.replace(i, 1, "<br>"); + i += 4; + break; + } + } + return res; +} + +void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str) +{ + QString* text = reinterpret_cast<QString*>(data); + QString str2 = print_filter(str); + + if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) { + *text += QString().sprintf("<a href=\"s%p\">", sym); + *text += str2; + *text += "</a>"; + } else + *text += str2; +} + +Q3PopupMenu* ConfigInfoView::createPopupMenu(const QPoint& pos) +{ + Q3PopupMenu* popup = Parent::createPopupMenu(pos); + Q3Action* action = new Q3Action(NULL, _("Show Debug Info"), 0, popup); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool))); + connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool))); + action->setOn(showDebug()); + popup->insertSeparator(); + action->addTo(popup); + return popup; +} + +void ConfigInfoView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + Parent::contentsContextMenuEvent(e); +} + +ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *name) + : Parent(parent, name), result(NULL) +{ + setCaption("Search Config"); + + QVBoxLayout* layout1 = new QVBoxLayout(this, 11, 6); + QHBoxLayout* layout2 = new QHBoxLayout(0, 0, 6); + layout2->addWidget(new QLabel(_("Find:"), this)); + editField = new QLineEdit(this); + connect(editField, SIGNAL(returnPressed()), SLOT(search())); + layout2->addWidget(editField); + searchButton = new QPushButton(_("Search"), this); + searchButton->setAutoDefault(FALSE); + connect(searchButton, SIGNAL(clicked()), SLOT(search())); + layout2->addWidget(searchButton); + layout1->addLayout(layout2); + + split = new QSplitter(this); + split->setOrientation(Qt::Vertical); + list = new ConfigView(split, name); + list->list->mode = listMode; + info = new ConfigInfoView(split, name); + connect(list->list, SIGNAL(menuChanged(struct menu *)), + info, SLOT(setInfo(struct menu *))); + connect(list->list, SIGNAL(menuChanged(struct menu *)), + parent, SLOT(setMenuLink(struct menu *))); + + layout1->addWidget(split); + + if (name) { + int x, y, width, height; + bool ok; + + configSettings->beginGroup(name); + width = configSettings->readNumEntry("/window width", parent->width() / 2); + height = configSettings->readNumEntry("/window height", parent->height() / 2); + resize(width, height); + x = configSettings->readNumEntry("/window x", 0, &ok); + if (ok) + y = configSettings->readNumEntry("/window y", 0, &ok); + if (ok) + move(x, y); + Q3ValueList<int> sizes = configSettings->readSizes("/split", &ok); + if (ok) + split->setSizes(sizes); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigSearchWindow::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/window x", pos().x()); + configSettings->writeEntry("/window y", pos().y()); + configSettings->writeEntry("/window width", size().width()); + configSettings->writeEntry("/window height", size().height()); + configSettings->writeSizes("/split", split->sizes()); + configSettings->endGroup(); + } +} + +void ConfigSearchWindow::search(void) +{ + struct symbol **p; + struct property *prop; + ConfigItem *lastItem = NULL; + + free(result); + list->list->clear(); + info->clear(); + + result = sym_re_search(editField->text().latin1()); + if (!result) + return; + for (p = result; *p; p++) { + for_all_prompts((*p), prop) + lastItem = new ConfigItem(list->list, lastItem, prop->menu, + menu_is_visible(prop->menu)); + } +} + +/* + * Construct the complete config widget + */ +ConfigMainWindow::ConfigMainWindow(void) + : searchWindow(0) +{ + QMenuBar* menu; + bool ok; + int x, y, width, height; + char title[256]; + + QDesktopWidget *d = configApp->desktop(); + snprintf(title, sizeof(title), "%s%s", + rootmenu.prompt->text, +#if QT_VERSION < 0x040000 + " (Qt3)" +#else + "" +#endif + ); + setCaption(title); + + width = configSettings->readNumEntry("/window width", d->width() - 64); + height = configSettings->readNumEntry("/window height", d->height() - 64); + resize(width, height); + x = configSettings->readNumEntry("/window x", 0, &ok); + if (ok) + y = configSettings->readNumEntry("/window y", 0, &ok); + if (ok) + move(x, y); + + split1 = new QSplitter(this); + split1->setOrientation(Qt::Horizontal); + setCentralWidget(split1); + + menuView = new ConfigView(split1, "menu"); + menuList = menuView->list; + + split2 = new QSplitter(split1); + split2->setOrientation(Qt::Vertical); + + // create config tree + configView = new ConfigView(split2, "config"); + configList = configView->list; + + helpText = new ConfigInfoView(split2, "help"); + helpText->setTextFormat(Qt::RichText); + + setTabOrder(configList, helpText); + configList->setFocus(); + + menu = menuBar(); + toolBar = new Q3ToolBar("Tools", this); + + backAction = new Q3Action("Back", QPixmap(xpm_back), _("Back"), 0, this); + connect(backAction, SIGNAL(activated()), SLOT(goBack())); + backAction->setEnabled(FALSE); + Q3Action *quitAction = new Q3Action("Quit", _("&Quit"), Qt::CTRL + Qt::Key_Q, this); + connect(quitAction, SIGNAL(activated()), SLOT(close())); + Q3Action *loadAction = new Q3Action("Load", QPixmap(xpm_load), _("&Load"), Qt::CTRL + Qt::Key_L, this); + connect(loadAction, SIGNAL(activated()), SLOT(loadConfig())); + saveAction = new Q3Action("Save", QPixmap(xpm_save), _("&Save"), Qt::CTRL + Qt::Key_S, this); + connect(saveAction, SIGNAL(activated()), SLOT(saveConfig())); + conf_set_changed_callback(conf_changed); + // Set saveAction's initial state + conf_changed(); + Q3Action *saveAsAction = new Q3Action("Save As...", _("Save &As..."), 0, this); + connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs())); + Q3Action *searchAction = new Q3Action("Find", _("&Find"), Qt::CTRL + Qt::Key_F, this); + connect(searchAction, SIGNAL(activated()), SLOT(searchConfig())); + Q3Action *singleViewAction = new Q3Action("Single View", QPixmap(xpm_single_view), _("Single View"), 0, this); + connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView())); + Q3Action *splitViewAction = new Q3Action("Split View", QPixmap(xpm_split_view), _("Split View"), 0, this); + connect(splitViewAction, SIGNAL(activated()), SLOT(showSplitView())); + Q3Action *fullViewAction = new Q3Action("Full View", QPixmap(xpm_tree_view), _("Full View"), 0, this); + connect(fullViewAction, SIGNAL(activated()), SLOT(showFullView())); + + Q3Action *showNameAction = new Q3Action(NULL, _("Show Name"), 0, this); + showNameAction->setToggleAction(TRUE); + connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool))); + connect(configView, SIGNAL(showNameChanged(bool)), showNameAction, SLOT(setOn(bool))); + showNameAction->setOn(configView->showName()); + Q3Action *showRangeAction = new Q3Action(NULL, _("Show Range"), 0, this); + showRangeAction->setToggleAction(TRUE); + connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool))); + connect(configView, SIGNAL(showRangeChanged(bool)), showRangeAction, SLOT(setOn(bool))); + showRangeAction->setOn(configList->showRange); + Q3Action *showDataAction = new Q3Action(NULL, _("Show Data"), 0, this); + showDataAction->setToggleAction(TRUE); + connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool))); + connect(configView, SIGNAL(showDataChanged(bool)), showDataAction, SLOT(setOn(bool))); + showDataAction->setOn(configList->showData); + + QActionGroup *optGroup = new QActionGroup(this); + optGroup->setExclusive(TRUE); + connect(optGroup, SIGNAL(selected(QAction *)), configView, + SLOT(setOptionMode(QAction *))); + connect(optGroup, SIGNAL(selected(QAction *)), menuView, + SLOT(setOptionMode(QAction *))); + +#if QT_VERSION >= 0x040000 + configView->showNormalAction = new QAction(_("Show Normal Options"), optGroup); + configView->showAllAction = new QAction(_("Show All Options"), optGroup); + configView->showPromptAction = new QAction(_("Show Prompt Options"), optGroup); +#else + configView->showNormalAction = new QAction(_("Show Normal Options"), 0, optGroup); + configView->showAllAction = new QAction(_("Show All Options"), 0, optGroup); + configView->showPromptAction = new QAction(_("Show Prompt Options"), 0, optGroup); +#endif + configView->showNormalAction->setToggleAction(TRUE); + configView->showNormalAction->setOn(configList->optMode == normalOpt); + configView->showAllAction->setToggleAction(TRUE); + configView->showAllAction->setOn(configList->optMode == allOpt); + configView->showPromptAction->setToggleAction(TRUE); + configView->showPromptAction->setOn(configList->optMode == promptOpt); + + Q3Action *showDebugAction = new Q3Action(NULL, _("Show Debug Info"), 0, this); + showDebugAction->setToggleAction(TRUE); + connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool))); + connect(helpText, SIGNAL(showDebugChanged(bool)), showDebugAction, SLOT(setOn(bool))); + showDebugAction->setOn(helpText->showDebug()); + + Q3Action *showIntroAction = new Q3Action(NULL, _("Introduction"), 0, this); + connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro())); + Q3Action *showAboutAction = new Q3Action(NULL, _("About"), 0, this); + connect(showAboutAction, SIGNAL(activated()), SLOT(showAbout())); + + // init tool bar + backAction->addTo(toolBar); + toolBar->addSeparator(); + loadAction->addTo(toolBar); + saveAction->addTo(toolBar); + toolBar->addSeparator(); + singleViewAction->addTo(toolBar); + splitViewAction->addTo(toolBar); + fullViewAction->addTo(toolBar); + + // create config menu + Q3PopupMenu* config = new Q3PopupMenu(this); + menu->insertItem(_("&File"), config); + loadAction->addTo(config); + saveAction->addTo(config); + saveAsAction->addTo(config); + config->insertSeparator(); + quitAction->addTo(config); + + // create edit menu + Q3PopupMenu* editMenu = new Q3PopupMenu(this); + menu->insertItem(_("&Edit"), editMenu); + searchAction->addTo(editMenu); + + // create options menu + Q3PopupMenu* optionMenu = new Q3PopupMenu(this); + menu->insertItem(_("&Option"), optionMenu); + showNameAction->addTo(optionMenu); + showRangeAction->addTo(optionMenu); + showDataAction->addTo(optionMenu); + optionMenu->insertSeparator(); + optGroup->addTo(optionMenu); + optionMenu->insertSeparator(); + + // create help menu + Q3PopupMenu* helpMenu = new Q3PopupMenu(this); + menu->insertSeparator(); + menu->insertItem(_("&Help"), helpMenu); + showIntroAction->addTo(helpMenu); + showAboutAction->addTo(helpMenu); + + connect(configList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(configList, SIGNAL(menuSelected(struct menu *)), + SLOT(changeMenu(struct menu *))); + connect(configList, SIGNAL(parentSelected()), + SLOT(goBack())); + connect(menuList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(menuSelected(struct menu *)), + SLOT(changeMenu(struct menu *))); + + connect(configList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), + SLOT(listFocusChanged(void))); + connect(helpText, SIGNAL(menuSelected(struct menu *)), + SLOT(setMenuLink(struct menu *))); + + QString listMode = configSettings->readEntry("/listMode", "symbol"); + if (listMode == "single") + showSingleView(); + else if (listMode == "full") + showFullView(); + else /*if (listMode == "split")*/ + showSplitView(); + + // UI setup done, restore splitter positions + Q3ValueList<int> sizes = configSettings->readSizes("/split1", &ok); + if (ok) + split1->setSizes(sizes); + + sizes = configSettings->readSizes("/split2", &ok); + if (ok) + split2->setSizes(sizes); +} + +void ConfigMainWindow::loadConfig(void) +{ + QString s = Q3FileDialog::getOpenFileName(conf_get_configname(), NULL, this); + if (s.isNull()) + return; + if (conf_read(QFile::encodeName(s))) + QMessageBox::information(this, "qconf", _("Unable to load configuration!")); + ConfigView::updateListAll(); +} + +bool ConfigMainWindow::saveConfig(void) +{ + if (conf_write(NULL)) { + QMessageBox::information(this, "qconf", _("Unable to save configuration!")); + return false; + } + return true; +} + +void ConfigMainWindow::saveConfigAs(void) +{ + QString s = Q3FileDialog::getSaveFileName(conf_get_configname(), NULL, this); + if (s.isNull()) + return; + saveConfig(); +} + +void ConfigMainWindow::searchConfig(void) +{ + if (!searchWindow) + searchWindow = new ConfigSearchWindow(this, "search"); + searchWindow->show(); +} + +void ConfigMainWindow::changeMenu(struct menu *menu) +{ + configList->setRootMenu(menu); + if (configList->rootEntry->parent == &rootmenu) + backAction->setEnabled(FALSE); + else + backAction->setEnabled(TRUE); +} + +void ConfigMainWindow::setMenuLink(struct menu *menu) +{ + struct menu *parent; + ConfigList* list = NULL; + ConfigItem* item; + + if (configList->menuSkip(menu)) + return; + + switch (configList->mode) { + case singleMode: + list = configList; + parent = menu_get_parent_menu(menu); + if (!parent) + return; + list->setRootMenu(parent); + break; + case symbolMode: + if (menu->flags & MENU_ROOT) { + configList->setRootMenu(menu); + configList->clearSelection(); + list = menuList; + } else { + list = configList; + parent = menu_get_parent_menu(menu->parent); + if (!parent) + return; + item = menuList->findConfigItem(parent); + if (item) { + menuList->setSelected(item, TRUE); + menuList->ensureItemVisible(item); + } + list->setRootMenu(parent); + } + break; + case fullMode: + list = configList; + break; + default: + break; + } + + if (list) { + item = list->findConfigItem(menu); + if (item) { + list->setSelected(item, TRUE); + list->ensureItemVisible(item); + list->setFocus(); + } + } +} + +void ConfigMainWindow::listFocusChanged(void) +{ + if (menuList->mode == menuMode) + configList->clearSelection(); +} + +void ConfigMainWindow::goBack(void) +{ + ConfigItem* item; + + configList->setParentMenu(); + if (configList->rootEntry == &rootmenu) + backAction->setEnabled(FALSE); + item = (ConfigItem*)menuList->selectedItem(); + while (item) { + if (item->menu == configList->rootEntry) { + menuList->setSelected(item, TRUE); + break; + } + item = (ConfigItem*)item->parent(); + } +} + +void ConfigMainWindow::showSingleView(void) +{ + menuView->hide(); + menuList->setRootMenu(0); + configList->mode = singleMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setAllOpen(TRUE); + configList->setFocus(); +} + +void ConfigMainWindow::showSplitView(void) +{ + configList->mode = symbolMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setAllOpen(TRUE); + configApp->processEvents(); + menuList->mode = menuMode; + menuList->setRootMenu(&rootmenu); + menuList->setAllOpen(TRUE); + menuView->show(); + menuList->setFocus(); +} + +void ConfigMainWindow::showFullView(void) +{ + menuView->hide(); + menuList->setRootMenu(0); + configList->mode = fullMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setAllOpen(FALSE); + configList->setFocus(); +} + +/* + * ask for saving configuration before quitting + * TODO ask only when something changed + */ +void ConfigMainWindow::closeEvent(QCloseEvent* e) +{ + if (!conf_get_changed()) { + e->accept(); + return; + } + QMessageBox mb("qconf", _("Save configuration?"), QMessageBox::Warning, + QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape); + mb.setButtonText(QMessageBox::Yes, _("&Save Changes")); + mb.setButtonText(QMessageBox::No, _("&Discard Changes")); + mb.setButtonText(QMessageBox::Cancel, _("Cancel Exit")); + switch (mb.exec()) { + case QMessageBox::Yes: + if (saveConfig()) + e->accept(); + else + e->ignore(); + break; + case QMessageBox::No: + e->accept(); + break; + case QMessageBox::Cancel: + e->ignore(); + break; + } +} + +void ConfigMainWindow::showIntro(void) +{ + static const QString str = _("Welcome to the qconf graphical configuration tool.\n\n" + "For each option, a blank box indicates the feature is disabled, a check\n" + "indicates it is enabled, and a dot indicates that it is to be compiled\n" + "as a module. Clicking on the box will cycle through the three states.\n\n" + "If you do not see an option (e.g., a device driver) that you believe\n" + "should be present, try turning on Show All Options under the Options menu.\n" + "Although there is no cross reference yet to help you figure out what other\n" + "options must be enabled to support the option you are interested in, you can\n" + "still view the help of a grayed-out option.\n\n" + "Toggling Show Debug Info under the Options menu will show the dependencies,\n" + "which you can then match by examining other options.\n\n"); + + QMessageBox::information(this, "qconf", str); +} + +void ConfigMainWindow::showAbout(void) +{ + static const QString str = _("qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n\n" + "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n"); + + QMessageBox::information(this, "qconf", str); +} + +void ConfigMainWindow::saveSettings(void) +{ + configSettings->writeEntry("/window x", pos().x()); + configSettings->writeEntry("/window y", pos().y()); + configSettings->writeEntry("/window width", size().width()); + configSettings->writeEntry("/window height", size().height()); + + QString entry; + switch(configList->mode) { + case singleMode : + entry = "single"; + break; + + case symbolMode : + entry = "split"; + break; + + case fullMode : + entry = "full"; + break; + + default: + break; + } + configSettings->writeEntry("/listMode", entry); + + configSettings->writeSizes("/split1", split1->sizes()); + configSettings->writeSizes("/split2", split2->sizes()); +} + +void ConfigMainWindow::conf_changed(void) +{ + if (saveAction) + saveAction->setEnabled(conf_get_changed()); +} + +void fixup_rootmenu(struct menu *menu) +{ + struct menu *child; + static int menu_cnt = 0; + + menu->flags |= MENU_ROOT; + for (child = menu->list; child; child = child->next) { + if (child->prompt && child->prompt->type == P_MENU) { + menu_cnt++; + fixup_rootmenu(child); + menu_cnt--; + } else if (!menu_cnt) + fixup_rootmenu(child); + } +} + +static const char *progname; + +static void usage(void) +{ + printf(_("%s <config>\n"), progname); + exit(0); +} + +int main(int ac, char** av) +{ + ConfigMainWindow* v; + const char *name; + + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + progname = av[0]; + configApp = new QApplication(ac, av); + if (ac > 1 && av[1][0] == '-') { + switch (av[1][1]) { + case 'h': + case '?': + usage(); + } + name = av[2]; + } else + name = av[1]; + if (!name) + usage(); + + conf_parse(name); + fixup_rootmenu(&rootmenu); + conf_read(NULL); + //zconfdump(stdout); + + configSettings = new ConfigSettings(); + configSettings->beginGroup("/kconfig/qconf"); + v = new ConfigMainWindow(); + + //zconfdump(stdout); + configApp->setMainWidget(v); + configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); + configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); + v->show(); + configApp->exec(); + + configSettings->endGroup(); + delete configSettings; + + return 0; +} diff --git a/qemu/roms/seabios/scripts/kconfig/qconf.h b/qemu/roms/seabios/scripts/kconfig/qconf.h new file mode 100644 index 000000000..bde0c6b6f --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/qconf.h @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#if QT_VERSION < 0x040000 +#include <qlistview.h> +#else +#include <q3listview.h> +#endif +#include <qsettings.h> + +#if QT_VERSION < 0x040000 +#define Q3ValueList QValueList +#define Q3PopupMenu QPopupMenu +#define Q3ListView QListView +#define Q3ListViewItem QListViewItem +#define Q3VBox QVBox +#define Q3TextBrowser QTextBrowser +#define Q3MainWindow QMainWindow +#define Q3Action QAction +#define Q3ToolBar QToolBar +#define Q3ListViewItemIterator QListViewItemIterator +#define Q3FileDialog QFileDialog +#endif + +class ConfigView; +class ConfigList; +class ConfigItem; +class ConfigLineEdit; +class ConfigMainWindow; + +class ConfigSettings : public QSettings { +public: + ConfigSettings(); + Q3ValueList<int> readSizes(const QString& key, bool *ok); + bool writeSizes(const QString& key, const Q3ValueList<int>& value); +}; + +enum colIdx { + promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx, colNr +}; +enum listMode { + singleMode, menuMode, symbolMode, fullMode, listMode +}; +enum optionMode { + normalOpt = 0, allOpt, promptOpt +}; + +class ConfigList : public Q3ListView { + Q_OBJECT + typedef class Q3ListView Parent; +public: + ConfigList(ConfigView* p, const char *name = 0); + void reinit(void); + ConfigView* parent(void) const + { + return (ConfigView*)Parent::parent(); + } + ConfigItem* findConfigItem(struct menu *); + +protected: + void keyPressEvent(QKeyEvent *e); + void contentsMousePressEvent(QMouseEvent *e); + void contentsMouseReleaseEvent(QMouseEvent *e); + void contentsMouseMoveEvent(QMouseEvent *e); + void contentsMouseDoubleClickEvent(QMouseEvent *e); + void focusInEvent(QFocusEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + +public slots: + void setRootMenu(struct menu *menu); + + void updateList(ConfigItem *item); + void setValue(ConfigItem* item, tristate val); + void changeValue(ConfigItem* item); + void updateSelection(void); + void saveSettings(void); +signals: + void menuChanged(struct menu *menu); + void menuSelected(struct menu *menu); + void parentSelected(void); + void gotFocus(struct menu *); + +public: + void updateListAll(void) + { + updateAll = true; + updateList(NULL); + updateAll = false; + } + ConfigList* listView() + { + return this; + } + ConfigItem* firstChild() const + { + return (ConfigItem *)Parent::firstChild(); + } + int mapIdx(colIdx idx) + { + return colMap[idx]; + } + void addColumn(colIdx idx, const QString& label) + { + colMap[idx] = Parent::addColumn(label); + colRevMap[colMap[idx]] = idx; + } + void removeColumn(colIdx idx) + { + int col = colMap[idx]; + if (col >= 0) { + Parent::removeColumn(col); + colRevMap[col] = colMap[idx] = -1; + } + } + void setAllOpen(bool open); + void setParentMenu(void); + + bool menuSkip(struct menu *); + + template <class P> + void updateMenuList(P*, struct menu*); + + bool updateAll; + + QPixmap symbolYesPix, symbolModPix, symbolNoPix; + QPixmap choiceYesPix, choiceNoPix; + QPixmap menuPix, menuInvPix, menuBackPix, voidPix; + + bool showName, showRange, showData; + enum listMode mode; + enum optionMode optMode; + struct menu *rootEntry; + QColorGroup disabledColorGroup; + QColorGroup inactivedColorGroup; + Q3PopupMenu* headerPopup; + +private: + int colMap[colNr]; + int colRevMap[colNr]; +}; + +class ConfigItem : public Q3ListViewItem { + typedef class Q3ListViewItem Parent; +public: + ConfigItem(Q3ListView *parent, ConfigItem *after, struct menu *m, bool v) + : Parent(parent, after), menu(m), visible(v), goParent(false) + { + init(); + } + ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v) + : Parent(parent, after), menu(m), visible(v), goParent(false) + { + init(); + } + ConfigItem(Q3ListView *parent, ConfigItem *after, bool v) + : Parent(parent, after), menu(0), visible(v), goParent(true) + { + init(); + } + ~ConfigItem(void); + void init(void); + void okRename(int col); + void updateMenu(void); + void testUpdateMenu(bool v); + ConfigList* listView() const + { + return (ConfigList*)Parent::listView(); + } + ConfigItem* firstChild() const + { + return (ConfigItem *)Parent::firstChild(); + } + ConfigItem* nextSibling() const + { + return (ConfigItem *)Parent::nextSibling(); + } + void setText(colIdx idx, const QString& text) + { + Parent::setText(listView()->mapIdx(idx), text); + } + QString text(colIdx idx) const + { + return Parent::text(listView()->mapIdx(idx)); + } + void setPixmap(colIdx idx, const QPixmap& pm) + { + Parent::setPixmap(listView()->mapIdx(idx), pm); + } + const QPixmap* pixmap(colIdx idx) const + { + return Parent::pixmap(listView()->mapIdx(idx)); + } + void paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align); + + ConfigItem* nextItem; + struct menu *menu; + bool visible; + bool goParent; +}; + +class ConfigLineEdit : public QLineEdit { + Q_OBJECT + typedef class QLineEdit Parent; +public: + ConfigLineEdit(ConfigView* parent); + ConfigView* parent(void) const + { + return (ConfigView*)Parent::parent(); + } + void show(ConfigItem *i); + void keyPressEvent(QKeyEvent *e); + +public: + ConfigItem *item; +}; + +class ConfigView : public Q3VBox { + Q_OBJECT + typedef class Q3VBox Parent; +public: + ConfigView(QWidget* parent, const char *name = 0); + ~ConfigView(void); + static void updateList(ConfigItem* item); + static void updateListAll(void); + + bool showName(void) const { return list->showName; } + bool showRange(void) const { return list->showRange; } + bool showData(void) const { return list->showData; } +public slots: + void setShowName(bool); + void setShowRange(bool); + void setShowData(bool); + void setOptionMode(QAction *); +signals: + void showNameChanged(bool); + void showRangeChanged(bool); + void showDataChanged(bool); +public: + ConfigList* list; + ConfigLineEdit* lineEdit; + + static ConfigView* viewList; + ConfigView* nextView; + + static QAction *showNormalAction; + static QAction *showAllAction; + static QAction *showPromptAction; +}; + +class ConfigInfoView : public Q3TextBrowser { + Q_OBJECT + typedef class Q3TextBrowser Parent; +public: + ConfigInfoView(QWidget* parent, const char *name = 0); + bool showDebug(void) const { return _showDebug; } + +public slots: + void setInfo(struct menu *menu); + void saveSettings(void); + void setShowDebug(bool); + +signals: + void showDebugChanged(bool); + void menuSelected(struct menu *); + +protected: + void symbolInfo(void); + void menuInfo(void); + QString debug_info(struct symbol *sym); + static QString print_filter(const QString &str); + static void expr_print_help(void *data, struct symbol *sym, const char *str); + Q3PopupMenu* createPopupMenu(const QPoint& pos); + void contentsContextMenuEvent(QContextMenuEvent *e); + + struct symbol *sym; + struct menu *_menu; + bool _showDebug; +}; + +class ConfigSearchWindow : public QDialog { + Q_OBJECT + typedef class QDialog Parent; +public: + ConfigSearchWindow(ConfigMainWindow* parent, const char *name = 0); + +public slots: + void saveSettings(void); + void search(void); + +protected: + QLineEdit* editField; + QPushButton* searchButton; + QSplitter* split; + ConfigView* list; + ConfigInfoView* info; + + struct symbol **result; +}; + +class ConfigMainWindow : public Q3MainWindow { + Q_OBJECT + + static Q3Action *saveAction; + static void conf_changed(void); +public: + ConfigMainWindow(void); +public slots: + void changeMenu(struct menu *); + void setMenuLink(struct menu *); + void listFocusChanged(void); + void goBack(void); + void loadConfig(void); + bool saveConfig(void); + void saveConfigAs(void); + void searchConfig(void); + void showSingleView(void); + void showSplitView(void); + void showFullView(void); + void showIntro(void); + void showAbout(void); + void saveSettings(void); + +protected: + void closeEvent(QCloseEvent *e); + + ConfigSearchWindow *searchWindow; + ConfigView *menuView; + ConfigList *menuList; + ConfigView *configView; + ConfigList *configList; + ConfigInfoView *helpText; + Q3ToolBar *toolBar; + Q3Action *backAction; + QSplitter* split1; + QSplitter* split2; +}; diff --git a/qemu/roms/seabios/scripts/kconfig/streamline_config.pl b/qemu/roms/seabios/scripts/kconfig/streamline_config.pl new file mode 100644 index 000000000..9cb8522d8 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/streamline_config.pl @@ -0,0 +1,647 @@ +#!/usr/bin/perl -w +# +# Copyright 2005-2009 - Steven Rostedt +# Licensed under the terms of the GNU GPL License version 2 +# +# It's simple enough to figure out how this works. +# If not, then you can ask me at stripconfig@goodmis.org +# +# What it does? +# +# If you have installed a Linux kernel from a distribution +# that turns on way too many modules than you need, and +# you only want the modules you use, then this program +# is perfect for you. +# +# It gives you the ability to turn off all the modules that are +# not loaded on your system. +# +# Howto: +# +# 1. Boot up the kernel that you want to stream line the config on. +# 2. Change directory to the directory holding the source of the +# kernel that you just booted. +# 3. Copy the configuraton file to this directory as .config +# 4. Have all your devices that you need modules for connected and +# operational (make sure that their corresponding modules are loaded) +# 5. Run this script redirecting the output to some other file +# like config_strip. +# 6. Back up your old config (if you want too). +# 7. copy the config_strip file to .config +# 8. Run "make oldconfig" +# +# Now your kernel is ready to be built with only the modules that +# are loaded. +# +# Here's what I did with my Debian distribution. +# +# cd /usr/src/linux-2.6.10 +# cp /boot/config-2.6.10-1-686-smp .config +# ~/bin/streamline_config > config_strip +# mv .config config_sav +# mv config_strip .config +# make oldconfig +# +use strict; +use Getopt::Long; + +# set the environment variable LOCALMODCONFIG_DEBUG to get +# debug output. +my $debugprint = 0; +$debugprint = 1 if (defined($ENV{LOCALMODCONFIG_DEBUG})); + +sub dprint { + return if (!$debugprint); + print STDERR @_; +} + +my $config = ".config"; + +my $uname = `uname -r`; +chomp $uname; + +my @searchconfigs = ( + { + "file" => ".config", + "exec" => "cat", + }, + { + "file" => "/proc/config.gz", + "exec" => "zcat", + }, + { + "file" => "/boot/config-$uname", + "exec" => "cat", + }, + { + "file" => "/boot/vmlinuz-$uname", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "vmlinux", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "/lib/modules/$uname/kernel/kernel/configs.ko", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "kernel/configs.ko", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "kernel/configs.o", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, +); + +sub read_config { + foreach my $conf (@searchconfigs) { + my $file = $conf->{"file"}; + + next if ( ! -f "$file"); + + if (defined($conf->{"test"})) { + `$conf->{"test"} $conf->{"file"} 2>/dev/null`; + next if ($?); + } + + my $exec = $conf->{"exec"}; + + print STDERR "using config: '$file'\n"; + + open(my $infile, '-|', "$exec $file") || die "Failed to run $exec $file"; + my @x = <$infile>; + close $infile; + return @x; + } + die "No config file found"; +} + +my @config_file = read_config; + +# Parse options +my $localmodconfig = 0; +my $localyesconfig = 0; + +GetOptions("localmodconfig" => \$localmodconfig, + "localyesconfig" => \$localyesconfig); + +# Get the build source and top level Kconfig file (passed in) +my $ksource = ($ARGV[0] ? $ARGV[0] : '.'); +my $kconfig = $ARGV[1]; +my $lsmod_file = $ENV{'LSMOD'}; + +my @makefiles = `find $ksource -name Makefile 2>/dev/null`; +chomp @makefiles; + +my %depends; +my %selects; +my %prompts; +my %objects; +my $var; +my $iflevel = 0; +my @ifdeps; + +# prevent recursion +my %read_kconfigs; + +sub read_kconfig { + my ($kconfig) = @_; + + my $state = "NONE"; + my $config; + + my $cont = 0; + my $line; + + my $source = "$ksource/$kconfig"; + my $last_source = ""; + + # Check for any environment variables used + while ($source =~ /\$(\w+)/ && $last_source ne $source) { + my $env = $1; + $last_source = $source; + $source =~ s/\$$env/$ENV{$env}/; + } + + open(my $kinfile, '<', $source) || die "Can't open $kconfig"; + while (<$kinfile>) { + chomp; + + # Make sure that lines ending with \ continue + if ($cont) { + $_ = $line . " " . $_; + } + + if (s/\\$//) { + $cont = 1; + $line = $_; + next; + } + + $cont = 0; + + # collect any Kconfig sources + if (/^source\s*"(.*)"/) { + my $kconfig = $1; + # prevent reading twice. + if (!defined($read_kconfigs{$kconfig})) { + $read_kconfigs{$kconfig} = 1; + read_kconfig($kconfig); + } + next; + } + + # configs found + if (/^\s*(menu)?config\s+(\S+)\s*$/) { + $state = "NEW"; + $config = $2; + + # Add depends for 'if' nesting + for (my $i = 0; $i < $iflevel; $i++) { + if ($i) { + $depends{$config} .= " " . $ifdeps[$i]; + } else { + $depends{$config} = $ifdeps[$i]; + } + $state = "DEP"; + } + + # collect the depends for the config + } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) { + $state = "DEP"; + $depends{$config} = $1; + } elsif ($state eq "DEP" && /^\s*depends\s+on\s+(.*)$/) { + $depends{$config} .= " " . $1; + } elsif ($state eq "DEP" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) { + my $dep = $3; + if ($dep !~ /^\s*(y|m|n)\s*$/) { + $dep =~ s/.*\sif\s+//; + $depends{$config} .= " " . $dep; + dprint "Added default depends $dep to $config\n"; + } + + # Get the configs that select this config + } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) { + my $conf = $1; + if (defined($selects{$conf})) { + $selects{$conf} .= " " . $config; + } else { + $selects{$conf} = $config; + } + + # configs without prompts must be selected + } elsif ($state ne "NONE" && /^\s*tristate\s\S/) { + # note if the config has a prompt + $prompts{$config} = 1; + + # Check for if statements + } elsif (/^if\s+(.*\S)\s*$/) { + my $deps = $1; + # remove beginning and ending non text + $deps =~ s/^[^a-zA-Z0-9_]*//; + $deps =~ s/[^a-zA-Z0-9_]*$//; + + my @deps = split /[^a-zA-Z0-9_]+/, $deps; + + $ifdeps[$iflevel++] = join ':', @deps; + + } elsif (/^endif/) { + + $iflevel-- if ($iflevel); + + # stop on "help" + } elsif (/^\s*help\s*$/) { + $state = "NONE"; + } + } + close($kinfile); +} + +if ($kconfig) { + read_kconfig($kconfig); +} + +# Makefiles can use variables to define their dependencies +sub convert_vars { + my ($line, %vars) = @_; + + my $process = ""; + + while ($line =~ s/^(.*?)(\$\((.*?)\))//) { + my $start = $1; + my $variable = $2; + my $var = $3; + + if (defined($vars{$var})) { + $process .= $start . $vars{$var}; + } else { + $process .= $start . $variable; + } + } + + $process .= $line; + + return $process; +} + +# Read all Makefiles to map the configs to the objects +foreach my $makefile (@makefiles) { + + my $line = ""; + my %make_vars; + + open(my $infile, '<', $makefile) || die "Can't open $makefile"; + while (<$infile>) { + # if this line ends with a backslash, continue + chomp; + if (/^(.*)\\$/) { + $line .= $1; + next; + } + + $line .= $_; + $_ = $line; + $line = ""; + + my $objs; + + # Convert variables in a line (could define configs) + $_ = convert_vars($_, %make_vars); + + # collect objects after obj-$(CONFIG_FOO_BAR) + if (/obj-\$\((CONFIG_[^\)]*)\)\s*[+:]?=\s*(.*)/) { + $var = $1; + $objs = $2; + + # check if variables are set + } elsif (/^\s*(\S+)\s*[:]?=\s*(.*\S)/) { + $make_vars{$1} = $2; + } + if (defined($objs)) { + foreach my $obj (split /\s+/,$objs) { + $obj =~ s/-/_/g; + if ($obj =~ /(.*)\.o$/) { + # Objects may be enabled by more than one config. + # Store configs in an array. + my @arr; + + if (defined($objects{$1})) { + @arr = @{$objects{$1}}; + } + + $arr[$#arr+1] = $var; + + # The objects have a hash mapping to a reference + # of an array of configs. + $objects{$1} = \@arr; + } + } + } + } + close($infile); +} + +my %modules; +my $linfile; + +if (defined($lsmod_file)) { + if ( ! -f $lsmod_file) { + if ( -f $ENV{'objtree'}."/".$lsmod_file) { + $lsmod_file = $ENV{'objtree'}."/".$lsmod_file; + } else { + die "$lsmod_file not found"; + } + } + + my $otype = ( -x $lsmod_file) ? '-|' : '<'; + open($linfile, $otype, $lsmod_file); + +} else { + + # see what modules are loaded on this system + my $lsmod; + + foreach my $dir ( ("/sbin", "/bin", "/usr/sbin", "/usr/bin") ) { + if ( -x "$dir/lsmod" ) { + $lsmod = "$dir/lsmod"; + last; + } +} + if (!defined($lsmod)) { + # try just the path + $lsmod = "lsmod"; + } + + open($linfile, '-|', $lsmod) || die "Can not call lsmod with $lsmod"; +} + +while (<$linfile>) { + next if (/^Module/); # Skip the first line. + if (/^(\S+)/) { + $modules{$1} = 1; + } +} +close ($linfile); + +# add to the configs hash all configs that are needed to enable +# a loaded module. This is a direct obj-${CONFIG_FOO} += bar.o +# where we know we need bar.o so we add FOO to the list. +my %configs; +foreach my $module (keys(%modules)) { + if (defined($objects{$module})) { + my @arr = @{$objects{$module}}; + foreach my $conf (@arr) { + $configs{$conf} = $module; + dprint "$conf added by direct ($module)\n"; + if ($debugprint) { + my $c=$conf; + $c =~ s/^CONFIG_//; + if (defined($depends{$c})) { + dprint " deps = $depends{$c}\n"; + } else { + dprint " no deps\n"; + } + } + } + } else { + # Most likely, someone has a custom (binary?) module loaded. + print STDERR "$module config not found!!\n"; + } +} + +# Read the current config, and see what is enabled. We want to +# ignore configs that we would not enable anyway. + +my %orig_configs; +my $valid = "A-Za-z_0-9"; + +foreach my $line (@config_file) { + $_ = $line; + + if (/(CONFIG_[$valid]*)=(m|y)/) { + $orig_configs{$1} = $2; + } +} + +my $repeat = 1; + +my $depconfig; + +# +# Note, we do not care about operands (like: &&, ||, !) we want to add any +# config that is in the depend list of another config. This script does +# not enable configs that are not already enabled. If we come across a +# config A that depends on !B, we can still add B to the list of depends +# to keep on. If A was on in the original config, B would not have been +# and B would not be turned on by this script. +# +sub parse_config_depends +{ + my ($p) = @_; + + while ($p =~ /[$valid]/) { + + if ($p =~ /^[^$valid]*([$valid]+)/) { + my $conf = "CONFIG_" . $1; + + $p =~ s/^[^$valid]*[$valid]+//; + + # We only need to process if the depend config is a module + if (!defined($orig_configs{$conf}) || !$orig_configs{conf} eq "m") { + next; + } + + if (!defined($configs{$conf})) { + # We must make sure that this config has its + # dependencies met. + $repeat = 1; # do again + dprint "$conf selected by depend $depconfig\n"; + $configs{$conf} = 1; + } + } else { + die "this should never happen"; + } + } +} + +# Select is treated a bit differently than depends. We call this +# when a config has no prompt and requires another config to be +# selected. We use to just select all configs that selected this +# config, but found that that can balloon into enabling hundreds +# of configs that we do not care about. +# +# The idea is we look at all the configs that select it. If one +# is already in our list of configs to enable, then there's nothing +# else to do. If there isn't, we pick the first config that was +# enabled in the orignal config and use that. +sub parse_config_selects +{ + my ($config, $p) = @_; + + my $next_config; + + while ($p =~ /[$valid]/) { + + if ($p =~ /^[^$valid]*([$valid]+)/) { + my $conf = "CONFIG_" . $1; + + $p =~ s/^[^$valid]*[$valid]+//; + + # Make sure that this config exists in the current .config file + if (!defined($orig_configs{$conf})) { + dprint "$conf not set for $config select\n"; + next; + } + + # Check if something other than a module selects this config + if (defined($orig_configs{$conf}) && $orig_configs{$conf} ne "m") { + dprint "$conf (non module) selects config, we are good\n"; + # we are good with this + return; + } + if (defined($configs{$conf})) { + dprint "$conf selects $config so we are good\n"; + # A set config selects this config, we are good + return; + } + # Set this config to be selected + if (!defined($next_config)) { + $next_config = $conf; + } + } else { + die "this should never happen"; + } + } + + # If no possible config selected this, then something happened. + if (!defined($next_config)) { + print STDERR "WARNING: $config is required, but nothing in the\n"; + print STDERR " current config selects it.\n"; + return; + } + + # If we are here, then we found no config that is set and + # selects this config. Repeat. + $repeat = 1; + # Make this config need to be selected + $configs{$next_config} = 1; + dprint "$next_config selected by select $config\n"; +} + +my %process_selects; + +# loop through all configs, select their dependencies. +sub loop_depend { + $repeat = 1; + + while ($repeat) { + $repeat = 0; + + forloop: + foreach my $config (keys %configs) { + + # If this config is not a module, we do not need to process it + if (defined($orig_configs{$config}) && $orig_configs{$config} ne "m") { + next forloop; + } + + $config =~ s/^CONFIG_//; + $depconfig = $config; + + if (defined($depends{$config})) { + # This config has dependencies. Make sure they are also included + parse_config_depends $depends{$config}; + } + + # If the config has no prompt, then we need to check if a config + # that is enabled selected it. Or if we need to enable one. + if (!defined($prompts{$config}) && defined($selects{$config})) { + $process_selects{$config} = 1; + } + } + } +} + +sub loop_select { + + foreach my $config (keys %process_selects) { + $config =~ s/^CONFIG_//; + + dprint "Process select $config\n"; + + # config has no prompt and must be selected. + parse_config_selects $config, $selects{$config}; + } +} + +while ($repeat) { + # Get the first set of configs and their dependencies. + loop_depend; + + $repeat = 0; + + # Now we need to see if we have to check selects; + loop_select; +} + +my %setconfigs; + +# Finally, read the .config file and turn off any module enabled that +# we could not find a reason to keep enabled. +foreach my $line (@config_file) { + $_ = $line; + + if (/CONFIG_IKCONFIG/) { + if (/# CONFIG_IKCONFIG is not set/) { + # enable IKCONFIG at least as a module + print "CONFIG_IKCONFIG=m\n"; + # don't ask about PROC + print "# CONFIG_IKCONFIG_PROC is not set\n"; + } else { + print; + } + next; + } + + if (/^(CONFIG.*)=(m|y)/) { + if (defined($configs{$1})) { + if ($localyesconfig) { + $setconfigs{$1} = 'y'; + print "$1=y\n"; + next; + } else { + $setconfigs{$1} = $2; + } + } elsif ($2 eq "m") { + print "# $1 is not set\n"; + next; + } + } + print; +} + +# Integrity check, make sure all modules that we want enabled do +# indeed have their configs set. +loop: +foreach my $module (keys(%modules)) { + if (defined($objects{$module})) { + my @arr = @{$objects{$module}}; + foreach my $conf (@arr) { + if (defined($setconfigs{$conf})) { + next loop; + } + } + print STDERR "module $module did not have configs"; + foreach my $conf (@arr) { + print STDERR " " , $conf; + } + print STDERR "\n"; + } +} diff --git a/qemu/roms/seabios/scripts/kconfig/symbol.c b/qemu/roms/seabios/scripts/kconfig/symbol.c new file mode 100644 index 000000000..7caabdb51 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/symbol.c @@ -0,0 +1,1373 @@ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> +#include <sys/utsname.h> + +#include "lkc.h" + +struct symbol symbol_yes = { + .name = "y", + .curr = { "y", yes }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_mod = { + .name = "m", + .curr = { "m", mod }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_no = { + .name = "n", + .curr = { "n", no }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_empty = { + .name = "", + .curr = { "", no }, + .flags = SYMBOL_VALID, +}; + +struct symbol *sym_defconfig_list; +struct symbol *modules_sym; +tristate modules_val; + +struct expr *sym_env_list; + +static void sym_add_default(struct symbol *sym, const char *def) +{ + struct property *prop = prop_alloc(P_DEFAULT, sym); + + prop->expr = expr_alloc_symbol(sym_lookup(def, SYMBOL_CONST)); +} + +void sym_init(void) +{ + struct symbol *sym; + struct utsname uts; + static bool inited = false; + + if (inited) + return; + inited = true; + + uname(&uts); + + sym = sym_lookup("UNAME_RELEASE", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + sym_add_default(sym, uts.release); +} + +enum symbol_type sym_get_type(struct symbol *sym) +{ + enum symbol_type type = sym->type; + + if (type == S_TRISTATE) { + if (sym_is_choice_value(sym) && sym->visible == yes) + type = S_BOOLEAN; + else if (modules_val == no) + type = S_BOOLEAN; + } + return type; +} + +const char *sym_type_name(enum symbol_type type) +{ + switch (type) { + case S_BOOLEAN: + return "boolean"; + case S_TRISTATE: + return "tristate"; + case S_INT: + return "integer"; + case S_HEX: + return "hex"; + case S_STRING: + return "string"; + case S_UNKNOWN: + return "unknown"; + case S_OTHER: + break; + } + return "???"; +} + +struct property *sym_get_choice_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_choices(sym, prop) + return prop; + return NULL; +} + +struct property *sym_get_env_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_ENV) + return prop; + return NULL; +} + +struct property *sym_get_default_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static struct property *sym_get_range_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_RANGE) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static long long sym_get_range_val(struct symbol *sym, int base) +{ + sym_calc_value(sym); + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + break; + } + return strtoll(sym->curr.val, NULL, base); +} + +static void sym_validate_range(struct symbol *sym) +{ + struct property *prop; + int base; + long long val, val2; + char str[64]; + + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + return; + } + prop = sym_get_range_prop(sym); + if (!prop) + return; + val = strtoll(sym->curr.val, NULL, base); + val2 = sym_get_range_val(prop->expr->left.sym, base); + if (val >= val2) { + val2 = sym_get_range_val(prop->expr->right.sym, base); + if (val <= val2) + return; + } + if (sym->type == S_INT) + sprintf(str, "%lld", val2); + else + sprintf(str, "0x%llx", val2); + sym->curr.val = strdup(str); +} + +static void sym_calc_visibility(struct symbol *sym) +{ + struct property *prop; + tristate tri; + + /* any prompt visible? */ + tri = no; + for_all_prompts(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + tri = EXPR_OR(tri, prop->visible.tri); + } + if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) + tri = yes; + if (sym->visible != tri) { + sym->visible = tri; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym)) + return; + /* defaulting to "yes" if no explicit "depends on" are given */ + tri = yes; + if (sym->dir_dep.expr) + tri = expr_calc_value(sym->dir_dep.expr); + if (tri == mod) + tri = yes; + if (sym->dir_dep.tri != tri) { + sym->dir_dep.tri = tri; + sym_set_changed(sym); + } + tri = no; + if (sym->rev_dep.expr) + tri = expr_calc_value(sym->rev_dep.expr); + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) + tri = yes; + if (sym->rev_dep.tri != tri) { + sym->rev_dep.tri = tri; + sym_set_changed(sym); + } +} + +/* + * Find the default symbol for a choice. + * First try the default values for the choice symbol + * Next locate the first visible choice value + * Return NULL if none was found + */ +struct symbol *sym_choice_default(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + + /* any of the defaults visible? */ + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri == no) + continue; + def_sym = prop_get_symbol(prop); + if (def_sym->visible != no) + return def_sym; + } + + /* just get the first visible value */ + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, def_sym) + if (def_sym->visible != no) + return def_sym; + + /* failed to locate any defaults */ + return NULL; +} + +static struct symbol *sym_calc_choice(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + int flags; + + /* first calculate all choice values' visibilities */ + flags = sym->flags; + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, def_sym) { + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + flags &= def_sym->flags; + } + + sym->flags &= flags | ~SYMBOL_DEF_USER; + + /* is the user choice visible? */ + def_sym = sym->def[S_DEF_USER].val; + if (def_sym && def_sym->visible != no) + return def_sym; + + def_sym = sym_choice_default(sym); + + if (def_sym == NULL) + /* no choice? reset tristate value */ + sym->curr.tri = no; + + return def_sym; +} + +void sym_calc_value(struct symbol *sym) +{ + struct symbol_value newval, oldval; + struct property *prop; + struct expr *e; + + if (!sym) + return; + + if (sym->flags & SYMBOL_VALID) + return; + + if (sym_is_choice_value(sym) && + sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) { + sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES; + prop = sym_get_choice_prop(sym); + sym_calc_value(prop_get_symbol(prop)); + } + + sym->flags |= SYMBOL_VALID; + + oldval = sym->curr; + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + newval = symbol_empty.curr; + break; + case S_BOOLEAN: + case S_TRISTATE: + newval = symbol_no.curr; + break; + default: + sym->curr.val = sym->name; + sym->curr.tri = no; + return; + } + if (!sym_is_choice_value(sym)) + sym->flags &= ~SYMBOL_WRITE; + + sym_calc_visibility(sym); + + /* set default if recursively called */ + sym->curr = newval; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_is_choice_value(sym) && sym->visible == yes) { + prop = sym_get_choice_prop(sym); + newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + } else { + if (sym->visible != no) { + /* if the symbol is visible use the user value + * if available, otherwise try the default value + */ + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri, + sym->visible); + goto calc_newval; + } + } + if (sym->rev_dep.tri != no) + sym->flags |= SYMBOL_WRITE; + if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) { + sym->flags |= SYMBOL_WRITE; + newval.tri = EXPR_AND(expr_calc_value(prop->expr), + prop->visible.tri); + } + } + calc_newval: + if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) { + struct expr *e; + e = expr_simplify_unmet_dep(sym->rev_dep.expr, + sym->dir_dep.expr); + fprintf(stderr, "warning: ("); + expr_fprint(e, stderr); + fprintf(stderr, ") selects %s which has unmet direct dependencies (", + sym->name); + expr_fprint(sym->dir_dep.expr, stderr); + fprintf(stderr, ")\n"); + expr_free(e); + } + newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); + } + if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) + newval.tri = yes; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (sym->visible != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.val = sym->def[S_DEF_USER].val; + break; + } + } + prop = sym_get_default_prop(sym); + if (prop) { + struct symbol *ds = prop_get_symbol(prop); + if (ds) { + sym->flags |= SYMBOL_WRITE; + sym_calc_value(ds); + newval.val = ds->curr.val; + } + } + break; + default: + ; + } + + sym->curr = newval; + if (sym_is_choice(sym) && newval.tri == yes) + sym->curr.val = sym_calc_choice(sym); + sym_validate_range(sym); + + if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { + sym_set_changed(sym); + if (modules_sym == sym) { + sym_set_all_changed(); + modules_val = modules_sym->curr.tri; + } + } + + if (sym_is_choice(sym)) { + struct symbol *choice_sym; + + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, choice_sym) { + if ((sym->flags & SYMBOL_WRITE) && + choice_sym->visible != no) + choice_sym->flags |= SYMBOL_WRITE; + if (sym->flags & SYMBOL_CHANGED) + sym_set_changed(choice_sym); + } + } + + if (sym->flags & SYMBOL_AUTO) + sym->flags &= ~SYMBOL_WRITE; + + if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) + set_all_choice_values(sym); +} + +void sym_clear_all_valid(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym->flags &= ~SYMBOL_VALID; + sym_add_change_count(1); + if (modules_sym) + sym_calc_value(modules_sym); +} + +void sym_set_changed(struct symbol *sym) +{ + struct property *prop; + + sym->flags |= SYMBOL_CHANGED; + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu) + prop->menu->flags |= MENU_CHANGED; + } +} + +void sym_set_all_changed(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym_set_changed(sym); +} + +bool sym_tristate_within_range(struct symbol *sym, tristate val) +{ + int type = sym_get_type(sym); + + if (sym->visible == no) + return false; + + if (type != S_BOOLEAN && type != S_TRISTATE) + return false; + + if (type == S_BOOLEAN && val == mod) + return false; + if (sym->visible <= sym->rev_dep.tri) + return false; + if (sym_is_choice_value(sym) && sym->visible == yes) + return val == yes; + return val >= sym->rev_dep.tri && val <= sym->visible; +} + +bool sym_set_tristate_value(struct symbol *sym, tristate val) +{ + tristate oldval = sym_get_tristate_value(sym); + + if (oldval != val && !sym_tristate_within_range(sym, val)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + /* + * setting a choice value also resets the new flag of the choice + * symbol and all other choice values. + */ + if (sym_is_choice_value(sym) && val == yes) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + struct property *prop; + struct expr *e; + + cs->def[S_DEF_USER].val = sym; + cs->flags |= SYMBOL_DEF_USER; + prop = sym_get_choice_prop(cs); + for (e = prop->expr; e; e = e->left.expr) { + if (e->right.sym->visible != no) + e->right.sym->flags |= SYMBOL_DEF_USER; + } + } + + sym->def[S_DEF_USER].tri = val; + if (oldval != val) + sym_clear_all_valid(); + + return true; +} + +tristate sym_toggle_tristate_value(struct symbol *sym) +{ + tristate oldval, newval; + + oldval = newval = sym_get_tristate_value(sym); + do { + switch (newval) { + case no: + newval = mod; + break; + case mod: + newval = yes; + break; + case yes: + newval = no; + break; + } + if (sym_set_tristate_value(sym, newval)) + break; + } while (oldval != newval); + return newval; +} + +bool sym_string_valid(struct symbol *sym, const char *str) +{ + signed char ch; + + switch (sym->type) { + case S_STRING: + return true; + case S_INT: + ch = *str++; + if (ch == '-') + ch = *str++; + if (!isdigit(ch)) + return false; + if (ch == '0' && *str != 0) + return false; + while ((ch = *str++)) { + if (!isdigit(ch)) + return false; + } + return true; + case S_HEX: + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + str += 2; + ch = *str++; + do { + if (!isxdigit(ch)) + return false; + } while ((ch = *str++)); + return true; + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + case 'm': case 'M': + case 'n': case 'N': + return true; + } + return false; + default: + return false; + } +} + +bool sym_string_within_range(struct symbol *sym, const char *str) +{ + struct property *prop; + long long val; + + switch (sym->type) { + case S_STRING: + return sym_string_valid(sym, str); + case S_INT: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtoll(str, NULL, 10); + return val >= sym_get_range_val(prop->expr->left.sym, 10) && + val <= sym_get_range_val(prop->expr->right.sym, 10); + case S_HEX: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtoll(str, NULL, 16); + return val >= sym_get_range_val(prop->expr->left.sym, 16) && + val <= sym_get_range_val(prop->expr->right.sym, 16); + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + return sym_tristate_within_range(sym, yes); + case 'm': case 'M': + return sym_tristate_within_range(sym, mod); + case 'n': case 'N': + return sym_tristate_within_range(sym, no); + } + return false; + default: + return false; + } +} + +bool sym_set_string_value(struct symbol *sym, const char *newval) +{ + const char *oldval; + char *val; + int size; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (newval[0]) { + case 'y': case 'Y': + return sym_set_tristate_value(sym, yes); + case 'm': case 'M': + return sym_set_tristate_value(sym, mod); + case 'n': case 'N': + return sym_set_tristate_value(sym, no); + } + return false; + default: + ; + } + + if (!sym_string_within_range(sym, newval)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + + oldval = sym->def[S_DEF_USER].val; + size = strlen(newval) + 1; + if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { + size += 2; + sym->def[S_DEF_USER].val = val = xmalloc(size); + *val++ = '0'; + *val++ = 'x'; + } else if (!oldval || strcmp(oldval, newval)) + sym->def[S_DEF_USER].val = val = xmalloc(size); + else + return true; + + strcpy(val, newval); + free((void *)oldval); + sym_clear_all_valid(); + + return true; +} + +/* + * Find the default value associated to a symbol. + * For tristate symbol handle the modules=n case + * in which case "m" becomes "y". + * If the symbol does not have any default then fallback + * to the fixed default values. + */ +const char *sym_get_string_default(struct symbol *sym) +{ + struct property *prop; + struct symbol *ds; + const char *str; + tristate val; + + sym_calc_visibility(sym); + sym_calc_value(modules_sym); + val = symbol_no.curr.tri; + str = symbol_empty.curr.val; + + /* If symbol has a default value look it up */ + prop = sym_get_default_prop(sym); + if (prop != NULL) { + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + /* The visibility may limit the value from yes => mod */ + val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); + break; + default: + /* + * The following fails to handle the situation + * where a default value is further limited by + * the valid range. + */ + ds = prop_get_symbol(prop); + if (ds != NULL) { + sym_calc_value(ds); + str = (const char *)ds->curr.val; + } + } + } + + /* Handle select statements */ + val = EXPR_OR(val, sym->rev_dep.tri); + + /* transpose mod to yes if modules are not enabled */ + if (val == mod) + if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no) + val = yes; + + /* transpose mod to yes if type is bool */ + if (sym->type == S_BOOLEAN && val == mod) + val = yes; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (val) { + case no: return "n"; + case mod: return "m"; + case yes: return "y"; + } + case S_INT: + case S_HEX: + return str; + case S_STRING: + return str; + case S_OTHER: + case S_UNKNOWN: + break; + } + return ""; +} + +const char *sym_get_string_value(struct symbol *sym) +{ + tristate val; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + return "n"; + case mod: + sym_calc_value(modules_sym); + return (modules_sym->curr.tri == no) ? "n" : "m"; + case yes: + return "y"; + } + break; + default: + ; + } + return (const char *)sym->curr.val; +} + +bool sym_is_changable(struct symbol *sym) +{ + return sym->visible > sym->rev_dep.tri; +} + +static unsigned strhash(const char *s) +{ + /* fnv32 hash */ + unsigned hash = 2166136261U; + for (; *s; s++) + hash = (hash ^ *s) * 0x01000193; + return hash; +} + +struct symbol *sym_lookup(const char *name, int flags) +{ + struct symbol *symbol; + char *new_name; + int hash; + + if (name) { + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + hash = strhash(name) % SYMBOL_HASHSIZE; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (symbol->name && + !strcmp(symbol->name, name) && + (flags ? symbol->flags & flags + : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE)))) + return symbol; + } + new_name = strdup(name); + } else { + new_name = NULL; + hash = 0; + } + + symbol = xmalloc(sizeof(*symbol)); + memset(symbol, 0, sizeof(*symbol)); + symbol->name = new_name; + symbol->type = S_UNKNOWN; + symbol->flags |= flags; + + symbol->next = symbol_hash[hash]; + symbol_hash[hash] = symbol; + + return symbol; +} + +struct symbol *sym_find(const char *name) +{ + struct symbol *symbol = NULL; + int hash = 0; + + if (!name) + return NULL; + + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + hash = strhash(name) % SYMBOL_HASHSIZE; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (symbol->name && + !strcmp(symbol->name, name) && + !(symbol->flags & SYMBOL_CONST)) + break; + } + + return symbol; +} + +/* + * Expand symbol's names embedded in the string given in argument. Symbols' + * name to be expanded shall be prefixed by a '$'. Unknown symbol expands to + * the empty string. + */ +const char *sym_expand_string_value(const char *in) +{ + const char *src; + char *res; + size_t reslen; + + reslen = strlen(in) + 1; + res = xmalloc(reslen); + res[0] = '\0'; + + while ((src = strchr(in, '$'))) { + char *p, name[SYMBOL_MAXLENGTH]; + const char *symval = ""; + struct symbol *sym; + size_t newlen; + + strncat(res, in, src - in); + src++; + + p = name; + while (isalnum(*src) || *src == '_') + *p++ = *src++; + *p = '\0'; + + sym = sym_find(name); + if (sym != NULL) { + sym_calc_value(sym); + symval = sym_get_string_value(sym); + } + + newlen = strlen(res) + strlen(symval) + strlen(src) + 1; + if (newlen > reslen) { + reslen = newlen; + res = realloc(res, reslen); + } + + strcat(res, symval); + in = src; + } + strcat(res, in); + + return res; +} + +const char *sym_escape_string_value(const char *in) +{ + const char *p; + size_t reslen; + char *res; + size_t l; + + reslen = strlen(in) + strlen("\"\"") + 1; + + p = in; + for (;;) { + l = strcspn(p, "\"\\"); + p += l; + + if (p[0] == '\0') + break; + + reslen++; + p++; + } + + res = xmalloc(reslen); + res[0] = '\0'; + + strcat(res, "\""); + + p = in; + for (;;) { + l = strcspn(p, "\"\\"); + strncat(res, p, l); + p += l; + + if (p[0] == '\0') + break; + + strcat(res, "\\"); + strncat(res, p++, 1); + } + + strcat(res, "\""); + return res; +} + +struct sym_match { + struct symbol *sym; + off_t so, eo; +}; + +/* Compare matched symbols as thus: + * - first, symbols that match exactly + * - then, alphabetical sort + */ +static int sym_rel_comp(const void *sym1, const void *sym2) +{ + const struct sym_match *s1 = sym1; + const struct sym_match *s2 = sym2; + int exact1, exact2; + + /* Exact match: + * - if matched length on symbol s1 is the length of that symbol, + * then this symbol should come first; + * - if matched length on symbol s2 is the length of that symbol, + * then this symbol should come first. + * Note: since the search can be a regexp, both symbols may match + * exactly; if this is the case, we can't decide which comes first, + * and we fallback to sorting alphabetically. + */ + exact1 = (s1->eo - s1->so) == strlen(s1->sym->name); + exact2 = (s2->eo - s2->so) == strlen(s2->sym->name); + if (exact1 && !exact2) + return -1; + if (!exact1 && exact2) + return 1; + + /* As a fallback, sort symbols alphabetically */ + return strcmp(s1->sym->name, s2->sym->name); +} + +struct symbol **sym_re_search(const char *pattern) +{ + struct symbol *sym, **sym_arr = NULL; + struct sym_match *sym_match_arr = NULL; + int i, cnt, size; + regex_t re; + regmatch_t match[1]; + + cnt = size = 0; + /* Skip if empty */ + if (strlen(pattern) == 0) + return NULL; + if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE)) + return NULL; + + for_all_symbols(i, sym) { + if (sym->flags & SYMBOL_CONST || !sym->name) + continue; + if (regexec(&re, sym->name, 1, match, 0)) + continue; + if (cnt >= size) { + void *tmp; + size += 16; + tmp = realloc(sym_match_arr, size * sizeof(struct sym_match)); + if (!tmp) + goto sym_re_search_free; + sym_match_arr = tmp; + } + sym_calc_value(sym); + /* As regexec returned 0, we know we have a match, so + * we can use match[0].rm_[se]o without further checks + */ + sym_match_arr[cnt].so = match[0].rm_so; + sym_match_arr[cnt].eo = match[0].rm_eo; + sym_match_arr[cnt++].sym = sym; + } + if (sym_match_arr) { + qsort(sym_match_arr, cnt, sizeof(struct sym_match), sym_rel_comp); + sym_arr = malloc((cnt+1) * sizeof(struct symbol)); + if (!sym_arr) + goto sym_re_search_free; + for (i = 0; i < cnt; i++) + sym_arr[i] = sym_match_arr[i].sym; + sym_arr[cnt] = NULL; + } +sym_re_search_free: + /* sym_match_arr can be NULL if no match, but free(NULL) is OK */ + free(sym_match_arr); + regfree(&re); + + return sym_arr; +} + +/* + * When we check for recursive dependencies we use a stack to save + * current state so we can print out relevant info to user. + * The entries are located on the call stack so no need to free memory. + * Note insert() remove() must always match to properly clear the stack. + */ +static struct dep_stack { + struct dep_stack *prev, *next; + struct symbol *sym; + struct property *prop; + struct expr *expr; +} *check_top; + +static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym) +{ + memset(stack, 0, sizeof(*stack)); + if (check_top) + check_top->next = stack; + stack->prev = check_top; + stack->sym = sym; + check_top = stack; +} + +static void dep_stack_remove(void) +{ + check_top = check_top->prev; + if (check_top) + check_top->next = NULL; +} + +/* + * Called when we have detected a recursive dependency. + * check_top point to the top of the stact so we use + * the ->prev pointer to locate the bottom of the stack. + */ +static void sym_check_print_recursive(struct symbol *last_sym) +{ + struct dep_stack *stack; + struct symbol *sym, *next_sym; + struct menu *menu = NULL; + struct property *prop; + struct dep_stack cv_stack; + + if (sym_is_choice_value(last_sym)) { + dep_stack_insert(&cv_stack, last_sym); + last_sym = prop_get_symbol(sym_get_choice_prop(last_sym)); + } + + for (stack = check_top; stack != NULL; stack = stack->prev) + if (stack->sym == last_sym) + break; + if (!stack) { + fprintf(stderr, "unexpected recursive dependency error\n"); + return; + } + + for (; stack; stack = stack->next) { + sym = stack->sym; + next_sym = stack->next ? stack->next->sym : last_sym; + prop = stack->prop; + if (prop == NULL) + prop = stack->sym->prop; + + /* for choice values find the menu entry (used below) */ + if (sym_is_choice(sym) || sym_is_choice_value(sym)) { + for (prop = sym->prop; prop; prop = prop->next) { + menu = prop->menu; + if (prop->menu) + break; + } + } + if (stack->sym == last_sym) + fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", + prop->file->name, prop->lineno); + if (stack->expr) { + fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "<choice>", + prop_get_type_name(prop->type), + next_sym->name ? next_sym->name : "<choice>"); + } else if (stack->prop) { + fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } else if (sym_is_choice(sym)) { + fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n", + menu->file->name, menu->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } else if (sym_is_choice_value(sym)) { + fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", + menu->file->name, menu->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } else { + fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "<choice>", + next_sym->name ? next_sym->name : "<choice>"); + } + } + + if (check_top == &cv_stack) + dep_stack_remove(); +} + +static struct symbol *sym_check_expr_deps(struct expr *e) +{ + struct symbol *sym; + + if (!e) + return NULL; + switch (e->type) { + case E_OR: + case E_AND: + sym = sym_check_expr_deps(e->left.expr); + if (sym) + return sym; + return sym_check_expr_deps(e->right.expr); + case E_NOT: + return sym_check_expr_deps(e->left.expr); + case E_EQUAL: + case E_UNEQUAL: + sym = sym_check_deps(e->left.sym); + if (sym) + return sym; + return sym_check_deps(e->right.sym); + case E_SYMBOL: + return sym_check_deps(e->left.sym); + default: + break; + } + printf("Oops! How to check %d?\n", e->type); + return NULL; +} + +/* return NULL when dependencies are OK */ +static struct symbol *sym_check_sym_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + struct dep_stack stack; + + dep_stack_insert(&stack, sym); + + sym2 = sym_check_expr_deps(sym->rev_dep.expr); + if (sym2) + goto out; + + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->type == P_CHOICE || prop->type == P_SELECT) + continue; + stack.prop = prop; + sym2 = sym_check_expr_deps(prop->visible.expr); + if (sym2) + break; + if (prop->type != P_DEFAULT || sym_is_choice(sym)) + continue; + stack.expr = prop->expr; + sym2 = sym_check_expr_deps(prop->expr); + if (sym2) + break; + stack.expr = NULL; + } + +out: + dep_stack_remove(); + + return sym2; +} + +static struct symbol *sym_check_choice_deps(struct symbol *choice) +{ + struct symbol *sym, *sym2; + struct property *prop; + struct expr *e; + struct dep_stack stack; + + dep_stack_insert(&stack, choice); + + prop = sym_get_choice_prop(choice); + expr_list_for_each_sym(prop->expr, e, sym) + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + + choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_sym_deps(choice); + choice->flags &= ~SYMBOL_CHECK; + if (sym2) + goto out; + + expr_list_for_each_sym(prop->expr, e, sym) { + sym2 = sym_check_sym_deps(sym); + if (sym2) + break; + } +out: + expr_list_for_each_sym(prop->expr, e, sym) + sym->flags &= ~SYMBOL_CHECK; + + if (sym2 && sym_is_choice_value(sym2) && + prop_get_symbol(sym_get_choice_prop(sym2)) == choice) + sym2 = choice; + + dep_stack_remove(); + + return sym2; +} + +struct symbol *sym_check_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + + if (sym->flags & SYMBOL_CHECK) { + sym_check_print_recursive(sym); + return sym; + } + if (sym->flags & SYMBOL_CHECKED) + return NULL; + + if (sym_is_choice_value(sym)) { + struct dep_stack stack; + + /* for choice groups start the check with main choice symbol */ + dep_stack_insert(&stack, sym); + prop = sym_get_choice_prop(sym); + sym2 = sym_check_deps(prop_get_symbol(prop)); + dep_stack_remove(); + } else if (sym_is_choice(sym)) { + sym2 = sym_check_choice_deps(sym); + } else { + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_sym_deps(sym); + sym->flags &= ~SYMBOL_CHECK; + } + + if (sym2 && sym2 == sym) + sym2 = NULL; + + return sym2; +} + +struct property *prop_alloc(enum prop_type type, struct symbol *sym) +{ + struct property *prop; + struct property **propp; + + prop = xmalloc(sizeof(*prop)); + memset(prop, 0, sizeof(*prop)); + prop->type = type; + prop->sym = sym; + prop->file = current_file; + prop->lineno = zconf_lineno(); + + /* append property to the prop list of symbol */ + if (sym) { + for (propp = &sym->prop; *propp; propp = &(*propp)->next) + ; + *propp = prop; + } + + return prop; +} + +struct symbol *prop_get_symbol(struct property *prop) +{ + if (prop->expr && (prop->expr->type == E_SYMBOL || + prop->expr->type == E_LIST)) + return prop->expr->left.sym; + return NULL; +} + +const char *prop_get_type_name(enum prop_type type) +{ + switch (type) { + case P_PROMPT: + return "prompt"; + case P_ENV: + return "env"; + case P_COMMENT: + return "comment"; + case P_MENU: + return "menu"; + case P_DEFAULT: + return "default"; + case P_CHOICE: + return "choice"; + case P_SELECT: + return "select"; + case P_RANGE: + return "range"; + case P_SYMBOL: + return "symbol"; + case P_UNKNOWN: + break; + } + return "unknown"; +} + +static void prop_add_env(const char *env) +{ + struct symbol *sym, *sym2; + struct property *prop; + char *p; + + sym = current_entry->sym; + sym->flags |= SYMBOL_AUTO; + for_all_properties(sym, prop, P_ENV) { + sym2 = prop_get_symbol(prop); + if (strcmp(sym2->name, env)) + menu_warn(current_entry, "redefining environment symbol from %s", + sym2->name); + return; + } + + prop = prop_alloc(P_ENV, sym); + prop->expr = expr_alloc_symbol(sym_lookup(env, SYMBOL_CONST)); + + sym_env_list = expr_alloc_one(E_LIST, sym_env_list); + sym_env_list->right.sym = sym; + + p = getenv(env); + if (p) + sym_add_default(sym, p); + else + menu_warn(current_entry, "environment variable %s undefined", env); +} diff --git a/qemu/roms/seabios/scripts/kconfig/util.c b/qemu/roms/seabios/scripts/kconfig/util.c new file mode 100644 index 000000000..94f9c83e3 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/util.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org> + * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org> + * + * Released under the terms of the GNU GPL v2.0. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include "lkc.h" + +/* file already present in list? If not add it */ +struct file *file_lookup(const char *name) +{ + struct file *file; + const char *file_name = sym_expand_string_value(name); + + for (file = file_list; file; file = file->next) { + if (!strcmp(name, file->name)) { + free((void *)file_name); + return file; + } + } + + file = xmalloc(sizeof(*file)); + memset(file, 0, sizeof(*file)); + file->name = file_name; + file->next = file_list; + file_list = file; + return file; +} + +/* write a dependency file as used by kbuild to track dependencies */ +int file_write_dep(const char *name) +{ + struct symbol *sym, *env_sym; + struct expr *e; + struct file *file; + FILE *out; + + if (!name) + name = ".kconfig.d"; + out = fopen("..config.tmp", "w"); + if (!out) + return 1; + fprintf(out, "deps_config := \\\n"); + for (file = file_list; file; file = file->next) { + if (file->next) + fprintf(out, "\t%s \\\n", file->name); + else + fprintf(out, "\t%s\n", file->name); + } + fprintf(out, "\n%s: \\\n" + "\t$(deps_config)\n\n", conf_get_autoconfig_name()); + + expr_list_for_each_sym(sym_env_list, e, sym) { + struct property *prop; + const char *value; + + prop = sym_get_env_prop(sym); + env_sym = prop_get_symbol(prop); + if (!env_sym) + continue; + value = getenv(env_sym->name); + if (!value) + value = ""; + fprintf(out, "ifneq \"$(%s)\" \"%s\"\n", env_sym->name, value); + fprintf(out, "%s: FORCE\n", conf_get_autoconfig_name()); + fprintf(out, "endif\n"); + } + + fprintf(out, "\n$(deps_config): ;\n"); + fclose(out); + rename("..config.tmp", name); + return 0; +} + + +/* Allocate initial growable string */ +struct gstr str_new(void) +{ + struct gstr gs; + gs.s = xmalloc(sizeof(char) * 64); + gs.len = 64; + gs.max_width = 0; + strcpy(gs.s, "\0"); + return gs; +} + +/* Allocate and assign growable string */ +struct gstr str_assign(const char *s) +{ + struct gstr gs; + gs.s = strdup(s); + gs.len = strlen(s) + 1; + gs.max_width = 0; + return gs; +} + +/* Free storage for growable string */ +void str_free(struct gstr *gs) +{ + if (gs->s) + free(gs->s); + gs->s = NULL; + gs->len = 0; +} + +/* Append to growable string */ +void str_append(struct gstr *gs, const char *s) +{ + size_t l; + if (s) { + l = strlen(gs->s) + strlen(s) + 1; + if (l > gs->len) { + gs->s = realloc(gs->s, l); + gs->len = l; + } + strcat(gs->s, s); + } +} + +/* Append printf formatted string to growable string */ +void str_printf(struct gstr *gs, const char *fmt, ...) +{ + va_list ap; + char s[10000]; /* big enough... */ + va_start(ap, fmt); + vsnprintf(s, sizeof(s), fmt, ap); + str_append(gs, s); + va_end(ap); +} + +/* Retrieve value of growable string */ +const char *str_get(struct gstr *gs) +{ + return gs->s; +} + +void *xmalloc(size_t size) +{ + void *p = malloc(size); + if (p) + return p; + fprintf(stderr, "Out of memory.\n"); + exit(1); +} + +void *xcalloc(size_t nmemb, size_t size) +{ + void *p = calloc(nmemb, size); + if (p) + return p; + fprintf(stderr, "Out of memory.\n"); + exit(1); +} diff --git a/qemu/roms/seabios/scripts/kconfig/zconf.gperf b/qemu/roms/seabios/scripts/kconfig/zconf.gperf new file mode 100644 index 000000000..b6ac02d60 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/zconf.gperf @@ -0,0 +1,48 @@ +%language=ANSI-C +%define hash-function-name kconf_id_hash +%define lookup-function-name kconf_id_lookup +%define string-pool-name kconf_id_strings +%compare-strncmp +%enum +%pic +%struct-type + +struct kconf_id; + +static const struct kconf_id *kconf_id_lookup(register const char *str, register unsigned int len); + +%% +mainmenu, T_MAINMENU, TF_COMMAND +menu, T_MENU, TF_COMMAND +endmenu, T_ENDMENU, TF_COMMAND +source, T_SOURCE, TF_COMMAND +choice, T_CHOICE, TF_COMMAND +endchoice, T_ENDCHOICE, TF_COMMAND +comment, T_COMMENT, TF_COMMAND +config, T_CONFIG, TF_COMMAND +menuconfig, T_MENUCONFIG, TF_COMMAND +help, T_HELP, TF_COMMAND +if, T_IF, TF_COMMAND|TF_PARAM +endif, T_ENDIF, TF_COMMAND +depends, T_DEPENDS, TF_COMMAND +optional, T_OPTIONAL, TF_COMMAND +default, T_DEFAULT, TF_COMMAND, S_UNKNOWN +prompt, T_PROMPT, TF_COMMAND +tristate, T_TYPE, TF_COMMAND, S_TRISTATE +def_tristate, T_DEFAULT, TF_COMMAND, S_TRISTATE +bool, T_TYPE, TF_COMMAND, S_BOOLEAN +boolean, T_TYPE, TF_COMMAND, S_BOOLEAN +def_bool, T_DEFAULT, TF_COMMAND, S_BOOLEAN +int, T_TYPE, TF_COMMAND, S_INT +hex, T_TYPE, TF_COMMAND, S_HEX +string, T_TYPE, TF_COMMAND, S_STRING +select, T_SELECT, TF_COMMAND +range, T_RANGE, TF_COMMAND +visible, T_VISIBLE, TF_COMMAND +option, T_OPTION, TF_COMMAND +on, T_ON, TF_PARAM +modules, T_OPT_MODULES, TF_OPTION +defconfig_list, T_OPT_DEFCONFIG_LIST,TF_OPTION +env, T_OPT_ENV, TF_OPTION +allnoconfig_y, T_OPT_ALLNOCONFIG_Y,TF_OPTION +%% diff --git a/qemu/roms/seabios/scripts/kconfig/zconf.hash.c_shipped b/qemu/roms/seabios/scripts/kconfig/zconf.hash.c_shipped new file mode 100644 index 000000000..c77a8eff1 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/zconf.hash.c_shipped @@ -0,0 +1,289 @@ +/* ANSI-C code produced by gperf version 3.0.4 */ +/* Command-line: gperf -t --output-file scripts/kconfig/zconf.hash.c_shipped -a -C -E -g -k '1,3,$' -p -t scripts/kconfig/zconf.gperf */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." +#endif + +#line 10 "scripts/kconfig/zconf.gperf" +struct kconf_id; + +static const struct kconf_id *kconf_id_lookup(register const char *str, register unsigned int len); +/* maximum key range = 71, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +kconf_id_hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 5, 25, 25, + 0, 0, 0, 5, 0, 0, 73, 73, 5, 0, + 10, 5, 45, 73, 20, 20, 0, 15, 15, 73, + 20, 5, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval + asso_values[(unsigned char)str[len - 1]]; +} + +struct kconf_id_strings_t + { + char kconf_id_strings_str2[sizeof("if")]; + char kconf_id_strings_str3[sizeof("int")]; + char kconf_id_strings_str5[sizeof("endif")]; + char kconf_id_strings_str7[sizeof("default")]; + char kconf_id_strings_str8[sizeof("tristate")]; + char kconf_id_strings_str9[sizeof("endchoice")]; + char kconf_id_strings_str12[sizeof("def_tristate")]; + char kconf_id_strings_str13[sizeof("def_bool")]; + char kconf_id_strings_str14[sizeof("defconfig_list")]; + char kconf_id_strings_str17[sizeof("on")]; + char kconf_id_strings_str18[sizeof("optional")]; + char kconf_id_strings_str21[sizeof("option")]; + char kconf_id_strings_str22[sizeof("endmenu")]; + char kconf_id_strings_str23[sizeof("mainmenu")]; + char kconf_id_strings_str25[sizeof("menuconfig")]; + char kconf_id_strings_str27[sizeof("modules")]; + char kconf_id_strings_str28[sizeof("allnoconfig_y")]; + char kconf_id_strings_str29[sizeof("menu")]; + char kconf_id_strings_str31[sizeof("select")]; + char kconf_id_strings_str32[sizeof("comment")]; + char kconf_id_strings_str33[sizeof("env")]; + char kconf_id_strings_str35[sizeof("range")]; + char kconf_id_strings_str36[sizeof("choice")]; + char kconf_id_strings_str39[sizeof("bool")]; + char kconf_id_strings_str41[sizeof("source")]; + char kconf_id_strings_str42[sizeof("visible")]; + char kconf_id_strings_str43[sizeof("hex")]; + char kconf_id_strings_str46[sizeof("config")]; + char kconf_id_strings_str47[sizeof("boolean")]; + char kconf_id_strings_str51[sizeof("string")]; + char kconf_id_strings_str54[sizeof("help")]; + char kconf_id_strings_str56[sizeof("prompt")]; + char kconf_id_strings_str72[sizeof("depends")]; + }; +static const struct kconf_id_strings_t kconf_id_strings_contents = + { + "if", + "int", + "endif", + "default", + "tristate", + "endchoice", + "def_tristate", + "def_bool", + "defconfig_list", + "on", + "optional", + "option", + "endmenu", + "mainmenu", + "menuconfig", + "modules", + "allnoconfig_y", + "menu", + "select", + "comment", + "env", + "range", + "choice", + "bool", + "source", + "visible", + "hex", + "config", + "boolean", + "string", + "help", + "prompt", + "depends" + }; +#define kconf_id_strings ((const char *) &kconf_id_strings_contents) +#ifdef __GNUC__ +__inline +#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct kconf_id * +kconf_id_lookup (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 33, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 14, + MIN_HASH_VALUE = 2, + MAX_HASH_VALUE = 72 + }; + + static const struct kconf_id wordlist[] = + { + {-1}, {-1}, +#line 25 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM}, +#line 36 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT}, + {-1}, +#line 26 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND}, + {-1}, +#line 29 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, +#line 31 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE}, +#line 20 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND}, + {-1}, {-1}, +#line 32 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_TRISTATE}, +#line 35 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, +#line 45 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_OPT_DEFCONFIG_LIST,TF_OPTION}, + {-1}, {-1}, +#line 43 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_ON, TF_PARAM}, +#line 28 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_OPTIONAL, TF_COMMAND}, + {-1}, {-1}, +#line 42 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_OPTION, TF_COMMAND}, +#line 17 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ENDMENU, TF_COMMAND}, +#line 15 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_MAINMENU, TF_COMMAND}, + {-1}, +#line 23 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str25, T_MENUCONFIG, TF_COMMAND}, + {-1}, +#line 44 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_OPT_MODULES, TF_OPTION}, +#line 47 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPT_ALLNOCONFIG_Y,TF_OPTION}, +#line 16 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str29, T_MENU, TF_COMMAND}, + {-1}, +#line 39 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND}, +#line 21 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND}, +#line 46 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_OPT_ENV, TF_OPTION}, + {-1}, +#line 40 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str35, T_RANGE, TF_COMMAND}, +#line 19 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str36, T_CHOICE, TF_COMMAND}, + {-1}, {-1}, +#line 33 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str39, T_TYPE, TF_COMMAND, S_BOOLEAN}, + {-1}, +#line 18 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_SOURCE, TF_COMMAND}, +#line 41 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str42, T_VISIBLE, TF_COMMAND}, +#line 37 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str43, T_TYPE, TF_COMMAND, S_HEX}, + {-1}, {-1}, +#line 22 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_CONFIG, TF_COMMAND}, +#line 34 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str47, T_TYPE, TF_COMMAND, S_BOOLEAN}, + {-1}, {-1}, {-1}, +#line 38 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str51, T_TYPE, TF_COMMAND, S_STRING}, + {-1}, {-1}, +#line 24 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str54, T_HELP, TF_COMMAND}, + {-1}, +#line 30 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str56, T_PROMPT, TF_COMMAND}, + {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, + {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, +#line 27 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str72, T_DEPENDS, TF_COMMAND} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = kconf_id_hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register int o = wordlist[key].name; + if (o >= 0) + { + register const char *s = o + kconf_id_strings; + + if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') + return &wordlist[key]; + } + } + } + return 0; +} +#line 48 "scripts/kconfig/zconf.gperf" + diff --git a/qemu/roms/seabios/scripts/kconfig/zconf.l b/qemu/roms/seabios/scripts/kconfig/zconf.l new file mode 100644 index 000000000..6c62d93b4 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/zconf.l @@ -0,0 +1,363 @@ +%option nostdinit noyywrap never-interactive full ecs +%option 8bit nodefault perf-report perf-report +%option noinput +%x COMMAND HELP STRING PARAM +%{ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +static void new_string(void) +{ + text = xmalloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +static void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +static void alloc_string(const char *str, int size) +{ + text = xmalloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} +%} + +n [A-Za-z0-9_] + +%% + int str = 0; + int ts, i; + +[ \t]*#.*\n | +[ \t]*\n { + current_file->lineno++; + return T_EOL; +} +[ \t]*#.* + + +[ \t]+ { + BEGIN(COMMAND); +} + +. { + unput(yytext[0]); + BEGIN(COMMAND); +} + + +<COMMAND>{ + {n}+ { + const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + . + \n { + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } +} + +<PARAM>{ + "&&" return T_AND; + "||" return T_OR; + "(" return T_OPEN_PAREN; + ")" return T_CLOSE_PAREN; + "!" return T_NOT; + "=" return T_EQUAL; + "!=" return T_UNEQUAL; + \"|\' { + str = yytext[0]; + new_string(); + BEGIN(STRING); + } + \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; + --- /* ignore */ + ({n}|[-/.])+ { + const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + #.* /* comment */ + \\\n current_file->lineno++; + . + <<EOF>> { + BEGIN(INITIAL); + } +} + +<STRING>{ + [^'"\\\n]+/\n { + append_string(yytext, yyleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + [^'"\\\n]+ { + append_string(yytext, yyleng); + } + \\.?/\n { + append_string(yytext + 1, yyleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + \\.? { + append_string(yytext + 1, yyleng - 1); + } + \'|\" { + if (str == yytext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(yytext, 1); + } + \n { + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + <<EOF>> { + BEGIN(INITIAL); + } +} + +<HELP>{ + [ \t]+ { + ts = 0; + for (i = 0; i < yyleng; i++) { + if (yytext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + [ \t]*\n/[^ \t\n] { + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + [ \t]*\n { + current_file->lineno++; + append_string("\n", 1); + } + [^ \t\n].* { + while (yyleng) { + if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t')) + break; + yyleng--; + } + append_string(yytext, yyleng); + if (!first_ts) + first_ts = last_ts; + } + <<EOF>> { + zconf_endhelp(); + return T_HELPTEXT; + } +} + +<<EOF>> { + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(yyin); + yyterminate(); +} + +%% +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + yyin = zconf_fopen(name); + if (!yyin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = xmalloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; +} + +void zconf_nextfile(const char *name) +{ + struct file *iter; + struct file *file = file_lookup(name); + struct buffer *buf = xmalloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + yyin = zconf_fopen(file->name); + if (!yyin) { + printf("%s:%d: can't open file \"%s\"\n", + zconf_curname(), zconf_lineno(), file->name); + exit(1); + } + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + for (iter = current_file->parent; iter; iter = iter->parent ) { + if (!strcmp(current_file->name,iter->name) ) { + printf("%s:%d: recursive inclusion detected. " + "Inclusion path:\n current file : '%s'\n", + zconf_curname(), zconf_lineno(), + zconf_curname()); + iter = current_file->parent; + while (iter && \ + strcmp(iter->name,current_file->name)) { + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno-1); + iter = iter->parent; + } + if (iter) + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno+1); + exit(1); + } + } + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +const char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : "<none>"; +} diff --git a/qemu/roms/seabios/scripts/kconfig/zconf.lex.c_shipped b/qemu/roms/seabios/scripts/kconfig/zconf.lex.c_shipped new file mode 100644 index 000000000..349a7f243 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/zconf.lex.c_shipped @@ -0,0 +1,2420 @@ + +#line 3 "scripts/kconfig/zconf.lex.c_shipped" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer zconf_create_buffer +#define yy_delete_buffer zconf_delete_buffer +#define yy_flex_debug zconf_flex_debug +#define yy_init_buffer zconf_init_buffer +#define yy_flush_buffer zconf_flush_buffer +#define yy_load_buffer_state zconf_load_buffer_state +#define yy_switch_to_buffer zconf_switch_to_buffer +#define yyin zconfin +#define yyleng zconfleng +#define yylex zconflex +#define yylineno zconflineno +#define yyout zconfout +#define yyrestart zconfrestart +#define yytext zconftext +#define yywrap zconfwrap +#define yyalloc zconfalloc +#define yyrealloc zconfrealloc +#define yyfree zconffree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE zconfrestart(zconfin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int zconfleng; + +extern FILE *zconfin, *zconfout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up zconftext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via zconfrestart()), so that the user can continue scanning by + * just pointing zconfin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when zconftext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int zconfleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow zconfwrap()'s to do buffer switches + * instead of setting up a fresh zconfin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void zconfrestart (FILE *input_file ); +void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size ); +void zconf_delete_buffer (YY_BUFFER_STATE b ); +void zconf_flush_buffer (YY_BUFFER_STATE b ); +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void zconfpop_buffer_state (void ); + +static void zconfensure_buffer_stack (void ); +static void zconf_load_buffer_state (void ); +static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len ); + +void *zconfalloc (yy_size_t ); +void *zconfrealloc (void *,yy_size_t ); +void zconffree (void * ); + +#define yy_new_buffer zconf_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define zconfwrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0; + +typedef int yy_state_type; + +extern int zconflineno; + +int zconflineno = 1; + +extern char *zconftext; +#define yytext_ptr zconftext +static yyconst flex_int16_t yy_nxt[][17] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 16 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 18, 16, 16, 16 + + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, + 33, 34, 35, 35, 36, 37, 38 + + }, + + { + 11, 26, 26, 27, 28, 29, 30, 31, 29, 32, + 33, 34, 35, 35, 36, 37, 38 + }, + + { + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11 + }, + + { + 11, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12 + }, + + { + 11, -13, 39, 40, -13, -13, 41, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13 + }, + + { + 11, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14 + + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16 + }, + + { + 11, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17 + }, + + { + 11, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, -18, -18, 44, -18, -18, -18 + }, + + { + 11, 45, 45, -19, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45 + + }, + + { + 11, -20, 46, 47, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20 + }, + + { + 11, 48, -21, -21, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48 + }, + + { + 11, 49, 49, 50, 49, -22, 49, 49, -22, 49, + 49, 49, 49, 49, 49, -22, 49 + }, + + { + 11, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23 + }, + + { + 11, -24, -24, -24, -24, -24, -24, -24, -24, -24, + -24, -24, -24, -24, -24, -24, -24 + + }, + + { + 11, 51, 51, 52, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51 + }, + + { + 11, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26 + }, + + { + 11, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27 + }, + + { + 11, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, 53, -28, -28 + }, + + { + 11, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29 + + }, + + { + 11, 54, 54, -30, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54 + }, + + { + 11, -31, -31, -31, -31, -31, -31, 55, -31, -31, + -31, -31, -31, -31, -31, -31, -31 + }, + + { + 11, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32 + }, + + { + 11, -33, -33, -33, -33, -33, -33, -33, -33, -33, + -33, -33, -33, -33, -33, -33, -33 + }, + + { + 11, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, 56, 57, 57, -34, -34, -34 + + }, + + { + 11, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, 57, 57, 57, -35, -35, -35 + }, + + { + 11, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36 + }, + + { + 11, -37, -37, 58, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37 + }, + + { + 11, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, 59 + }, + + { + 11, -39, 39, 40, -39, -39, 41, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39 + + }, + + { + 11, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40 + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, 42, 42, 43, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42 + }, + + { + 11, -43, -43, -43, -43, -43, -43, -43, -43, -43, + -43, -43, -43, -43, -43, -43, -43 + }, + + { + 11, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, 44, -44, -44, -44 + + }, + + { + 11, 45, 45, -45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45 + }, + + { + 11, -46, 46, 47, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46 + }, + + { + 11, 48, -47, -47, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48 + }, + + { + 11, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48 + }, + + { + 11, 49, 49, 50, 49, -49, 49, 49, -49, 49, + 49, 49, 49, 49, 49, -49, 49 + + }, + + { + 11, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50 + }, + + { + 11, -51, -51, 52, -51, -51, -51, -51, -51, -51, + -51, -51, -51, -51, -51, -51, -51 + }, + + { + 11, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52 + }, + + { + 11, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53 + }, + + { + 11, 54, 54, -54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54 + + }, + + { + 11, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55 + }, + + { + 11, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, 60, 57, 57, -56, -56, -56 + }, + + { + 11, -57, -57, -57, -57, -57, -57, -57, -57, -57, + -57, 57, 57, 57, -57, -57, -57 + }, + + { + 11, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58 + }, + + { + 11, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59 + + }, + + { + 11, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, 57, 57, 57, -60, -60, -60 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up zconftext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + zconfleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 33 +#define YY_END_OF_BUFFER 34 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[61] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 34, 5, 4, 2, 3, 7, 8, 6, 32, 29, + 31, 24, 28, 27, 26, 22, 17, 13, 16, 20, + 22, 11, 12, 19, 19, 14, 22, 22, 4, 2, + 3, 3, 1, 6, 32, 29, 31, 30, 24, 23, + 26, 25, 15, 20, 9, 19, 19, 21, 10, 18 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 7, 8, 9, + 10, 1, 1, 1, 11, 12, 12, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, + 14, 1, 1, 1, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 15, 1, 1, 13, 1, 13, 13, 13, 13, + + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 1, 16, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int zconf_flex_debug; +int zconf_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *zconftext; +#define YY_NO_INPUT 1 + +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +static void new_string(void) +{ + text = xmalloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +static void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +static void alloc_string(const char *str, int size) +{ + text = xmalloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} + +#define INITIAL 0 +#define COMMAND 1 +#define HELP 2 +#define STRING 3 +#define PARAM 4 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int zconflex_destroy (void ); + +int zconfget_debug (void ); + +void zconfset_debug (int debug_flag ); + +YY_EXTRA_TYPE zconfget_extra (void ); + +void zconfset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *zconfget_in (void ); + +void zconfset_in (FILE * in_str ); + +FILE *zconfget_out (void ); + +void zconfset_out (FILE * out_str ); + +int zconfget_leng (void ); + +char *zconfget_text (void ); + +int zconfget_lineno (void ); + +void zconfset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int zconfwrap (void ); +#else +extern int zconfwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( zconftext, zconfleng, 1, zconfout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(zconfin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int zconflex (void); + +#define YY_DECL int zconflex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after zconftext and zconfleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + int str = 0; + int ts, i; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! zconfin ) + zconfin = stdin; + + if ( ! zconfout ) + zconfout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of zconftext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +/* rule 1 can match eol */ +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + return T_EOL; +} + YY_BREAK +case 3: +YY_RULE_SETUP + + YY_BREAK +case 4: +YY_RULE_SETUP +{ + BEGIN(COMMAND); +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ + unput(zconftext[0]); + BEGIN(COMMAND); +} + YY_BREAK + +case 6: +YY_RULE_SETUP +{ + const struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 7: +YY_RULE_SETUP + + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +{ + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } + YY_BREAK + +case 9: +YY_RULE_SETUP +return T_AND; + YY_BREAK +case 10: +YY_RULE_SETUP +return T_OR; + YY_BREAK +case 11: +YY_RULE_SETUP +return T_OPEN_PAREN; + YY_BREAK +case 12: +YY_RULE_SETUP +return T_CLOSE_PAREN; + YY_BREAK +case 13: +YY_RULE_SETUP +return T_NOT; + YY_BREAK +case 14: +YY_RULE_SETUP +return T_EQUAL; + YY_BREAK +case 15: +YY_RULE_SETUP +return T_UNEQUAL; + YY_BREAK +case 16: +YY_RULE_SETUP +{ + str = zconftext[0]; + new_string(); + BEGIN(STRING); + } + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +BEGIN(INITIAL); current_file->lineno++; return T_EOL; + YY_BREAK +case 18: +YY_RULE_SETUP +/* ignore */ + YY_BREAK +case 19: +YY_RULE_SETUP +{ + const struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 20: +YY_RULE_SETUP +/* comment */ + YY_BREAK +case 21: +/* rule 21 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 22: +YY_RULE_SETUP + + YY_BREAK +case YY_STATE_EOF(PARAM): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 23: +/* rule 23 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 24: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + } + YY_BREAK +case 25: +/* rule 25 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 26: +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + } + YY_BREAK +case 27: +YY_RULE_SETUP +{ + if (str == zconftext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(zconftext, 1); + } + YY_BREAK +case 28: +/* rule 28 can match eol */ +YY_RULE_SETUP +{ + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + YY_BREAK +case YY_STATE_EOF(STRING): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 29: +YY_RULE_SETUP +{ + ts = 0; + for (i = 0; i < zconfleng; i++) { + if (zconftext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + YY_BREAK +case 30: +/* rule 30 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK +case 31: +/* rule 31 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + append_string("\n", 1); + } + YY_BREAK +case 32: +YY_RULE_SETUP +{ + while (zconfleng) { + if ((zconftext[zconfleng-1] != ' ') && (zconftext[zconfleng-1] != '\t')) + break; + zconfleng--; + } + append_string(zconftext, zconfleng); + if (!first_ts) + first_ts = last_ts; + } + YY_BREAK +case YY_STATE_EOF(HELP): +{ + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK + +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(COMMAND): +{ + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(zconfin); + yyterminate(); +} + YY_BREAK +case 33: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed zconfin at a new source and called + * zconflex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( zconfwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * zconftext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of zconflex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + zconfrestart(zconfin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) zconfrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up zconftext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + zconfrestart(zconfin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( zconfwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve zconftext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void zconfrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_init_buffer(YY_CURRENT_BUFFER,input_file ); + zconf_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * zconfpop_buffer_state(); + * zconfpush_buffer_state(new_buffer); + */ + zconfensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + zconf_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (zconfwrap()) processing, but the only time this flag + * is looked at is after zconfwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void zconf_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + zconf_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with zconf_create_buffer() + * + */ + void zconf_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + zconffree((void *) b->yy_ch_buf ); + + zconffree((void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a zconfrestart() or at EOF. + */ + static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + zconf_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then zconf_init_buffer was _probably_ + * called from zconfrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void zconf_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + zconf_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + zconfensure_buffer_stack(); + + /* This block is copied from zconf_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from zconf_switch_to_buffer. */ + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void zconfpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void zconfensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + zconf_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to zconflex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * zconf_scan_bytes() instead. + */ +YY_BUFFER_STATE zconf_scan_string (yyconst char * yystr ) +{ + + return zconf_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) zconfalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = zconf_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + zconftext[zconfleng] = (yy_hold_char); \ + (yy_c_buf_p) = zconftext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + zconfleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int zconfget_lineno (void) +{ + + return zconflineno; +} + +/** Get the input stream. + * + */ +FILE *zconfget_in (void) +{ + return zconfin; +} + +/** Get the output stream. + * + */ +FILE *zconfget_out (void) +{ + return zconfout; +} + +/** Get the length of the current token. + * + */ +int zconfget_leng (void) +{ + return zconfleng; +} + +/** Get the current token. + * + */ + +char *zconfget_text (void) +{ + return zconftext; +} + +/** Set the current line number. + * @param line_number + * + */ +void zconfset_lineno (int line_number ) +{ + + zconflineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see zconf_switch_to_buffer + */ +void zconfset_in (FILE * in_str ) +{ + zconfin = in_str ; +} + +void zconfset_out (FILE * out_str ) +{ + zconfout = out_str ; +} + +int zconfget_debug (void) +{ + return zconf_flex_debug; +} + +void zconfset_debug (int bdebug ) +{ + zconf_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from zconflex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + zconfin = stdin; + zconfout = stdout; +#else + zconfin = (FILE *) 0; + zconfout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * zconflex_init() + */ + return 0; +} + +/* zconflex_destroy is for both reentrant and non-reentrant scanners. */ +int zconflex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + zconfpop_buffer_state(); + } + + /* Destroy the stack itself. */ + zconffree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * zconflex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *zconfalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *zconfrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void zconffree (void * ptr ) +{ + free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = xmalloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; +} + +void zconf_nextfile(const char *name) +{ + struct file *iter; + struct file *file = file_lookup(name); + struct buffer *buf = xmalloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + zconfin = zconf_fopen(file->name); + if (!zconfin) { + printf("%s:%d: can't open file \"%s\"\n", + zconf_curname(), zconf_lineno(), file->name); + exit(1); + } + zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + for (iter = current_file->parent; iter; iter = iter->parent ) { + if (!strcmp(current_file->name,iter->name) ) { + printf("%s:%d: recursive inclusion detected. " + "Inclusion path:\n current file : '%s'\n", + zconf_curname(), zconf_lineno(), + zconf_curname()); + iter = current_file->parent; + while (iter && \ + strcmp(iter->name,current_file->name)) { + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno-1); + iter = iter->parent; + } + if (iter) + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno+1); + exit(1); + } + } + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(zconfin); + zconf_delete_buffer(YY_CURRENT_BUFFER); + zconf_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +const char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : "<none>"; +} + diff --git a/qemu/roms/seabios/scripts/kconfig/zconf.tab.c_shipped b/qemu/roms/seabios/scripts/kconfig/zconf.tab.c_shipped new file mode 100644 index 000000000..de5e84ed3 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/zconf.tab.c_shipped @@ -0,0 +1,2538 @@ +/* A Bison parser, made by GNU Bison 2.5. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + 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 <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.5" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse zconfparse +#define yylex zconflex +#define yyerror zconferror +#define yylval zconflval +#define yychar zconfchar +#define yydebug zconfdebug +#define yynerrs zconfnerrs + + +/* Copy the first part of user declarations. */ + + +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include "lkc.h" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconf_error(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken); + +struct symbol *symbol_hash[SYMBOL_HASHSIZE]; + +static struct menu *current_menu, *current_entry; + + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + T_MAINMENU = 258, + T_MENU = 259, + T_ENDMENU = 260, + T_SOURCE = 261, + T_CHOICE = 262, + T_ENDCHOICE = 263, + T_COMMENT = 264, + T_CONFIG = 265, + T_MENUCONFIG = 266, + T_HELP = 267, + T_HELPTEXT = 268, + T_IF = 269, + T_ENDIF = 270, + T_DEPENDS = 271, + T_OPTIONAL = 272, + T_PROMPT = 273, + T_TYPE = 274, + T_DEFAULT = 275, + T_SELECT = 276, + T_RANGE = 277, + T_VISIBLE = 278, + T_OPTION = 279, + T_ON = 280, + T_WORD = 281, + T_WORD_QUOTE = 282, + T_UNEQUAL = 283, + T_CLOSE_PAREN = 284, + T_OPEN_PAREN = 285, + T_EOL = 286, + T_OR = 287, + T_AND = 288, + T_EQUAL = 289, + T_NOT = 290 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + + + char *string; + struct file *file; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + const struct kconf_id *id; + + + +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Include zconf.hash.c here so it can see the token constants. */ +#include "zconf.hash.c" + + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 11 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 290 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 36 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 50 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 118 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 191 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 290 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 6, 8, 11, 13, 14, 17, 20, + 23, 26, 31, 36, 40, 42, 44, 46, 48, 50, + 52, 54, 56, 58, 60, 62, 64, 66, 68, 72, + 75, 79, 82, 86, 89, 90, 93, 96, 99, 102, + 105, 108, 112, 117, 122, 127, 133, 137, 138, 142, + 143, 146, 150, 153, 155, 159, 160, 163, 166, 169, + 172, 175, 180, 184, 187, 192, 193, 196, 200, 202, + 206, 207, 210, 213, 216, 220, 224, 228, 230, 234, + 235, 238, 241, 244, 248, 252, 255, 258, 261, 262, + 265, 268, 271, 276, 277, 280, 283, 286, 287, 290, + 292, 294, 297, 300, 303, 305, 308, 309, 312, 314, + 318, 322, 326, 329, 333, 337, 339, 341, 342 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 37, 0, -1, 81, 38, -1, 38, -1, 63, 39, + -1, 39, -1, -1, 39, 41, -1, 39, 55, -1, + 39, 67, -1, 39, 80, -1, 39, 26, 1, 31, + -1, 39, 40, 1, 31, -1, 39, 1, 31, -1, + 16, -1, 18, -1, 19, -1, 21, -1, 17, -1, + 22, -1, 20, -1, 23, -1, 31, -1, 61, -1, + 71, -1, 44, -1, 46, -1, 69, -1, 26, 1, + 31, -1, 1, 31, -1, 10, 26, 31, -1, 43, + 47, -1, 11, 26, 31, -1, 45, 47, -1, -1, + 47, 48, -1, 47, 49, -1, 47, 75, -1, 47, + 73, -1, 47, 42, -1, 47, 31, -1, 19, 78, + 31, -1, 18, 79, 82, 31, -1, 20, 83, 82, + 31, -1, 21, 26, 82, 31, -1, 22, 84, 84, + 82, 31, -1, 24, 50, 31, -1, -1, 50, 26, + 51, -1, -1, 34, 79, -1, 7, 85, 31, -1, + 52, 56, -1, 80, -1, 53, 58, 54, -1, -1, + 56, 57, -1, 56, 75, -1, 56, 73, -1, 56, + 31, -1, 56, 42, -1, 18, 79, 82, 31, -1, + 19, 78, 31, -1, 17, 31, -1, 20, 26, 82, + 31, -1, -1, 58, 41, -1, 14, 83, 81, -1, + 80, -1, 59, 62, 60, -1, -1, 62, 41, -1, + 62, 67, -1, 62, 55, -1, 3, 79, 81, -1, + 4, 79, 31, -1, 64, 76, 74, -1, 80, -1, + 65, 68, 66, -1, -1, 68, 41, -1, 68, 67, + -1, 68, 55, -1, 6, 79, 31, -1, 9, 79, + 31, -1, 70, 74, -1, 12, 31, -1, 72, 13, + -1, -1, 74, 75, -1, 74, 31, -1, 74, 42, + -1, 16, 25, 83, 31, -1, -1, 76, 77, -1, + 76, 31, -1, 23, 82, -1, -1, 79, 82, -1, + 26, -1, 27, -1, 5, 31, -1, 8, 31, -1, + 15, 31, -1, 31, -1, 81, 31, -1, -1, 14, + 83, -1, 84, -1, 84, 34, 84, -1, 84, 28, + 84, -1, 30, 83, 29, -1, 35, 83, -1, 83, + 32, 83, -1, 83, 33, 83, -1, 26, -1, 27, + -1, -1, 26, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 103, 103, 103, 105, 105, 107, 109, 110, 111, + 112, 113, 114, 118, 122, 122, 122, 122, 122, 122, + 122, 122, 126, 127, 128, 129, 130, 131, 135, 136, + 142, 150, 156, 164, 174, 176, 177, 178, 179, 180, + 181, 184, 192, 198, 208, 214, 220, 223, 225, 236, + 237, 242, 251, 256, 264, 267, 269, 270, 271, 272, + 273, 276, 282, 293, 299, 309, 311, 316, 324, 332, + 335, 337, 338, 339, 344, 351, 358, 363, 371, 374, + 376, 377, 378, 381, 389, 396, 403, 409, 416, 418, + 419, 420, 423, 431, 433, 434, 437, 444, 446, 451, + 452, 455, 456, 457, 461, 462, 465, 466, 469, 470, + 471, 472, 473, 474, 475, 478, 479, 482, 483 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU", + "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG", + "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS", + "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT", "T_SELECT", "T_RANGE", + "T_VISIBLE", "T_OPTION", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", + "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL", + "T_NOT", "$accept", "input", "start", "stmt_list", "option_name", + "common_stmt", "option_error", "config_entry_start", "config_stmt", + "menuconfig_entry_start", "menuconfig_stmt", "config_option_list", + "config_option", "symbol_option", "symbol_option_list", + "symbol_option_arg", "choice", "choice_entry", "choice_end", + "choice_stmt", "choice_option_list", "choice_option", "choice_block", + "if_entry", "if_end", "if_stmt", "if_block", "mainmenu_stmt", "menu", + "menu_entry", "menu_end", "menu_stmt", "menu_block", "source_stmt", + "comment", "comment_stmt", "help_start", "help", "depends_list", + "depends", "visibility_list", "visible", "prompt_stmt_opt", "prompt", + "end", "nl", "if_expr", "expr", "symbol", "word_opt", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 36, 37, 37, 38, 38, 39, 39, 39, 39, + 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, + 40, 40, 41, 41, 41, 41, 41, 41, 42, 42, + 43, 44, 45, 46, 47, 47, 47, 47, 47, 47, + 47, 48, 48, 48, 48, 48, 49, 50, 50, 51, + 51, 52, 53, 54, 55, 56, 56, 56, 56, 56, + 56, 57, 57, 57, 57, 58, 58, 59, 60, 61, + 62, 62, 62, 62, 63, 64, 65, 66, 67, 68, + 68, 68, 68, 69, 70, 71, 72, 73, 74, 74, + 74, 74, 75, 76, 76, 76, 77, 78, 78, 79, + 79, 80, 80, 80, 81, 81, 82, 82, 83, 83, + 83, 83, 83, 83, 83, 84, 84, 85, 85 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 2, 1, 2, 1, 0, 2, 2, 2, + 2, 4, 4, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, + 3, 2, 3, 2, 0, 2, 2, 2, 2, 2, + 2, 3, 4, 4, 4, 5, 3, 0, 3, 0, + 2, 3, 2, 1, 3, 0, 2, 2, 2, 2, + 2, 4, 3, 2, 4, 0, 2, 3, 1, 3, + 0, 2, 2, 2, 3, 3, 3, 1, 3, 0, + 2, 2, 2, 3, 3, 2, 2, 2, 0, 2, + 2, 2, 4, 0, 2, 2, 2, 0, 2, 1, + 1, 2, 2, 2, 1, 2, 0, 2, 1, 3, + 3, 3, 2, 3, 3, 1, 1, 0, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 6, 0, 104, 0, 3, 0, 6, 6, 99, 100, + 0, 1, 0, 0, 0, 0, 117, 0, 0, 0, + 0, 0, 0, 14, 18, 15, 16, 20, 17, 19, + 21, 0, 22, 0, 7, 34, 25, 34, 26, 55, + 65, 8, 70, 23, 93, 79, 9, 27, 88, 24, + 10, 0, 105, 2, 74, 13, 0, 101, 0, 118, + 0, 102, 0, 0, 0, 115, 116, 0, 0, 0, + 108, 103, 0, 0, 0, 0, 0, 0, 0, 88, + 0, 0, 75, 83, 51, 84, 30, 32, 0, 112, + 0, 0, 67, 0, 0, 11, 12, 0, 0, 0, + 0, 97, 0, 0, 0, 47, 0, 40, 39, 35, + 36, 0, 38, 37, 0, 0, 97, 0, 59, 60, + 56, 58, 57, 66, 54, 53, 71, 73, 69, 72, + 68, 106, 95, 0, 94, 80, 82, 78, 81, 77, + 90, 91, 89, 111, 113, 114, 110, 109, 29, 86, + 0, 106, 0, 106, 106, 106, 0, 0, 0, 87, + 63, 106, 0, 106, 0, 96, 0, 0, 41, 98, + 0, 0, 106, 49, 46, 28, 0, 62, 0, 107, + 92, 42, 43, 44, 0, 0, 48, 61, 64, 45, + 50 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 3, 4, 5, 33, 34, 108, 35, 36, 37, + 38, 74, 109, 110, 157, 186, 39, 40, 124, 41, + 76, 120, 77, 42, 128, 43, 78, 6, 44, 45, + 137, 46, 80, 47, 48, 49, 111, 112, 81, 113, + 79, 134, 152, 153, 50, 7, 165, 69, 70, 60 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -90 +static const yytype_int16 yypact[] = +{ + 4, 42, -90, 96, -90, 111, -90, 15, -90, -90, + 75, -90, 82, 42, 104, 42, 110, 107, 42, 115, + 125, -4, 121, -90, -90, -90, -90, -90, -90, -90, + -90, 162, -90, 163, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, 139, -90, -90, 138, -90, 142, -90, 143, -90, + 152, -90, 164, 167, 168, -90, -90, -4, -4, 77, + -18, -90, 177, 185, 33, 71, 195, 247, 236, -2, + 236, 171, -90, -90, -90, -90, -90, -90, 41, -90, + -4, -4, 138, 97, 97, -90, -90, 186, 187, 194, + 42, 42, -4, 196, 97, -90, 219, -90, -90, -90, + -90, 210, -90, -90, 204, 42, 42, 199, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, 222, -90, 223, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, 215, -90, -90, -90, -90, -90, + -4, 222, 228, 222, -5, 222, 97, 35, 229, -90, + -90, 222, 232, 222, -4, -90, 135, 233, -90, -90, + 234, 235, 222, 240, -90, -90, 237, -90, 239, -13, + -90, -90, -90, -90, 244, 42, -90, -90, -90, -90, + -90 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -90, -90, 269, 271, -90, 23, -70, -90, -90, -90, + -90, 243, -90, -90, -90, -90, -90, -90, -90, -48, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -20, -90, -90, -90, -90, -90, 206, 205, -68, + -90, -90, 169, -1, 27, -7, 118, -66, -89, -90 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -86 +static const yytype_int16 yytable[] = +{ + 10, 88, 89, 54, 146, 147, 119, 1, 122, 164, + 93, 141, 56, 142, 58, 156, 94, 62, 1, 90, + 91, 131, 65, 66, 144, 145, 67, 90, 91, 132, + 127, 68, 136, -31, 97, 2, 154, -31, -31, -31, + -31, -31, -31, -31, -31, 98, 52, -31, -31, 99, + -31, 100, 101, 102, 103, 104, -31, 105, 129, 106, + 138, 173, 92, 141, 107, 142, 174, 172, 8, 9, + 143, -33, 97, 90, 91, -33, -33, -33, -33, -33, + -33, -33, -33, 98, 166, -33, -33, 99, -33, 100, + 101, 102, 103, 104, -33, 105, 11, 106, 179, 151, + 123, 126, 107, 135, 125, 130, 2, 139, 2, 90, + 91, -5, 12, 55, 161, 13, 14, 15, 16, 17, + 18, 19, 20, 65, 66, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 57, 59, 31, 61, -4, + 12, 63, 32, 13, 14, 15, 16, 17, 18, 19, + 20, 64, 71, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 72, 73, 31, 180, 90, 91, 52, + 32, -85, 97, 82, 83, -85, -85, -85, -85, -85, + -85, -85, -85, 84, 190, -85, -85, 99, -85, -85, + -85, -85, -85, -85, -85, 85, 97, 106, 86, 87, + -52, -52, 140, -52, -52, -52, -52, 98, 95, -52, + -52, 99, 114, 115, 116, 117, 96, 148, 149, 150, + 158, 106, 155, 159, 97, 163, 118, -76, -76, -76, + -76, -76, -76, -76, -76, 160, 164, -76, -76, 99, + 13, 14, 15, 16, 17, 18, 19, 20, 91, 106, + 21, 22, 14, 15, 140, 17, 18, 19, 20, 168, + 175, 21, 22, 177, 181, 182, 183, 32, 187, 167, + 188, 169, 170, 171, 185, 189, 53, 51, 32, 176, + 75, 178, 121, 0, 133, 162, 0, 0, 0, 0, + 184 +}; + +#define yypact_value_is_default(yystate) \ + ((yystate) == (-90)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + +static const yytype_int16 yycheck[] = +{ + 1, 67, 68, 10, 93, 94, 76, 3, 76, 14, + 28, 81, 13, 81, 15, 104, 34, 18, 3, 32, + 33, 23, 26, 27, 90, 91, 30, 32, 33, 31, + 78, 35, 80, 0, 1, 31, 102, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 31, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 78, 26, + 80, 26, 69, 133, 31, 133, 31, 156, 26, 27, + 29, 0, 1, 32, 33, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 150, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 0, 26, 164, 100, + 77, 78, 31, 80, 77, 78, 31, 80, 31, 32, + 33, 0, 1, 31, 115, 4, 5, 6, 7, 8, + 9, 10, 11, 26, 27, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 31, 26, 26, 31, 0, + 1, 26, 31, 4, 5, 6, 7, 8, 9, 10, + 11, 26, 31, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 1, 1, 26, 31, 32, 33, 31, + 31, 0, 1, 31, 31, 4, 5, 6, 7, 8, + 9, 10, 11, 31, 185, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 31, 1, 26, 31, 31, + 5, 6, 31, 8, 9, 10, 11, 12, 31, 14, + 15, 16, 17, 18, 19, 20, 31, 31, 31, 25, + 1, 26, 26, 13, 1, 26, 31, 4, 5, 6, + 7, 8, 9, 10, 11, 31, 14, 14, 15, 16, + 4, 5, 6, 7, 8, 9, 10, 11, 33, 26, + 14, 15, 5, 6, 31, 8, 9, 10, 11, 31, + 31, 14, 15, 31, 31, 31, 31, 31, 31, 151, + 31, 153, 154, 155, 34, 31, 7, 6, 31, 161, + 37, 163, 76, -1, 79, 116, -1, -1, -1, -1, + 172 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 31, 37, 38, 39, 63, 81, 26, 27, + 79, 0, 1, 4, 5, 6, 7, 8, 9, 10, + 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 26, 31, 40, 41, 43, 44, 45, 46, 52, + 53, 55, 59, 61, 64, 65, 67, 69, 70, 71, + 80, 39, 31, 38, 81, 31, 79, 31, 79, 26, + 85, 31, 79, 26, 26, 26, 27, 30, 35, 83, + 84, 31, 1, 1, 47, 47, 56, 58, 62, 76, + 68, 74, 31, 31, 31, 31, 31, 31, 83, 83, + 32, 33, 81, 28, 34, 31, 31, 1, 12, 16, + 18, 19, 20, 21, 22, 24, 26, 31, 42, 48, + 49, 72, 73, 75, 17, 18, 19, 20, 31, 42, + 57, 73, 75, 41, 54, 80, 41, 55, 60, 67, + 80, 23, 31, 74, 77, 41, 55, 66, 67, 80, + 31, 42, 75, 29, 83, 83, 84, 84, 31, 31, + 25, 79, 78, 79, 83, 26, 84, 50, 1, 13, + 31, 79, 78, 26, 14, 82, 83, 82, 31, 82, + 82, 82, 84, 26, 31, 31, 82, 31, 82, 83, + 31, 31, 31, 31, 82, 34, 51, 31, 31, 31, + 79 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* This macro is provided for backward compatibility. */ + +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 53: /* "choice_entry" */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); + if (current_menu == (yyvaluep->menu)) + menu_end_menu(); +}; + + break; + case 59: /* "if_entry" */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); + if (current_menu == (yyvaluep->menu)) + menu_end_menu(); +}; + + break; + case 65: /* "menu_entry" */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + (yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno); + if (current_menu == (yyvaluep->menu)) + menu_end_menu(); +}; + + break; + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 10: + + { zconf_error("unexpected end statement"); } + break; + + case 11: + + { zconf_error("unknown statement \"%s\"", (yyvsp[(2) - (4)].string)); } + break; + + case 12: + + { + zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[(2) - (4)].id)->name); +} + break; + + case 13: + + { zconf_error("invalid statement"); } + break; + + case 28: + + { zconf_error("unknown option \"%s\"", (yyvsp[(1) - (3)].string)); } + break; + + case 29: + + { zconf_error("invalid option"); } + break; + + case 30: + + { + struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string)); +} + break; + + case 31: + + { + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +} + break; + + case 32: + + { + struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string)); +} + break; + + case 33: + + { + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +} + break; + + case 41: + + { + menu_set_type((yyvsp[(1) - (3)].id)->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[(1) - (3)].id)->stype); +} + break; + + case 42: + + { + menu_add_prompt(P_PROMPT, (yyvsp[(2) - (4)].string), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +} + break; + + case 43: + + { + menu_add_expr(P_DEFAULT, (yyvsp[(2) - (4)].expr), (yyvsp[(3) - (4)].expr)); + if ((yyvsp[(1) - (4)].id)->stype != S_UNKNOWN) + menu_set_type((yyvsp[(1) - (4)].id)->stype); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[(1) - (4)].id)->stype); +} + break; + + case 44: + + { + menu_add_symbol(P_SELECT, sym_lookup((yyvsp[(2) - (4)].string), 0), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +} + break; + + case 45: + + { + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[(2) - (5)].symbol), (yyvsp[(3) - (5)].symbol)), (yyvsp[(4) - (5)].expr)); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +} + break; + + case 48: + + { + const struct kconf_id *id = kconf_id_lookup((yyvsp[(2) - (3)].string), strlen((yyvsp[(2) - (3)].string))); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, (yyvsp[(3) - (3)].string)); + else + zconfprint("warning: ignoring unknown option %s", (yyvsp[(2) - (3)].string)); + free((yyvsp[(2) - (3)].string)); +} + break; + + case 49: + + { (yyval.string) = NULL; } + break; + + case 50: + + { (yyval.string) = (yyvsp[(2) - (2)].string); } + break; + + case 51: + + { + struct symbol *sym = sym_lookup((yyvsp[(2) - (3)].string), SYMBOL_CHOICE); + sym->flags |= SYMBOL_AUTO; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +} + break; + + case 52: + + { + (yyval.menu) = menu_add_menu(); +} + break; + + case 53: + + { + if (zconf_endtoken((yyvsp[(1) - (1)].id), T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +} + break; + + case 61: + + { + menu_add_prompt(P_PROMPT, (yyvsp[(2) - (4)].string), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +} + break; + + case 62: + + { + if ((yyvsp[(1) - (3)].id)->stype == S_BOOLEAN || (yyvsp[(1) - (3)].id)->stype == S_TRISTATE) { + menu_set_type((yyvsp[(1) - (3)].id)->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[(1) - (3)].id)->stype); + } else + YYERROR; +} + break; + + case 63: + + { + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +} + break; + + case 64: + + { + if ((yyvsp[(1) - (4)].id)->stype == S_UNKNOWN) { + menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[(2) - (4)].string), 0), (yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:default\n", + zconf_curname(), zconf_lineno()); + } else + YYERROR; +} + break; + + case 67: + + { + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep((yyvsp[(2) - (3)].expr)); + (yyval.menu) = menu_add_menu(); +} + break; + + case 68: + + { + if (zconf_endtoken((yyvsp[(1) - (1)].id), T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +} + break; + + case 74: + + { + menu_add_prompt(P_MENU, (yyvsp[(2) - (3)].string), NULL); +} + break; + + case 75: + + { + menu_add_entry(NULL); + menu_add_prompt(P_MENU, (yyvsp[(2) - (3)].string), NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +} + break; + + case 76: + + { + (yyval.menu) = menu_add_menu(); +} + break; + + case 77: + + { + if (zconf_endtoken((yyvsp[(1) - (1)].id), T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +} + break; + + case 83: + + { + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[(2) - (3)].string)); + zconf_nextfile((yyvsp[(2) - (3)].string)); +} + break; + + case 84: + + { + menu_add_entry(NULL); + menu_add_prompt(P_COMMENT, (yyvsp[(2) - (3)].string), NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +} + break; + + case 85: + + { + menu_end_entry(); +} + break; + + case 86: + + { + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +} + break; + + case 87: + + { + current_entry->help = (yyvsp[(2) - (2)].string); +} + break; + + case 92: + + { + menu_add_dep((yyvsp[(3) - (4)].expr)); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +} + break; + + case 96: + + { + menu_add_visibility((yyvsp[(2) - (2)].expr)); +} + break; + + case 98: + + { + menu_add_prompt(P_PROMPT, (yyvsp[(1) - (2)].string), (yyvsp[(2) - (2)].expr)); +} + break; + + case 101: + + { (yyval.id) = (yyvsp[(1) - (2)].id); } + break; + + case 102: + + { (yyval.id) = (yyvsp[(1) - (2)].id); } + break; + + case 103: + + { (yyval.id) = (yyvsp[(1) - (2)].id); } + break; + + case 106: + + { (yyval.expr) = NULL; } + break; + + case 107: + + { (yyval.expr) = (yyvsp[(2) - (2)].expr); } + break; + + case 108: + + { (yyval.expr) = expr_alloc_symbol((yyvsp[(1) - (1)].symbol)); } + break; + + case 109: + + { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); } + break; + + case 110: + + { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[(1) - (3)].symbol), (yyvsp[(3) - (3)].symbol)); } + break; + + case 111: + + { (yyval.expr) = (yyvsp[(2) - (3)].expr); } + break; + + case 112: + + { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[(2) - (2)].expr)); } + break; + + case 113: + + { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); } + break; + + case 114: + + { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); } + break; + + case 115: + + { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), 0); free((yyvsp[(1) - (1)].string)); } + break; + + case 116: + + { (yyval.symbol) = sym_lookup((yyvsp[(1) - (1)].string), SYMBOL_CONST); free((yyvsp[(1) - (1)].string)); } + break; + + case 117: + + { (yyval.string) = NULL; } + break; + + + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + + + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + _menu_init(); + rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL); + + if (getenv("ZCONF_DEBUG")) + zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + if (!modules_sym) + modules_sym = sym_find( "n" ); + + rootmenu.prompt->text = _(rootmenu.prompt->text); + rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text); + + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (sym_check_deps(sym)) + zconfnerrs++; + } + if (zconfnerrs) + exit(1); + sym_set_change_count(1); +} + +static const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + case T_DEPENDS: return "depends"; + case T_VISIBLE: return "visible"; + } + return "<token>"; +} + +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken) +{ + if (id->token != endtoken) { + zconf_error("unexpected '%s' within %s block", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconf_error("'%s' in different file than '%s'", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->file->name, current_menu->lineno, + zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + zconfnerrs++; + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +static void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +static void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "\nchoice\n"); + else + fprintf(out, "\nconfig %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + case P_SELECT: + fputs( " select ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_RANGE: + fputs( " range ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_MENU: + fputs( " menu ", out); + print_quoted_string(out, prop->text); + fputc('\n', out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "zconf.lex.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" + diff --git a/qemu/roms/seabios/scripts/kconfig/zconf.y b/qemu/roms/seabios/scripts/kconfig/zconf.y new file mode 100644 index 000000000..0f683cfa5 --- /dev/null +++ b/qemu/roms/seabios/scripts/kconfig/zconf.y @@ -0,0 +1,733 @@ +%{ +/* + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> + * Released under the terms of the GNU GPL v2.0. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include "lkc.h" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconf_error(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken); + +struct symbol *symbol_hash[SYMBOL_HASHSIZE]; + +static struct menu *current_menu, *current_entry; + +%} +%expect 30 + +%union +{ + char *string; + struct file *file; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + const struct kconf_id *id; +} + +%token <id>T_MAINMENU +%token <id>T_MENU +%token <id>T_ENDMENU +%token <id>T_SOURCE +%token <id>T_CHOICE +%token <id>T_ENDCHOICE +%token <id>T_COMMENT +%token <id>T_CONFIG +%token <id>T_MENUCONFIG +%token <id>T_HELP +%token <string> T_HELPTEXT +%token <id>T_IF +%token <id>T_ENDIF +%token <id>T_DEPENDS +%token <id>T_OPTIONAL +%token <id>T_PROMPT +%token <id>T_TYPE +%token <id>T_DEFAULT +%token <id>T_SELECT +%token <id>T_RANGE +%token <id>T_VISIBLE +%token <id>T_OPTION +%token <id>T_ON +%token <string> T_WORD +%token <string> T_WORD_QUOTE +%token T_UNEQUAL +%token T_CLOSE_PAREN +%token T_OPEN_PAREN +%token T_EOL + +%left T_OR +%left T_AND +%left T_EQUAL T_UNEQUAL +%nonassoc T_NOT + +%type <string> prompt +%type <symbol> symbol +%type <expr> expr +%type <expr> if_expr +%type <id> end +%type <id> option_name +%type <menu> if_entry menu_entry choice_entry +%type <string> symbol_option_arg word_opt + +%destructor { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + $$->file->name, $$->lineno); + if (current_menu == $$) + menu_end_menu(); +} if_entry menu_entry choice_entry + +%{ +/* Include zconf.hash.c here so it can see the token constants. */ +#include "zconf.hash.c" +%} + +%% +input: nl start | start; + +start: mainmenu_stmt stmt_list | stmt_list; + +stmt_list: + /* empty */ + | stmt_list common_stmt + | stmt_list choice_stmt + | stmt_list menu_stmt + | stmt_list end { zconf_error("unexpected end statement"); } + | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } + | stmt_list option_name error T_EOL +{ + zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name); +} + | stmt_list error T_EOL { zconf_error("invalid statement"); } +; + +option_name: + T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT | T_VISIBLE +; + +common_stmt: + T_EOL + | if_stmt + | comment_stmt + | config_stmt + | menuconfig_stmt + | source_stmt +; + +option_error: + T_WORD error T_EOL { zconf_error("unknown option \"%s\"", $1); } + | error T_EOL { zconf_error("invalid option"); } +; + + +/* config/menuconfig entry */ + +config_entry_start: T_CONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +config_stmt: config_entry_start config_option_list +{ + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +menuconfig_stmt: menuconfig_entry_start config_option_list +{ + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +config_option_list: + /* empty */ + | config_option_list config_option + | config_option_list symbol_option + | config_option_list depends + | config_option_list help + | config_option_list option_error + | config_option_list T_EOL +; + +config_option: T_TYPE prompt_stmt_opt T_EOL +{ + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEFAULT expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + if ($1->stype != S_UNKNOWN) + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_SELECT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_RANGE symbol symbol if_expr T_EOL +{ + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +}; + +symbol_option: T_OPTION symbol_option_list T_EOL +; + +symbol_option_list: + /* empty */ + | symbol_option_list T_WORD symbol_option_arg +{ + const struct kconf_id *id = kconf_id_lookup($2, strlen($2)); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, $3); + else + zconfprint("warning: ignoring unknown option %s", $2); + free($2); +}; + +symbol_option_arg: + /* empty */ { $$ = NULL; } + | T_EQUAL prompt { $$ = $2; } +; + +/* choice entry */ + +choice: T_CHOICE word_opt T_EOL +{ + struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE); + sym->flags |= SYMBOL_AUTO; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +}; + +choice_entry: choice choice_option_list +{ + $$ = menu_add_menu(); +}; + +choice_end: end +{ + if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +}; + +choice_stmt: choice_entry choice_block choice_end +; + +choice_option_list: + /* empty */ + | choice_option_list choice_option + | choice_option_list depends + | choice_option_list help + | choice_option_list T_EOL + | choice_option_list option_error +; + +choice_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_TYPE prompt_stmt_opt T_EOL +{ + if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) { + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); + } else + YYERROR; +}; + +choice_option: T_OPTIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_DEFAULT T_WORD if_expr T_EOL +{ + if ($1->stype == S_UNKNOWN) { + menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:default\n", + zconf_curname(), zconf_lineno()); + } else + YYERROR; +}; + +choice_block: + /* empty */ + | choice_block common_stmt +; + +/* if entry */ + +if_entry: T_IF expr nl +{ + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep($2); + $$ = menu_add_menu(); +}; + +if_end: end +{ + if (zconf_endtoken($1, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +}; + +if_stmt: if_entry if_block if_end +; + +if_block: + /* empty */ + | if_block common_stmt + | if_block menu_stmt + | if_block choice_stmt +; + +/* mainmenu entry */ + +mainmenu_stmt: T_MAINMENU prompt nl +{ + menu_add_prompt(P_MENU, $2, NULL); +}; + +/* menu entry */ + +menu: T_MENU prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_MENU, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +}; + +menu_entry: menu visibility_list depends_list +{ + $$ = menu_add_menu(); +}; + +menu_end: end +{ + if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +}; + +menu_stmt: menu_entry menu_block menu_end +; + +menu_block: + /* empty */ + | menu_block common_stmt + | menu_block menu_stmt + | menu_block choice_stmt +; + +source_stmt: T_SOURCE prompt T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); + zconf_nextfile($2); +}; + +/* comment entry */ + +comment: T_COMMENT prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_COMMENT, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +}; + +comment_stmt: comment depends_list +{ + menu_end_entry(); +}; + +/* help option */ + +help_start: T_HELP T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +}; + +help: help_start T_HELPTEXT +{ + current_entry->help = $2; +}; + +/* depends option */ + +depends_list: + /* empty */ + | depends_list depends + | depends_list T_EOL + | depends_list option_error +; + +depends: T_DEPENDS T_ON expr T_EOL +{ + menu_add_dep($3); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +}; + +/* visibility option */ + +visibility_list: + /* empty */ + | visibility_list visible + | visibility_list T_EOL +; + +visible: T_VISIBLE if_expr +{ + menu_add_visibility($2); +}; + +/* prompt statement */ + +prompt_stmt_opt: + /* empty */ + | prompt if_expr +{ + menu_add_prompt(P_PROMPT, $1, $2); +}; + +prompt: T_WORD + | T_WORD_QUOTE +; + +end: T_ENDMENU T_EOL { $$ = $1; } + | T_ENDCHOICE T_EOL { $$ = $1; } + | T_ENDIF T_EOL { $$ = $1; } +; + +nl: + T_EOL + | nl T_EOL +; + +if_expr: /* empty */ { $$ = NULL; } + | T_IF expr { $$ = $2; } +; + +expr: symbol { $$ = expr_alloc_symbol($1); } + | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } + | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } + | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } + | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } + | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } + | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } +; + +symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); } + | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } +; + +word_opt: /* empty */ { $$ = NULL; } + | T_WORD + +%% + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + _menu_init(); + rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL); + + if (getenv("ZCONF_DEBUG")) + zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + if (!modules_sym) + modules_sym = sym_find( "n" ); + + rootmenu.prompt->text = _(rootmenu.prompt->text); + rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text); + + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (sym_check_deps(sym)) + zconfnerrs++; + } + if (zconfnerrs) + exit(1); + sym_set_change_count(1); +} + +static const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + case T_DEPENDS: return "depends"; + case T_VISIBLE: return "visible"; + } + return "<token>"; +} + +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken) +{ + if (id->token != endtoken) { + zconf_error("unexpected '%s' within %s block", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconf_error("'%s' in different file than '%s'", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->file->name, current_menu->lineno, + zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + zconfnerrs++; + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +static void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +static void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "\nchoice\n"); + else + fprintf(out, "\nconfig %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + case P_SELECT: + fputs( " select ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_RANGE: + fputs( " range ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_MENU: + fputs( " menu ", out); + print_quoted_string(out, prop->text); + fputc('\n', out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "zconf.lex.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" diff --git a/qemu/roms/seabios/scripts/layoutrom.py b/qemu/roms/seabios/scripts/layoutrom.py new file mode 100755 index 000000000..dd770fe49 --- /dev/null +++ b/qemu/roms/seabios/scripts/layoutrom.py @@ -0,0 +1,691 @@ +#!/usr/bin/env python +# Script to analyze code and arrange ld sections. +# +# Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import operator +import sys + +# LD script headers/trailers +COMMONHEADER = """ +/* DO NOT EDIT! This is an autogenerated file. See scripts/layoutrom.py. */ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386") +SECTIONS +{ +""" +COMMONTRAILER = """ + + /* Discard regular data sections to force a link error if + * code attempts to access data not marked with VAR16 (or other + * appropriate macro) + */ + /DISCARD/ : { + *(.text*) *(.data*) *(.bss*) *(.rodata*) + *(COMMON) *(.discard*) *(.eh_frame) *(.note*) + } +} +""" + + +###################################################################### +# Determine section locations +###################################################################### + +# Align 'pos' to 'alignbytes' offset +def alignpos(pos, alignbytes): + mask = alignbytes - 1 + return (pos + mask) & ~mask + +# Determine the final addresses for a list of sections that end at an +# address. +def setSectionsStart(sections, endaddr, minalign=1, segoffset=0): + totspace = 0 + for section in sections: + if section.align > minalign: + minalign = section.align + totspace = alignpos(totspace, section.align) + section.size + startaddr = int((endaddr - totspace) / minalign) * minalign + curaddr = startaddr + for section in sections: + curaddr = alignpos(curaddr, section.align) + section.finalloc = curaddr + section.finalsegloc = curaddr - segoffset + curaddr += section.size + return startaddr, minalign + +# The 16bit code can't exceed 64K of space. +BUILD_BIOS_ADDR = 0xf0000 +BUILD_BIOS_SIZE = 0x10000 +BUILD_ROM_START = 0xc0000 +BUILD_LOWRAM_END = 0xa0000 +# Space to reserve in f-segment for dynamic allocations +BUILD_MIN_BIOSTABLE = 2048 + +# Layout the 16bit code. This ensures sections with fixed offset +# requirements are placed in the correct location. It also places the +# 16bit code as high as possible in the f-segment. +def fitSections(sections, fillsections): + # fixedsections = [(addr, section), ...] + fixedsections = [] + for section in sections: + if section.name.startswith('.fixedaddr.'): + addr = int(section.name[11:], 16) + section.finalloc = addr + BUILD_BIOS_ADDR + section.finalsegloc = addr + fixedsections.append((addr, section)) + if section.align != 1: + print("Error: Fixed section %s has non-zero alignment (%d)" % ( + section.name, section.align)) + sys.exit(1) + fixedsections.sort(key=operator.itemgetter(0)) + firstfixed = fixedsections[0][0] + + # Find freespace in fixed address area + # fixedAddr = [(freespace, section), ...] + fixedAddr = [] + for i in range(len(fixedsections)): + fixedsectioninfo = fixedsections[i] + addr, section = fixedsectioninfo + if i == len(fixedsections) - 1: + nextaddr = BUILD_BIOS_SIZE + else: + nextaddr = fixedsections[i+1][0] + avail = nextaddr - addr - section.size + fixedAddr.append((avail, section)) + fixedAddr.sort(key=operator.itemgetter(0)) + + # Attempt to fit other sections into fixed area + canrelocate = [(section.size, section.align, section.name, section) + for section in fillsections] + canrelocate.sort() + canrelocate = [section for size, align, name, section in canrelocate] + totalused = 0 + for freespace, fixedsection in fixedAddr: + addpos = fixedsection.finalsegloc + fixedsection.size + totalused += fixedsection.size + nextfixedaddr = addpos + freespace +# print("Filling section %x uses %d, next=%x, available=%d" % ( +# fixedsection.finalloc, fixedsection.size, nextfixedaddr, freespace)) + while 1: + canfit = None + for fitsection in canrelocate: + if addpos + fitsection.size > nextfixedaddr: + # Can't fit and nothing else will fit. + break + fitnextaddr = alignpos(addpos, fitsection.align) + fitsection.size +# print("Test %s - %x vs %x" % ( +# fitsection.name, fitnextaddr, nextfixedaddr)) + if fitnextaddr > nextfixedaddr: + # This item can't fit. + continue + canfit = (fitnextaddr, fitsection) + if canfit is None: + break + # Found a section that can fit. + fitnextaddr, fitsection = canfit + canrelocate.remove(fitsection) + fitsection.finalloc = addpos + BUILD_BIOS_ADDR + fitsection.finalsegloc = addpos + addpos = fitnextaddr + totalused += fitsection.size +# print(" Adding %s (size %d align %d) pos=%x avail=%d" % ( +# fitsection[2], fitsection[0], fitsection[1] +# , fitnextaddr, nextfixedaddr - fitnextaddr)) + + # Report stats + total = BUILD_BIOS_SIZE-firstfixed + slack = total - totalused + print ("Fixed space: 0x%x-0x%x total: %d slack: %d" + " Percent slack: %.1f%%" % ( + firstfixed, BUILD_BIOS_SIZE, total, slack, + (float(slack) / total) * 100.0)) + + return firstfixed + BUILD_BIOS_ADDR + +# Return the subset of sections with a given category +def getSectionsCategory(sections, category): + return [section for section in sections if section.category == category] + +# Return the subset of sections with a given fileid +def getSectionsFileid(sections, fileid): + return [section for section in sections if section.fileid == fileid] + +# Return the subset of sections with a given name prefix +def getSectionsPrefix(sections, prefix): + return [section for section in sections + if section.name.startswith(prefix)] + +# The sections (and associated information) to be placed in output rom +class LayoutInfo: + sections = None + genreloc = None + sec32init_start = sec32init_end = sec32init_align = None + sec32low_start = sec32low_end = None + zonelow_base = final_sec32low_start = None + zonefseg_start = zonefseg_end = None + final_readonly_start = None + varlowsyms = entrysym = None + +# Determine final memory addresses for sections +def doLayout(sections, config, genreloc): + li = LayoutInfo() + li.sections = sections + li.genreloc = genreloc + # Determine 16bit positions + sections16 = getSectionsCategory(sections, '16') + textsections = getSectionsPrefix(sections16, '.text.') + rodatasections = getSectionsPrefix(sections16, '.rodata') + datasections = getSectionsPrefix(sections16, '.data16.') + fixedsections = getSectionsCategory(sections, 'fixed') + + firstfixed = fitSections(fixedsections, textsections) + remsections = [s for s in textsections+rodatasections+datasections + if s.finalloc is None] + sec16_start, sec16_align = setSectionsStart( + remsections, firstfixed, segoffset=BUILD_BIOS_ADDR) + + # Determine 32seg positions + sections32seg = getSectionsCategory(sections, '32seg') + textsections = getSectionsPrefix(sections32seg, '.text.') + rodatasections = getSectionsPrefix(sections32seg, '.rodata') + datasections = getSectionsPrefix(sections32seg, '.data32seg.') + + sec32seg_start, sec32seg_align = setSectionsStart( + textsections + rodatasections + datasections, sec16_start + , segoffset=BUILD_BIOS_ADDR) + + # Determine 32bit "fseg memory" data positions + sections32textfseg = getSectionsCategory(sections, '32textfseg') + sec32textfseg_start, sec32textfseg_align = setSectionsStart( + sections32textfseg, sec32seg_start, 16) + + sections32fseg = getSectionsCategory(sections, '32fseg') + sec32fseg_start, sec32fseg_align = setSectionsStart( + sections32fseg, sec32textfseg_start, 16 + , segoffset=BUILD_BIOS_ADDR) + + # Determine 32flat runtime positions + sections32flat = getSectionsCategory(sections, '32flat') + textsections = getSectionsPrefix(sections32flat, '.text.') + rodatasections = getSectionsPrefix(sections32flat, '.rodata') + datasections = getSectionsPrefix(sections32flat, '.data.') + bsssections = getSectionsPrefix(sections32flat, '.bss.') + + sec32flat_start, sec32flat_align = setSectionsStart( + textsections + rodatasections + datasections + bsssections + , sec32fseg_start, 16) + + # Determine 32flat init positions + sections32init = getSectionsCategory(sections, '32init') + init32_textsections = getSectionsPrefix(sections32init, '.text.') + init32_rodatasections = getSectionsPrefix(sections32init, '.rodata') + init32_datasections = getSectionsPrefix(sections32init, '.data.') + init32_bsssections = getSectionsPrefix(sections32init, '.bss.') + + sec32init_start, sec32init_align = setSectionsStart( + init32_textsections + init32_rodatasections + + init32_datasections + init32_bsssections + , sec32flat_start, 16) + + # Determine location of ZoneFSeg memory. + zonefseg_end = sec32flat_start + if not genreloc: + zonefseg_end = sec32init_start + zonefseg_start = BUILD_BIOS_ADDR + if zonefseg_start + BUILD_MIN_BIOSTABLE > zonefseg_end: + # Not enough ZoneFSeg space - force a minimum space. + zonefseg_end = sec32fseg_start + zonefseg_start = zonefseg_end - BUILD_MIN_BIOSTABLE + sec32flat_start, sec32flat_align = setSectionsStart( + textsections + rodatasections + datasections + bsssections + , zonefseg_start, 16) + sec32init_start, sec32init_align = setSectionsStart( + init32_textsections + init32_rodatasections + + init32_datasections + init32_bsssections + , sec32flat_start, 16) + li.sec32init_start = sec32init_start + li.sec32init_end = sec32flat_start + li.sec32init_align = sec32init_align + final_readonly_start = min(BUILD_BIOS_ADDR, sec32flat_start) + if not genreloc: + final_readonly_start = min(BUILD_BIOS_ADDR, sec32init_start) + li.zonefseg_start = zonefseg_start + li.zonefseg_end = zonefseg_end + li.final_readonly_start = final_readonly_start + + # Determine "low memory" data positions + sections32low = getSectionsCategory(sections, '32low') + sec32low_end = sec32init_start + if config.get('CONFIG_MALLOC_UPPERMEMORY'): + final_sec32low_end = final_readonly_start + zonelow_base = final_sec32low_end - 64*1024 + zonelow_base = max(BUILD_ROM_START, alignpos(zonelow_base, 2*1024)) + else: + final_sec32low_end = BUILD_LOWRAM_END + zonelow_base = final_sec32low_end - 64*1024 + relocdelta = final_sec32low_end - sec32low_end + li.sec32low_start, li.sec32low_align = setSectionsStart( + sections32low, sec32low_end, 16 + , segoffset=zonelow_base - relocdelta) + li.sec32low_end = sec32low_end + li.zonelow_base = zonelow_base + li.final_sec32low_start = li.sec32low_start + relocdelta + + # Print statistics + size16 = BUILD_BIOS_ADDR + BUILD_BIOS_SIZE - sec16_start + size32seg = sec16_start - sec32seg_start + size32textfseg = sec32seg_start - sec32textfseg_start + size32fseg = sec32textfseg_start - sec32fseg_start + size32flat = sec32fseg_start - sec32flat_start + size32init = sec32flat_start - sec32init_start + sizelow = li.sec32low_end - li.sec32low_start + print("16bit size: %d" % size16) + print("32bit segmented size: %d" % size32seg) + print("32bit flat size: %d" % (size32flat + size32textfseg)) + print("32bit flat init size: %d" % size32init) + print("Lowmem size: %d" % sizelow) + print("f-segment var size: %d" % size32fseg) + return li + + +###################################################################### +# Linker script output +###################################################################### + +# Write LD script includes for the given cross references +def outXRefs(sections, useseg=0, exportsyms=[], forcedelta=0): + xrefs = dict([(symbol.name, symbol) for symbol in exportsyms]) + out = "" + for section in sections: + for reloc in section.relocs: + symbol = reloc.symbol + if (symbol.section is not None + and (symbol.section.fileid != section.fileid + or symbol.name != reloc.symbolname)): + xrefs[reloc.symbolname] = symbol + for symbolname, symbol in xrefs.items(): + loc = symbol.section.finalloc + if useseg: + loc = symbol.section.finalsegloc + out += "%s = 0x%x ;\n" % (symbolname, loc + forcedelta + symbol.offset) + return out + +# Write LD script includes for the given sections +def outSections(sections, useseg=0): + out = "" + for section in sections: + loc = section.finalloc + if useseg: + loc = section.finalsegloc + out += "%s 0x%x : { *(%s) }\n" % (section.name, loc, section.name) + return out + +# Write LD script includes for the given sections using relative offsets +def outRelSections(sections, startsym, useseg=0): + sections = [(section.finalloc, section) for section in sections + if section.finalloc is not None] + sections.sort(key=operator.itemgetter(0)) + out = "" + for addr, section in sections: + loc = section.finalloc + if useseg: + loc = section.finalsegloc + out += ". = ( 0x%x - %s ) ;\n" % (loc, startsym) + if section.name in ('.rodata.str1.1', '.rodata'): + out += "_rodata%s = . ;\n" % (section.fileid,) + out += "*%s.*(%s)\n" % (section.fileid, section.name) + return out + +# Build linker script output for a list of relocations. +def strRelocs(outname, outrel, relocs): + relocs.sort() + return (" %s_start = ABSOLUTE(.) ;\n" % (outname,) + + "".join(["LONG(0x%x - %s)\n" % (pos, outrel) + for pos in relocs]) + + " %s_end = ABSOLUTE(.) ;\n" % (outname,)) + +# Find relocations to the given sections +def getRelocs(sections, tosection, type=None): + return [section.finalloc + reloc.offset + for section in sections + for reloc in section.relocs + if (reloc.symbol.section in tosection + and (type is None or reloc.type == type))] + +# Output the linker scripts for all required sections. +def writeLinkerScripts(li, out16, out32seg, out32flat): + # Write 16bit linker script + filesections16 = getSectionsFileid(li.sections, '16') + out = outXRefs(filesections16, useseg=1) + """ + zonelow_base = 0x%x ; + _zonelow_seg = 0x%x ; + +%s +""" % (li.zonelow_base, + int(li.zonelow_base / 16), + outSections(filesections16, useseg=1)) + outfile = open(out16, 'w') + outfile.write(COMMONHEADER + out + COMMONTRAILER) + outfile.close() + + # Write 32seg linker script + filesections32seg = getSectionsFileid(li.sections, '32seg') + out = (outXRefs(filesections32seg, useseg=1) + + outSections(filesections32seg, useseg=1)) + outfile = open(out32seg, 'w') + outfile.write(COMMONHEADER + out + COMMONTRAILER) + outfile.close() + + # Write 32flat linker script + sec32all_start = li.sec32low_start + relocstr = "" + if li.genreloc: + # Generate relocations + initsections = dict([ + (s, 1) for s in getSectionsCategory(li.sections, '32init')]) + noninitsections = dict([(s, 1) for s in li.sections + if s not in initsections]) + absrelocs = getRelocs(initsections, initsections, type='R_386_32') + relrelocs = getRelocs(initsections, noninitsections, type='R_386_PC32') + initrelocs = getRelocs(noninitsections, initsections) + relocstr = (strRelocs("_reloc_abs", "code32init_start", absrelocs) + + strRelocs("_reloc_rel", "code32init_start", relrelocs) + + strRelocs("_reloc_init", "code32flat_start", initrelocs)) + numrelocs = len(absrelocs + relrelocs + initrelocs) + sec32all_start -= numrelocs * 4 + filesections32flat = getSectionsFileid(li.sections, '32flat') + out = outXRefs([], exportsyms=li.varlowsyms + , forcedelta=li.final_sec32low_start-li.sec32low_start) + out += outXRefs(filesections32flat, exportsyms=[li.entrysym]) + """ + _reloc_min_align = 0x%x ; + zonefseg_start = 0x%x ; + zonefseg_end = 0x%x ; + zonelow_base = 0x%x ; + final_varlow_start = 0x%x ; + final_readonly_start = 0x%x ; + varlow_start = 0x%x ; + varlow_end = 0x%x ; + code32init_start = 0x%x ; + code32init_end = 0x%x ; + + code32flat_start = 0x%x ; + .text code32flat_start : { +%s +%s + code32flat_end = ABSOLUTE(.) ; + } :text +""" % (li.sec32init_align, + li.zonefseg_start, + li.zonefseg_end, + li.zonelow_base, + li.final_sec32low_start, + li.final_readonly_start, + li.sec32low_start, + li.sec32low_end, + li.sec32init_start, + li.sec32init_end, + sec32all_start, + relocstr, + outRelSections(li.sections, 'code32flat_start')) + out = COMMONHEADER + out + COMMONTRAILER + """ +ENTRY(%s) +PHDRS +{ + text PT_LOAD AT ( code32flat_start ) ; +} +""" % (li.entrysym.name,) + outfile = open(out32flat, 'w') + outfile.write(out) + outfile.close() + + +###################################################################### +# Detection of unused sections and init sections +###################################################################### + +# Visit all sections reachable from a given set of start sections +def findReachable(anchorsections, checkreloc, data): + anchorsections = dict([(section, []) for section in anchorsections]) + pending = list(anchorsections) + while pending: + section = pending.pop() + for reloc in section.relocs: + chain = anchorsections[section] + [section.name] + if not checkreloc(reloc, section, data, chain): + continue + nextsection = reloc.symbol.section + if nextsection not in anchorsections: + anchorsections[nextsection] = chain + pending.append(nextsection) + return anchorsections + +# Find "runtime" sections (ie, not init only sections). +def checkRuntime(reloc, rsection, data, chain): + section = reloc.symbol.section + if section is None or '.init.' in section.name: + return 0 + if '.data.varinit.' in section.name: + print("ERROR: %s is VARVERIFY32INIT but used from %s" % ( + section.name, chain)) + sys.exit(1) + return 1 + +# Find and keep the section associated with a symbol (if available). +def checkKeepSym(reloc, syms, fileid, isxref): + symbolname = reloc.symbolname + mustbecfunc = symbolname.startswith('_cfunc') + if mustbecfunc: + symprefix = '_cfunc' + fileid + '_' + if not symbolname.startswith(symprefix): + return 0 + symbolname = symbolname[len(symprefix):] + symbol = syms.get(symbolname) + if (symbol is None or symbol.section is None + or symbol.section.name.startswith('.discard.')): + return 0 + isdestcfunc = (symbol.section.name.startswith('.text.') + and not symbol.section.name.startswith('.text.asm.')) + if ((mustbecfunc and not isdestcfunc) + or (not mustbecfunc and isdestcfunc and isxref)): + return 0 + + reloc.symbol = symbol + return 1 + +# Resolve a relocation and check if it should be kept in the final binary. +def checkKeep(reloc, section, symbols, chain): + ret = checkKeepSym(reloc, symbols[section.fileid], section.fileid, 0) + if ret: + return ret + # Not in primary sections - it may be a cross 16/32 reference + for fileid in ('16', '32seg', '32flat'): + if fileid != section.fileid: + ret = checkKeepSym(reloc, symbols[fileid], fileid, 1) + if ret: + return ret + return 0 + + +###################################################################### +# Startup and input parsing +###################################################################### + +class Section: + name = size = alignment = fileid = relocs = None + finalloc = finalsegloc = category = None +class Reloc: + offset = type = symbolname = symbol = None +class Symbol: + name = offset = section = None + +# Read in output from objdump +def parseObjDump(file, fileid): + # sections = [section, ...] + sections = [] + sectionmap = {} + # symbols[symbolname] = symbol + symbols = {} + + state = None + for line in file.readlines(): + line = line.rstrip() + if line == 'Sections:': + state = 'section' + continue + if line == 'SYMBOL TABLE:': + state = 'symbol' + continue + if line.startswith('RELOCATION RECORDS FOR ['): + sectionname = line[24:-2] + if sectionname.startswith('.debug_'): + # Skip debugging sections (to reduce parsing time) + state = None + continue + state = 'reloc' + relocsection = sectionmap[sectionname] + continue + + if state == 'section': + try: + idx, name, size, vma, lma, fileoff, align = line.split() + if align[:3] != '2**': + continue + section = Section() + section.name = name + section.size = int(size, 16) + section.align = 2**int(align[3:]) + section.fileid = fileid + section.relocs = [] + sections.append(section) + sectionmap[name] = section + except ValueError: + pass + continue + if state == 'symbol': + try: + parts = line[17:].split() + if len(parts) == 3: + sectionname, size, name = parts + elif len(parts) == 4 and parts[2] == '.hidden': + sectionname, size, hidden, name = parts + else: + continue + symbol = Symbol() + symbol.size = int(size, 16) + symbol.offset = int(line[:8], 16) + symbol.name = name + symbol.section = sectionmap.get(sectionname) + symbols[name] = symbol + except ValueError: + pass + continue + if state == 'reloc': + try: + off, type, symbolname = line.split() + reloc = Reloc() + reloc.offset = int(off, 16) + reloc.type = type + reloc.symbolname = symbolname + reloc.symbol = symbols.get(symbolname) + if reloc.symbol is None: + # Some binutils (2.20.1) give section name instead + # of a symbol - create a dummy symbol. + reloc.symbol = symbol = Symbol() + symbol.size = 0 + symbol.offset = 0 + symbol.name = symbolname + symbol.section = sectionmap.get(symbolname) + symbols[symbolname] = symbol + relocsection.relocs.append(reloc) + except ValueError: + pass + return sections, symbols + +# Parser for constants in simple C header files. +def scanconfig(file): + f = open(file, 'r') + opts = {} + for l in f.readlines(): + parts = l.split() + if len(parts) != 3: + continue + if parts[0] != '#define': + continue + value = parts[2] + if value.isdigit() or (value.startswith('0x') and value[2:].isdigit()): + value = int(value, 0) + opts[parts[1]] = value + return opts + +def main(): + # Get output name + in16, in32seg, in32flat, cfgfile, out16, out32seg, out32flat = sys.argv[1:] + + # Read in the objdump information + infile16 = open(in16, 'r') + infile32seg = open(in32seg, 'r') + infile32flat = open(in32flat, 'r') + + # infoX = (sections, symbols) + info16 = parseObjDump(infile16, '16') + info32seg = parseObjDump(infile32seg, '32seg') + info32flat = parseObjDump(infile32flat, '32flat') + + # Read kconfig config file + config = scanconfig(cfgfile) + + # Figure out which sections to keep. + allsections = info16[0] + info32seg[0] + info32flat[0] + symbols = {'16': info16[1], '32seg': info32seg[1], '32flat': info32flat[1]} + if config.get('CONFIG_COREBOOT'): + entrysym = symbols['16'].get('entry_elf') + elif config.get('CONFIG_CSM'): + entrysym = symbols['16'].get('entry_csm') + else: + entrysym = symbols['16'].get('reset_vector') + anchorsections = [entrysym.section] + [ + section for section in allsections + if section.name.startswith('.fixedaddr.')] + keepsections = findReachable(anchorsections, checkKeep, symbols) + sections = [section for section in allsections if section in keepsections] + + # Separate 32bit flat into runtime, init, and special variable parts + anchorsections = [ + section for section in sections + if ('.data.varlow.' in section.name or '.data.varfseg.' in section.name + or '.fixedaddr.' in section.name or '.runtime.' in section.name)] + runtimesections = findReachable(anchorsections, checkRuntime, None) + for section in sections: + if section.name.startswith('.data.varlow.'): + section.category = '32low' + elif section.name.startswith('.data.varfseg.'): + section.category = '32fseg' + elif section.name.startswith('.text.32fseg.'): + section.category = '32textfseg' + elif section.name.startswith('.fixedaddr.'): + section.category = 'fixed' + elif section.fileid == '32flat' and section not in runtimesections: + section.category = '32init' + else: + section.category = section.fileid + + # Determine the final memory locations of each kept section. + genreloc = '_reloc_abs_start' in symbols['32flat'] + li = doLayout(sections, config, genreloc) + + # Exported symbols + li.varlowsyms = [symbol for symbol in symbols['32flat'].values() + if (symbol.section is not None + and symbol.section.finalloc is not None + and '.data.varlow.' in symbol.section.name + and symbol.name != symbol.section.name)] + li.entrysym = entrysym + + # Write out linker script files. + writeLinkerScripts(li, out16, out32seg, out32flat) + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/python23compat.py b/qemu/roms/seabios/scripts/python23compat.py new file mode 100644 index 000000000..572b7f185 --- /dev/null +++ b/qemu/roms/seabios/scripts/python23compat.py @@ -0,0 +1,14 @@ +# Helper code for compatibility of the code with both Python 2 and Python 3 +# +# Copyright (C) 2014 Johannes Krampf <johannes.krampf@googlemail.com> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys + +if (sys.version_info > (3, 0)): + def as_bytes(str): + return bytes(str, "ASCII") +else: + def as_bytes(str): + return str diff --git a/qemu/roms/seabios/scripts/readserial.py b/qemu/roms/seabios/scripts/readserial.py new file mode 100755 index 000000000..a7383e835 --- /dev/null +++ b/qemu/roms/seabios/scripts/readserial.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# Script that can read from a serial device and show timestamps. +# +# Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +# Usage: +# scripts/readserial.py /dev/ttyUSB0 115200 + +import sys, os, time, select, optparse + +from python23compat import as_bytes + +# Reset time counter after this much idle time. +RESTARTINTERVAL = 60 +# Number of bits in a transmitted byte - 8N1 is 1 start bit + 8 data +# bits + 1 stop bit. +BITSPERBYTE = 10 + +def calibrateserialwrite(outfile, byteadjust): + # Build 4000 bytes of dummy data. + data = "0123456789" * 4 + "012345678" + "\n" + data = data * 80 + while 1: + st = time.time() + outfile.write(as_bytes(data)) + outfile.flush() + et = time.time() + sys.stdout.write( + "Wrote %d - %.1fus per char (theory states %.1fus)\n" % ( + len(data), (et-st) / len(data) * 1000000, byteadjust * 1000000)) + sys.stdout.flush() + time.sleep(3) + +def calibrateserialread(infile, byteadjust): + starttime = lasttime = 0 + totalchars = 0 + while 1: + select.select([infile], [], []) + d = infile.read(4096) + curtime = time.time() + if curtime - lasttime > 1.0: + if starttime and totalchars: + sys.stdout.write( + "Calibrating on %d bytes - %.1fus per char" + " (theory states %.1fus)\n" % ( + totalchars, + float(lasttime - starttime) * 1000000 / totalchars, + byteadjust * 1000000)) + totalchars = 0 + starttime = curtime + else: + totalchars += len(d) + lasttime = curtime + +def readserial(infile, logfile, byteadjust): + lasttime = 0 + while 1: + # Read data + try: + res = select.select([infile, sys.stdin], [], []) + except KeyboardInterrupt: + sys.stdout.write("\n") + return -1 + if sys.stdin in res[0]: + # Got keyboard input - force reset on next serial input + sys.stdin.read(1) + lasttime = 0 + if len(res[0]) == 1: + continue + d = infile.read(4096) + if not d: + return 0 + datatime = time.time() + + datatime -= len(d) * byteadjust + + # Reset start time if no data for some time + if datatime - lasttime > RESTARTINTERVAL: + starttime = datatime + charcount = 0 + isnewline = 1 + msg = "\n\n======= %s (adjust=%.1fus)\n" % ( + time.asctime(time.localtime(datatime)), byteadjust * 1000000) + sys.stdout.write(msg) + logfile.write(as_bytes(msg)) + lasttime = datatime + + # Translate unprintable chars; add timestamps + out = as_bytes("") + for c in d: + if isnewline: + delta = datatime - starttime - (charcount * byteadjust) + out += "%06.3f: " % delta + isnewline = 0 + oc = ord(c) + charcount += 1 + datatime += byteadjust + if oc == 0x0d: + continue + if oc == 0x00: + out += "<00>\n" + isnewline = 1 + continue + if oc == 0x0a: + out += "\n" + isnewline = 1 + continue + if oc < 0x20 or oc >= 0x7f and oc != 0x09: + out += "<%02x>" % oc + continue + out += c + + if (sys.version_info > (3, 0)): + sys.stdout.buffer.write(out) + else: + sys.stdout.write(out) + sys.stdout.flush() + logfile.write(out) + logfile.flush() + +def main(): + usage = "%prog [options] [<serialdevice> [<baud>]]" + opts = optparse.OptionParser(usage) + opts.add_option("-f", "--file", + action="store_false", dest="serial", default=True, + help="read from unix named pipe instead of serialdevice") + opts.add_option("-n", "--no-adjust", + action="store_false", dest="adjustbaud", default=True, + help="don't adjust times by serial rate") + opts.add_option("-c", "--calibrate-read", + action="store_true", dest="calibrate_read", default=False, + help="read from serial port to calibrate it") + opts.add_option("-C", "--calibrate-write", + action="store_true", dest="calibrate_write", default=False, + help="write to serial port to calibrate it") + opts.add_option("-t", "--time", + type="float", dest="time", default=None, + help="time to write one byte on serial port (in us)") + options, args = opts.parse_args() + serialport = 0 + baud = 115200 + if len(args) > 2: + opts.error("Too many arguments") + if len(args) > 0: + serialport = args[0] + if len(args) > 1: + baud = int(args[1]) + byteadjust = float(BITSPERBYTE) / baud + if options.time is not None: + byteadjust = options.time / 1000000.0 + if not options.adjustbaud: + byteadjust = 0.0 + + if options.serial: + # Read from serial port + try: + import serial + except ImportError: + print(""" +Unable to find pyserial package ( http://pyserial.sourceforge.net/ ). +On Linux machines try: yum install pyserial +Or: apt-get install python-serial +""") + sys.exit(1) + ser = serial.Serial(serialport, baud, timeout=0) + + if options.calibrate_read: + calibrateserialread(ser, byteadjust) + return + if options.calibrate_write: + calibrateserialwrite(ser, byteadjust) + return + + logname = time.strftime("seriallog-%Y%m%d_%H%M%S.log") + f = open(logname, 'wb') + if options.serial: + readserial(ser, f, byteadjust) + else: + # Read from a pipe + while 1: + ser = os.fdopen(os.open(serialport, os.O_RDONLY|os.O_NONBLOCK), 'rb') + res = readserial(ser, f, byteadjust) + ser.close() + if res < 0: + break + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/tarball.sh b/qemu/roms/seabios/scripts/tarball.sh new file mode 100755 index 000000000..06d855466 --- /dev/null +++ b/qemu/roms/seabios/scripts/tarball.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Script to create seabios release and snapshot tarballs. +# Accepts conmmit (hash, tag, branch, ...) as first argument, +# uses HEAD if unspecified. +# + +commit="${1-HEAD}" + +# figure name for the tarball +reltag="$(git describe --tags --match 'rel-*' --exact $commit 2>/dev/null)" +if test "$reltag" != ""; then + # release + name="${reltag#rel-}" +else + # snapshot + reltag="$(git describe --tags --match 'rel-*' $commit 2>/dev/null)" + name="snap-${reltag#rel-}" +fi + +# export tarball archive from git +prefix="seabios-${name}/" +output="seabios-${name}.tar" +echo "# commit $commit -> tarball: ${output}.gz" +rm -f "$output" "${output}.gz" +git archive --format=tar --prefix="$prefix" "$commit" > "$output" + +# add .version file to tarball +dotver="$(mktemp dotver.XXXXXX)" +echo "$name" > "$dotver" +tar --append --file="$output" --owner=root --group=root --mode=0664 \ + --transform "s:${dotver}:${prefix}.version:" "$dotver" +rm -f "$dotver" + +# finally compress it +gzip "$output" diff --git a/qemu/roms/seabios/scripts/test-build.sh b/qemu/roms/seabios/scripts/test-build.sh new file mode 100755 index 000000000..081f9fd25 --- /dev/null +++ b/qemu/roms/seabios/scripts/test-build.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# Script to test if the build works properly. + +# Test IASL is installed. +$IASL -h > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "The SeaBIOS project requires the 'iasl' package be installed." >&2 + echo "Many Linux distributions have this package." >&2 + echo "Try: sudo yum install iasl" >&2 + echo "Or: sudo apt-get install iasl" >&2 + echo "" >&2 + echo "Please install iasl and retry." >&2 + echo -1 + exit 0 +fi + +mkdir -p ${OUT} +TMPFILE1=${OUT}/tmp_testcompile1.c +TMPFILE1o=${OUT}/tmp_testcompile1.o +TMPFILE1_ld=${OUT}/tmp_testcompile1.lds +TMPFILE2=${OUT}/tmp_testcompile2.c +TMPFILE2o=${OUT}/tmp_testcompile2.o +TMPFILE3o=${OUT}/tmp_testcompile3.o + +# Test if ld's alignment handling is correct. This is a known problem +# with the linker that ships with Ubuntu 11.04. +cat - > $TMPFILE1 <<EOF +const char v1[] __attribute__((section(".text.v1"))) = "0123456789"; +const char v2[] __attribute__((section(".text.v2"))) = "0123456789"; +EOF +cat - > $TMPFILE1_ld <<EOF +SECTIONS +{ + .mysection 0x88f0 : { +. = 0x10 ; +*(.text.v1) +. = 0x20 ; +*(.text.v2) +. = 0x30 ; + } +} +EOF +$CC -O -g -c $TMPFILE1 -o $TMPFILE1o > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "Unable to execute the C compiler ($CC)." >&2 + echo "" >&2 + echo "Please install a working compiler and retry." >&2 + echo -1 + exit 0 +fi +$LD -T $TMPFILE1_ld $TMPFILE1o -o $TMPFILE2o > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "The version of LD on this system ($LD) does not properly handle" >&2 + echo "alignments. As a result, this project can not be built." >&2 + echo "" >&2 + echo "The problem may be the result of this LD bug report:" >&2 + echo " http://sourceware.org/bugzilla/show_bug.cgi?id=12726" >&2 + echo "" >&2 + echo "Please update to a working version of binutils and retry." >&2 + echo -1 + exit 0 +fi + +# Test for "-fwhole-program". Older versions of gcc (pre v4.1) don't +# support the whole-program optimization - detect that. +$CC -fwhole-program -S -o /dev/null -xc /dev/null > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo " Working around no -fwhole-program" >&2 + echo 2 + exit 0 +fi + +# Test if "visible" variables and functions are marked global. On +# OpenSuse 10.3 "visible" variables declared with "extern" first +# aren't marked as global in the resulting assembler. On Ubuntu 7.10 +# "visible" functions aren't marked as global in the resulting +# assembler. +cat - > $TMPFILE1 <<EOF +void __attribute__((externally_visible)) t1() { } +extern unsigned char v1; +unsigned char v1 __attribute__((section(".data16.foo.19"))) __attribute__((externally_visible)); +EOF +$CC -Os -c -fwhole-program $TMPFILE1 -o $TMPFILE1o > /dev/null 2>&1 +cat - > $TMPFILE2 <<EOF +void t1(); +extern unsigned char v1; +int __attribute__((externally_visible)) main() { t1(); return v1; } +EOF +$CC -Os -c -fwhole-program $TMPFILE2 -o $TMPFILE2o > /dev/null 2>&1 +$CC -nostdlib -Os $TMPFILE1o $TMPFILE2o -o $TMPFILE3o > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo " Working around non-functional -fwhole-program" >&2 + echo 2 + exit 0 +fi + +echo 0 + +# Also, the Ubuntu 8.04 compiler has a bug causing corruption when the +# "ebp" register is clobberred in an "asm" statement. The code has +# been modified to not clobber "ebp" - no test is available yet. + +rm -f $TMPFILE1 $TMPFILE1o $TMPFILE1_ld $TMPFILE2 $TMPFILE2o $TMPFILE3o diff --git a/qemu/roms/seabios/scripts/transdump.py b/qemu/roms/seabios/scripts/transdump.py new file mode 100755 index 000000000..665f04a00 --- /dev/null +++ b/qemu/roms/seabios/scripts/transdump.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# This script is useful for taking the output of memdump() and +# converting it back into binary output. This can be useful, for +# example, when one wants to push that data into other tools like +# objdump or hexdump. +# +# (C) Copyright 2010 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +import sys +import struct + +def unhex(str): + return int(str, 16) + +def parseMem(filehdl): + mem = [] + for line in filehdl: + parts = line.split(':') + if len(parts) < 2: + continue + try: + vaddr = unhex(parts[0]) + parts = parts[1].split() + mem.extend([unhex(v) for v in parts]) + except ValueError: + continue + return mem + +def printUsage(): + sys.stderr.write("Usage:\n %s <file | ->\n" + % (sys.argv[0],)) + sys.exit(1) + +def main(): + if len(sys.argv) != 2: + printUsage() + filename = sys.argv[1] + if filename == '-': + filehdl = sys.stdin + else: + filehdl = open(filename, 'r') + mem = parseMem(filehdl) + for i in mem: + if (sys.version_info > (3, 0)): + sys.stdout.buffer.write(struct.pack("<I", i)) + else: + sys.stdout.write(struct.pack("<I", i)) + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/scripts/vgafixup.py b/qemu/roms/seabios/scripts/vgafixup.py new file mode 100644 index 000000000..2053cd5d7 --- /dev/null +++ b/qemu/roms/seabios/scripts/vgafixup.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# Work around x86emu bugs by replacing problematic instructions. +# +# Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net> +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +# The x86emu code widely used in Linux distributions when running Xorg +# in vesamode is known to have issues with "retl", "leavel", "entryl", +# "leal", and some variants of "calll". This code modifies those +# instructions that are known to be generated by gcc to avoid +# triggering the x86emu bugs. + +# It is also known that the Windows vgabios emulator has issues with +# addressing negative offsets to the %esp register. That has been +# worked around by not using the gcc parameter "-fomit-frame-pointer" +# when compiling. + +import sys, re + +# leal parameter regex - example string: -3(%edx,%eax,8), %eax +re_leal = re.compile( + r'^\s*(?P<offset>[^(]*?)\s*' + r'\(\s*(?P<base>[^,)]*?)\s*(?:,\s*(?P<index>[^,)]*?)\s*)?' + r'(?:,\s*(?P<scale>[^,)]*?)\s*)?\)\s*' + r',\s*(?P<dest>.*?)\s*$') + +# Find an alternate set of instructions for a given "leal" instruction +def handle_leal(sline): + m = re_leal.match(sline[5:]) + if m is None or m.group('index') == '%esp': + print("Unable to fixup leal instruction: %s" % (sline,)) + sys.exit(-1) + offset, base, index, scale, dest = m.group( + 'offset', 'base', 'index', 'scale', 'dest') + if dest == '%esp': + # If destination is %esp then just use 16bit leaw instead + return 'leaw %s\n' % (sline[5:].replace('%e', '%'),) + if not scale: + scale = '1' + scale = {1: 0, 2: 1, 4: 2, 8: 3}[int(scale, 0)] + # Try to rearrange arguments to simplify 'base' (to improve code gen) + if not scale and base == index: + base, index, scale = '', index, 1 + elif not index or (not scale and base in (dest, '%esp') and index != dest): + base, index, scale = index, base, 0 + # Produce instructions to calculate "leal" + insns = ['pushfw'] + if base != dest: + # Calculate "leal" directly in dest register + if index != dest: + insns.insert(0, 'movl %s, %s' % (index, dest)) + if scale: + insns.append('shll $%d, %s' % (scale, dest)) + if base: + if base == '%esp': + offset += '+2' + insns.append('addl %s, %s' % (base, dest)) + elif base == index: + # Use "imull" method + insns.append('imull $%d, %s' % ((1<<scale)+1, dest)) + else: + # Backup/restore index register and do scaling in index register + insns.append('pushl %s' % (index,)) + insns.append('shll $%d, %s' % (scale, index)) + insns.append('addl %s, %s' % (index, dest)) + insns.append('popl %s' % (index,)) + if offset and offset != '0': + insns.append('addl $%s, %s' % (offset, dest)) + insns.append('popfw\n') + return ' ; '.join(insns) + +def main(): + infilename, outfilename = sys.argv[1:] + infile = open(infilename, 'r') + out = [] + for line in infile: + sline = line.strip() + if sline == 'ret': + out.append('retw $2\n') + elif sline == 'leave': + out.append('movl %ebp, %esp ; popl %ebp\n') + elif sline.startswith('call'): + out.append('pushw %ax ; callw' + sline[4:] + '\n') + elif sline.startswith('leal'): + out.append(handle_leal(sline)) + #print("-> %s\n %s" % (sline, out[-1].strip())) + else: + out.append(line) + infile.close() + outfile = open(outfilename, 'w') + outfile.write(''.join(out)) + outfile.close() + +if __name__ == '__main__': + main() diff --git a/qemu/roms/seabios/src/Kconfig b/qemu/roms/seabios/src/Kconfig new file mode 100644 index 000000000..45ca59cf3 --- /dev/null +++ b/qemu/roms/seabios/src/Kconfig @@ -0,0 +1,516 @@ +# Kconfig SeaBIOS configuration + +mainmenu "SeaBIOS Configuration" + +menu "General Features" + +choice + prompt "Build Target" + default QEMU + + config COREBOOT + bool "Build for coreboot" + help + Configure as a coreboot payload. + + config QEMU + bool "Build for QEMU/Xen/KVM/Bochs" + select QEMU_HARDWARE + help + Configure for an emulated machine (QEMU, Xen, KVM, or Bochs). + + config CSM + bool "Build as Compatibilty Support Module for EFI BIOS" + help + Configure to be used by EFI firmware as Compatibility Support + module (CSM) to provide legacy BIOS services. + +endchoice + + config QEMU_HARDWARE + bool "Support hardware found on emulators (QEMU/Xen/KVM/Bochs)" if !QEMU + default n + help + Support virtual hardware when the code detects it is + running on an emulator. + + config XEN + depends on QEMU + bool "Support Xen HVM" + default y + help + Configure to be used by xen hvmloader, for a HVM guest. + + config THREADS + bool "Parallelize hardware init" + default y + help + Support running hardware initialization in parallel. + + config RELOCATE_INIT + bool "Copy init code to high memory" + default y + help + Support relocating the one time initialization code to high memory. + + config BOOTMENU + depends on BOOT + bool "Bootmenu" + default y + help + Support an interactive boot menu at end of post. + config BOOTSPLASH + depends on BOOTMENU + bool "Graphical boot splash screen" + default y + help + Support showing a graphical boot splash screen. + config BOOTORDER + depends on BOOT + bool "Boot ordering" + default y + help + Support controlling of the boot order via the fw_cfg/CBFS + "bootorder" file. + + config COREBOOT_FLASH + depends on COREBOOT + bool "coreboot CBFS support" + default y + help + Support searching coreboot flash format. + config LZMA + depends on COREBOOT_FLASH + bool "CBFS lzma support" + default y + help + Support CBFS files compressed using the lzma decompression + algorithm. + config CBFS_LOCATION + depends on COREBOOT_FLASH + hex "CBFS memory end location" + default 0 + help + Memory address of where the CBFS data ends. This should + be zero for normal builds. It may be a non-zero value if + the CBFS filesystem is at a non-standard location (eg, + 0xffe00000 if CBFS ends 2Meg below the end of flash). + + config FLASH_FLOPPY + depends on COREBOOT_FLASH + bool "Floppy images in CBFS" + default y + help + Support floppy images in coreboot flash. + config ENTRY_EXTRASTACK + bool "Use internal stack for 16bit interrupt entry points" + default y + help + Utilize an internal stack for all the legacy 16bit + interrupt entry points. This reduces the amount of space + on the caller's stack that SeaBIOS uses. This may + adversely impact any legacy operating systems that call + the BIOS in 16bit protected mode. + + config MALLOC_UPPERMEMORY + bool "Allocate memory that needs to be in first Meg above 0xc0000" + default y + help + Use the "Upper Memory Block" area (0xc0000-0xf0000) for + internal "low memory" allocations. If this is not + selected, the memory is instead allocated from the + "9-segment" (0x90000-0xa0000). + + config ROM_SIZE + int "ROM size (in KB)" + default 0 + help + Set the ROM size. Say '0' here to make seabios figure the + needed size automatically. + + Currently SeaBIOS will easily fit into 256 KB. To make it fit + it into 128 KB (which was big enouth for a long time) you'll + probably have to disable some featues such as xhci support. + +endmenu + +menu "Hardware support" + config ATA + depends on DRIVES + bool "ATA controllers" + default y + help + Support for IDE disk code. + config ATA_DMA + depends on ATA + bool "ATA DMA" + default n + help + Detect and try to use ATA bus mastering DMA controllers. + config ATA_PIO32 + depends on ATA + bool "ATA 32bit PIO" + default n + help + Use 32bit PIO accesses on ATA (minor optimization on PCI transfers). + config AHCI + depends on DRIVES + bool "AHCI controllers" + default y + help + Support for AHCI disk code. + config SDCARD + depends on DRIVES && QEMU_HARDWARE + bool "SD controllers" + default y + help + Support for SD cards on PCI host controllers. + config VIRTIO_BLK + depends on DRIVES && QEMU_HARDWARE + bool "virtio-blk controllers" + default y + help + Support boot from virtio-blk storage. + config VIRTIO_SCSI + depends on DRIVES && QEMU_HARDWARE + bool "virtio-scsi controllers" + default y + help + Support boot from virtio-scsi storage. + config PVSCSI + depends on DRIVES && QEMU_HARDWARE + bool "PVSCSI controllers" + default y + help + Support boot from Paravirtualized SCSI storage. This kind of storage + is mainly supported by VMware ESX hypervisor. It is commonly used + to allow fast storage access by communicating directly with the + underlying hypervisor. Enabling this type of boot will allow + booting directly from images imported from an ESX platform, + without the need to use slower emulation of storage controllers + such as IDE. + config ESP_SCSI + depends on DRIVES && QEMU_HARDWARE + bool "AMD PCscsi controllers" + default y + help + Support boot from AMD PCscsi storage. + config LSI_SCSI + depends on DRIVES && QEMU_HARDWARE + bool "lsi53c895a scsi controllers" + default y + help + Support boot from qemu-emulated lsi53c895a scsi storage. + config MEGASAS + depends on DRIVES + bool "LSI MegaRAID SAS controllers" + default y + help + Support boot from LSI MegaRAID SAS scsi storage. + config FLOPPY + depends on DRIVES + bool "Floppy controller" + default y + help + Support floppy drive access. + + config PS2PORT + depends on KEYBOARD || MOUSE + bool "PS/2 port" + default y + help + Support PS2 ports (keyboard and mouse). + + config USB + bool "USB" + default y + help + Support USB devices. + config USB_UHCI + depends on USB + bool "USB UHCI controllers" + default y + help + Support USB UHCI controllers. + config USB_OHCI + depends on USB + bool "USB OHCI controllers" + default y + help + Support USB OHCI controllers. + config USB_EHCI + depends on USB + bool "USB EHCI controllers" + default y + help + Support USB EHCI controllers. + config USB_XHCI + depends on USB + bool "USB XHCI controllers" + default y + help + Support USB XHCI controllers. + config USB_MSC + depends on USB && DRIVES + bool "USB drives" + default y + help + Support USB BOT (bulk-only transport) disks. + config USB_UAS + depends on USB && DRIVES + bool "UAS drives" + default y + help + Support USB UAS (usb attached scsi) disks. + config USB_HUB + depends on USB + bool "USB hubs" + default y + help + Support USB hubs. + config USB_KEYBOARD + depends on USB && KEYBOARD + bool "USB keyboards" + default y + help + Support USB keyboards. + config USB_MOUSE + depends on USB && MOUSE + bool "USB mice" + default y + help + Support USB mice. + + config SERIAL + bool "Serial port" + default y + help + Support serial ports. This also enables int 14 serial port calls. + config LPT + bool "Parallel port" + default y + help + Support parallel ports. This also enables int 17 parallel port calls. + + config USE_SMM + depends on QEMU + bool "System Management Mode (SMM)" + default y + help + Support System Management Mode (on emulators). + config CALL32_SMM + bool + depends on USE_SMM + default y + config MTRR_INIT + depends on QEMU + bool "Initialize MTRRs" + default y + help + Initialize the Memory Type Range Registers (on emulators). + config PMTIMER + bool "Use ACPI timer" + default y + help + Use the ACPI timer instead of the TSC for timekeeping (on qemu). +endmenu + +menu "BIOS interfaces" + config DRIVES + bool "Drive interface" + default y + help + Support int13 disk/floppy drive functions. + + config CDROM_BOOT + depends on DRIVES + bool "DVD/CDROM booting" + default y + help + Support for booting from a CD. (El Torito spec support.) + config CDROM_EMU + depends on CDROM_BOOT + bool "DVD/CDROM boot drive emulation" + default y + help + Support bootable CDROMs that emulate a floppy/harddrive. + + config PCIBIOS + bool "PCIBIOS interface" + default y + help + Support int 1a/b1 PCI BIOS calls. + config APMBIOS + bool "APM interface" + default y + help + Support int 15/53 APM BIOS calls. + config PNPBIOS + bool "PnP BIOS interface" + default y + help + Support PnP BIOS entry point. + config OPTIONROMS + bool "Option ROMS" + default y + help + Support finding and running option roms during POST. + config OPTIONROMS_DEPLOYED + depends on OPTIONROMS && QEMU + bool "Option roms are already at 0xc0000-0xf0000" + default n + help + Select this if option ROMs are already copied to + 0xc0000-0xf0000. This must only be selected when using + Bochs or QEMU versions older than 0.12. + config PMM + depends on OPTIONROMS + bool "PMM interface" + default y + help + Support Post Memory Manager (PMM) entry point. + config BOOT + bool "Boot interface" + default y + help + Support int 19/18 system bootup support. + config KEYBOARD + bool "Keyboard interface" + default y + help + Support int 16 keyboard calls. + config KBD_CALL_INT15_4F + depends on KEYBOARD + bool "Keyboard hook interface" + default y + help + Support calling int155f on each keyboard event. + config MOUSE + bool "Mouse interface" + default y + help + Support for int15c2 mouse calls. + + config S3_RESUME + bool "S3 resume" + default y + help + Support S3 resume handler. + + config VGAHOOKS + bool "Hardware specific VGA helpers" + default y + help + Support int 155f BIOS callbacks specific to some Intel and + VIA on-board vga devices. + + config DISABLE_A20 + bool "Disable A20" + default n + help + Disable A20 on 16bit boot. + + config WRITABLE_UPPERMEMORY + depends on QEMU + bool "Make unused UMB memory read/writeable." + default n + help + When selected, the "Upper Memory Block" area + (0x90000-0xa0000) that is not used for option roms will be + made writable. This allows the ram to be directly + modified by programs. However, some old DOS high memory + managers may require the UMB region to be read-only. + +endmenu + +menu "BIOS Tables" + depends on QEMU + config PIRTABLE + bool "PIR table" + default y + help + Support generation of a PIR table in 0xf000 segment. + config MPTABLE + bool "MPTable" + default y + help + Support generation of MPTable. + config SMBIOS + bool "SMBIOS" + default y + help + Support generation of SM BIOS tables. This is also + sometimes called DMI. + config ACPI + bool "ACPI" + default y + help + Support generation of ACPI tables. + config ACPI_DSDT + bool "Include default ACPI DSDT" + default y + depends on ACPI + help + Include default DSDT ACPI table in BIOS. + Required for QEMU 1.3 and older. + This option can be disabled for QEMU 1.4 and newer + to save some space in the ROM file. + If unsure, say Y. + config FW_ROMFILE_LOAD + bool "Load BIOS tables from ROM files" + depends on QEMU_HARDWARE + default y + help + Support loading BIOS firmware tables from ROM files. + At the moment, only ACPI tables can be loaded in this way. + Required for QEMU 1.7 and newer. + This option can be disabled for QEMU 1.6 and older + to save some space in the ROM file. + If unsure, say Y. +endmenu + +source vgasrc/Kconfig + +menu "Debugging" + config DEBUG_LEVEL + int "Debug level" + default 1 + help + Control how verbose debug output is. The higher the + number, the more verbose SeaBIOS will be. + + Set to zero to disable debugging. + + config DEBUG_SERIAL + depends on DEBUG_LEVEL != 0 + bool "Serial port debugging" + default n + help + Send debugging information to serial port. + config DEBUG_SERIAL_PORT + depends on DEBUG_SERIAL + hex "Serial port base address" + default 0x3f8 + help + Base port for serial - generally 0x3f8, 0x2f8, 0x3e8, or 0x2e8. + + config DEBUG_IO + depends on QEMU_HARDWARE && DEBUG_LEVEL != 0 + bool "Special IO port debugging" + default y + help + Some emulators or hypervisors provide with a way to output debug + information by outputing strings in a special port present in the + IO space. + + config DEBUG_COREBOOT + depends on COREBOOT && DEBUG_LEVEL != 0 + bool "coreboot cbmem debug logging" + default y + help + Send debugging information to the coreboot cbmem console buffer. + Needs CONFIG_CONSOLE_CBMEM in coreboot. You can read the log + after boot using 'cbmem -c'. Only 32bit code (basically every- + thing before booting the OS) writes to the log buffer. + +endmenu diff --git a/qemu/roms/seabios/src/apm.c b/qemu/roms/seabios/src/apm.c new file mode 100644 index 000000000..f7c2306c3 --- /dev/null +++ b/qemu/roms/seabios/src/apm.c @@ -0,0 +1,215 @@ +// Basic support for apmbios callbacks. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2005 Struan Bartlett +// Copyright (C) 2004 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "stacks.h" // yield_toirq +#include "util.h" // apm_shutdown +#include "x86.h" // outb + +// APM installation check +static void +handle_155300(struct bregs *regs) +{ + regs->ah = 1; // APM major version + regs->al = 2; // APM minor version + regs->bh = 'P'; + regs->bl = 'M'; + // bit 0 : 16 bit interface supported + // bit 1 : 32 bit interface supported + regs->cx = 0x03; + set_success(regs); +} + +// APM real mode interface connect +static void +handle_155301(struct bregs *regs) +{ + set_success(regs); +} + +// APM 16 bit protected mode interface connect +static void +handle_155302(struct bregs *regs) +{ + extern void entry_apm16(void); + regs->bx = (u32)entry_apm16; + regs->ax = SEG_BIOS; // 16 bit code segment base + regs->si = 0xfff0; // 16 bit code segment size + regs->cx = SEG_BIOS; // data segment address + regs->di = 0xfff0; // data segment length + set_success(regs); +} + +// APM 32 bit protected mode interface connect +static void +handle_155303(struct bregs *regs) +{ + extern void entry_apm32(void); + regs->ax = SEG_BIOS; // 32 bit code segment base + regs->ebx = (u32)entry_apm32; + regs->cx = SEG_BIOS; // 16 bit code segment base + // 32 bit code segment size (low 16 bits) + // 16 bit code segment size (high 16 bits) + regs->esi = 0xfff0fff0; + regs->dx = SEG_BIOS; // data segment address + regs->di = 0xfff0; // data segment length + set_success(regs); +} + +// APM interface disconnect +static void +handle_155304(struct bregs *regs) +{ + set_success(regs); +} + +// APM cpu idle +static void +handle_155305(struct bregs *regs) +{ + yield_toirq(); + set_success(regs); +} + +// APM cpu busy +static void +handle_155306(struct bregs *regs) +{ + set_success(regs); +} + +void +apm_shutdown(void) +{ + u16 pm1a_cnt = GET_GLOBAL(acpi_pm1a_cnt); + if (pm1a_cnt) + outw(0x2000, pm1a_cnt); + + irq_disable(); + for (;;) + hlt(); +} + +// APM Set Power State +static void +handle_155307(struct bregs *regs) +{ + if (regs->bx != 1) { + set_success(regs); + return; + } + switch (regs->cx) { + case 1: + dprintf(1, "APM standby request\n"); + break; + case 2: + dprintf(1, "APM suspend request\n"); + break; + case 3: + apm_shutdown(); + break; + } + set_success(regs); +} + +static void +handle_155308(struct bregs *regs) +{ + set_success(regs); +} + +// Get Power Status +static void +handle_15530a(struct bregs *regs) +{ + regs->bh = 0x01; // on line + regs->bl = 0xff; // unknown battery status + regs->ch = 0x80; // no system battery + regs->cl = 0xff; // unknown remaining time + regs->dx = 0xffff; // unknown remaining time + regs->si = 0x00; // zero battery + set_success(regs); +} + +#define RET_ENOEVENT 0x80 + +// Get PM Event +static void +handle_15530b(struct bregs *regs) +{ + set_code_invalid_silent(regs, RET_ENOEVENT); +} + +// APM Driver Version +static void +handle_15530e(struct bregs *regs) +{ + regs->ah = 1; + regs->al = 2; + set_success(regs); +} + +// APM Engage / Disengage +static void +handle_15530f(struct bregs *regs) +{ + set_success(regs); +} + +// APM Get Capabilities +static void +handle_155310(struct bregs *regs) +{ + regs->bl = 0; + regs->cx = 0; + set_success(regs); +} + +static void +handle_1553XX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +void +handle_1553(struct bregs *regs) +{ + if (! CONFIG_APMBIOS) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + + //debug_stub(regs); + switch (regs->al) { + case 0x00: handle_155300(regs); break; + case 0x01: handle_155301(regs); break; + case 0x02: handle_155302(regs); break; + case 0x03: handle_155303(regs); break; + case 0x04: handle_155304(regs); break; + case 0x05: handle_155305(regs); break; + case 0x06: handle_155306(regs); break; + case 0x07: handle_155307(regs); break; + case 0x08: handle_155308(regs); break; + case 0x0a: handle_15530a(regs); break; + case 0x0b: handle_15530b(regs); break; + case 0x0e: handle_15530e(regs); break; + case 0x0f: handle_15530f(regs); break; + case 0x10: handle_155310(regs); break; + default: handle_1553XX(regs); break; + } +} + +void VISIBLE16 VISIBLE32SEG +handle_apm(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_apm); + handle_1553(regs); +} diff --git a/qemu/roms/seabios/src/asm-offsets.c b/qemu/roms/seabios/src/asm-offsets.c new file mode 100644 index 000000000..b98f3b5a8 --- /dev/null +++ b/qemu/roms/seabios/src/asm-offsets.c @@ -0,0 +1,23 @@ +// Generate assembler offsets. + +#include "gen-defs.h" // OFFSET +#include "bregs.h" // struct bregs + +/* workaround for a warning with -Wmissing-prototypes */ +void foo(void) VISIBLE16; + +void foo(void) +{ + COMMENT("BREGS"); + OFFSET(BREGS_es, bregs, es); + OFFSET(BREGS_ds, bregs, ds); + OFFSET(BREGS_eax, bregs, eax); + OFFSET(BREGS_ebx, bregs, ebx); + OFFSET(BREGS_ecx, bregs, ecx); + OFFSET(BREGS_edx, bregs, edx); + OFFSET(BREGS_ebp, bregs, ebp); + OFFSET(BREGS_esi, bregs, esi); + OFFSET(BREGS_edi, bregs, edi); + OFFSET(BREGS_flags, bregs, flags); + OFFSET(BREGS_code, bregs, code); +} diff --git a/qemu/roms/seabios/src/biosvar.h b/qemu/roms/seabios/src/biosvar.h new file mode 100644 index 000000000..58bcbcedb --- /dev/null +++ b/qemu/roms/seabios/src/biosvar.h @@ -0,0 +1,130 @@ +// Memory access to BIOS variables. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __BIOSVAR_H +#define __BIOSVAR_H + +#include "config.h" // SEG_BDA +#include "farptr.h" // GET_FARVAR +#include "std/bda.h" // struct bios_data_area_s + + +/**************************************************************** + * Interupt vector table + ****************************************************************/ + +#define GET_IVT(vector) \ + GET_FARVAR(SEG_IVT, ((struct rmode_IVT *)0)->ivec[vector]) +#define SET_IVT(vector, segoff) \ + SET_FARVAR(SEG_IVT, ((struct rmode_IVT *)0)->ivec[vector], segoff) + +#define FUNC16(func) ({ \ + ASSERT32FLAT(); \ + extern void func (void); \ + SEGOFF(SEG_BIOS, (u32)func - BUILD_BIOS_ADDR); \ + }) + + +/**************************************************************** + * Bios Data Area (BDA) + ****************************************************************/ + +// Accessor functions +#define GET_BDA(var) \ + GET_FARVAR(SEG_BDA, ((struct bios_data_area_s *)0)->var) +#define SET_BDA(var, val) \ + SET_FARVAR(SEG_BDA, ((struct bios_data_area_s *)0)->var, (val)) + +// Helper function to set the bits of the equipment_list_flags variable. +static inline void set_equipment_flags(u16 clear, u16 set) { + u16 eqf = GET_BDA(equipment_list_flags); + SET_BDA(equipment_list_flags, (eqf & ~clear) | set); +} + + +/**************************************************************** + * Extended Bios Data Area (EBDA) + ****************************************************************/ + +// The initial size and location of EBDA +#define EBDA_SIZE_START \ + DIV_ROUND_UP(sizeof(struct extended_bios_data_area_s), 1024) +#define EBDA_SEGMENT_START \ + FLATPTR_TO_SEG(BUILD_LOWRAM_END - EBDA_SIZE_START*1024) + +// Accessor functions +static inline u16 get_ebda_seg(void) { + return GET_BDA(ebda_seg); +} +static inline struct extended_bios_data_area_s * +get_ebda_ptr(void) +{ + ASSERT32FLAT(); + return MAKE_FLATPTR(get_ebda_seg(), 0); +} +#define GET_EBDA(eseg, var) \ + GET_FARVAR(eseg, ((struct extended_bios_data_area_s *)0)->var) +#define SET_EBDA(eseg, var, val) \ + SET_FARVAR(eseg, ((struct extended_bios_data_area_s *)0)->var, (val)) + + +/**************************************************************** + * Global variables + ****************************************************************/ + +#if MODE16 == 0 && MODESEGMENT == 1 +// In 32bit segmented mode %cs may not be readable and the code may be +// relocated. The entry code sets up %gs with a readable segment and +// the code offset can be determined by get_global_offset(). +#define GLOBAL_SEGREG GS +static inline u32 __attribute_const get_global_offset(void) { + u32 ret; + asm(" calll 1f\n" + "1:popl %0\n" + " subl $1b, %0" + : "=r"(ret)); + return ret; +} +#else +#define GLOBAL_SEGREG CS +static inline u32 __attribute_const get_global_offset(void) { + return 0; +} +#endif +static inline u16 get_global_seg(void) { + return GET_SEG(GLOBAL_SEGREG); +} +#define GET_GLOBAL(var) \ + GET_VAR(GLOBAL_SEGREG, *(typeof(&(var)))((void*)&(var) \ + + get_global_offset())) +#if MODESEGMENT +#define GLOBALFLAT2GLOBAL(var) ((typeof(var))((void*)(var) - BUILD_BIOS_ADDR)) +#else +#define GLOBALFLAT2GLOBAL(var) (var) +#endif +// Access a "flat" pointer known to point to the f-segment. +#define GET_GLOBALFLAT(var) GET_GLOBAL(*GLOBALFLAT2GLOBAL(&(var))) + + +/**************************************************************** + * "Low" memory variables + ****************************************************************/ + +extern u8 _zonelow_seg, zonelow_base[]; +#define SEG_LOW ((u32)&_zonelow_seg) + +#if MODESEGMENT +#define GET_LOW(var) GET_FARVAR(SEG_LOW, (var)) +#define SET_LOW(var, val) SET_FARVAR(SEG_LOW, (var), (val)) +#define LOWFLAT2LOW(var) ((typeof(var))((void*)(var) - (u32)zonelow_base)) +#else +#define GET_LOW(var) (var) +#define SET_LOW(var, val) do { (var) = (val); } while (0) +#define LOWFLAT2LOW(var) (var) +#endif +#define GET_LOWFLAT(var) GET_LOW(*LOWFLAT2LOW(&(var))) +#define SET_LOWFLAT(var, val) SET_LOW(*LOWFLAT2LOW(&(var)), (val)) + +#endif // __BIOSVAR_H diff --git a/qemu/roms/seabios/src/block.c b/qemu/roms/seabios/src/block.c new file mode 100644 index 000000000..3f7ecb1d7 --- /dev/null +++ b/qemu/roms/seabios/src/block.c @@ -0,0 +1,582 @@ +// Disk setup and access +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "block.h" // process_op +#include "hw/ata.h" // process_ata_op +#include "hw/ahci.h" // process_ahci_op +#include "hw/blockcmd.h" // cdb_* +#include "hw/pci.h" // pci_bdf_to_bus +#include "hw/rtc.h" // rtc_read +#include "hw/virtio-blk.h" // process_virtio_blk_op +#include "malloc.h" // malloc_low +#include "output.h" // dprintf +#include "stacks.h" // stack_hop +#include "std/disk.h" // struct dpte_s +#include "string.h" // checksum +#include "util.h" // process_floppy_op + +u8 FloppyCount VARFSEG; +u8 CDCount; +struct drive_s *IDMap[3][BUILD_MAX_EXTDRIVE] VARFSEG; +u8 *bounce_buf_fl VARFSEG; + +struct drive_s * +getDrive(u8 exttype, u8 extdriveoffset) +{ + if (extdriveoffset >= ARRAY_SIZE(IDMap[0])) + return NULL; + return GET_GLOBAL(IDMap[exttype][extdriveoffset]); +} + +int getDriveId(u8 exttype, struct drive_s *drive) +{ + ASSERT32FLAT(); + int i; + for (i = 0; i < ARRAY_SIZE(IDMap[0]); i++) + if (getDrive(exttype, i) == drive) + return i; + return -1; +} + +int create_bounce_buf(void) +{ + if (bounce_buf_fl) + return 0; + + u8 *buf = malloc_low(CDROM_SECTOR_SIZE); + if (!buf) { + warn_noalloc(); + return -1; + } + bounce_buf_fl = buf; + return 0; +} + +/**************************************************************** + * Disk geometry translation + ****************************************************************/ + +static u8 +get_translation(struct drive_s *drive) +{ + u8 type = drive->type; + if (CONFIG_QEMU && type == DTYPE_ATA) { + // Emulators pass in the translation info via nvram. + u8 ataid = drive->cntl_id; + u8 channel = ataid / 2; + u8 translation = rtc_read(CMOS_BIOS_DISKTRANSFLAG + channel/2); + translation >>= 2 * (ataid % 4); + translation &= 0x03; + return translation; + } + + // Otherwise use a heuristic to determine translation type. + u16 heads = drive->pchs.head; + u16 cylinders = drive->pchs.cylinder; + u16 spt = drive->pchs.sector; + u64 sectors = drive->sectors; + u64 psectors = (u64)heads * cylinders * spt; + if (!heads || !cylinders || !spt || psectors > sectors) + // pchs doesn't look valid - use LBA. + return TRANSLATION_LBA; + + if (cylinders <= 1024 && heads <= 16 && spt <= 63) + return TRANSLATION_NONE; + if (cylinders * heads <= 131072) + return TRANSLATION_LARGE; + return TRANSLATION_LBA; +} + +static void +setup_translation(struct drive_s *drive) +{ + u8 translation = get_translation(drive); + drive->translation = translation; + + u16 heads = drive->pchs.head ; + u16 cylinders = drive->pchs.cylinder; + u16 spt = drive->pchs.sector; + u64 sectors = drive->sectors; + const char *desc = NULL; + + switch (translation) { + default: + case TRANSLATION_NONE: + desc = "none"; + break; + case TRANSLATION_LBA: + desc = "lba"; + spt = 63; + if (sectors > 63*255*1024) { + heads = 255; + cylinders = 1024; + break; + } + u32 sect = (u32)sectors / 63; + heads = sect / 1024; + if (heads>128) + heads = 255; + else if (heads>64) + heads = 128; + else if (heads>32) + heads = 64; + else if (heads>16) + heads = 32; + else + heads = 16; + cylinders = sect / heads; + break; + case TRANSLATION_RECHS: + desc = "r-echs"; + // Take care not to overflow + if (heads==16) { + if (cylinders>61439) + cylinders=61439; + heads=15; + cylinders = (u16)((u32)(cylinders)*16/15); + } + // then go through the large bitshift process + case TRANSLATION_LARGE: + if (translation == TRANSLATION_LARGE) + desc = "large"; + while (cylinders > 1024) { + cylinders >>= 1; + heads <<= 1; + + // If we max out the head count + if (heads > 127) + break; + } + break; + } + // clip to 1024 cylinders in lchs + if (cylinders > 1024) + cylinders = 1024; + dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n" + , drive + , drive->pchs.cylinder, drive->pchs.head, drive->pchs.sector + , desc + , cylinders, heads, spt + , (u32)sectors); + + drive->lchs.head = heads; + drive->lchs.cylinder = cylinders; + drive->lchs.sector = spt; +} + + +/**************************************************************** + * Drive mapping + ****************************************************************/ + +// Fill in Fixed Disk Parameter Table (located in ebda). +static void +fill_fdpt(struct drive_s *drive, int hdid) +{ + if (hdid > 1) + return; + + u16 nlc = drive->lchs.cylinder; + u16 nlh = drive->lchs.head; + u16 nls = drive->lchs.sector; + + u16 npc = drive->pchs.cylinder; + u16 nph = drive->pchs.head; + u16 nps = drive->pchs.sector; + + struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid]; + fdpt->precompensation = 0xffff; + fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3); + fdpt->landing_zone = npc; + fdpt->cylinders = nlc; + fdpt->heads = nlh; + fdpt->sectors = nls; + + if (nlc != npc || nlh != nph || nls != nps) { + // Logical mapping present - use extended structure. + + // complies with Phoenix style Translated Fixed Disk Parameter + // Table (FDPT) + fdpt->phys_cylinders = npc; + fdpt->phys_heads = nph; + fdpt->phys_sectors = nps; + fdpt->a0h_signature = 0xa0; + + // Checksum structure. + fdpt->checksum -= checksum(fdpt, sizeof(*fdpt)); + } + + if (hdid == 0) + SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof( + struct extended_bios_data_area_s, fdpt[0]))); + else + SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof( + struct extended_bios_data_area_s, fdpt[1]))); +} + +// Find spot to add a drive +static void +add_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive) +{ + if (*count >= ARRAY_SIZE(IDMap[0])) { + warn_noalloc(); + return; + } + idmap[*count] = drive; + *count = *count + 1; +} + +// Map a hard drive +void +map_hd_drive(struct drive_s *drive) +{ + ASSERT32FLAT(); + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + int hdid = bda->hdcount; + dprintf(3, "Mapping hd drive %p to %d\n", drive, hdid); + add_drive(IDMap[EXTTYPE_HD], &bda->hdcount, drive); + + // Setup disk geometry translation. + setup_translation(drive); + + // Fill "fdpt" structure. + fill_fdpt(drive, hdid); +} + +// Map a cd +void +map_cd_drive(struct drive_s *drive) +{ + ASSERT32FLAT(); + dprintf(3, "Mapping cd drive %p\n", drive); + add_drive(IDMap[EXTTYPE_CD], &CDCount, drive); +} + +// Map a floppy +void +map_floppy_drive(struct drive_s *drive) +{ + ASSERT32FLAT(); + dprintf(3, "Mapping floppy drive %p\n", drive); + add_drive(IDMap[EXTTYPE_FLOPPY], &FloppyCount, drive); + + // Update equipment word bits for floppy + if (FloppyCount == 1) { + // 1 drive, ready for boot + set_equipment_flags(0x41, 0x01); + SET_BDA(floppy_harddisk_info, 0x07); + } else if (FloppyCount >= 2) { + // 2 drives, ready for boot + set_equipment_flags(0x41, 0x41); + SET_BDA(floppy_harddisk_info, 0x77); + } +} + + +/**************************************************************** + * Extended Disk Drive (EDD) get drive parameters + ****************************************************************/ + +static int +fill_generic_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf + , u32 dpte_so, char *iface_type + , int bdf, u8 channel, u16 iobase, u64 device_path) +{ + u16 size = GET_FARVAR(seg, param_far->size); + u16 t13 = size == 74; + + // Buffer is too small + if (size < 26) + return DISK_RET_EPARAM; + + // EDD 1.x + + u8 type = GET_GLOBALFLAT(drive_gf->type); + u16 npc = GET_GLOBALFLAT(drive_gf->pchs.cylinder); + u16 nph = GET_GLOBALFLAT(drive_gf->pchs.head); + u16 nps = GET_GLOBALFLAT(drive_gf->pchs.sector); + u64 lba = GET_GLOBALFLAT(drive_gf->sectors); + u16 blksize = GET_GLOBALFLAT(drive_gf->blksize); + + dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n" + , size, type, npc, nph, nps, (u32)lba, blksize); + + SET_FARVAR(seg, param_far->size, 26); + if (lba == (u64)-1) { + // 0x74 = removable, media change, lockable, max values + SET_FARVAR(seg, param_far->infos, 0x74); + SET_FARVAR(seg, param_far->cylinders, 0xffffffff); + SET_FARVAR(seg, param_far->heads, 0xffffffff); + SET_FARVAR(seg, param_far->spt, 0xffffffff); + } else { + if (lba > (u64)nps*nph*0x3fff) { + SET_FARVAR(seg, param_far->infos, 0x00); // geometry is invalid + SET_FARVAR(seg, param_far->cylinders, 0x3fff); + } else { + SET_FARVAR(seg, param_far->infos, 0x02); // geometry is valid + SET_FARVAR(seg, param_far->cylinders, (u32)npc); + } + SET_FARVAR(seg, param_far->heads, (u32)nph); + SET_FARVAR(seg, param_far->spt, (u32)nps); + } + SET_FARVAR(seg, param_far->sector_count, lba); + SET_FARVAR(seg, param_far->blksize, blksize); + + if (size < 30 || !dpte_so) + return DISK_RET_SUCCESS; + + // EDD 2.x + + SET_FARVAR(seg, param_far->size, 30); + SET_FARVAR(seg, param_far->dpte.segoff, dpte_so); + + if (size < 66 || !iface_type) + return DISK_RET_SUCCESS; + + // EDD 3.x + SET_FARVAR(seg, param_far->key, 0xbedd); + SET_FARVAR(seg, param_far->dpi_length, t13 ? 44 : 36); + SET_FARVAR(seg, param_far->reserved1, 0); + SET_FARVAR(seg, param_far->reserved2, 0); + + int i; + for (i=0; i<sizeof(param_far->iface_type); i++) + SET_FARVAR(seg, param_far->iface_type[i], GET_GLOBAL(iface_type[i])); + + if (bdf != -1) { + SET_FARVAR(seg, param_far->host_bus[0], 'P'); + SET_FARVAR(seg, param_far->host_bus[1], 'C'); + SET_FARVAR(seg, param_far->host_bus[2], 'I'); + SET_FARVAR(seg, param_far->host_bus[3], ' '); + + u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8) + | (pci_bdf_to_fn(bdf) << 16)); + if (t13) + path |= channel << 24; + + SET_FARVAR(seg, param_far->iface_path, path); + } else { + // ISA + SET_FARVAR(seg, param_far->host_bus[0], 'I'); + SET_FARVAR(seg, param_far->host_bus[1], 'S'); + SET_FARVAR(seg, param_far->host_bus[2], 'A'); + SET_FARVAR(seg, param_far->host_bus[3], ' '); + + SET_FARVAR(seg, param_far->iface_path, iobase); + } + + if (t13) { + SET_FARVAR(seg, param_far->t13.device_path[0], device_path); + SET_FARVAR(seg, param_far->t13.device_path[1], 0); + + SET_FARVAR(seg, param_far->t13.checksum + , -checksum_far(seg, (void*)param_far+30, 43)); + } else { + SET_FARVAR(seg, param_far->phoenix.device_path, device_path); + + SET_FARVAR(seg, param_far->phoenix.checksum + , -checksum_far(seg, (void*)param_far+30, 35)); + } + + return DISK_RET_SUCCESS; +} + +struct dpte_s DefaultDPTE VARLOW; + +static int +fill_ata_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) +{ + if (!CONFIG_ATA) + return DISK_RET_EPARAM; + + // Fill in dpte + struct atadrive_s *adrive_gf = container_of( + drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + u8 irq = GET_GLOBALFLAT(chan_gf->irq); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + int bdf = GET_GLOBALFLAT(chan_gf->pci_bdf); + u8 channel = GET_GLOBALFLAT(chan_gf->chanid); + + u16 options = 0; + if (GET_GLOBALFLAT(drive_gf->type) == DTYPE_ATA) { + u8 translation = GET_GLOBALFLAT(drive_gf->translation); + if (translation != TRANSLATION_NONE) { + options |= 1<<3; // CHS translation + if (translation == TRANSLATION_LBA) + options |= 1<<9; + if (translation == TRANSLATION_RECHS) + options |= 3<<9; + } + } else { + // ATAPI + options |= 1<<5; // removable device + options |= 1<<6; // atapi device + } + options |= 1<<4; // lba translation + if (CONFIG_ATA_PIO32) + options |= 1<<7; + + SET_LOW(DefaultDPTE.iobase1, iobase1); + SET_LOW(DefaultDPTE.iobase2, iobase2 + ATA_CB_DC); + SET_LOW(DefaultDPTE.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) + | ATA_CB_DH_LBA)); + SET_LOW(DefaultDPTE.unused, 0xcb); + SET_LOW(DefaultDPTE.irq, irq); + SET_LOW(DefaultDPTE.blkcount, 1); + SET_LOW(DefaultDPTE.dma, 0); + SET_LOW(DefaultDPTE.pio, 0); + SET_LOW(DefaultDPTE.options, options); + SET_LOW(DefaultDPTE.reserved, 0); + SET_LOW(DefaultDPTE.revision, 0x11); + + u8 sum = checksum_far(SEG_LOW, &DefaultDPTE, 15); + SET_LOW(DefaultDPTE.checksum, -sum); + + return fill_generic_edd( + seg, param_far, drive_gf, SEGOFF(SEG_LOW, (u32)&DefaultDPTE).segoff + , "ATA ", bdf, channel, iobase1, slave); +} + +int noinline +fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) +{ + switch (GET_GLOBALFLAT(drive_gf->type)) { + case DTYPE_ATA: + case DTYPE_ATA_ATAPI: + return fill_ata_edd(seg, param_far, drive_gf); + case DTYPE_VIRTIO_BLK: + case DTYPE_VIRTIO_SCSI: + return fill_generic_edd( + seg, param_far, drive_gf, 0xffffffff + , "SCSI ", GET_GLOBALFLAT(drive_gf->cntl_id), 0, 0, 0); + default: + return fill_generic_edd(seg, param_far, drive_gf, 0, NULL, 0, 0, 0, 0); + } +} + + +/**************************************************************** + * 16bit calling interface + ****************************************************************/ + +int VISIBLE32FLAT +process_atapi_op(struct disk_op_s *op) +{ + switch (op->command) { + case CMD_WRITE: + case CMD_FORMAT: + return DISK_RET_EWRITEPROTECT; + default: + return scsi_process_op(op); + } +} + +// Execute a disk_op request. +int +process_op(struct disk_op_s *op) +{ + ASSERT16(); + int ret, origcount = op->count; + if (origcount * GET_GLOBALFLAT(op->drive_gf->blksize) > 64*1024) { + op->count = 0; + return DISK_RET_EBOUNDARY; + } + u8 type = GET_GLOBALFLAT(op->drive_gf->type); + switch (type) { + case DTYPE_FLOPPY: + ret = process_floppy_op(op); + break; + case DTYPE_ATA: + ret = process_ata_op(op); + break; + case DTYPE_RAMDISK: + ret = process_ramdisk_op(op); + break; + case DTYPE_CDEMU: + ret = process_cdemu_op(op); + break; + case DTYPE_VIRTIO_BLK: + ret = process_virtio_blk_op(op); + break; + case DTYPE_AHCI: ; + extern void _cfunc32flat_process_ahci_op(void); + ret = call32(_cfunc32flat_process_ahci_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + case DTYPE_ATA_ATAPI: + ret = process_atapi_op(op); + break; + case DTYPE_AHCI_ATAPI: ; + extern void _cfunc32flat_process_atapi_op(void); + ret = call32(_cfunc32flat_process_atapi_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + case DTYPE_SDCARD: ; + extern void _cfunc32flat_process_sdcard_op(void); + ret = call32(_cfunc32flat_process_sdcard_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + case DTYPE_USB: + case DTYPE_UAS: + case DTYPE_VIRTIO_SCSI: + case DTYPE_LSI_SCSI: + case DTYPE_ESP_SCSI: + case DTYPE_MEGASAS: + ret = scsi_process_op(op); + break; + case DTYPE_USB_32: + case DTYPE_UAS_32: + case DTYPE_PVSCSI: ; + extern void _cfunc32flat_scsi_process_op(void); + ret = call32(_cfunc32flat_scsi_process_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + default: + ret = DISK_RET_EPARAM; + break; + } + if (ret && op->count == origcount) + // If the count hasn't changed on error, assume no data transferred. + op->count = 0; + return ret; +} + +// Execute a "disk_op_s" request - this runs on the extra stack. +static int +__send_disk_op(struct disk_op_s *op_far, u16 op_seg) +{ + struct disk_op_s dop; + memcpy_far(GET_SEG(SS), &dop + , op_seg, op_far + , sizeof(dop)); + + dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n" + , dop.drive_gf, (u32)dop.lba, dop.buf_fl + , dop.count, dop.command); + + int status = process_op(&dop); + + // Update count with total sectors transferred. + SET_FARVAR(op_seg, op_far->count, dop.count); + + return status; +} + +// Execute a "disk_op_s" request by jumping to the extra 16bit stack. +int +send_disk_op(struct disk_op_s *op) +{ + ASSERT16(); + if (! CONFIG_DRIVES) + return -1; + + return stack_hop((u32)op, GET_SEG(SS), __send_disk_op); +} diff --git a/qemu/roms/seabios/src/block.h b/qemu/roms/seabios/src/block.h new file mode 100644 index 000000000..8182288d4 --- /dev/null +++ b/qemu/roms/seabios/src/block.h @@ -0,0 +1,109 @@ +#ifndef __BLOCK_H +#define __BLOCK_H + +#include "types.h" // u32 + + +/**************************************************************** + * Disk command request + ****************************************************************/ + +struct disk_op_s { + u64 lba; + void *buf_fl; + struct drive_s *drive_gf; + u16 count; + u8 command; +}; + +#define CMD_RESET 0x00 +#define CMD_READ 0x02 +#define CMD_WRITE 0x03 +#define CMD_VERIFY 0x04 +#define CMD_FORMAT 0x05 +#define CMD_SEEK 0x07 +#define CMD_ISREADY 0x10 + + +/**************************************************************** + * Global storage + ****************************************************************/ + +struct chs_s { + u16 head; + u16 cylinder; + u16 sector; + u16 pad; +}; + +struct drive_s { + u8 type; // Driver type (DTYPE_*) + u8 floppy_type; // Type of floppy (only for floppy drives). + struct chs_s lchs; // Logical CHS + u64 sectors; // Total sectors count + u32 cntl_id; // Unique id for a given driver type. + u8 removable; // Is media removable (currently unused) + + // Info for EDD calls + u8 translation; // type of translation + u16 blksize; // block size + struct chs_s pchs; // Physical CHS +}; + +#define DISK_SECTOR_SIZE 512 +#define CDROM_SECTOR_SIZE 2048 + +#define DTYPE_NONE 0x00 +#define DTYPE_FLOPPY 0x10 +#define DTYPE_ATA 0x20 +#define DTYPE_ATA_ATAPI 0x21 +#define DTYPE_RAMDISK 0x30 +#define DTYPE_CDEMU 0x40 +#define DTYPE_AHCI 0x50 +#define DTYPE_AHCI_ATAPI 0x51 +#define DTYPE_VIRTIO_SCSI 0x60 +#define DTYPE_VIRTIO_BLK 0x61 +#define DTYPE_USB 0x70 +#define DTYPE_USB_32 0x71 +#define DTYPE_UAS 0x72 +#define DTYPE_UAS_32 0x73 +#define DTYPE_LSI_SCSI 0x80 +#define DTYPE_ESP_SCSI 0x81 +#define DTYPE_MEGASAS 0x82 +#define DTYPE_PVSCSI 0x83 +#define DTYPE_SDCARD 0x90 + +#define MAXDESCSIZE 80 + +#define TRANSLATION_NONE 0 +#define TRANSLATION_LBA 1 +#define TRANSLATION_LARGE 2 +#define TRANSLATION_RECHS 3 + +#define EXTTYPE_FLOPPY 0 +#define EXTTYPE_HD 1 +#define EXTTYPE_CD 2 + +#define EXTSTART_HD 0x80 +#define EXTSTART_CD 0xE0 + + +/**************************************************************** + * Function defs + ****************************************************************/ + +// block.c +extern u8 FloppyCount, CDCount; +extern u8 *bounce_buf_fl; +struct drive_s *getDrive(u8 exttype, u8 extdriveoffset); +int getDriveId(u8 exttype, struct drive_s *drive); +void map_floppy_drive(struct drive_s *drive); +void map_hd_drive(struct drive_s *drive); +void map_cd_drive(struct drive_s *drive); +struct int13dpt_s; +int fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf); +int process_op(struct disk_op_s *op); +int send_disk_op(struct disk_op_s *op); +int create_bounce_buf(void); + +#endif // block.h diff --git a/qemu/roms/seabios/src/bmp.c b/qemu/roms/seabios/src/bmp.c new file mode 100644 index 000000000..d8e76b789 --- /dev/null +++ b/qemu/roms/seabios/src/bmp.c @@ -0,0 +1,117 @@ +/* +* Basic BMP data process and Raw picture data handle functions. +* Could be used to adjust pixel data format, get infomation, etc. +* +* Copyright (C) 2011 Wayne Xia <xiawenc@cn.ibm.com> +* +* This work is licensed under the terms of the GNU LGPLv3. +*/ +#include "malloc.h" // malloc_tmphigh +#include "string.h" // memcpy +#include "util.h" // struct bmp_decdata + +struct bmp_decdata { + struct tagRGBQUAD *quadp; + unsigned char *datap; + int width; + int height; + int bpp; +}; + +#define bmp_load4byte(addr) (*(u32 *)(addr)) +#define bmp_load2byte(addr) (*(u16 *)(addr)) + +typedef struct tagBITMAPFILEHEADER { + u8 bfType[2]; + u8 bfSize[4]; + u8 bfReserved1[2]; + u8 bfReserved2[2]; + u8 bfOffBits[4]; +} BITMAPFILEHEADER, tagBITMAPFILEHEADER; + +typedef struct tagBITMAPINFOHEADER { + u8 biSize[4]; + u8 biWidth[4]; + u8 biHeight[4]; + u8 biPlanes[2]; + u8 biBitCount[2]; + u8 biCompression[4]; + u8 biSizeImage[4]; + u8 biXPelsPerMeter[4]; + u8 biYPelsPerMeter[4]; + u8 biClrUsed[4]; + u8 biClrImportant[4]; +} BITMAPINFOHEADER, tagBITMAPINFOHEADER; + +typedef struct tagRGBQUAD { + u8 rgbBlue; + u8 rgbGreen; + u8 rgbRed; + u8 rgbReserved; +} RGBQUAD, tagRGBQUAD; + +/* flat picture data adjusting function +* description: +* switch the vertical line sequence +* arrange horizontal pixel data, add extra space in the dest buffer +* for every line +*/ +static void raw_data_format_adjust_24bpp(u8 *src, u8 *dest, int width, + int height, int bytes_per_line_dest) +{ + int bytes_per_line_src = 3 * width; + int i; + for (i = 0 ; i < height ; i++) { + memcpy(dest + i * bytes_per_line_dest, + src + (height - 1 - i) * bytes_per_line_src, bytes_per_line_src); + } +} + +/* allocate decdata struct */ +struct bmp_decdata *bmp_alloc(void) +{ + struct bmp_decdata *bmp = malloc_tmphigh(sizeof(*bmp)); + return bmp; +} + +/* extract information from bmp file data */ +int bmp_decode(struct bmp_decdata *bmp, unsigned char *data, int data_size) +{ + if (data_size < 54) + return 1; + + u16 bmp_filehead = bmp_load2byte(data + 0); + if (bmp_filehead != 0x4d42) + return 2; + u32 bmp_recordsize = bmp_load4byte(data + 2); + if (bmp_recordsize != data_size) + return 3; + u32 bmp_dataoffset = bmp_load4byte(data + 10); + bmp->datap = (unsigned char *)data + bmp_dataoffset; + bmp->width = bmp_load4byte(data + 18); + bmp->height = bmp_load4byte(data + 22); + bmp->bpp = bmp_load2byte(data + 28); + return 0; +} + +/* get bmp properties */ +void bmp_get_size(struct bmp_decdata *bmp, int *width, int *height) +{ + *width = bmp->width; + *height = bmp->height; +} + +/* flush flat picture data to *pc */ +int bmp_show(struct bmp_decdata *bmp, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest) +{ + if (bmp->datap == pic) + return 0; + /* now only support 24bpp bmp file */ + if ((depth == 24) && (bmp->bpp == 24)) { + raw_data_format_adjust_24bpp(bmp->datap, pic, width, height, + bytes_per_line_dest); + return 0; + } + return 1; +} diff --git a/qemu/roms/seabios/src/boot.c b/qemu/roms/seabios/src/boot.c new file mode 100644 index 000000000..f23e9e154 --- /dev/null +++ b/qemu/roms/seabios/src/boot.c @@ -0,0 +1,756 @@ +// Code to load disk image and start system boot. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // qemu_cfg_show_boot_menu +#include "hw/pci.h" // pci_bdf_to_* +#include "hw/rtc.h" // rtc_read +#include "hw/usb.h" // struct usbdevice_s +#include "list.h" // hlist_node +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "std/disk.h" // struct mbr_s +#include "string.h" // memset +#include "util.h" // irqtimer_calc + + +/**************************************************************** + * Boot priority ordering + ****************************************************************/ + +static char **Bootorder VARVERIFY32INIT; +static int BootorderCount; + +static void +loadBootOrder(void) +{ + if (!CONFIG_BOOTORDER) + return; + + char *f = romfile_loadfile("bootorder", NULL); + if (!f) + return; + + int i = 0; + BootorderCount = 1; + while (f[i]) { + if (f[i] == '\n') + BootorderCount++; + i++; + } + Bootorder = malloc_tmphigh(BootorderCount*sizeof(char*)); + if (!Bootorder) { + warn_noalloc(); + free(f); + BootorderCount = 0; + return; + } + + dprintf(1, "boot order:\n"); + i = 0; + do { + Bootorder[i] = f; + f = strchr(f, '\n'); + if (f) + *(f++) = '\0'; + Bootorder[i] = nullTrailingSpace(Bootorder[i]); + dprintf(1, "%d: %s\n", i+1, Bootorder[i]); + i++; + } while (f); +} + +// See if 'str' starts with 'glob' - if glob contains an '*' character +// it will match any number of characters in str that aren't a '/' or +// the next glob character. +static char * +glob_prefix(const char *glob, const char *str) +{ + for (;;) { + if (!*glob && (!*str || *str == '/')) + return (char*)str; + if (*glob == '*') { + if (!*str || *str == '/' || *str == glob[1]) + glob++; + else + str++; + continue; + } + if (*glob != *str) + return NULL; + glob++; + str++; + } +} + +// Search the bootorder list for the given glob pattern. +static int +find_prio(const char *glob) +{ + dprintf(1, "Searching bootorder for: %s\n", glob); + int i; + for (i = 0; i < BootorderCount; i++) + if (glob_prefix(glob, Bootorder[i])) + return i+1; + return -1; +} + +#define FW_PCI_DOMAIN "/pci@i0cf8" + +static char * +build_pci_path(char *buf, int max, const char *devname, struct pci_device *pci) +{ + // Build the string path of a bdf - for example: /pci@i0cf8/isa@1,2 + char *p = buf; + if (pci->parent) { + p = build_pci_path(p, max, "pci-bridge", pci->parent); + } else { + if (pci->rootbus) + p += snprintf(p, max, "/pci-root@%x", pci->rootbus); + p += snprintf(p, buf+max-p, "%s", FW_PCI_DOMAIN); + } + + int dev = pci_bdf_to_dev(pci->bdf), fn = pci_bdf_to_fn(pci->bdf); + p += snprintf(p, buf+max-p, "/%s@%x", devname, dev); + if (fn) + p += snprintf(p, buf+max-p, ",%x", fn); + return p; +} + +int bootprio_find_pci_device(struct pci_device *pci) +{ + if (CONFIG_CSM) + return csm_bootprio_pci(pci); + if (!CONFIG_BOOTORDER) + return -1; + // Find pci device - for example: /pci@i0cf8/ethernet@5 + char desc[256]; + build_pci_path(desc, sizeof(desc), "*", pci); + return find_prio(desc); +} + +int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun) +{ + if (!CONFIG_BOOTORDER) + return -1; + if (!pci) + // support only pci machine for now + return -1; + // Find scsi drive - for example: /pci@i0cf8/scsi@5/channel@0/disk@1,0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "*", pci); + snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%x,%x", target, lun); + return find_prio(desc); +} + +int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave) +{ + if (CONFIG_CSM) + return csm_bootprio_ata(pci, chanid, slave); + if (!CONFIG_BOOTORDER) + return -1; + if (!pci) + // support only pci machine for now + return -1; + // Find ata drive - for example: /pci@i0cf8/ide@1,1/drive@1/disk@0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "*", pci); + snprintf(p, desc+sizeof(desc)-p, "/drive@%x/disk@%x", chanid, slave); + return find_prio(desc); +} + +int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid) +{ + if (CONFIG_CSM) + return csm_bootprio_fdc(pci, port, fdid); + if (!CONFIG_BOOTORDER) + return -1; + if (!pci) + // support only pci machine for now + return -1; + // Find floppy - for example: /pci@i0cf8/isa@1/fdc@03f1/floppy@0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "isa", pci); + snprintf(p, desc+sizeof(desc)-p, "/fdc@%04x/floppy@%x", port, fdid); + return find_prio(desc); +} + +int bootprio_find_pci_rom(struct pci_device *pci, int instance) +{ + if (!CONFIG_BOOTORDER) + return -1; + // Find pci rom - for example: /pci@i0cf8/scsi@3:rom2 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "*", pci); + if (instance) + snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance); + return find_prio(desc); +} + +int bootprio_find_named_rom(const char *name, int instance) +{ + if (!CONFIG_BOOTORDER) + return -1; + // Find named rom - for example: /rom@genroms/linuxboot.bin + char desc[256], *p; + p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name); + if (instance) + snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance); + return find_prio(desc); +} + +static char * +build_usb_path(char *buf, int max, struct usbhub_s *hub) +{ + if (!hub->usbdev) + // Root hub - nothing to add. + return buf; + char *p = build_usb_path(buf, max, hub->usbdev->hub); + p += snprintf(p, buf+max-p, "/hub@%x", hub->usbdev->port+1); + return p; +} + +int bootprio_find_usb(struct usbdevice_s *usbdev, int lun) +{ + if (!CONFIG_BOOTORDER) + return -1; + // Find usb - for example: /pci@i0cf8/usb@1,2/storage@1/channel@0/disk@0,0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "usb", usbdev->hub->cntl->pci); + p = build_usb_path(p, desc+sizeof(desc)-p, usbdev->hub); + snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%x" + , usbdev->port+1, lun); + int ret = find_prio(desc); + if (ret >= 0) + return ret; + // Try usb-host/redir - for example: /pci@i0cf8/usb@1,2/usb-host@1 + snprintf(p, desc+sizeof(desc)-p, "/usb-*@%x", usbdev->port+1); + return find_prio(desc); +} + + +/**************************************************************** + * Boot setup + ****************************************************************/ + +static int BootRetryTime; +static int CheckFloppySig = 1; + +#define DEFAULT_PRIO 9999 + +static int DefaultFloppyPrio = 101; +static int DefaultCDPrio = 102; +static int DefaultHDPrio = 103; +static int DefaultBEVPrio = 104; + +void +boot_init(void) +{ + if (! CONFIG_BOOT) + return; + + if (CONFIG_QEMU) { + // On emulators, get boot order from nvram. + if (rtc_read(CMOS_BIOS_BOOTFLAG1) & 1) + CheckFloppySig = 0; + u32 bootorder = (rtc_read(CMOS_BIOS_BOOTFLAG2) + | ((rtc_read(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4)); + DefaultFloppyPrio = DefaultCDPrio = DefaultHDPrio + = DefaultBEVPrio = DEFAULT_PRIO; + int i; + for (i=101; i<104; i++) { + u32 val = bootorder & 0x0f; + bootorder >>= 4; + switch (val) { + case 1: DefaultFloppyPrio = i; break; + case 2: DefaultHDPrio = i; break; + case 3: DefaultCDPrio = i; break; + case 4: DefaultBEVPrio = i; break; + } + } + } + + BootRetryTime = romfile_loadint("etc/boot-fail-wait", 60*1000); + + loadBootOrder(); +} + + +/**************************************************************** + * BootList handling + ****************************************************************/ + +struct bootentry_s { + int type; + union { + u32 data; + struct segoff_s vector; + struct drive_s *drive; + }; + int priority; + const char *description; + struct hlist_node node; +}; +static struct hlist_head BootList VARVERIFY32INIT; + +#define IPL_TYPE_FLOPPY 0x01 +#define IPL_TYPE_HARDDISK 0x02 +#define IPL_TYPE_CDROM 0x03 +#define IPL_TYPE_CBFS 0x20 +#define IPL_TYPE_BEV 0x80 +#define IPL_TYPE_BCV 0x81 +#define IPL_TYPE_HALT 0xf0 + +static void +bootentry_add(int type, int prio, u32 data, const char *desc) +{ + if (! CONFIG_BOOT) + return; + struct bootentry_s *be = malloc_tmp(sizeof(*be)); + if (!be) { + warn_noalloc(); + return; + } + be->type = type; + be->priority = prio; + be->data = data; + be->description = desc ?: "?"; + dprintf(3, "Registering bootable: %s (type:%d prio:%d data:%x)\n" + , be->description, type, prio, data); + + // Add entry in sorted order. + struct hlist_node **pprev; + struct bootentry_s *pos; + hlist_for_each_entry_pprev(pos, pprev, &BootList, node) { + if (be->priority < pos->priority) + break; + if (be->priority > pos->priority) + continue; + if (be->type < pos->type) + break; + if (be->type > pos->type) + continue; + if (be->type <= IPL_TYPE_CDROM + && (be->drive->type < pos->drive->type + || (be->drive->type == pos->drive->type + && be->drive->cntl_id < pos->drive->cntl_id))) + break; + } + hlist_add(&be->node, pprev); +} + +// Return the given priority if it's set - defaultprio otherwise. +static inline int defPrio(int priority, int defaultprio) { + return (priority < 0) ? defaultprio : priority; +} + +// Add a BEV vector for a given pnp compatible option rom. +void +boot_add_bev(u16 seg, u16 bev, u16 desc, int prio) +{ + bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio) + , SEGOFF(seg, bev).segoff + , desc ? MAKE_FLATPTR(seg, desc) : "Unknown"); + DefaultBEVPrio = DEFAULT_PRIO; +} + +// Add a bcv entry for an expansion card harddrive or legacy option rom +void +boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio) +{ + bootentry_add(IPL_TYPE_BCV, defPrio(prio, DefaultHDPrio) + , SEGOFF(seg, ip).segoff + , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom"); +} + +void +boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio) + , (u32)drive_g, desc); +} + +void +boot_add_hd(struct drive_s *drive_g, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio) + , (u32)drive_g, desc); +} + +void +boot_add_cd(struct drive_s *drive_g, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio) + , (u32)drive_g, desc); +} + +// Add a CBFS payload entry +void +boot_add_cbfs(void *data, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc); +} + + +/**************************************************************** + * Keyboard calls + ****************************************************************/ + +// See if a keystroke is pending in the keyboard buffer. +static int +check_for_keystroke(void) +{ + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF|F_ZF; + br.ah = 1; + call16_int(0x16, &br); + return !(br.flags & F_ZF); +} + +// Return a keystroke - waiting forever if necessary. +static int +get_raw_keystroke(void) +{ + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x16, &br); + return br.ah; +} + +// Read a keystroke - waiting up to 'msec' milliseconds. +static int +get_keystroke(int msec) +{ + u32 end = irqtimer_calc(msec); + for (;;) { + if (check_for_keystroke()) + return get_raw_keystroke(); + if (irqtimer_check(end)) + return -1; + yield_toirq(); + } +} + + +/**************************************************************** + * Boot menu and BCV execution + ****************************************************************/ + +#define DEFAULT_BOOTMENU_WAIT 2500 + +// Show IPL option menu. +void +interactive_bootmenu(void) +{ + // XXX - show available drives? + + if (! CONFIG_BOOTMENU || !romfile_loadint("etc/show-boot-menu", 1)) + return; + + while (get_keystroke(0) >= 0) + ; + + char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); + int menukey = romfile_loadint("etc/boot-menu-key", 0x86); + printf("%s", bootmsg ?: "\nPress F12 for boot menu.\n\n"); + free(bootmsg); + + u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); + enable_bootsplash(); + int scan_code = get_keystroke(menutime); + disable_bootsplash(); + if (scan_code != menukey) + return; + + while (get_keystroke(0) >= 0) + ; + + printf("Select boot device:\n\n"); + wait_threads(); + + // Show menu items + int maxmenu = 0; + struct bootentry_s *pos; + hlist_for_each_entry(pos, &BootList, node) { + char desc[60]; + maxmenu++; + printf("%d. %s\n", maxmenu + , strtcpy(desc, pos->description, ARRAY_SIZE(desc))); + } + + // Get key press + for (;;) { + scan_code = get_keystroke(1000); + if (scan_code >= 1 && scan_code <= maxmenu+1) + break; + } + printf("\n"); + if (scan_code == 0x01) + // ESC + return; + + // Find entry and make top priority. + int choice = scan_code - 1; + hlist_for_each_entry(pos, &BootList, node) { + if (! --choice) + break; + } + hlist_del(&pos->node); + pos->priority = 0; + hlist_add_head(&pos->node, &BootList); +} + +// BEV (Boot Execution Vector) list +struct bev_s { + int type; + u32 vector; +}; +static struct bev_s BEV[20]; +static int BEVCount; +static int HaveHDBoot, HaveFDBoot; + +static void +add_bev(int type, u32 vector) +{ + if (type == IPL_TYPE_HARDDISK && HaveHDBoot++) + return; + if (type == IPL_TYPE_FLOPPY && HaveFDBoot++) + return; + if (BEVCount >= ARRAY_SIZE(BEV)) + return; + struct bev_s *bev = &BEV[BEVCount++]; + bev->type = type; + bev->vector = vector; +} + +// Prepare for boot - show menu and run bcvs. +void +bcv_prepboot(void) +{ + if (! CONFIG_BOOT) + return; + + int haltprio = find_prio("HALT"); + if (haltprio >= 0) + bootentry_add(IPL_TYPE_HALT, haltprio, 0, "HALT"); + + // Map drives and populate BEV list + struct bootentry_s *pos; + hlist_for_each_entry(pos, &BootList, node) { + switch (pos->type) { + case IPL_TYPE_BCV: + call_bcv(pos->vector.seg, pos->vector.offset); + add_bev(IPL_TYPE_HARDDISK, 0); + break; + case IPL_TYPE_FLOPPY: + map_floppy_drive(pos->drive); + add_bev(IPL_TYPE_FLOPPY, 0); + break; + case IPL_TYPE_HARDDISK: + map_hd_drive(pos->drive); + add_bev(IPL_TYPE_HARDDISK, 0); + break; + case IPL_TYPE_CDROM: + map_cd_drive(pos->drive); + // NO BREAK + default: + add_bev(pos->type, pos->data); + break; + } + } + + // If nothing added a floppy/hd boot - add it manually. + add_bev(IPL_TYPE_FLOPPY, 0); + add_bev(IPL_TYPE_HARDDISK, 0); +} + + +/**************************************************************** + * Boot code (int 18/19) + ****************************************************************/ + +// Jump to a bootup entry point. +static void +call_boot_entry(struct segoff_s bootsegip, u8 bootdrv) +{ + dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.code = bootsegip; + // Set the magic number in ax and the boot drive in dl. + br.dl = bootdrv; + br.ax = 0xaa55; + farcall16(&br); +} + +// Boot from a disk (either floppy or harddrive) +static void +boot_disk(u8 bootdrv, int checksig) +{ + u16 bootseg = 0x07c0; + + // Read sector + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.dl = bootdrv; + br.es = bootseg; + br.ah = 2; + br.al = 1; + br.cl = 1; + call16_int(0x13, &br); + + if (br.flags & F_CF) { + printf("Boot failed: could not read the boot disk\n\n"); + return; + } + + if (checksig) { + struct mbr_s *mbr = (void*)0; + if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) { + printf("Boot failed: not a bootable disk\n\n"); + return; + } + } + + /* Canonicalize bootseg:bootip */ + u16 bootip = (bootseg & 0x0fff) << 4; + bootseg &= 0xf000; + + call_boot_entry(SEGOFF(bootseg, bootip), bootdrv); +} + +// Boot from a CD-ROM +static void +boot_cdrom(struct drive_s *drive_g) +{ + if (! CONFIG_CDROM_BOOT) + return; + printf("Booting from DVD/CD...\n"); + + int status = cdrom_boot(drive_g); + if (status) { + printf("Boot failed: Could not read from CDROM (code %04x)\n", status); + return; + } + + u8 bootdrv = CDEmu.emulated_drive; + u16 bootseg = CDEmu.load_segment; + /* Canonicalize bootseg:bootip */ + u16 bootip = (bootseg & 0x0fff) << 4; + bootseg &= 0xf000; + + call_boot_entry(SEGOFF(bootseg, bootip), bootdrv); +} + +// Boot from a CBFS payload +static void +boot_cbfs(struct cbfs_file *file) +{ + if (!CONFIG_COREBOOT_FLASH) + return; + printf("Booting from CBFS...\n"); + cbfs_run_payload(file); +} + +// Boot from a BEV entry on an optionrom. +static void +boot_rom(u32 vector) +{ + printf("Booting from ROM...\n"); + struct segoff_s so; + so.segoff = vector; + call_boot_entry(so, 0); +} + +// Unable to find bootable device - warn user and eventually retry. +static void +boot_fail(void) +{ + if (BootRetryTime == (u32)-1) + printf("No bootable device.\n"); + else + printf("No bootable device. Retrying in %d seconds.\n" + , BootRetryTime/1000); + // Wait for 'BootRetryTime' milliseconds and then reboot. + u32 end = irqtimer_calc(BootRetryTime); + for (;;) { + if (BootRetryTime != (u32)-1 && irqtimer_check(end)) + break; + yield_toirq(); + } + printf("Rebooting.\n"); + reset(); +} + +// Determine next boot method and attempt a boot using it. +static void +do_boot(int seq_nr) +{ + if (! CONFIG_BOOT) + panic("Boot support not compiled in.\n"); + + if (seq_nr >= BEVCount) + boot_fail(); + + // Boot the given BEV type. + struct bev_s *ie = &BEV[seq_nr]; + switch (ie->type) { + case IPL_TYPE_FLOPPY: + printf("Booting from Floppy...\n"); + boot_disk(0x00, CheckFloppySig); + break; + case IPL_TYPE_HARDDISK: + printf("Booting from Hard Disk...\n"); + boot_disk(0x80, 1); + break; + case IPL_TYPE_CDROM: + boot_cdrom((void*)ie->vector); + break; + case IPL_TYPE_CBFS: + boot_cbfs((void*)ie->vector); + break; + case IPL_TYPE_BEV: + boot_rom(ie->vector); + break; + case IPL_TYPE_HALT: + boot_fail(); + break; + } + + // Boot failed: invoke the boot recovery function + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x18, &br); +} + +int BootSequence VARLOW = -1; + +// Boot Failure recovery: try the next device. +void VISIBLE32FLAT +handle_18(void) +{ + debug_enter(NULL, DEBUG_HDL_18); + int seq = BootSequence + 1; + BootSequence = seq; + do_boot(seq); +} + +// INT 19h Boot Load Service Entry Point +void VISIBLE32FLAT +handle_19(void) +{ + debug_enter(NULL, DEBUG_HDL_19); + BootSequence = 0; + do_boot(0); +} diff --git a/qemu/roms/seabios/src/bootsplash.c b/qemu/roms/seabios/src/bootsplash.c new file mode 100644 index 000000000..c572685de --- /dev/null +++ b/qemu/roms/seabios/src/bootsplash.c @@ -0,0 +1,254 @@ +// Initialize the VGA console and possibly show a boot splash image. +// +// Copyright (C) 2009-2010 coresystems GmbH +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "farptr.h" // FLATPTR_TO_SEG +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadfile +#include "stacks.h" // call16_int +#include "std/vbe.h" // struct vbe_info +#include "string.h" // memset +#include "util.h" // enable_bootsplash + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Call int10 vga handler. +static void +call16_int10(struct bregs *br) +{ + br->flags = F_IF; + start_preempt(); + call16_int(0x10, br); + finish_preempt(); +} + + +/**************************************************************** + * VGA text / graphics console + ****************************************************************/ + +void +enable_vga_console(void) +{ + dprintf(1, "Turning on vga text mode console\n"); + struct bregs br; + + /* Enable VGA text mode */ + memset(&br, 0, sizeof(br)); + br.ax = 0x0003; + call16_int10(&br); + + // Write to screen. + printf("SeaBIOS (version %s)\n", VERSION); + display_uuid(); +} + +static int +find_videomode(struct vbe_info *vesa_info, struct vbe_mode_info *mode_info + , int width, int height, int bpp_req) +{ + dprintf(3, "Finding vesa mode with dimensions %d/%d\n", width, height); + u16 *videomodes = SEGOFF_TO_FLATPTR(vesa_info->video_mode); + for (;; videomodes++) { + u16 videomode = *videomodes; + if (videomode == 0xffff) { + dprintf(1, "Unable to find vesa video mode dimensions %d/%d\n" + , width, height); + return -1; + } + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ax = 0x4f01; + br.cx = videomode; + br.di = FLATPTR_TO_OFFSET(mode_info); + br.es = FLATPTR_TO_SEG(mode_info); + call16_int10(&br); + if (br.ax != 0x4f) { + dprintf(1, "get_mode failed.\n"); + continue; + } + if (mode_info->xres != width + || mode_info->yres != height) + continue; + u8 depth = mode_info->bits_per_pixel; + if (bpp_req == 0) { + if (depth != 16 && depth != 24 && depth != 32) + continue; + } else { + if (depth != bpp_req) + continue; + } + return videomode; + } +} + +static int BootsplashActive; + +void +enable_bootsplash(void) +{ + if (!CONFIG_BOOTSPLASH) + return; + /* splash picture can be bmp or jpeg file */ + dprintf(3, "Checking for bootsplash\n"); + u8 type = 0; /* 0 means jpg, 1 means bmp, default is 0=jpg */ + int filesize; + u8 *filedata = romfile_loadfile("bootsplash.jpg", &filesize); + if (!filedata) { + filedata = romfile_loadfile("bootsplash.bmp", &filesize); + if (!filedata) + return; + type = 1; + } + dprintf(3, "start showing bootsplash\n"); + + u8 *picture = NULL; /* data buff used to be flushed to the video buf */ + struct jpeg_decdata *jpeg = NULL; + struct bmp_decdata *bmp = NULL; + struct vbe_info *vesa_info = malloc_tmplow(sizeof(*vesa_info)); + struct vbe_mode_info *mode_info = malloc_tmplow(sizeof(*mode_info)); + if (!vesa_info || !mode_info) { + warn_noalloc(); + goto done; + } + + /* Check whether we have a VESA 2.0 compliant BIOS */ + memset(vesa_info, 0, sizeof(struct vbe_info)); + vesa_info->signature = VBE2_SIGNATURE; + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ax = 0x4f00; + br.di = FLATPTR_TO_OFFSET(vesa_info); + br.es = FLATPTR_TO_SEG(vesa_info); + call16_int10(&br); + if (vesa_info->signature != VESA_SIGNATURE) { + dprintf(1,"No VBE2 found.\n"); + goto done; + } + + /* Print some debugging information about our card. */ + char *vendor = SEGOFF_TO_FLATPTR(vesa_info->oem_vendor_string); + char *product = SEGOFF_TO_FLATPTR(vesa_info->oem_product_string); + dprintf(3, "VESA %d.%d\nVENDOR: %s\nPRODUCT: %s\n", + vesa_info->version>>8, vesa_info->version&0xff, + vendor, product); + + int ret, width, height; + int bpp_require = 0; + if (type == 0) { + jpeg = jpeg_alloc(); + if (!jpeg) { + warn_noalloc(); + goto done; + } + /* Parse jpeg and get image size. */ + dprintf(5, "Decoding bootsplash.jpg\n"); + ret = jpeg_decode(jpeg, filedata); + if (ret) { + dprintf(1, "jpeg_decode failed with return code %d...\n", ret); + goto done; + } + jpeg_get_size(jpeg, &width, &height); + } else { + bmp = bmp_alloc(); + if (!bmp) { + warn_noalloc(); + goto done; + } + /* Parse bmp and get image size. */ + dprintf(5, "Decoding bootsplash.bmp\n"); + ret = bmp_decode(bmp, filedata, filesize); + if (ret) { + dprintf(1, "bmp_decode failed with return code %d...\n", ret); + goto done; + } + bmp_get_size(bmp, &width, &height); + bpp_require = 24; + } + /* jpeg would use 16 or 24 bpp video mode, BMP use 24bpp mode only */ + + // Try to find a graphics mode with the corresponding dimensions. + int videomode = find_videomode(vesa_info, mode_info, width, height, + bpp_require); + if (videomode < 0) { + dprintf(1, "failed to find a videomode with %dx%d %dbpp (0=any).\n", + width, height, bpp_require); + goto done; + } + void *framebuffer = (void *)mode_info->phys_base; + int depth = mode_info->bits_per_pixel; + dprintf(3, "mode: %04x\n", videomode); + dprintf(3, "framebuffer: %p\n", framebuffer); + dprintf(3, "bytes per scanline: %d\n", mode_info->bytes_per_scanline); + dprintf(3, "bits per pixel: %d\n", depth); + + // Allocate space for image and decompress it. + int imagesize = height * mode_info->bytes_per_scanline; + picture = malloc_tmphigh(imagesize); + if (!picture) { + warn_noalloc(); + goto done; + } + + if (type == 0) { + dprintf(5, "Decompressing bootsplash.jpg\n"); + ret = jpeg_show(jpeg, picture, width, height, depth, + mode_info->bytes_per_scanline); + if (ret) { + dprintf(1, "jpeg_show failed with return code %d...\n", ret); + goto done; + } + } else { + dprintf(5, "Decompressing bootsplash.bmp\n"); + ret = bmp_show(bmp, picture, width, height, depth, + mode_info->bytes_per_scanline); + if (ret) { + dprintf(1, "bmp_show failed with return code %d...\n", ret); + goto done; + } + } + + /* Switch to graphics mode */ + dprintf(5, "Switching to graphics mode\n"); + memset(&br, 0, sizeof(br)); + br.ax = 0x4f02; + br.bx = videomode | VBE_MODE_LINEAR_FRAME_BUFFER; + call16_int10(&br); + if (br.ax != 0x4f) { + dprintf(1, "set_mode failed.\n"); + goto done; + } + + /* Show the picture */ + dprintf(5, "Showing bootsplash picture\n"); + iomemcpy(framebuffer, picture, imagesize); + dprintf(5, "Bootsplash copy complete\n"); + BootsplashActive = 1; + +done: + free(filedata); + free(picture); + free(vesa_info); + free(mode_info); + free(jpeg); + free(bmp); + return; +} + +void +disable_bootsplash(void) +{ + if (!CONFIG_BOOTSPLASH || !BootsplashActive) + return; + BootsplashActive = 0; + enable_vga_console(); +} diff --git a/qemu/roms/seabios/src/bregs.h b/qemu/roms/seabios/src/bregs.h new file mode 100644 index 000000000..d40526345 --- /dev/null +++ b/qemu/roms/seabios/src/bregs.h @@ -0,0 +1,80 @@ +// Structure layout of cpu registers that the bios uses. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#ifndef __BREGS_H +#define __BREGS_H + +#include "types.h" // u16 +#include "x86.h" // F_CF + + +/**************************************************************** + * Registers saved/restored in romlayout.S + ****************************************************************/ + +#define UREG(ER, R, RH, RL) union { u32 ER; struct { u16 R; u16 R ## _hi; }; struct { u8 RL; u8 RH; u8 R ## _hilo; u8 R ## _hihi; }; } + +// Layout of registers passed in to irq handlers. Note that this +// layout corresponds to code in romlayout.S - don't change it here +// without also updating the assembler code. +struct bregs { + u16 ds; + u16 es; + UREG(edi, di, di8u, di8l); + UREG(esi, si, si8u, si8l); + UREG(ebp, bp, bp8u, bp8l); + UREG(ebx, bx, bh, bl); + UREG(edx, dx, dh, dl); + UREG(ecx, cx, ch, cl); + UREG(eax, ax, ah, al); + struct segoff_s code; + u16 flags; +} PACKED; + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +static inline void +set_cf(struct bregs *regs, int cond) +{ + if (cond) + regs->flags |= F_CF; + else + regs->flags &= ~F_CF; +} + +// Frequently used return codes +#define RET_EUNSUPPORTED 0x86 + +static inline void +set_success(struct bregs *regs) +{ + set_cf(regs, 0); +} + +static inline void +set_code_success(struct bregs *regs) +{ + regs->ah = 0; + set_cf(regs, 0); +} + +static inline void +set_invalid_silent(struct bregs *regs) +{ + set_cf(regs, 1); +} + +static inline void +set_code_invalid_silent(struct bregs *regs, u8 code) +{ + regs->ah = code; + set_cf(regs, 1); +} + +#endif // bregs.h diff --git a/qemu/roms/seabios/src/byteorder.h b/qemu/roms/seabios/src/byteorder.h new file mode 100644 index 000000000..928c1b807 --- /dev/null +++ b/qemu/roms/seabios/src/byteorder.h @@ -0,0 +1,71 @@ +#ifndef __BYTEORDER_H +#define __BYTEORDER_H + +#include "types.h" // u32 + +static inline u16 __swab16_constant(u16 val) { + return (val<<8) | (val>>8); +} +static inline u32 __swab32_constant(u32 val) { + return (val<<24) | ((val&0xff00)<<8) | ((val&0xff0000)>>8) | (val>>24); +} +static inline u64 __swab64_constant(u64 val) { + return ((u64)__swab32_constant(val) << 32) | __swab32_constant(val>>32); +} +static inline u32 __swab32(u32 val) { + asm("bswapl %0" : "+r"(val)); + return val; +} +static inline u64 __swab64(u64 val) { + union u64_u32_u i, o; + i.val = val; + o.lo = __swab32(i.hi); + o.hi = __swab32(i.lo); + return o.val; +} + +#define swab16(x) __swab16_constant(x) +#define swab32(x) (__builtin_constant_p((u32)(x)) \ + ? __swab32_constant(x) : __swab32(x)) +#define swab64(x) (__builtin_constant_p((u64)(x)) \ + ? __swab64_constant(x) : __swab64(x)) + +static inline u16 cpu_to_le16(u16 x) { + return x; +} +static inline u32 cpu_to_le32(u32 x) { + return x; +} +static inline u64 cpu_to_le64(u64 x) { + return x; +} +static inline u16 le16_to_cpu(u16 x) { + return x; +} +static inline u32 le32_to_cpu(u32 x) { + return x; +} +static inline u64 le64_to_cpu(u64 x) { + return x; +} + +static inline u16 cpu_to_be16(u16 x) { + return swab16(x); +} +static inline u32 cpu_to_be32(u32 x) { + return swab32(x); +} +static inline u64 cpu_to_be64(u64 x) { + return swab64(x); +} +static inline u16 be16_to_cpu(u16 x) { + return swab16(x); +} +static inline u32 be32_to_cpu(u32 x) { + return swab32(x); +} +static inline u64 be64_to_cpu(u64 x) { + return swab64(x); +} + +#endif // byteorder.h diff --git a/qemu/roms/seabios/src/cdrom.c b/qemu/roms/seabios/src/cdrom.c new file mode 100644 index 000000000..92f34f42b --- /dev/null +++ b/qemu/roms/seabios/src/cdrom.c @@ -0,0 +1,281 @@ +// Support for booting from cdroms (the "El Torito" spec). +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "hw/ata.h" // ATA_CMD_REQUEST_SENSE +#include "hw/blockcmd.h" // CDB_CMD_REQUEST_SENSE +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // cdrom_prepboot + +// Locks for removable devices +u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; + + +/**************************************************************** + * CD emulation + ****************************************************************/ + +struct eltorito_s CDEmu VARLOW = { .size=sizeof(CDEmu) }; +struct drive_s *emulated_drive_gf VARLOW; +struct drive_s *cdemu_drive_gf VARFSEG; + +static int +cdemu_read(struct disk_op_s *op) +{ + struct drive_s *drive_gf = GET_LOW(emulated_drive_gf); + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = op->command; + dop.lba = GET_LOW(CDEmu.ilba) + op->lba / 4; + + int count = op->count; + op->count = 0; + u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl); + + if (op->lba & 3) { + // Partial read of first block. + dop.count = 1; + dop.buf_fl = cdbuf_fl; + int ret = process_op(&dop); + if (ret) + return ret; + u8 thiscount = 4 - (op->lba & 3); + if (thiscount > count) + thiscount = count; + count -= thiscount; + memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512); + op->buf_fl += thiscount * 512; + op->count += thiscount; + dop.lba++; + } + + if (count > 3) { + // Read n number of regular blocks. + dop.count = count / 4; + dop.buf_fl = op->buf_fl; + int ret = process_op(&dop); + op->count += dop.count * 4; + if (ret) + return ret; + u8 thiscount = count & ~3; + count &= 3; + op->buf_fl += thiscount * 512; + dop.lba += thiscount / 4; + } + + if (count) { + // Partial read on last block. + dop.count = 1; + dop.buf_fl = cdbuf_fl; + int ret = process_op(&dop); + if (ret) + return ret; + u8 thiscount = count; + memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512); + op->count += thiscount; + } + + return DISK_RET_SUCCESS; +} + +int +process_cdemu_op(struct disk_op_s *op) +{ + if (!CONFIG_CDROM_EMU) + return 0; + + switch (op->command) { + case CMD_READ: + return cdemu_read(op); + case CMD_WRITE: + case CMD_FORMAT: + return DISK_RET_EWRITEPROTECT; + case CMD_VERIFY: + case CMD_RESET: + case CMD_SEEK: + case CMD_ISREADY: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + +void +cdrom_prepboot(void) +{ + if (!CONFIG_CDROM_EMU) + return; + if (!CDCount) + return; + if (create_bounce_buf() < 0) + return; + + struct drive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + free(drive); + return; + } + cdemu_drive_gf = drive; + memset(drive, 0, sizeof(*drive)); + drive->type = DTYPE_CDEMU; + drive->blksize = DISK_SECTOR_SIZE; + drive->sectors = (u64)-1; +} + + +/**************************************************************** + * CD booting + ****************************************************************/ + +int +cdrom_boot(struct drive_s *drive) +{ + ASSERT32FLAT(); + struct disk_op_s dop; + int cdid = getDriveId(EXTTYPE_CD, drive); + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = drive; + if (!dop.drive_gf || cdid < 0) + return 1; + + int ret = scsi_is_ready(&dop); + if (ret) + dprintf(1, "scsi_is_ready returned %d\n", ret); + + // Read the Boot Record Volume Descriptor + u8 buffer[CDROM_SECTOR_SIZE]; + dop.command = CMD_READ; + dop.lba = 0x11; + dop.count = 1; + dop.buf_fl = buffer; + ret = scsi_process_op(&dop); + if (ret) + return 3; + + // Validity checks + if (buffer[0]) + return 4; + if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0) + return 5; + + // ok, now we calculate the Boot catalog address + u32 lba = *(u32*)&buffer[0x47]; + + // And we read the Boot Catalog + dop.lba = lba; + dop.count = 1; + ret = scsi_process_op(&dop); + if (ret) + return 7; + + // Validation entry + if (buffer[0x00] != 0x01) + return 8; // Header + if (buffer[0x01] != 0x00) + return 9; // Platform + if (buffer[0x1E] != 0x55) + return 10; // key 1 + if (buffer[0x1F] != 0xAA) + return 10; // key 2 + + // Initial/Default Entry + if (buffer[0x20] != 0x88) + return 11; // Bootable + + // Fill in el-torito cdrom emulation fields. + emulated_drive_gf = drive; + u8 media = buffer[0x21]; + + u16 boot_segment = *(u16*)&buffer[0x22]; + if (!boot_segment) + boot_segment = 0x07C0; + CDEmu.load_segment = boot_segment; + CDEmu.buffer_segment = 0x0000; + + u16 nbsectors = *(u16*)&buffer[0x26]; + CDEmu.sector_count = nbsectors; + + lba = *(u32*)&buffer[0x28]; + CDEmu.ilba = lba; + + CDEmu.controller_index = drive->cntl_id / 2; + CDEmu.device_spec = drive->cntl_id % 2; + + // And we read the image in memory + nbsectors = DIV_ROUND_UP(nbsectors, 4); + dop.lba = lba; + dop.buf_fl = MAKE_FLATPTR(boot_segment, 0); + while (nbsectors) { + int count = nbsectors; + if (count > 64*1024/CDROM_SECTOR_SIZE) + count = 64*1024/CDROM_SECTOR_SIZE; + dop.count = count; + ret = scsi_process_op(&dop); + if (ret) + return 12; + nbsectors -= count; + dop.lba += count; + dop.buf_fl += count*CDROM_SECTOR_SIZE; + } + + if (media == 0) { + // No emulation requested - return success. + CDEmu.emulated_drive = EXTSTART_CD + cdid; + return 0; + } + + // Emulation of a floppy/harddisk requested + if (! CONFIG_CDROM_EMU || !cdemu_drive_gf) + return 13; + + // Set emulated drive id and increase bios installed hardware + // number of devices + if (media < 4) { + // Floppy emulation + CDEmu.emulated_drive = 0x00; + // XXX - get and set actual floppy count. + set_equipment_flags(0x41, 0x41); + + switch (media) { + case 0x01: // 1.2M floppy + CDEmu.chs.sptcyl = 15; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; + break; + case 0x02: // 1.44M floppy + CDEmu.chs.sptcyl = 18; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; + break; + case 0x03: // 2.88M floppy + CDEmu.chs.sptcyl = 36; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; + break; + } + } else { + // Harddrive emulation + CDEmu.emulated_drive = 0x80; + SET_BDA(hdcount, GET_BDA(hdcount) + 1); + + // Peak at partition table to get chs. + struct mbr_s *mbr = MAKE_FLATPTR(boot_segment, 0); + CDEmu.chs = mbr->partitions[0].last; + } + + // everything is ok, so from now on, the emulation is active + CDEmu.media = media; + dprintf(6, "cdemu media=%d\n", media); + + return 0; +} diff --git a/qemu/roms/seabios/src/clock.c b/qemu/roms/seabios/src/clock.c new file mode 100644 index 000000000..9ab0ac026 --- /dev/null +++ b/qemu/roms/seabios/src/clock.c @@ -0,0 +1,462 @@ +// 16bit code to handle system clocks. +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "hw/pic.h" // pic_eoi1 +#include "hw/rtc.h" // rtc_read +#include "hw/usb-hid.h" // usb_check_event +#include "output.h" // debug_enter +#include "stacks.h" // yield +#include "string.h" // memset +#include "util.h" // clock_setup + + +/**************************************************************** + * Init + ****************************************************************/ + +static u32 +bcd2bin(u8 val) +{ + return (val & 0xf) + ((val >> 4) * 10); +} + +u8 Century VARLOW; + +void +clock_setup(void) +{ + dprintf(3, "init timer\n"); + pit_setup(); + + rtc_setup(); + rtc_updating(); + u32 seconds = bcd2bin(rtc_read(CMOS_RTC_SECONDS)); + u32 minutes = bcd2bin(rtc_read(CMOS_RTC_MINUTES)); + u32 hours = bcd2bin(rtc_read(CMOS_RTC_HOURS)); + u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000); + SET_BDA(timer_counter, ticks % TICKS_PER_DAY); + + // Setup Century storage + if (CONFIG_QEMU) { + Century = rtc_read(CMOS_CENTURY); + } else { + // Infer current century from the year. + u8 year = rtc_read(CMOS_RTC_YEAR); + if (year > 0x80) + Century = 0x19; + else + Century = 0x20; + } + + enable_hwirq(0, FUNC16(entry_08)); + enable_hwirq(8, FUNC16(entry_70)); +} + + +/**************************************************************** + * Standard clock functions + ****************************************************************/ + +// get current clock count +static void +handle_1a00(struct bregs *regs) +{ + yield(); + u32 ticks = GET_BDA(timer_counter); + regs->cx = ticks >> 16; + regs->dx = ticks; + regs->al = GET_BDA(timer_rollover); + SET_BDA(timer_rollover, 0); // reset flag + set_success(regs); +} + +// Set Current Clock Count +static void +handle_1a01(struct bregs *regs) +{ + u32 ticks = (regs->cx << 16) | regs->dx; + SET_BDA(timer_counter, ticks); + SET_BDA(timer_rollover, 0); // reset flag + // XXX - should use set_code_success()? + regs->ah = 0; + set_success(regs); +} + +// Read CMOS Time +static void +handle_1a02(struct bregs *regs) +{ + if (rtc_updating()) { + set_invalid(regs); + return; + } + + regs->dh = rtc_read(CMOS_RTC_SECONDS); + regs->cl = rtc_read(CMOS_RTC_MINUTES); + regs->ch = rtc_read(CMOS_RTC_HOURS); + regs->dl = rtc_read(CMOS_STATUS_B) & RTC_B_DSE; + regs->ah = 0; + regs->al = regs->ch; + set_success(regs); +} + +// Set CMOS Time +static void +handle_1a03(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 + // before 1111 1101 0111 1101 0000 0000 + // after 0110 0010 0110 0010 0000 0010 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = ((RegB & 01100000b) | 00000010b) + if (rtc_updating()) { + rtc_setup(); + // fall through as if an update were not in progress + } + rtc_write(CMOS_RTC_SECONDS, regs->dh); + rtc_write(CMOS_RTC_MINUTES, regs->cl); + rtc_write(CMOS_RTC_HOURS, regs->ch); + // Set Daylight Savings time enabled bit to requested value + u8 val8 = ((rtc_read(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE)) + | RTC_B_24HR | (regs->dl & RTC_B_DSE)); + rtc_write(CMOS_STATUS_B, val8); + regs->ah = 0; + regs->al = val8; // val last written to Reg B + set_success(regs); +} + +// Read CMOS Date +static void +handle_1a04(struct bregs *regs) +{ + regs->ah = 0; + if (rtc_updating()) { + set_invalid(regs); + return; + } + regs->cl = rtc_read(CMOS_RTC_YEAR); + regs->dh = rtc_read(CMOS_RTC_MONTH); + regs->dl = rtc_read(CMOS_RTC_DAY_MONTH); + regs->ch = GET_LOW(Century); + regs->al = regs->ch; + set_success(regs); +} + +// Set CMOS Date +static void +handle_1a05(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 try#4 + // before 1111 1101 0111 1101 0000 0010 0000 0000 + // after 0110 1101 0111 1101 0000 0010 0000 0000 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = (RegB & 01111111b) + if (rtc_updating()) { + rtc_setup(); + set_invalid(regs); + return; + } + rtc_write(CMOS_RTC_YEAR, regs->cl); + rtc_write(CMOS_RTC_MONTH, regs->dh); + rtc_write(CMOS_RTC_DAY_MONTH, regs->dl); + SET_LOW(Century, regs->ch); + // clear halt-clock bit + u8 val8 = rtc_read(CMOS_STATUS_B) & ~RTC_B_SET; + rtc_write(CMOS_STATUS_B, val8); + regs->ah = 0; + regs->al = val8; // AL = val last written to Reg B + set_success(regs); +} + +// Set Alarm Time in CMOS +static void +handle_1a06(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 + // before 1101 1111 0101 1111 0000 0000 + // after 0110 1111 0111 1111 0010 0000 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = ((RegB & 01111111b) | 00100000b) + u8 val8 = rtc_read(CMOS_STATUS_B); // Get Status Reg B + regs->ax = 0; + if (val8 & RTC_B_AIE) { + // Alarm interrupt enabled already + set_invalid(regs); + return; + } + if (rtc_updating()) { + rtc_setup(); + // fall through as if an update were not in progress + } + rtc_write(CMOS_RTC_SECONDS_ALARM, regs->dh); + rtc_write(CMOS_RTC_MINUTES_ALARM, regs->cl); + rtc_write(CMOS_RTC_HOURS_ALARM, regs->ch); + // enable Status Reg B alarm bit, clear halt clock bit + rtc_write(CMOS_STATUS_B, (val8 & ~RTC_B_SET) | RTC_B_AIE); + set_success(regs); +} + +// Turn off Alarm +static void +handle_1a07(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 try#4 + // before 1111 1101 0111 1101 0010 0000 0010 0010 + // after 0100 0101 0101 0101 0000 0000 0000 0010 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = (RegB & 01010111b) + u8 val8 = rtc_read(CMOS_STATUS_B); // Get Status Reg B + // clear clock-halt bit, disable alarm bit + rtc_write(CMOS_STATUS_B, val8 & ~(RTC_B_SET|RTC_B_AIE)); + regs->ah = 0; + regs->al = val8; // val last written to Reg B + set_success(regs); +} + +// Unsupported +static void +handle_1aXX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +// INT 1Ah Time-of-day Service Entry Point +void VISIBLE16 +handle_1a(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_1a); + switch (regs->ah) { + case 0x00: handle_1a00(regs); break; + case 0x01: handle_1a01(regs); break; + case 0x02: handle_1a02(regs); break; + case 0x03: handle_1a03(regs); break; + case 0x04: handle_1a04(regs); break; + case 0x05: handle_1a05(regs); break; + case 0x06: handle_1a06(regs); break; + case 0x07: handle_1a07(regs); break; + default: handle_1aXX(regs); break; + } +} + +// INT 08h System Timer ISR Entry Point +void VISIBLE16 +handle_08(void) +{ + debug_isr(DEBUG_ISR_08); + + // Update counter + u32 counter = GET_BDA(timer_counter); + counter++; + // compare to one days worth of timer ticks at 18.2 hz + if (counter >= TICKS_PER_DAY) { + // there has been a midnight rollover at this point + counter = 0; + SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1); + } + SET_BDA(timer_counter, counter); + + // Check for internal events. + floppy_tick(); + usb_check_event(); + + // chain to user timer tick INT #0x1c + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x1c, &br); + + pic_eoi1(); +} + + +/**************************************************************** + * IRQ based timer + ****************************************************************/ + +// Calculate the timer value at 'count' number of full timer ticks in +// the future. +u32 +irqtimer_calc_ticks(u32 count) +{ + return (GET_BDA(timer_counter) + count + 1) % TICKS_PER_DAY; +} + +// Return the timer value that is 'msecs' time in the future. +u32 +irqtimer_calc(u32 msecs) +{ + if (!msecs) + return GET_BDA(timer_counter); + return irqtimer_calc_ticks(ticks_from_ms(msecs)); +} + +// Check if the given timer value has passed. +int +irqtimer_check(u32 end) +{ + return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY) + < (TICKS_PER_DAY/2)); +} + + +/**************************************************************** + * Periodic timer + ****************************************************************/ + +static int +set_usertimer(u32 usecs, u16 seg, u16 offset) +{ + if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING) + return -1; + + // Interval not already set. + SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte. + SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset)); + SET_BDA(user_wait_timeout, usecs); + rtc_use(); + return 0; +} + +static void +clear_usertimer(void) +{ + if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)) + return; + // Turn off status byte. + SET_BDA(rtc_wait_flag, 0); + rtc_release(); +} + +#define RET_ECLOCKINUSE 0x83 + +// Wait for CX:DX microseconds +void +handle_1586(struct bregs *regs) +{ + // Use the rtc to wait for the specified time. + u8 statusflag = 0; + u32 count = (regs->cx << 16) | regs->dx; + int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag); + if (ret) { + set_code_invalid(regs, RET_ECLOCKINUSE); + return; + } + while (!statusflag) + yield_toirq(); + set_success(regs); +} + +// Set Interval requested. +static void +handle_158300(struct bregs *regs) +{ + int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx); + if (ret) + // Interval already set. + set_code_invalid(regs, RET_EUNSUPPORTED); + else + set_success(regs); +} + +// Clear interval requested +static void +handle_158301(struct bregs *regs) +{ + clear_usertimer(); + set_success(regs); +} + +static void +handle_1583XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); + regs->al--; +} + +void +handle_1583(struct bregs *regs) +{ + switch (regs->al) { + case 0x00: handle_158300(regs); break; + case 0x01: handle_158301(regs); break; + default: handle_1583XX(regs); break; + } +} + +#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024) + +// int70h: IRQ8 - CMOS RTC +void VISIBLE16 +handle_70(void) +{ + debug_isr(DEBUG_ISR_70); + + // Check which modes are enabled and have occurred. + u8 registerB = rtc_read(CMOS_STATUS_B); + u8 registerC = rtc_read(CMOS_STATUS_C); + + if (!(registerB & (RTC_B_PIE|RTC_B_AIE))) + goto done; + if (registerC & RTC_B_AIE) { + // Handle Alarm Interrupt. + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x4a, &br); + } + if (!(registerC & RTC_B_PIE)) + goto done; + + // Handle Periodic Interrupt. + + check_preempt(); + + if (!GET_BDA(rtc_wait_flag)) + goto done; + + // Wait Interval (Int 15, AH=83) active. + u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds. + if (time < USEC_PER_RTC) { + // Done waiting - write to specified flag byte. + struct segoff_s segoff = GET_BDA(user_wait_complete_flag); + u16 ptr_seg = segoff.seg; + u8 *ptr_far = (u8*)(segoff.offset+0); + u8 oldval = GET_FARVAR(ptr_seg, *ptr_far); + SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80); + + clear_usertimer(); + } else { + // Continue waiting. + time -= USEC_PER_RTC; + SET_BDA(user_wait_timeout, time); + } + +done: + pic_eoi2(); +} diff --git a/qemu/roms/seabios/src/code16gcc.s b/qemu/roms/seabios/src/code16gcc.s new file mode 100644 index 000000000..8f7121b87 --- /dev/null +++ b/qemu/roms/seabios/src/code16gcc.s @@ -0,0 +1 @@ +.code16gcc diff --git a/qemu/roms/seabios/src/config.h b/qemu/roms/seabios/src/config.h new file mode 100644 index 000000000..6da067d0b --- /dev/null +++ b/qemu/roms/seabios/src/config.h @@ -0,0 +1,108 @@ +#ifndef __CONFIG_H +#define __CONFIG_H + +#include "autoconf.h" + +// Configuration definitions. + +//#define BUILD_APPNAME "QEMU" +//#define BUILD_CPUNAME8 "QEMUCPU " +//#define BUILD_APPNAME6 "QEMU " +//#define BUILD_APPNAME4 "QEMU" +#define BUILD_APPNAME "Bochs" +#define BUILD_CPUNAME8 "BOCHSCPU" +#define BUILD_APPNAME6 "BOCHS " +#define BUILD_APPNAME4 "BXPC" + +// Maximum number of map entries in the e820 map +#define BUILD_MAX_E820 32 +// Space to reserve in high-memory for tables +#define BUILD_MAX_HIGHTABLE (256*1024) +// Largest supported externaly facing drive id +#define BUILD_MAX_EXTDRIVE 16 +// Number of bytes the smbios may be and still live in the f-segment +#define BUILD_MAX_SMBIOS_FSEG 600 + +#define BUILD_MODEL_ID 0xFC +#define BUILD_SUBMODEL_ID 0x00 +#define BUILD_BIOS_REVISION 0x01 + +// Various memory addresses used by the code. +#define BUILD_STACK_ADDR 0x7000 +#define BUILD_S3RESUME_STACK_ADDR 0x1000 +#define BUILD_AP_BOOT_ADDR 0x10000 +#define BUILD_EBDA_MINIMUM 0x90000 +#define BUILD_LOWRAM_END 0xa0000 +#define BUILD_ROM_START 0xc0000 +#define BUILD_BIOS_ADDR 0xf0000 +#define BUILD_BIOS_SIZE 0x10000 +#define BUILD_EXTRA_STACK_SIZE 0x800 +// 32KB for shadow ram copying (works around emulator deficiencies) +#define BUILD_BIOS_TMP_ADDR 0x30000 +#define BUILD_SMM_INIT_ADDR 0x30000 +#define BUILD_SMM_ADDR 0xa0000 + +#define BUILD_PCIMEM_START 0xe0000000 +#define BUILD_PCIMEM_END 0xfec00000 /* IOAPIC is mapped at */ +#define BUILD_PCIMEM64_START 0x8000000000ULL +#define BUILD_PCIMEM64_END 0x10000000000ULL + +#define BUILD_IOAPIC_ADDR 0xfec00000 +#define BUILD_IOAPIC_ID 0 +#define BUILD_HPET_ADDRESS 0xfed00000 +#define BUILD_APIC_ADDR 0xfee00000 + +// PCI IRQS +#define BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) + +// Important real-mode segments +#define SEG_IVT 0x0000 +#define SEG_BDA 0x0040 +#define SEG_BIOS 0xf000 + +// Segment definitions in protected mode (see rombios32_gdt in misc.c) +#define SEG32_MODE32_CS (1 << 3) +#define SEG32_MODE32_DS (2 << 3) +#define SEG32_MODE16_CS (3 << 3) +#define SEG32_MODE16_DS (4 << 3) +#define SEG32_MODE16BIG_CS (5 << 3) +#define SEG32_MODE16BIG_DS (6 << 3) + +// Debugging levels. If non-zero and CONFIG_DEBUG_LEVEL is greater +// than the specified value, then the corresponding irq handler will +// report every enter event. +#define DEBUG_ISR_02 1 +#define DEBUG_HDL_05 1 +#define DEBUG_ISR_08 20 +#define DEBUG_ISR_09 9 +#define DEBUG_ISR_0e 9 +#define DEBUG_HDL_10 20 +#define DEBUG_HDL_11 2 +#define DEBUG_HDL_12 2 +#define DEBUG_HDL_13 10 +#define DEBUG_HDL_14 2 +#define DEBUG_HDL_15 9 +#define DEBUG_HDL_16 9 +#define DEBUG_HDL_17 2 +#define DEBUG_HDL_18 1 +#define DEBUG_HDL_19 1 +#define DEBUG_HDL_1a 9 +#define DEBUG_HDL_40 1 +#define DEBUG_ISR_70 9 +#define DEBUG_ISR_74 9 +#define DEBUG_ISR_75 1 +#define DEBUG_ISR_76 10 +#define DEBUG_ISR_hwpic1 5 +#define DEBUG_ISR_hwpic2 5 +#define DEBUG_HDL_smi 9 +#define DEBUG_HDL_smp 1 +#define DEBUG_HDL_pnp 1 +#define DEBUG_HDL_pmm 1 +#define DEBUG_HDL_pcibios 9 +#define DEBUG_HDL_apm 9 + +#define DEBUG_unimplemented 2 +#define DEBUG_invalid 3 +#define DEBUG_thread 2 + +#endif // config.h diff --git a/qemu/roms/seabios/src/disk.c b/qemu/roms/seabios/src/disk.c new file mode 100644 index 000000000..0e0af24b3 --- /dev/null +++ b/qemu/roms/seabios/src/disk.c @@ -0,0 +1,748 @@ +// 16bit code to access hard drives. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "hw/ata.h" // ATA_CB_DC +#include "hw/pic.h" // pic_eoi2 +#include "output.h" // debug_enter +#include "stacks.h" // call16_int +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // CDRom_locks + + +/**************************************************************** + * Return status functions + ****************************************************************/ + +static void +__disk_ret(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + if (regs->dl < EXTSTART_HD) + SET_BDA(floppy_last_status, code); + else + SET_BDA(disk_last_status, code); + if (code) + __set_code_invalid(regs, linecode, fname); + else + set_code_success(regs); +} + +static void +__disk_ret_unimplemented(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + if (regs->dl < EXTSTART_HD) + SET_BDA(floppy_last_status, code); + else + SET_BDA(disk_last_status, code); + __set_code_unimplemented(regs, linecode, fname); +} + +static void +__disk_stub(struct bregs *regs, int lineno, const char *fname) +{ + __warn_unimplemented(regs, lineno, fname); + __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname); +} + +#define disk_ret(regs, code) \ + __disk_ret((regs), (code) | (__LINE__ << 8), __func__) +#define disk_ret_unimplemented(regs, code) \ + __disk_ret_unimplemented((regs), (code) | (__LINE__ << 8), __func__) +#define DISK_STUB(regs) \ + __disk_stub((regs), __LINE__, __func__) + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Get the cylinders/heads/sectors for the given drive. +static struct chs_s +getLCHS(struct drive_s *drive_gf) +{ + struct chs_s res = { }; + if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf)) { + // Emulated drive - get info from CDEmu. (It's not possible to + // populate the geometry directly in the driveid because the + // geometry is only known after the bios segment is made + // read-only). + u8 sptcyl = GET_LOW(CDEmu.chs.sptcyl); + res.cylinder = GET_LOW(CDEmu.chs.cyllow) + ((sptcyl << 2) & 0x300) + 1; + res.head = GET_LOW(CDEmu.chs.heads) + 1; + res.sector = sptcyl & 0x3f; + return res; + } + res.cylinder = GET_GLOBALFLAT(drive_gf->lchs.cylinder); + res.head = GET_GLOBALFLAT(drive_gf->lchs.head); + res.sector = GET_GLOBALFLAT(drive_gf->lchs.sector); + return res; +} + +// Perform read/write/verify using old-style chs accesses +static void noinline +basic_access(struct bregs *regs, struct drive_s *drive_gf, u16 command) +{ + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = command; + + u8 count = regs->al; + u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300); + u16 sector = regs->cl & 0x3f; + u16 head = regs->dh; + + if (count > 128 || count == 0 || sector == 0) { + warn_invalid(regs); + disk_ret(regs, DISK_RET_EPARAM); + return; + } + dop.count = count; + + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + + // sanity check on cyl heads, sec + if (cylinder >= nlc || head >= nlh || sector > nls) { + warn_invalid(regs); + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + // translate lchs to lba + dop.lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nls) + + (u32)sector - 1); + + dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx); + + int status = send_disk_op(&dop); + + regs->al = dop.count; + + disk_ret(regs, status); +} + +// Perform read/write/verify using new-style "int13ext" accesses. +static void noinline +extended_access(struct bregs *regs, struct drive_s *drive_gf, u16 command) +{ + struct disk_op_s dop; + struct int13ext_s *param_far = (struct int13ext_s*)(regs->si+0); + // Get lba and check. + dop.lba = GET_FARVAR(regs->ds, param_far->lba); + dop.command = command; + dop.drive_gf = drive_gf; + if (dop.lba >= GET_GLOBALFLAT(drive_gf->sectors)) { + warn_invalid(regs); + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + dop.buf_fl = SEGOFF_TO_FLATPTR(GET_FARVAR(regs->ds, param_far->data)); + dop.count = GET_FARVAR(regs->ds, param_far->count); + if (! dop.count) { + // Nothing to do. + disk_ret(regs, DISK_RET_SUCCESS); + return; + } + + int status = send_disk_op(&dop); + + SET_FARVAR(regs->ds, param_far->count, dop.count); + + disk_ret(regs, status); +} + + +/**************************************************************** + * Hard Drive functions + ****************************************************************/ + +// disk controller reset +static void +disk_1300(struct bregs *regs, struct drive_s *drive_gf) +{ + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = CMD_RESET; + dop.count = 0; + int status = send_disk_op(&dop); + disk_ret(regs, status); +} + +// read disk status +static void +disk_1301(struct bregs *regs, struct drive_s *drive_gf) +{ + u8 v; + if (regs->dl < EXTSTART_HD) + // Floppy + v = GET_BDA(floppy_last_status); + else + v = GET_BDA(disk_last_status); + regs->ah = v; + set_cf(regs, v); + // XXX - clear disk_last_status? +} + +// read disk sectors +static void +disk_1302(struct bregs *regs, struct drive_s *drive_gf) +{ + basic_access(regs, drive_gf, CMD_READ); +} + +// write disk sectors +static void +disk_1303(struct bregs *regs, struct drive_s *drive_gf) +{ + basic_access(regs, drive_gf, CMD_WRITE); +} + +// verify disk sectors +static void +disk_1304(struct bregs *regs, struct drive_s *drive_gf) +{ + basic_access(regs, drive_gf, CMD_VERIFY); +} + +// format disk track +static void noinline +disk_1305(struct bregs *regs, struct drive_s *drive_gf) +{ + debug_stub(regs); + + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + + u8 count = regs->al; + u8 cylinder = regs->ch; + u8 head = regs->dh; + + if (cylinder >= nlc || head >= nlh || count == 0 || count > nls) { + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = CMD_FORMAT; + dop.lba = (((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nls; + dop.count = count; + dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx); + int status = send_disk_op(&dop); + disk_ret(regs, status); +} + +// read disk drive parameters +static void noinline +disk_1308(struct bregs *regs, struct drive_s *drive_gf) +{ + // Get logical geometry from table + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + nlc--; + nlh--; + u8 count; + if (regs->dl < EXTSTART_HD) { + // Floppy + count = GET_GLOBAL(FloppyCount); + + if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf)) + regs->bx = GET_LOW(CDEmu.media) * 2; + else + regs->bx = GET_GLOBALFLAT(drive_gf->floppy_type); + + // set es & di to point to 11 byte diskette param table in ROM + regs->es = SEG_BIOS; + regs->di = (u32)&diskette_param_table2; + } else if (regs->dl < EXTSTART_CD) { + // Hard drive + count = GET_BDA(hdcount); + nlc--; // last sector reserved + } else { + // Not supported on CDROM + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + if (CONFIG_CDROM_EMU && GET_LOW(CDEmu.media)) { + u8 emudrive = GET_LOW(CDEmu.emulated_drive); + if (((emudrive ^ regs->dl) & 0x80) == 0) + // Note extra drive due to emulation. + count++; + if (regs->dl < EXTSTART_HD && count > 2) + // Max of two floppy drives. + count = 2; + } + + regs->al = 0; + regs->ch = nlc & 0xff; + regs->cl = ((nlc >> 2) & 0xc0) | (nls & 0x3f); + regs->dh = nlh; + + disk_ret(regs, DISK_RET_SUCCESS); + regs->dl = count; +} + +// initialize drive parameters +static void +disk_1309(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// seek to specified cylinder +static void +disk_130c(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// alternate disk reset +static void +disk_130d(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// check drive ready +static void +disk_1310(struct bregs *regs, struct drive_s *drive_gf) +{ + // should look at 40:8E also??? + + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = CMD_ISREADY; + dop.count = 0; + int status = send_disk_op(&dop); + disk_ret(regs, status); +} + +// recalibrate +static void +disk_1311(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// controller internal diagnostic +static void +disk_1314(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// read disk drive size +static void noinline +disk_1315(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); + if (regs->dl < EXTSTART_HD || regs->dl >= EXTSTART_CD) { + // Floppy or cdrom + regs->ah = 1; + return; + } + // Hard drive + + // Get logical geometry from table + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + + // Compute sector count seen by int13 + u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nls; + regs->cx = lba >> 16; + regs->dx = lba & 0xffff; + regs->ah = 3; // hard disk accessible +} + +static void +disk_1316(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl >= EXTSTART_HD) { + // Hard drive + disk_ret(regs, DISK_RET_EPARAM); + return; + } + disk_ret(regs, DISK_RET_ECHANGED); +} + +// IBM/MS installation check +static void +disk_1341(struct bregs *regs, struct drive_s *drive_gf) +{ + regs->bx = 0xaa55; // install check + regs->cx = 0x0007; // ext disk access and edd, removable supported + disk_ret(regs, DISK_RET_SUCCESS); + regs->ah = 0x30; // EDD 3.0 +} + +// IBM/MS extended read +static void +disk_1342(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_READ); +} + +// IBM/MS extended write +static void +disk_1343(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_WRITE); +} + +// IBM/MS verify +static void +disk_1344(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_VERIFY); +} + +// lock +static void +disk_134500(struct bregs *regs, struct drive_s *drive_gf) +{ + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + if (locks == 0xff) { + regs->al = 1; + disk_ret(regs, DISK_RET_ETOOMANYLOCKS); + return; + } + SET_LOW(CDRom_locks[cdid], locks + 1); + regs->al = 1; + disk_ret(regs, DISK_RET_SUCCESS); +} + +// unlock +static void +disk_134501(struct bregs *regs, struct drive_s *drive_gf) +{ + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + if (locks == 0x00) { + regs->al = 0; + disk_ret(regs, DISK_RET_ENOTLOCKED); + return; + } + locks--; + SET_LOW(CDRom_locks[cdid], locks); + regs->al = (locks ? 1 : 0); + disk_ret(regs, DISK_RET_SUCCESS); +} + +// status +static void +disk_134502(struct bregs *regs, struct drive_s *drive_gf) +{ + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + regs->al = (locks ? 1 : 0); + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_1345XX(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret_unimplemented(regs, DISK_RET_EPARAM); +} + +// IBM/MS lock/unlock drive +static void +disk_1345(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl < EXTSTART_CD) { + // Always success for HD + disk_ret(regs, DISK_RET_SUCCESS); + return; + } + + switch (regs->al) { + case 0x00: disk_134500(regs, drive_gf); break; + case 0x01: disk_134501(regs, drive_gf); break; + case 0x02: disk_134502(regs, drive_gf); break; + default: disk_1345XX(regs, drive_gf); break; + } +} + +// IBM/MS eject media +static void noinline +disk_1346(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl < EXTSTART_CD) { + // Volume Not Removable + disk_ret(regs, DISK_RET_ENOTREMOVABLE); + return; + } + + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + if (locks != 0) { + disk_ret(regs, DISK_RET_ELOCKED); + return; + } + + // FIXME should handle 0x31 no media in device + // FIXME should handle 0xb5 valid request failed + + // Call removable media eject + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ah = 0x52; + br.dl = regs->dl; + call16_int(0x15, &br); + + if (br.ah || br.flags & F_CF) { + disk_ret(regs, DISK_RET_ELOCKED); + return; + } + disk_ret(regs, DISK_RET_SUCCESS); +} + +// IBM/MS extended seek +static void +disk_1347(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_SEEK); +} + +// IBM/MS get drive parameters +static void +disk_1348(struct bregs *regs, struct drive_s *drive_gf) +{ + int ret = fill_edd(regs->ds, (void*)(regs->si+0), drive_gf); + disk_ret(regs, ret); +} + +// IBM/MS extended media change +static void +disk_1349(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl < EXTSTART_CD) { + // Always success for HD + disk_ret(regs, DISK_RET_SUCCESS); + return; + } + set_invalid(regs); + // always send changed ?? + regs->ah = DISK_RET_ECHANGED; +} + +static void +disk_134e01(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134e03(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134e04(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134e06(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134eXX(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_EPARAM); +} + +// IBM/MS set hardware configuration +static void +disk_134e(struct bregs *regs, struct drive_s *drive_gf) +{ + switch (regs->al) { + case 0x01: disk_134e01(regs, drive_gf); break; + case 0x03: disk_134e03(regs, drive_gf); break; + case 0x04: disk_134e04(regs, drive_gf); break; + case 0x06: disk_134e06(regs, drive_gf); break; + default: disk_134eXX(regs, drive_gf); break; + } +} + +static void +disk_13XX(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret_unimplemented(regs, DISK_RET_EPARAM); +} + +static void +disk_13(struct bregs *regs, struct drive_s *drive_gf) +{ + //debug_stub(regs); + + // clear completion flag + SET_BDA(disk_interrupt_flag, 0); + + switch (regs->ah) { + case 0x00: disk_1300(regs, drive_gf); break; + case 0x01: disk_1301(regs, drive_gf); break; + case 0x02: disk_1302(regs, drive_gf); break; + case 0x03: disk_1303(regs, drive_gf); break; + case 0x04: disk_1304(regs, drive_gf); break; + case 0x05: disk_1305(regs, drive_gf); break; + case 0x08: disk_1308(regs, drive_gf); break; + case 0x09: disk_1309(regs, drive_gf); break; + case 0x0c: disk_130c(regs, drive_gf); break; + case 0x0d: disk_130d(regs, drive_gf); break; + case 0x10: disk_1310(regs, drive_gf); break; + case 0x11: disk_1311(regs, drive_gf); break; + case 0x14: disk_1314(regs, drive_gf); break; + case 0x15: disk_1315(regs, drive_gf); break; + case 0x16: disk_1316(regs, drive_gf); break; + case 0x41: disk_1341(regs, drive_gf); break; + case 0x42: disk_1342(regs, drive_gf); break; + case 0x43: disk_1343(regs, drive_gf); break; + case 0x44: disk_1344(regs, drive_gf); break; + case 0x45: disk_1345(regs, drive_gf); break; + case 0x46: disk_1346(regs, drive_gf); break; + case 0x47: disk_1347(regs, drive_gf); break; + case 0x48: disk_1348(regs, drive_gf); break; + case 0x49: disk_1349(regs, drive_gf); break; + case 0x4e: disk_134e(regs, drive_gf); break; + default: disk_13XX(regs, drive_gf); break; + } +} + +static void +floppy_13(struct bregs *regs, struct drive_s *drive_gf) +{ + // Only limited commands are supported on floppies. + switch (regs->ah) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x08: + case 0x15: + case 0x16: + disk_13(regs, drive_gf); + break; + default: disk_13XX(regs, drive_gf); break; + } +} + +// ElTorito - Terminate disk emu +static void +cdemu_134b(struct bregs *regs) +{ + memcpy_far(regs->ds, (void*)(regs->si+0), SEG_LOW, &CDEmu, sizeof(CDEmu)); + + // If we have to terminate emulation + if (regs->al == 0x00) { + // FIXME ElTorito Various. Should be handled accordingly to spec + SET_LOW(CDEmu.media, 0x00); // bye bye + + // XXX - update floppy/hd count. + } + + disk_ret(regs, DISK_RET_SUCCESS); +} + + +/**************************************************************** + * Entry points + ****************************************************************/ + +static void +handle_legacy_disk(struct bregs *regs, u8 extdrive) +{ + if (! CONFIG_DRIVES) { + // XXX - support handle_1301 anyway? + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + if (extdrive < EXTSTART_HD) { + struct drive_s *drive_gf = getDrive(EXTTYPE_FLOPPY, extdrive); + if (!drive_gf) + goto fail; + floppy_13(regs, drive_gf); + return; + } + + struct drive_s *drive_gf; + if (extdrive >= EXTSTART_CD) + drive_gf = getDrive(EXTTYPE_CD, extdrive - EXTSTART_CD); + else + drive_gf = getDrive(EXTTYPE_HD, extdrive - EXTSTART_HD); + if (!drive_gf) + goto fail; + disk_13(regs, drive_gf); + return; + +fail: + // XXX - support 1301/1308/1315 anyway? + disk_ret(regs, DISK_RET_EPARAM); +} + +void VISIBLE16 +handle_40(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_40); + handle_legacy_disk(regs, regs->dl); +} + +// INT 13h Fixed Disk Services Entry Point +void VISIBLE16 +handle_13(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_13); + u8 extdrive = regs->dl; + + if (CONFIG_CDROM_EMU) { + if (regs->ah == 0x4b) { + cdemu_134b(regs); + return; + } + if (GET_LOW(CDEmu.media)) { + u8 emudrive = GET_LOW(CDEmu.emulated_drive); + if (extdrive == emudrive) { + // Access to an emulated drive. + struct drive_s *cdemu_gf = GET_GLOBAL(cdemu_drive_gf); + if (regs->ah > 0x16) { + // Only old-style commands supported. + disk_13XX(regs, cdemu_gf); + return; + } + disk_13(regs, cdemu_gf); + return; + } + if (extdrive < EXTSTART_CD && ((emudrive ^ extdrive) & 0x80) == 0) + // Adjust id to make room for emulated drive. + extdrive--; + } + } + handle_legacy_disk(regs, extdrive); +} + +// record completion in BIOS task complete flag +void VISIBLE16 +handle_76(void) +{ + debug_isr(DEBUG_ISR_76); + SET_BDA(disk_interrupt_flag, 0xff); + pic_eoi2(); +} diff --git a/qemu/roms/seabios/src/entryfuncs.S b/qemu/roms/seabios/src/entryfuncs.S new file mode 100644 index 000000000..7368bb6d5 --- /dev/null +++ b/qemu/roms/seabios/src/entryfuncs.S @@ -0,0 +1,165 @@ +// Macros for entering C code +// +// Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +/**************************************************************** + * Macros for save and restore of 'struct bregs' registers + ****************************************************************/ + +#define PUSHBREGS_size 32 + + // Save registers (matches struct bregs) to stack + .macro PUSHBREGS + pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + pushw %es + pushw %ds + .endm + + // Restore registers (from struct bregs) from stack + .macro POPBREGS + popw %ds + popw %es + popl %edi + popl %esi + popl %ebp + popl %ebx + popl %edx + popl %ecx + popl %eax + .endm + + // Save registers to struct bregs at %ds:%eax. The caller + // should "pushw %ds ; pushl %eax" prior to calling - this macro + // will pop them off. + .macro SAVEBREGS_POP_DSEAX + popl BREGS_eax(%eax) + popw BREGS_ds(%eax) + movl %edi, BREGS_edi(%eax) + movl %esi, BREGS_esi(%eax) + movl %ebp, BREGS_ebp(%eax) + movl %ebx, BREGS_ebx(%eax) + movl %edx, BREGS_edx(%eax) + movl %ecx, BREGS_ecx(%eax) + movw %es, BREGS_es(%eax) + .endm + + // Restore registers from struct bregs at %ds:%eax + .macro RESTOREBREGS_DSEAX + movl BREGS_edi(%eax), %edi + movl BREGS_esi(%eax), %esi + movl BREGS_ebp(%eax), %ebp + movl BREGS_ebx(%eax), %ebx + movl BREGS_edx(%eax), %edx + movl BREGS_ecx(%eax), %ecx + movw BREGS_es(%eax), %es + pushl BREGS_eax(%eax) + movw BREGS_ds(%eax), %ds + popl %eax + .endm + + +/**************************************************************** + * Entry macros + ****************************************************************/ + + // Call a C function - this does the minimal work necessary to + // call into C. It sets up %ds, backs up %es, and backs up + // those registers that are call clobbered by the C compiler. + .macro ENTRY cfunc + cli // In case something far-calls instead of using "int" + cld + pushl %eax // Save registers clobbered by C code + pushl %ecx + pushl %edx + pushw %es + pushw %ds + movw %ss, %ax // Move %ss to %ds + movw %ax, %ds + pushl %esp // Backup %esp, then clear high bits + movzwl %sp, %esp + calll \cfunc + popl %esp // Restore %esp (including high bits) + popw %ds // Restore registers saved above + popw %es + popl %edx + popl %ecx + popl %eax + .endm + + // Call a C function with current register list as an + // argument. This backs up the registers and sets %eax + // to point to the backup. On return, the registers are + // restored from the structure. + .macro ENTRY_ARG cfunc + cli + cld + PUSHBREGS + movw %ss, %ax // Move %ss to %ds + movw %ax, %ds + movl %esp, %ebx // Backup %esp, then zero high bits + movzwl %sp, %esp + movl %esp, %eax // First arg is pointer to struct bregs + calll \cfunc + movl %ebx, %esp // Restore %esp (including high bits) + POPBREGS + .endm + + // As above, but get calling function from stack. + .macro ENTRY_ARG_ST + cli + cld + pushl %ecx + pushl %edx + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + pushw %es + pushw %ds + movw %ss, %cx // Move %ss to %ds + movw %cx, %ds + movl %esp, %ebx // Backup %esp, then zero high bits + movzwl %sp, %esp + movl 28(%esp), %ecx // Get calling function + movl %eax, 28(%esp) // Save %eax + movl %esp, %eax // First arg is pointer to struct bregs + calll *%ecx + movl %ebx, %esp // Restore %esp (including high bits) + POPBREGS + .endm + + // Same as ENTRY_ARG, but don't mangle %esp + .macro ENTRY_ARG_ESP cfunc + cli + cld + PUSHBREGS + movw %ss, %ax // Move %ss to %ds + movw %ax, %ds + movl %esp, %eax // First arg is pointer to struct bregs + calll \cfunc + POPBREGS + .endm + + // Reset stack, transition to 32bit mode, and call a C function. + .macro ENTRY_INTO32 cfunc + xorw %dx, %dx + movw %dx, %ss + movl $ BUILD_STACK_ADDR , %esp + movl $ \cfunc , %edx + jmp transition32 + .endm + + // Declare a function + .macro DECLFUNC func + .section .text.asm.\func + .global \func + .endm diff --git a/qemu/roms/seabios/src/farptr.h b/qemu/roms/seabios/src/farptr.h new file mode 100644 index 000000000..0a4db68bf --- /dev/null +++ b/qemu/roms/seabios/src/farptr.h @@ -0,0 +1,208 @@ +// Code to access multiple segments within gcc. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __FARPTR_H +#define __FARPTR_H + +#include "x86.h" // insb + +// Dummy definitions used to make sure gcc understands dependencies +// between SET_SEG and GET/READ/WRITE_SEG macros. +extern u16 __segment_ES, __segment_CS, __segment_DS, __segment_SS; +extern u16 __segment_FS, __segment_GS; + +// Low level macros for reading/writing memory via a segment selector. +#define READ8_SEG(prefix, SEG, value, var) \ + __asm__(prefix "movb %%" #SEG ":%1, %b0" : "=Qi"(value) \ + : "m"(var), "m"(__segment_ ## SEG)) +#define READ16_SEG(prefix, SEG, value, var) \ + __asm__(prefix "movw %%" #SEG ":%1, %w0" : "=ri"(value) \ + : "m"(var), "m"(__segment_ ## SEG)) +#define READ32_SEG(prefix, SEG, value, var) \ + __asm__(prefix "movl %%" #SEG ":%1, %0" : "=ri"(value) \ + : "m"(var), "m"(__segment_ ## SEG)) +#define READ64_SEG(prefix, SEG, value, var) do { \ + union u64_u32_u __value; \ + union u64_u32_u *__r64_ptr = (union u64_u32_u *)&(var); \ + READ32_SEG(prefix, SEG, __value.lo, __r64_ptr->lo); \ + READ32_SEG(prefix, SEG, __value.hi, __r64_ptr->hi); \ + *(u64*)&(value) = __value.val; \ + } while (0) +#define WRITE8_SEG(prefix, SEG, var, value) \ + __asm__(prefix "movb %b1, %%" #SEG ":%0" : "=m"(var) \ + : "Q"(value), "m"(__segment_ ## SEG)) +#define WRITE16_SEG(prefix, SEG, var, value) \ + __asm__(prefix "movw %w1, %%" #SEG ":%0" : "=m"(var) \ + : "r"(value), "m"(__segment_ ## SEG)) +#define WRITE32_SEG(prefix, SEG, var, value) \ + __asm__(prefix "movl %1, %%" #SEG ":%0" : "=m"(var) \ + : "r"(value), "m"(__segment_ ## SEG)) +#define WRITE64_SEG(prefix, SEG, var, value) do { \ + union u64_u32_u __value; \ + union u64_u32_u *__w64_ptr = (union u64_u32_u *)&(var); \ + typeof(var) __value_tmp = (value); \ + __value.val = *(u64*)&__value_tmp; \ + WRITE32_SEG(prefix, SEG, __w64_ptr->lo, __value.lo); \ + WRITE32_SEG(prefix, SEG, __w64_ptr->hi, __value.hi); \ + } while (0) + +// Macros for automatically choosing the appropriate memory size +// access method. +extern void __force_link_error__unknown_type(void); + +#define __GET_VAR(prefix, seg, var) ({ \ + typeof(var) __val; \ + if (sizeof(__val) == 1) \ + READ8_SEG(prefix, seg, __val, var); \ + else if (sizeof(__val) == 2) \ + READ16_SEG(prefix, seg, __val, var); \ + else if (sizeof(__val) == 4) \ + READ32_SEG(prefix, seg, __val, var); \ + else if (sizeof(__val) == 8) \ + READ64_SEG(prefix, seg, __val, var); \ + else \ + __force_link_error__unknown_type(); \ + __val; }) + +#define __SET_VAR(prefix, seg, var, val) do { \ + if (sizeof(var) == 1) \ + WRITE8_SEG(prefix, seg, var, (val)); \ + else if (sizeof(var) == 2) \ + WRITE16_SEG(prefix, seg, var, (val)); \ + else if (sizeof(var) == 4) \ + WRITE32_SEG(prefix, seg, var, (val)); \ + else if (sizeof(var) == 8) \ + WRITE64_SEG(prefix, seg, var, (val)); \ + else \ + __force_link_error__unknown_type(); \ + } while (0) + +#define DECL_SEGFUNCS(SEG) \ +static inline void __set_seg_##SEG(u16 seg) { \ + __asm__("movw %w1, %%" #SEG : "=m"(__segment_##SEG) \ + : "rm"(seg)); \ +} \ +static inline u16 __get_seg_##SEG(void) { \ + u16 res; \ + __asm__("movw %%" #SEG ", %w0" : "=rm"(res) \ + : "m"(__segment_##SEG)); \ + return res; \ +} +DECL_SEGFUNCS(CS) +DECL_SEGFUNCS(DS) +DECL_SEGFUNCS(ES) +DECL_SEGFUNCS(FS) +DECL_SEGFUNCS(GS) +DECL_SEGFUNCS(SS) + +// Low level macros for getting/setting a segment register. +#define __SET_SEG(SEG, value) \ + __set_seg_##SEG(value) +#define __GET_SEG(SEG) \ + __get_seg_##SEG() + +// Macros for accessing a variable in another segment. (They +// automatically update the %es segment and then make the appropriate +// access.) +#define __GET_FARVAR(seg, var) ({ \ + SET_SEG(ES, (seg)); \ + GET_VAR(ES, (var)); }) +#define __SET_FARVAR(seg, var, val) do { \ + typeof(var) __sfv_val = (val); \ + SET_SEG(ES, (seg)); \ + SET_VAR(ES, (var), __sfv_val); \ + } while (0) + +// Macros for accesssing a 32bit flat mode pointer from 16bit real +// mode. (They automatically update the %es segment, break the +// pointer into segment/offset, and then make the access.) +#define __GET_FLATPTR(ptr) ({ \ + typeof(&(ptr)) __ptr = &(ptr); \ + GET_FARVAR(FLATPTR_TO_SEG(__ptr) \ + , *(typeof(__ptr))FLATPTR_TO_OFFSET(__ptr)); }) +#define __SET_FLATPTR(ptr, val) do { \ + typeof (&(ptr)) __ptr = &(ptr); \ + SET_FARVAR(FLATPTR_TO_SEG(__ptr) \ + , *(typeof(__ptr))FLATPTR_TO_OFFSET(__ptr) \ + , (val)); \ + } while (0) + +// Macros for converting to/from 32bit flat mode pointers to their +// equivalent 16bit segment/offset values. +#define FLATPTR_TO_SEG(p) (((u32)(p)) >> 4) +#define FLATPTR_TO_OFFSET(p) (((u32)(p)) & 0xf) +#define MAKE_FLATPTR(seg,off) ((void*)(((u32)(seg)<<4)+(u32)(off))) + + +#if MODESEGMENT == 1 + +// Definitions when using segmented mode. +#define GET_FARVAR(seg, var) __GET_FARVAR((seg), (var)) +#define SET_FARVAR(seg, var, val) __SET_FARVAR((seg), (var), (val)) +#define GET_VAR(seg, var) __GET_VAR("", seg, (var)) +#define SET_VAR(seg, var, val) __SET_VAR("", seg, (var), (val)) +#define SET_SEG(SEG, value) __SET_SEG(SEG, (value)) +#define GET_SEG(SEG) __GET_SEG(SEG) +#define GET_FLATPTR(ptr) __GET_FLATPTR(ptr) +#define SET_FLATPTR(ptr, val) __SET_FLATPTR((ptr), (val)) + +static inline void insb_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + insb(port, (u8*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void insw_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + insw(port, (u16*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void insl_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + insl(port, (u32*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void outsb_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + outsb(port, (u8*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void outsw_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + outsw(port, (u16*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void outsl_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + outsl(port, (u32*)FLATPTR_TO_OFFSET(ptr_fl), count); +} + +#else + +// In 32-bit flat mode there is no need to mess with the segments. +#define GET_FARVAR(seg, var) \ + (*((typeof(&(var)))MAKE_FLATPTR((seg), &(var)))) +#define SET_FARVAR(seg, var, val) \ + do { GET_FARVAR((seg), (var)) = (val); } while (0) +#define GET_VAR(seg, var) (var) +#define SET_VAR(seg, var, val) do { (var) = (val); } while (0) +#define SET_SEG(SEG, value) ((void)(value)) +#define GET_SEG(SEG) 0 +#define GET_FLATPTR(ptr) (ptr) +#define SET_FLATPTR(ptr, val) do { (ptr) = (val); } while (0) + +#define insb_fl(port, ptr_fl, count) insb(port, ptr_fl, count) +#define insw_fl(port, ptr_fl, count) insw(port, ptr_fl, count) +#define insl_fl(port, ptr_fl, count) insl(port, ptr_fl, count) +#define outsb_fl(port, ptr_fl, count) outsb(port, ptr_fl, count) +#define outsw_fl(port, ptr_fl, count) outsw(port, ptr_fl, count) +#define outsl_fl(port, ptr_fl, count) outsl(port, ptr_fl, count) + +#endif + +#define SEGOFF(s,o) ({struct segoff_s __so; __so.offset=(o); __so.seg=(s); __so;}) + +static inline struct segoff_s FLATPTR_TO_SEGOFF(void *p) { + return SEGOFF(FLATPTR_TO_SEG(p), FLATPTR_TO_OFFSET(p)); +} +static inline void *SEGOFF_TO_FLATPTR(struct segoff_s so) { + return MAKE_FLATPTR(so.seg, so.offset); +} + +#endif // farptr.h diff --git a/qemu/roms/seabios/src/font.c b/qemu/roms/seabios/src/font.c new file mode 100644 index 000000000..67e5d46b7 --- /dev/null +++ b/qemu/roms/seabios/src/font.c @@ -0,0 +1,139 @@ +#include "types.h" // u8 + +// Character Font for 320x200 & 640x200 Graphics (lower 128 characters) + +/* + * This font comes from the fntcol16.zip package (c) by Joseph Gil + * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip + * This font is public domain + */ +u8 vgafont8[128*8] VARFSEGFIXED(0xfa6e) = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, + 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, + 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, + 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, + 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, + 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, + 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, + 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, + 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, + 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, +}; diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl new file mode 100644 index 000000000..0f3e83b14 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl @@ -0,0 +1,78 @@ +/**************************************************************** + * CPU hotplug + ****************************************************************/ + +Scope(\_SB) { + /* Objects filled in by run-time generated SSDT */ + External(NTFY, MethodObj) + External(CPON, PkgObj) + + /* Methods called by run-time generated SSDT Processor objects */ + Method(CPMA, 1, NotSerialized) { + // _MAT method - create an madt apic buffer + // Arg0 = Processor ID = Local APIC ID + // Local0 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Arg0)), Local0) + // Local1 = Buffer (in madt apic form) to return + Store(Buffer(8) {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}, Local1) + // Update the processor id, lapic id, and enable/disable status + Store(Arg0, Index(Local1, 2)) + Store(Arg0, Index(Local1, 3)) + Store(Local0, Index(Local1, 4)) + Return (Local1) + } + Method(CPST, 1, NotSerialized) { + // _STA method - return ON status of cpu + // Arg0 = Processor ID = Local APIC ID + // Local0 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Arg0)), Local0) + If (Local0) { + Return (0xF) + } Else { + Return (0x0) + } + } + Method(CPEJ, 2, NotSerialized) { + // _EJ0 method - eject callback + Sleep(200) + } + + /* CPU hotplug notify method */ + OperationRegion(PRST, SystemIO, 0xaf00, 32) + Field(PRST, ByteAcc, NoLock, Preserve) { + PRS, 256 + } + Method(PRSC, 0) { + // Local5 = active cpu bitmap + Store(PRS, Local5) + // Local2 = last read byte from bitmap + Store(Zero, Local2) + // Local0 = Processor ID / APIC ID iterator + Store(Zero, Local0) + While (LLess(Local0, SizeOf(CPON))) { + // Local1 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Local0)), Local1) + If (And(Local0, 0x07)) { + // Shift down previously read bitmap byte + ShiftRight(Local2, 1, Local2) + } Else { + // Read next byte from cpu bitmap + Store(DerefOf(Index(Local5, ShiftRight(Local0, 3))), Local2) + } + // Local3 = active state for this cpu + Store(And(Local2, 1), Local3) + + If (LNotEqual(Local1, Local3)) { + // State change - update CPON with new state + Store(Local3, Index(CPON, Local0)) + // Do CPU notify + If (LEqual(Local3, 1)) { + NTFY(Local0, 1) + } Else { + NTFY(Local0, 3) + } + } + Increment(Local0) + } + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-dbug.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-dbug.dsl new file mode 100644 index 000000000..276321f61 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-dbug.dsl @@ -0,0 +1,26 @@ +/**************************************************************** + * Debugging + ****************************************************************/ + +Scope(\) { + /* Debug Output */ + OperationRegion(DBG, SystemIO, 0x0402, 0x01) + Field(DBG, ByteAcc, NoLock, Preserve) { + DBGB, 8, + } + + /* Debug method - use this method to send output to the QEMU + * BIOS debug port. This method handles strings, integers, + * and buffers. For example: DBUG("abc") DBUG(0x123) */ + Method(DBUG, 1) { + ToHexString(Arg0, Local0) + ToBuffer(Local0, Local0) + Subtract(SizeOf(Local0), 1, Local1) + Store(Zero, Local2) + While (LLess(Local2, Local1)) { + Store(DerefOf(Index(Local0, Local2)), DBGB) + Increment(Local2) + } + Store(0x0A, DBGB) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-hpet.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-hpet.dsl new file mode 100644 index 000000000..f33e52795 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-hpet.dsl @@ -0,0 +1,36 @@ +/**************************************************************** + * HPET + ****************************************************************/ + +Scope(\_SB) { + Device(HPET) { + Name(_HID, EISAID("PNP0103")) + Name(_UID, 0) + OperationRegion(HPTM, SystemMemory, 0xFED00000, 0x400) + Field(HPTM, DWordAcc, Lock, Preserve) { + VEND, 32, + PRD, 32, + } + Method(_STA, 0, NotSerialized) { + Store(VEND, Local0) + Store(PRD, Local1) + ShiftRight(Local0, 16, Local0) + If (LOr(LEqual(Local0, 0), LEqual(Local0, 0xffff))) { + Return (0x0) + } + If (LOr(LEqual(Local1, 0), LGreater(Local1, 100000000))) { + Return (0x0) + } + Return (0x0F) + } + Name(_CRS, ResourceTemplate() { +#if 0 /* This makes WinXP BSOD for not yet figured reasons. */ + IRQNoFlags() {2, 8} +#endif + Memory32Fixed(ReadOnly, + 0xFED00000, // Address Base + 0x00000400, // Address Length + ) + }) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-isa.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-isa.dsl new file mode 100644 index 000000000..23761dbba --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-isa.dsl @@ -0,0 +1,102 @@ +/* Common legacy ISA style devices. */ +Scope(\_SB.PCI0.ISA) { + + Device(RTC) { + Name(_HID, EisaId("PNP0B00")) + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0070, 0x0070, 0x10, 0x02) + IRQNoFlags() { 8 } + IO(Decode16, 0x0072, 0x0072, 0x02, 0x06) + }) + } + + Device(KBD) { + Name(_HID, EisaId("PNP0303")) + Method(_STA, 0, NotSerialized) { + Return (0x0f) + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0060, 0x0060, 0x01, 0x01) + IO(Decode16, 0x0064, 0x0064, 0x01, 0x01) + IRQNoFlags() { 1 } + }) + } + + Device(MOU) { + Name(_HID, EisaId("PNP0F13")) + Method(_STA, 0, NotSerialized) { + Return (0x0f) + } + Name(_CRS, ResourceTemplate() { + IRQNoFlags() { 12 } + }) + } + + Device(FDC0) { + Name(_HID, EisaId("PNP0700")) + Method(_STA, 0, NotSerialized) { + Store(FDEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x03F2, 0x03F2, 0x00, 0x04) + IO(Decode16, 0x03F7, 0x03F7, 0x00, 0x01) + IRQNoFlags() { 6 } + DMA(Compatibility, NotBusMaster, Transfer8) { 2 } + }) + } + + Device(LPT) { + Name(_HID, EisaId("PNP0400")) + Method(_STA, 0, NotSerialized) { + Store(LPEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0378, 0x0378, 0x08, 0x08) + IRQNoFlags() { 7 } + }) + } + + Device(COM1) { + Name(_HID, EisaId("PNP0501")) + Name(_UID, 0x01) + Method(_STA, 0, NotSerialized) { + Store(CAEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x03F8, 0x03F8, 0x00, 0x08) + IRQNoFlags() { 4 } + }) + } + + Device(COM2) { + Name(_HID, EisaId("PNP0501")) + Name(_UID, 0x02) + Method(_STA, 0, NotSerialized) { + Store(CBEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x02F8, 0x02F8, 0x00, 0x08) + IRQNoFlags() { 3 } + }) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl new file mode 100644 index 000000000..d4218914f --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl @@ -0,0 +1,90 @@ +/* PCI CRS (current resources) definition. */ +Scope(\_SB.PCI0) { + + Name(CRES, ResourceTemplate() { + WordBusNumber(ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x00FF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0100, // Address Length + ,, ) + IO(Decode16, + 0x0CF8, // Address Range Minimum + 0x0CF8, // Address Range Maximum + 0x01, // Address Alignment + 0x08, // Address Length + ) + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x0CF7, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0CF8, // Address Length + ,, , TypeStatic) + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0D00, // Address Range Minimum + 0xFFFF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0xF300, // Address Length + ,, , TypeStatic) + DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x000A0000, // Address Range Minimum + 0x000BFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x00020000, // Address Length + ,, , AddressRangeMemory, TypeStatic) + DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0xE0000000, // Address Range Minimum + 0xFEBFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x1EC00000, // Address Length + ,, PW32, AddressRangeMemory, TypeStatic) + }) + + Name(CR64, ResourceTemplate() { + QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x8000000000, // Address Range Minimum + 0xFFFFFFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x8000000000, // Address Length + ,, PW64, AddressRangeMemory, TypeStatic) + }) + + Method(_CRS, 0) { + /* Fields provided by dynamically created ssdt */ + External(P0S, IntObj) + External(P0E, IntObj) + External(P1V, IntObj) + External(P1S, BuffObj) + External(P1E, BuffObj) + External(P1L, BuffObj) + + /* fixup 32bit pci io window */ + CreateDWordField(CRES, \_SB.PCI0.PW32._MIN, PS32) + CreateDWordField(CRES, \_SB.PCI0.PW32._MAX, PE32) + CreateDWordField(CRES, \_SB.PCI0.PW32._LEN, PL32) + Store(P0S, PS32) + Store(P0E, PE32) + Store(Add(Subtract(P0E, P0S), 1), PL32) + + If (LEqual(P1V, Zero)) { + Return (CRES) + } + + /* fixup 64bit pci io window */ + CreateQWordField(CR64, \_SB.PCI0.PW64._MIN, PS64) + CreateQWordField(CR64, \_SB.PCI0.PW64._MAX, PE64) + CreateQWordField(CR64, \_SB.PCI0.PW64._LEN, PL64) + Store(P1S, PS64) + Store(P1E, PE64) + Store(P1L, PL64) + /* add window and return result */ + ConcatenateResTemplate(CRES, CR64, Local0) + Return (Local0) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt.dsl new file mode 100644 index 000000000..3556dcaf1 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt.dsl @@ -0,0 +1,342 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +ACPI_EXTRACT_ALL_CODE AmlCode + +DefinitionBlock ( + "acpi-dsdt.aml", // Output Filename + "DSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXDSDT", // TABLE ID + 0x1 // OEM Revision + ) +{ + +#include "acpi-dsdt-dbug.dsl" + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + + Scope(\_SB) { + Device(PCI0) { + Name(_HID, EisaId("PNP0A03")) + Name(_ADR, 0x00) + Name(_UID, 1) + } + } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(VGA) { + Name(_ADR, 0x00020000) + OperationRegion(PCIC, PCI_Config, Zero, 0x4) + Field(PCIC, DWordAcc, NoLock, Preserve) { + VEND, 32 + } + Method(_S1D, 0, NotSerialized) { + Return (0x00) + } + Method(_S2D, 0, NotSerialized) { + Return (0x00) + } + Method(_S3D, 0, NotSerialized) { + If (LEqual(VEND, 0x1001b36)) { + Return (0x03) // QXL + } Else { + Return (0x00) + } + } + } + } + + +/**************************************************************** + * PIIX4 PM + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(PX13) { + Name(_ADR, 0x00010003) + OperationRegion(P13C, PCI_Config, 0x00, 0xff) + } + } + + +/**************************************************************** + * PIIX3 ISA bridge + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(ISA) { + Name(_ADR, 0x00010000) + + /* PIIX PCI to ISA irq remapping */ + OperationRegion(P40C, PCI_Config, 0x60, 0x04) + + /* enable bits */ + Field(\_SB.PCI0.PX13.P13C, AnyAcc, NoLock, Preserve) { + Offset(0x5f), + , 7, + LPEN, 1, // LPT + Offset(0x67), + , 3, + CAEN, 1, // COM1 + , 3, + CBEN, 1, // COM2 + } + Name(FDEN, 1) + } + } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + + Scope(\_SB.PCI0) { + OperationRegion(PCST, SystemIO, 0xae00, 0x08) + Field(PCST, DWordAcc, NoLock, WriteAsZeros) { + PCIU, 32, + PCID, 32, + } + + OperationRegion(SEJ, SystemIO, 0xae08, 0x04) + Field(SEJ, DWordAcc, NoLock, WriteAsZeros) { + B0EJ, 32, + } + + /* Methods called by bulk generated PCI devices below */ + + /* Methods called by hotplug devices */ + Method(PCEJ, 1, NotSerialized) { + // _EJ0 method - eject callback + Store(ShiftLeft(1, Arg0), B0EJ) + } + + /* Hotplug notification method supplied by SSDT */ + External(\_SB.PCI0.PCNT, MethodObj) + + /* PCI hotplug notify method */ + Method(PCNF, 0) { + // Local0 = iterator + Store(Zero, Local0) + While (LLess(Local0, 31)) { + Increment(Local0) + If (And(PCIU, ShiftLeft(1, Local0))) { + PCNT(Local0, 1) + } + If (And(PCID, ShiftLeft(1, Local0))) { + PCNT(Local0, 3) + } + } + } + } + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + + Scope(\_SB) { + Scope(PCI0) { + Name(_PRT, Package() { + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + +#define prt_slot(nr, lnk0, lnk1, lnk2, lnk3) \ + Package() { nr##ffff, 0, lnk0, 0 }, \ + Package() { nr##ffff, 1, lnk1, 0 }, \ + Package() { nr##ffff, 2, lnk2, 0 }, \ + Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot0(nr) prt_slot(nr, LNKD, LNKA, LNKB, LNKC) +#define prt_slot1(nr) prt_slot(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot2(nr) prt_slot(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot3(nr) prt_slot(nr, LNKC, LNKD, LNKA, LNKB) + + prt_slot0(0x0000), + /* Device 1 is power mgmt device, and can only use irq 9 */ + prt_slot(0x0001, LNKS, LNKB, LNKC, LNKD), + prt_slot2(0x0002), + prt_slot3(0x0003), + prt_slot0(0x0004), + prt_slot1(0x0005), + prt_slot2(0x0006), + prt_slot3(0x0007), + prt_slot0(0x0008), + prt_slot1(0x0009), + prt_slot2(0x000a), + prt_slot3(0x000b), + prt_slot0(0x000c), + prt_slot1(0x000d), + prt_slot2(0x000e), + prt_slot3(0x000f), + prt_slot0(0x0010), + prt_slot1(0x0011), + prt_slot2(0x0012), + prt_slot3(0x0013), + prt_slot0(0x0014), + prt_slot1(0x0015), + prt_slot2(0x0016), + prt_slot3(0x0017), + prt_slot0(0x0018), + prt_slot1(0x0019), + prt_slot2(0x001a), + prt_slot3(0x001b), + prt_slot0(0x001c), + prt_slot1(0x001d), + prt_slot2(0x001e), + prt_slot3(0x001f), + }) + } + + Field(PCI0.ISA.P40C, ByteAcc, NoLock, Preserve) { + PRQ0, 8, + PRQ1, 8, + PRQ2, 8, + PRQ3, 8 + } + + Method(IQST, 1, NotSerialized) { + // _STA method - get status + If (And(0x80, Arg0)) { + Return (0x09) + } + Return (0x0B) + } + Method(IQCR, 1, Serialized) { + // _CRS method - get current settings + Name(PRR0, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 0 } + }) + CreateDWordField(PRR0, 0x05, PRRI) + If (LLess(Arg0, 0x80)) { + Store(Arg0, PRRI) + } + Return (PRR0) + } + +#define define_link(link, uid, reg) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + 5, 10, 11 \ + } \ + }) \ + Method(_STA, 0, NotSerialized) { \ + Return (IQST(reg)) \ + } \ + Method(_DIS, 0, NotSerialized) { \ + Or(reg, 0x80, reg) \ + } \ + Method(_CRS, 0, NotSerialized) { \ + Return (IQCR(reg)) \ + } \ + Method(_SRS, 1, NotSerialized) { \ + CreateDWordField(Arg0, 0x05, PRRI) \ + Store(PRRI, reg) \ + } \ + } + + define_link(LNKA, 0, PRQ0) + define_link(LNKB, 1, PRQ1) + define_link(LNKC, 2, PRQ2) + define_link(LNKD, 3, PRQ3) + + Device(LNKS) { + Name(_HID, EISAID("PNP0C0F")) + Name(_UID, 4) + Name(_PRS, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 9 } + }) + + // The SCI cannot be disabled and is always attached to GSI 9, + // so these are no-ops. We only need this link to override the + // polarity to active high and match the content of the MADT. + Method(_STA, 0, NotSerialized) { Return (0x0b) } + Method(_DIS, 0, NotSerialized) { } + Method(_CRS, 0, NotSerialized) { Return (_PRS) } + Method(_SRS, 1, NotSerialized) { } + } + } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + + Scope(\_GPE) { + Name(_HID, "ACPI0006") + + Method(_L00) { + } + Method(_E01) { + // PCI hotplug event + \_SB.PCI0.PCNF() + } + Method(_E02) { + // CPU hotplug event + \_SB.PRSC() + } + Method(_L03) { + } + Method(_L04) { + } + Method(_L05) { + } + Method(_L06) { + } + Method(_L07) { + } + Method(_L08) { + } + Method(_L09) { + } + Method(_L0A) { + } + Method(_L0B) { + } + Method(_L0C) { + } + Method(_L0D) { + } + Method(_L0E) { + } + Method(_L0F) { + } + } +} diff --git a/qemu/roms/seabios/src/fw/acpi.c b/qemu/roms/seabios/src/fw/acpi.c new file mode 100644 index 000000000..ecd1adcf1 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi.c @@ -0,0 +1,685 @@ +// Support for generating ACPI tables (on emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // cpu_to_le16 +#include "config.h" // CONFIG_* +#include "dev-q35.h" +#include "dev-piix.h" +#include "hw/pci.h" // pci_find_init_device +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_INTERRUPT_LINE +#include "malloc.h" // free +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_loadint +#include "std/acpi.h" // struct rsdp_descriptor +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // readl + +#include "src/fw/acpi-dsdt.hex" + +static void +build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev) +{ + h->signature = cpu_to_le32(sig); + h->length = cpu_to_le32(len); + h->revision = rev; + memcpy(h->oem_id, BUILD_APPNAME6, 6); + memcpy(h->oem_table_id, BUILD_APPNAME4, 4); + memcpy(h->oem_table_id + 4, (void*)&sig, 4); + h->oem_revision = cpu_to_le32(1); + memcpy(h->asl_compiler_id, BUILD_APPNAME4, 4); + h->asl_compiler_revision = cpu_to_le32(1); + h->checksum -= checksum(h, len); +} + +static void piix4_fadt_setup(struct pci_device *pci, void *arg) +{ + struct fadt_descriptor_rev1 *fadt = arg; + + fadt->model = 1; + fadt->reserved1 = 0; + fadt->sci_int = cpu_to_le16(PIIX_PM_INTRRUPT); + fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); + fadt->acpi_enable = PIIX_ACPI_ENABLE; + fadt->acpi_disable = PIIX_ACPI_DISABLE; + fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); + fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); + fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); + fadt->gpe0_blk = cpu_to_le32(PIIX_GPE0_BLK); + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = PIIX_GPE0_BLK_LEN; + fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported + fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported + fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | + ACPI_FADT_F_PROC_C1 | + ACPI_FADT_F_SLP_BUTTON | + ACPI_FADT_F_RTC_S4 | + ACPI_FADT_F_USE_PLATFORM_CLOCK); +} + +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +static void ich9_lpc_fadt_setup(struct pci_device *dev, void *arg) +{ + struct fadt_descriptor_rev1 *fadt = arg; + + fadt->model = 1; + fadt->reserved1 = 0; + fadt->sci_int = cpu_to_le16(9); + fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); + fadt->acpi_enable = ICH9_ACPI_ENABLE; + fadt->acpi_disable = ICH9_ACPI_DISABLE; + fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); + fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); + fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); + fadt->gpe0_blk = cpu_to_le32(acpi_pm_base + ICH9_PMIO_GPE0_STS); + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = ICH9_PMIO_GPE0_BLK_LEN; + fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported + fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported + fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | + ACPI_FADT_F_PROC_C1 | + ACPI_FADT_F_SLP_BUTTON | + ACPI_FADT_F_RTC_S4 | + ACPI_FADT_F_USE_PLATFORM_CLOCK); +} + +static const struct pci_device_id fadt_init_tbl[] = { + /* PIIX4 Power Management device (for ACPI) */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, + piix4_fadt_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + ich9_lpc_fadt_setup), + PCI_DEVICE_END +}; + +static void fill_dsdt(struct fadt_descriptor_rev1 *fadt, void *dsdt) +{ + if (fadt->dsdt) { + free((void *)le32_to_cpu(fadt->dsdt)); + } + fadt->dsdt = cpu_to_le32((u32)dsdt); + fadt->checksum -= checksum(fadt, sizeof(*fadt)); + dprintf(1, "ACPI DSDT=%p\n", dsdt); +} + +static void * +build_fadt(struct pci_device *pci) +{ + struct fadt_descriptor_rev1 *fadt = malloc_high(sizeof(*fadt)); + struct facs_descriptor_rev1 *facs = memalign_high(64, sizeof(*facs)); + + if (!fadt || !facs) { + warn_noalloc(); + return NULL; + } + + /* FACS */ + memset(facs, 0, sizeof(*facs)); + facs->signature = cpu_to_le32(FACS_SIGNATURE); + facs->length = cpu_to_le32(sizeof(*facs)); + + /* FADT */ + memset(fadt, 0, sizeof(*fadt)); + fadt->firmware_ctrl = cpu_to_le32((u32)facs); + fadt->dsdt = 0; /* dsdt will be filled later in acpi_setup() + by fill_dsdt() */ + pci_init_device(fadt_init_tbl, pci, fadt); + + build_header((void*)fadt, FACP_SIGNATURE, sizeof(*fadt), 1); + + return fadt; +} + +static void* +build_madt(void) +{ + int madt_size = (sizeof(struct multiple_apic_table) + + sizeof(struct madt_processor_apic) * MaxCountCPUs + + sizeof(struct madt_io_apic) + + sizeof(struct madt_intsrcovr) * 16 + + sizeof(struct madt_local_nmi)); + + struct multiple_apic_table *madt = malloc_high(madt_size); + if (!madt) { + warn_noalloc(); + return NULL; + } + memset(madt, 0, madt_size); + madt->local_apic_address = cpu_to_le32(BUILD_APIC_ADDR); + madt->flags = cpu_to_le32(1); + struct madt_processor_apic *apic = (void*)&madt[1]; + int i; + for (i=0; i<MaxCountCPUs; i++) { + apic->type = APIC_PROCESSOR; + apic->length = sizeof(*apic); + apic->processor_id = i; + apic->local_apic_id = i; + if (apic_id_is_present(apic->local_apic_id)) + apic->flags = cpu_to_le32(1); + else + apic->flags = cpu_to_le32(0); + apic++; + } + struct madt_io_apic *io_apic = (void*)apic; + io_apic->type = APIC_IO; + io_apic->length = sizeof(*io_apic); + io_apic->io_apic_id = BUILD_IOAPIC_ID; + io_apic->address = cpu_to_le32(BUILD_IOAPIC_ADDR); + io_apic->interrupt = cpu_to_le32(0); + + struct madt_intsrcovr *intsrcovr = (void*)&io_apic[1]; + if (romfile_loadint("etc/irq0-override", 0)) { + memset(intsrcovr, 0, sizeof(*intsrcovr)); + intsrcovr->type = APIC_XRUPT_OVERRIDE; + intsrcovr->length = sizeof(*intsrcovr); + intsrcovr->source = 0; + intsrcovr->gsi = cpu_to_le32(2); + intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */ + intsrcovr++; + } + for (i = 1; i < 16; i++) { + if (!(BUILD_PCI_IRQS & (1 << i))) + /* No need for a INT source override structure. */ + continue; + memset(intsrcovr, 0, sizeof(*intsrcovr)); + intsrcovr->type = APIC_XRUPT_OVERRIDE; + intsrcovr->length = sizeof(*intsrcovr); + intsrcovr->source = i; + intsrcovr->gsi = cpu_to_le32(i); + intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ + intsrcovr++; + } + + struct madt_local_nmi *local_nmi = (void*)intsrcovr; + local_nmi->type = APIC_LOCAL_NMI; + local_nmi->length = sizeof(*local_nmi); + local_nmi->processor_id = 0xff; /* all processors */ + local_nmi->flags = cpu_to_le16(0); + local_nmi->lint = 1; /* LINT1 */ + local_nmi++; + + build_header((void*)madt, APIC_SIGNATURE, (void*)local_nmi - (void*)madt, 1); + return madt; +} + +// Encode a hex value +static inline char getHex(u32 val) { + val &= 0x0f; + return (val <= 9) ? ('0' + val) : ('A' + val - 10); +} + +// Encode a length in an SSDT. +static u8 * +encodeLen(u8 *ssdt_ptr, int length, int bytes) +{ + switch (bytes) { + default: + case 4: ssdt_ptr[3] = ((length >> 20) & 0xff); + case 3: ssdt_ptr[2] = ((length >> 12) & 0xff); + case 2: ssdt_ptr[1] = ((length >> 4) & 0xff); + ssdt_ptr[0] = (((bytes-1) & 0x3) << 6) | (length & 0x0f); + break; + case 1: ssdt_ptr[0] = length & 0x3f; + } + return ssdt_ptr + bytes; +} + +#include "src/fw/ssdt-proc.hex" + +/* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */ +#define PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2) +#define PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4) +#define PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start) +#define PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start) +#define PROC_AML (ssdp_proc_aml + *ssdt_proc_start) + +/* 0x5B 0x82 DeviceOp PkgLength NameString */ +#define PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1) +#define PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start) +#define PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start) +#define PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start) +#define PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start) +#define PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start) +#define PCI_SLOTS 32 + +#define SSDT_SIGNATURE 0x54445353 // SSDT +#define SSDT_HEADER_LENGTH 36 + +#include "src/fw/ssdt-misc.hex" +#include "src/fw/ssdt-pcihp.hex" + +#define PCI_RMV_BASE 0xae0c + +static u8* +build_notify(u8 *ssdt_ptr, const char *name, int skip, int count, + const char *target, int ofs) +{ + count -= skip; + + *(ssdt_ptr++) = 0x14; // MethodOp + ssdt_ptr = encodeLen(ssdt_ptr, 2+5+(12*count), 2); + memcpy(ssdt_ptr, name, 4); + ssdt_ptr += 4; + *(ssdt_ptr++) = 0x02; // MethodOp + + int i; + for (i = skip; count-- > 0; i++) { + *(ssdt_ptr++) = 0xA0; // IfOp + ssdt_ptr = encodeLen(ssdt_ptr, 11, 1); + *(ssdt_ptr++) = 0x93; // LEqualOp + *(ssdt_ptr++) = 0x68; // Arg0Op + *(ssdt_ptr++) = 0x0A; // BytePrefix + *(ssdt_ptr++) = i; + *(ssdt_ptr++) = 0x86; // NotifyOp + memcpy(ssdt_ptr, target, 4); + ssdt_ptr[ofs] = getHex(i >> 4); + ssdt_ptr[ofs + 1] = getHex(i); + ssdt_ptr += 4; + *(ssdt_ptr++) = 0x69; // Arg1Op + } + return ssdt_ptr; +} + +static void patch_pcihp(int slot, u8 *ssdt_ptr, u32 eject) +{ + ssdt_ptr[PCIHP_OFFSET_HEX] = getHex(slot >> 4); + ssdt_ptr[PCIHP_OFFSET_HEX+1] = getHex(slot); + ssdt_ptr[PCIHP_OFFSET_ID] = slot; + ssdt_ptr[PCIHP_OFFSET_ADR + 2] = slot; + + /* Runtime patching of EJ0: to disable hotplug for a slot, + * replace the method name: _EJ0 by EJ0_. */ + /* Sanity check */ + if (memcmp(ssdt_ptr + PCIHP_OFFSET_EJ0, "_EJ0", 4)) { + warn_internalerror(); + } + if (!eject) { + memcpy(ssdt_ptr + PCIHP_OFFSET_EJ0, "EJ0_", 4); + } +} + +static void* +build_ssdt(void) +{ + int acpi_cpus = MaxCountCPUs > 0xff ? 0xff : MaxCountCPUs; + int length = (sizeof(ssdp_misc_aml) // _S3_ / _S4_ / _S5_ + + (1+3+4) // Scope(_SB_) + + (acpi_cpus * PROC_SIZEOF) // procs + + (1+2+5+(12*acpi_cpus)) // NTFY + + (6+2+1+(1*acpi_cpus)) // CPON + + (1+3+4) // Scope(PCI0) + + ((PCI_SLOTS - 1) * PCIHP_SIZEOF) // slots + + (1+2+5+(12*(PCI_SLOTS - 1)))); // PCNT + u8 *ssdt = malloc_high(length); + if (! ssdt) { + warn_noalloc(); + return NULL; + } + u8 *ssdt_ptr = ssdt; + + // Copy header and encode fwcfg values in the S3_ / S4_ / S5_ packages + int sys_state_size; + char *sys_states = romfile_loadfile("etc/system-states", &sys_state_size); + if (!sys_states || sys_state_size != 6) + sys_states = (char[]){128, 0, 0, 129, 128, 128}; + + memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml)); + if (!(sys_states[3] & 128)) + ssdt_ptr[acpi_s3_name[0]] = 'X'; + if (!(sys_states[4] & 128)) + ssdt_ptr[acpi_s4_name[0]] = 'X'; + else + ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127; + + // store pci io windows + *(u32*)&ssdt_ptr[acpi_pci32_start[0]] = cpu_to_le32(pcimem_start); + *(u32*)&ssdt_ptr[acpi_pci32_end[0]] = cpu_to_le32(pcimem_end - 1); + if (pcimem64_start) { + ssdt_ptr[acpi_pci64_valid[0]] = 1; + *(u64*)&ssdt_ptr[acpi_pci64_start[0]] = cpu_to_le64(pcimem64_start); + *(u64*)&ssdt_ptr[acpi_pci64_end[0]] = cpu_to_le64(pcimem64_end - 1); + *(u64*)&ssdt_ptr[acpi_pci64_length[0]] = cpu_to_le64( + pcimem64_end - pcimem64_start); + } else { + ssdt_ptr[acpi_pci64_valid[0]] = 0; + } + + int pvpanic_port = romfile_loadint("etc/pvpanic-port", 0x0); + *(u16 *)(ssdt_ptr + *ssdt_isa_pest) = pvpanic_port; + + ssdt_ptr += sizeof(ssdp_misc_aml); + + // build Scope(_SB_) header + *(ssdt_ptr++) = 0x10; // ScopeOp + ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); + *(ssdt_ptr++) = '_'; + *(ssdt_ptr++) = 'S'; + *(ssdt_ptr++) = 'B'; + *(ssdt_ptr++) = '_'; + + // build Processor object for each processor + int i; + for (i=0; i<acpi_cpus; i++) { + memcpy(ssdt_ptr, PROC_AML, PROC_SIZEOF); + ssdt_ptr[PROC_OFFSET_CPUHEX] = getHex(i >> 4); + ssdt_ptr[PROC_OFFSET_CPUHEX+1] = getHex(i); + ssdt_ptr[PROC_OFFSET_CPUID1] = i; + ssdt_ptr[PROC_OFFSET_CPUID2] = i; + ssdt_ptr += PROC_SIZEOF; + } + + // build "Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}" + // Arg0 = Processor ID = APIC ID + ssdt_ptr = build_notify(ssdt_ptr, "NTFY", 0, acpi_cpus, "CP00", 2); + + // build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" + *(ssdt_ptr++) = 0x08; // NameOp + *(ssdt_ptr++) = 'C'; + *(ssdt_ptr++) = 'P'; + *(ssdt_ptr++) = 'O'; + *(ssdt_ptr++) = 'N'; + *(ssdt_ptr++) = 0x12; // PackageOp + ssdt_ptr = encodeLen(ssdt_ptr, 2+1+(1*acpi_cpus), 2); + *(ssdt_ptr++) = acpi_cpus; + for (i=0; i<acpi_cpus; i++) + *(ssdt_ptr++) = (apic_id_is_present(i)) ? 0x01 : 0x00; + + // build Scope(PCI0) opcode + *(ssdt_ptr++) = 0x10; // ScopeOp + ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); + *(ssdt_ptr++) = 'P'; + *(ssdt_ptr++) = 'C'; + *(ssdt_ptr++) = 'I'; + *(ssdt_ptr++) = '0'; + + // build Device object for each slot + u32 rmvc_pcrm = inl(PCI_RMV_BASE); + for (i=1; i<PCI_SLOTS; i++) { + u32 eject = rmvc_pcrm & (0x1 << i); + memcpy(ssdt_ptr, PCIHP_AML, PCIHP_SIZEOF); + patch_pcihp(i, ssdt_ptr, eject != 0); + ssdt_ptr += PCIHP_SIZEOF; + } + + ssdt_ptr = build_notify(ssdt_ptr, "PCNT", 1, PCI_SLOTS, "S00_", 1); + + build_header((void*)ssdt, SSDT_SIGNATURE, ssdt_ptr - ssdt, 1); + + //hexdump(ssdt, ssdt_ptr - ssdt); + + return ssdt; +} + +#define HPET_ID 0x000 +#define HPET_PERIOD 0x004 + +static void* +build_hpet(void) +{ + struct acpi_20_hpet *hpet; + const void *hpet_base = (void *)BUILD_HPET_ADDRESS; + u32 hpet_vendor = readl(hpet_base + HPET_ID) >> 16; + u32 hpet_period = readl(hpet_base + HPET_PERIOD); + + if (hpet_vendor == 0 || hpet_vendor == 0xffff || + hpet_period == 0 || hpet_period > 100000000) + return NULL; + + hpet = malloc_high(sizeof(*hpet)); + if (!hpet) { + warn_noalloc(); + return NULL; + } + + memset(hpet, 0, sizeof(*hpet)); + /* Note timer_block_id value must be kept in sync with value advertised by + * emulated hpet + */ + hpet->timer_block_id = cpu_to_le32(0x8086a201); + hpet->addr.address = cpu_to_le64(BUILD_HPET_ADDRESS); + build_header((void*)hpet, HPET_SIGNATURE, sizeof(*hpet), 1); + + return hpet; +} + +static void +acpi_build_srat_memory(struct srat_memory_affinity *numamem, + u64 base, u64 len, int node, int enabled) +{ + numamem->type = SRAT_MEMORY; + numamem->length = sizeof(*numamem); + memset(numamem->proximity, 0, 4); + numamem->proximity[0] = node; + numamem->flags = cpu_to_le32(!!enabled); + numamem->base_addr = cpu_to_le64(base); + numamem->range_length = cpu_to_le64(len); +} + +static void * +build_srat(void) +{ + int numadatasize, numacpusize; + u64 *numadata = romfile_loadfile("etc/numa-nodes", &numadatasize); + u64 *numacpumap = romfile_loadfile("etc/numa-cpu-map", &numacpusize); + if (!numadata || !numacpumap) + goto fail; + int max_cpu = numacpusize / sizeof(u64); + int nb_numa_nodes = numadatasize / sizeof(u64); + + struct system_resource_affinity_table *srat; + int srat_size = sizeof(*srat) + + sizeof(struct srat_processor_affinity) * max_cpu + + sizeof(struct srat_memory_affinity) * (nb_numa_nodes + 2); + + srat = malloc_high(srat_size); + if (!srat) { + warn_noalloc(); + goto fail; + } + + memset(srat, 0, srat_size); + srat->reserved1=cpu_to_le32(1); + struct srat_processor_affinity *core = (void*)(srat + 1); + int i; + u64 curnode; + + for (i = 0; i < max_cpu; ++i) { + core->type = SRAT_PROCESSOR; + core->length = sizeof(*core); + core->local_apic_id = i; + curnode = *numacpumap++; + core->proximity_lo = curnode; + memset(core->proximity_hi, 0, 3); + core->local_sapic_eid = 0; + if (apic_id_is_present(i)) + core->flags = cpu_to_le32(1); + else + core->flags = cpu_to_le32(0); + core++; + } + + + /* the memory map is a bit tricky, it contains at least one hole + * from 640k-1M and possibly another one from 3.5G-4G. + */ + struct srat_memory_affinity *numamem = (void*)core; + int slots = 0; + u64 mem_len, mem_base, next_base = 0; + + acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1); + next_base = 1024 * 1024; + numamem++; + slots++; + for (i = 1; i < nb_numa_nodes + 1; ++i) { + mem_base = next_base; + mem_len = *numadata++; + if (i == 1) + mem_len -= 1024 * 1024; + next_base = mem_base + mem_len; + + /* Cut out the PCI hole */ + if (mem_base <= RamSize && next_base > RamSize) { + mem_len -= next_base - RamSize; + if (mem_len > 0) { + acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + numamem++; + slots++; + } + mem_base = 1ULL << 32; + mem_len = next_base - RamSize; + next_base += (1ULL << 32) - RamSize; + } + acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + numamem++; + slots++; + } + for (; slots < nb_numa_nodes + 2; slots++) { + acpi_build_srat_memory(numamem, 0, 0, 0, 0); + numamem++; + } + + build_header((void*)srat, SRAT_SIGNATURE, srat_size, 1); + + free(numadata); + free(numacpumap); + return srat; +fail: + free(numadata); + free(numacpumap); + return NULL; +} + +static void * +build_mcfg_q35(void) +{ + struct acpi_table_mcfg *mcfg; + + int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); + mcfg = malloc_high(len); + if (!mcfg) { + warn_noalloc(); + return NULL; + } + memset(mcfg, 0, len); + mcfg->allocation[0].address = cpu_to_le64(Q35_HOST_BRIDGE_PCIEXBAR_ADDR); + mcfg->allocation[0].pci_segment = cpu_to_le16(Q35_HOST_PCIE_PCI_SEGMENT); + mcfg->allocation[0].start_bus_number = Q35_HOST_PCIE_START_BUS_NUMBER; + mcfg->allocation[0].end_bus_number = Q35_HOST_PCIE_END_BUS_NUMBER; + + build_header((void *)mcfg, MCFG_SIGNATURE, len, 1); + return mcfg; +} + +static const struct pci_device_id acpi_find_tbl[] = { + /* PIIX4 Power Management device. */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, NULL), + PCI_DEVICE_END, +}; + +#define MAX_ACPI_TABLES 20 +void +acpi_setup(void) +{ + if (! CONFIG_ACPI) + return; + + dprintf(3, "init ACPI tables\n"); + + // This code is hardcoded for PIIX4 Power Management device. + struct pci_device *pci = pci_find_init_device(acpi_find_tbl, NULL); + if (!pci) + // Device not found + return; + + // Build ACPI tables + u32 tables[MAX_ACPI_TABLES], tbl_idx = 0; + +#define ACPI_INIT_TABLE(X) \ + do { \ + tables[tbl_idx] = cpu_to_le32((u32)(X)); \ + if (le32_to_cpu(tables[tbl_idx])) \ + tbl_idx++; \ + } while(0) + + struct fadt_descriptor_rev1 *fadt = build_fadt(pci); + ACPI_INIT_TABLE(fadt); + ACPI_INIT_TABLE(build_ssdt()); + ACPI_INIT_TABLE(build_madt()); + ACPI_INIT_TABLE(build_hpet()); + ACPI_INIT_TABLE(build_srat()); + if (pci->device == PCI_DEVICE_ID_INTEL_ICH9_LPC) + ACPI_INIT_TABLE(build_mcfg_q35()); + + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix("acpi/", file); + if (!file) + break; + struct acpi_table_header *table = malloc_high(file->size); + if (!table) { + warn_noalloc(); + continue; + } + int ret = file->copy(file, table, file->size); + if (ret <= sizeof(*table)) + continue; + if (table->signature == DSDT_SIGNATURE) { + if (fadt) { + fill_dsdt(fadt, table); + } + } else { + ACPI_INIT_TABLE(table); + } + if (tbl_idx == MAX_ACPI_TABLES) { + warn_noalloc(); + break; + } + } + + if (CONFIG_ACPI_DSDT && fadt && !fadt->dsdt) { + /* default DSDT */ + struct acpi_table_header *dsdt = malloc_high(sizeof(AmlCode)); + if (!dsdt) { + warn_noalloc(); + return; + } + memcpy(dsdt, AmlCode, sizeof(AmlCode)); + fill_dsdt(fadt, dsdt); + /* Strip out compiler-generated header if any */ + memset(dsdt, 0, sizeof *dsdt); + build_header(dsdt, DSDT_SIGNATURE, sizeof(AmlCode), 1); + } + + // Build final rsdt table + struct rsdt_descriptor_rev1 *rsdt; + size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx; + rsdt = malloc_high(rsdt_len); + if (!rsdt) { + warn_noalloc(); + return; + } + memset(rsdt, 0, rsdt_len); + memcpy(rsdt->table_offset_entry, tables, sizeof(u32) * tbl_idx); + build_header((void*)rsdt, RSDT_SIGNATURE, rsdt_len, 1); + + // Build rsdp pointer table + struct rsdp_descriptor rsdp; + memset(&rsdp, 0, sizeof(rsdp)); + rsdp.signature = cpu_to_le64(RSDP_SIGNATURE); + memcpy(rsdp.oem_id, BUILD_APPNAME6, 6); + rsdp.rsdt_physical_address = cpu_to_le32((u32)rsdt); + rsdp.checksum -= checksum(&rsdp, 20); + copy_acpi_rsdp(&rsdp); +} diff --git a/qemu/roms/seabios/src/fw/biostables.c b/qemu/roms/seabios/src/fw/biostables.c new file mode 100644 index 000000000..50a891be8 --- /dev/null +++ b/qemu/roms/seabios/src/fw/biostables.c @@ -0,0 +1,461 @@ +// Support for manipulating bios tables (pir, mptable, acpi, smbios). +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // le32_to_cpu +#include "config.h" // CONFIG_* +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "hw/pci.h" // pci_config_writeb +#include "std/acpi.h" // struct rsdp_descriptor +#include "std/mptable.h" // MPTABLE_SIGNATURE +#include "std/pirtable.h" // struct pir_header +#include "std/smbios.h" // struct smbios_entry_point +#include "romfile.h" +#include "string.h" // memcpy +#include "util.h" // copy_table +#include "x86.h" // outb + +struct pir_header *PirAddr VARFSEG; + +void +copy_pir(void *pos) +{ + struct pir_header *p = pos; + if (p->signature != PIR_SIGNATURE) + return; + if (PirAddr) + return; + if (p->size < sizeof(*p)) + return; + if (checksum(pos, p->size) != 0) + return; + void *newpos = malloc_fseg(p->size); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying PIR from %p to %p\n", pos, newpos); + memcpy(newpos, pos, p->size); + PirAddr = newpos; +} + +void +copy_mptable(void *pos) +{ + struct mptable_floating_s *p = pos; + if (p->signature != MPTABLE_SIGNATURE) + return; + if (!p->physaddr) + return; + if (checksum(pos, sizeof(*p)) != 0) + return; + u32 length = p->length * 16; + u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length; + // Allocate final memory location. (In theory the config + // structure can go in high memory, but Linux kernels before + // v2.6.30 crash with that.) + struct mptable_floating_s *newpos = malloc_fseg(length + mpclength); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying MPTABLE from %p/%x to %p\n", pos, p->physaddr, newpos); + memcpy(newpos, pos, length); + newpos->physaddr = (u32)newpos + length; + newpos->checksum -= checksum(newpos, sizeof(*newpos)); + memcpy((void*)newpos + length, (void*)p->physaddr, mpclength); +} + + +/**************************************************************** + * ACPI + ****************************************************************/ + +static int +get_acpi_rsdp_length(void *pos, unsigned size) +{ + struct rsdp_descriptor *p = pos; + if (p->signature != RSDP_SIGNATURE) + return -1; + u32 length = 20; + if (length > size) + return -1; + if (checksum(pos, length) != 0) + return -1; + if (p->revision > 1) { + length = p->length; + if (length > size) + return -1; + if (checksum(pos, length) != 0) + return -1; + } + return length; +} + +struct rsdp_descriptor *RsdpAddr; + +void +copy_acpi_rsdp(void *pos) +{ + if (RsdpAddr) + return; + int length = get_acpi_rsdp_length(pos, -1); + if (length < 0) + return; + void *newpos = malloc_fseg(length); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying ACPI RSDP from %p to %p\n", pos, newpos); + memcpy(newpos, pos, length); + RsdpAddr = newpos; +} + +void *find_acpi_rsdp(void) +{ + extern u8 zonefseg_start[], zonefseg_end[]; + unsigned long start = (unsigned long)zonefseg_start; + unsigned long end = (unsigned long)zonefseg_end; + unsigned long pos; + + for (pos = ALIGN(start, 0x10); pos <= ALIGN_DOWN(end, 0x10); pos += 0x10) + if (get_acpi_rsdp_length((void *)pos, end - pos) >= 0) + return (void *)pos; + + return NULL; +} + +static struct fadt_descriptor_rev1 * +find_fadt(void) +{ + dprintf(4, "rsdp=%p\n", RsdpAddr); + if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE) + return NULL; + struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address; + dprintf(4, "rsdt=%p\n", rsdt); + if (!rsdt || rsdt->signature != RSDT_SIGNATURE) + return NULL; + void *end = (void*)rsdt + rsdt->length; + int i; + for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) { + struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[i]; + if (!fadt || fadt->signature != FACP_SIGNATURE) + continue; + dprintf(4, "fadt=%p\n", fadt); + return fadt; + } + dprintf(4, "no fadt found\n"); + return NULL; +} + +u32 +find_resume_vector(void) +{ + struct fadt_descriptor_rev1 *fadt = find_fadt(); + if (!fadt) + return 0; + struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl; + dprintf(4, "facs=%p\n", facs); + if (! facs || facs->signature != FACS_SIGNATURE) + return 0; + // Found it. + dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector); + return facs->firmware_waking_vector; +} + +static struct acpi_20_generic_address acpi_reset_reg; +static u8 acpi_reset_val; +u32 acpi_pm1a_cnt VARFSEG; +u16 acpi_pm_base = 0xb000; + +#define acpi_ga_to_bdf(addr) pci_to_bdf(0, (addr >> 32) & 0xffff, (addr >> 16) & 0xffff) + +void +acpi_reboot(void) +{ + // Check it passed the sanity checks in acpi_set_reset_reg() and was set + if (acpi_reset_reg.register_bit_width != 8) + return; + + u64 addr = le64_to_cpu(acpi_reset_reg.address); + + dprintf(1, "ACPI hard reset %d:%llx (%x)\n", + acpi_reset_reg.address_space_id, addr, acpi_reset_val); + + switch (acpi_reset_reg.address_space_id) { + case 0: // System Memory + writeb((void *)(u32)addr, acpi_reset_val); + break; + case 1: // System I/O + outb(acpi_reset_val, addr); + break; + case 2: // PCI config space + pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val); + break; + } +} + +static void +acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val) +{ + if (!reg || reg->address_space_id > 2 || + reg->register_bit_width != 8 || reg->register_bit_offset) + return; + + acpi_reset_reg = *reg; + acpi_reset_val = val; +} + +void +find_acpi_features(void) +{ + struct fadt_descriptor_rev1 *fadt = find_fadt(); + if (!fadt) + return; + u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk); + u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk); + dprintf(4, "pm_tmr_blk=%x\n", pm_tmr); + if (pm_tmr) + pmtimer_setup(pm_tmr); + if (pm1a_cnt) + acpi_pm1a_cnt = pm1a_cnt; + + // Theoretically we should check the 'reset_reg_sup' flag, but Windows + // doesn't and thus nobody seems to *set* it. If the table is large enough + // to include it, let the sanity checks in acpi_set_reset_reg() suffice. + if (fadt->length >= 129) { + void *p = fadt; + acpi_set_reset_reg(p + 116, *(u8 *)(p + 128)); + } +} + + +/**************************************************************** + * SMBIOS + ****************************************************************/ + +// Iterator for each sub-table in the smbios blob. +void * +smbios_next(struct smbios_entry_point *smbios, void *prev) +{ + if (!smbios) + return NULL; + void *start = (void*)smbios->structure_table_address; + void *end = start + smbios->structure_table_length; + + if (!prev) { + prev = start; + } else { + struct smbios_structure_header *hdr = prev; + if (prev + sizeof(*hdr) > end) + return NULL; + prev += hdr->length + 2; + while (prev < end && (*(u8*)(prev-1) != '\0' || *(u8*)(prev-2) != '\0')) + prev++; + } + struct smbios_structure_header *hdr = prev; + if (prev >= end || prev + sizeof(*hdr) >= end || prev + hdr->length >= end) + return NULL; + return prev; +} + +struct smbios_entry_point *SMBiosAddr; + +void +copy_smbios(void *pos) +{ + if (SMBiosAddr) + return; + struct smbios_entry_point *p = pos; + if (memcmp(p->anchor_string, "_SM_", 4)) + return; + if (checksum(pos, 0x10) != 0) + return; + if (memcmp(p->intermediate_anchor_string, "_DMI_", 5)) + return; + if (checksum(pos+0x10, p->length-0x10) != 0) + return; + struct smbios_entry_point *newpos = malloc_fseg(p->length); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying SMBIOS entry point from %p to %p\n", pos, newpos); + memcpy(newpos, pos, p->length); + SMBiosAddr = newpos; +} + +void +display_uuid(void) +{ + struct smbios_type_1 *tbl = smbios_next(SMBiosAddr, NULL); + int minlen = offsetof(struct smbios_type_1, uuid) + sizeof(tbl->uuid); + for (; tbl; tbl = smbios_next(SMBiosAddr, tbl)) + if (tbl->header.type == 1 && tbl->header.length >= minlen) { + u8 *uuid = tbl->uuid; + u8 empty_uuid[sizeof(tbl->uuid)] = { 0 }; + if (memcmp(uuid, empty_uuid, sizeof(empty_uuid)) == 0) + return; + + printf("Machine UUID" + " %02x%02x%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x%02x%02x%02x%02x\n" + , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3] + , uuid[ 4], uuid[ 5] + , uuid[ 6], uuid[ 7] + , uuid[ 8], uuid[ 9] + , uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + return; + } +} + +#define set_str_field_or_skip(type, field, value) \ + do { \ + int size = (value != NULL) ? strlen(value) + 1 : 0; \ + if (size > 1) { \ + memcpy(end, value, size); \ + end += size; \ + p->field = ++str_index; \ + } else { \ + p->field = 0; \ + } \ + } while (0) + +static void * +smbios_new_type_0(void *start, + const char *vendor, const char *version, const char *date) +{ + struct smbios_type_0 *p = (struct smbios_type_0 *)start; + char *end = (char *)start + sizeof(struct smbios_type_0); + int str_index = 0; + + p->header.type = 0; + p->header.length = sizeof(struct smbios_type_0); + p->header.handle = 0; + + set_str_field_or_skip(0, vendor_str, vendor); + set_str_field_or_skip(0, bios_version_str, version); + p->bios_starting_address_segment = 0xe800; + set_str_field_or_skip(0, bios_release_date_str, date); + + p->bios_rom_size = 0; /* FIXME */ + + /* BIOS characteristics not supported */ + memset(p->bios_characteristics, 0, 8); + p->bios_characteristics[0] = 0x08; + + /* Enable targeted content distribution (needed for SVVP) */ + p->bios_characteristics_extension_bytes[0] = 0; + p->bios_characteristics_extension_bytes[1] = 4; + + p->system_bios_major_release = 0; + p->system_bios_minor_release = 0; + p->embedded_controller_major_release = 0xFF; + p->embedded_controller_minor_release = 0xFF; + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +#define BIOS_NAME "SeaBIOS" +#define BIOS_DATE "04/01/2014" + +static int +smbios_romfile_setup(void) +{ + struct romfile_s *f_anchor = romfile_find("etc/smbios/smbios-anchor"); + struct romfile_s *f_tables = romfile_find("etc/smbios/smbios-tables"); + struct smbios_entry_point ep; + struct smbios_type_0 *t0; + u16 qtables_len, need_t0 = 1; + u8 *qtables, *tables; + + if (!f_anchor || !f_tables || f_anchor->size != sizeof(ep)) + return 0; + + f_anchor->copy(f_anchor, &ep, f_anchor->size); + + if (f_tables->size != ep.structure_table_length) + return 0; + + qtables = malloc_tmphigh(f_tables->size); + if (!qtables) { + warn_noalloc(); + return 0; + } + f_tables->copy(f_tables, qtables, f_tables->size); + ep.structure_table_address = (u32)qtables; /* for smbios_next(), below */ + + /* did we get a type 0 structure ? */ + for (t0 = smbios_next(&ep, NULL); t0; t0 = smbios_next(&ep, t0)) + if (t0->header.type == 0) { + need_t0 = 0; + break; + } + + qtables_len = ep.structure_table_length; + if (need_t0) { + /* common case: add our own type 0, with 3 strings and 4 '\0's */ + u16 t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) + + strlen(VERSION) + strlen(BIOS_DATE) + 4; + ep.structure_table_length += t0_len; + if (t0_len > ep.max_structure_size) + ep.max_structure_size = t0_len; + ep.number_of_structures++; + } + + /* allocate final blob and record its address in the entry point */ + if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG) + tables = malloc_high(ep.structure_table_length); + else + tables = malloc_fseg(ep.structure_table_length); + if (!tables) { + warn_noalloc(); + free(qtables); + return 0; + } + ep.structure_table_address = (u32)tables; + + /* populate final blob */ + if (need_t0) + tables = smbios_new_type_0(tables, BIOS_NAME, VERSION, BIOS_DATE); + memcpy(tables, qtables, qtables_len); + free(qtables); + + /* finalize entry point */ + ep.checksum -= checksum(&ep, 0x10); + ep.intermediate_checksum -= checksum((void *)&ep + 0x10, ep.length - 0x10); + + copy_smbios(&ep); + return 1; +} + +void +smbios_setup(void) +{ + if (smbios_romfile_setup()) + return; + smbios_legacy_setup(); +} + +void +copy_table(void *pos) +{ + copy_pir(pos); + copy_mptable(pos); + copy_acpi_rsdp(pos); + copy_smbios(pos); +} diff --git a/qemu/roms/seabios/src/fw/coreboot.c b/qemu/roms/seabios/src/fw/coreboot.c new file mode 100644 index 000000000..8fd84493b --- /dev/null +++ b/qemu/roms/seabios/src/fw/coreboot.c @@ -0,0 +1,553 @@ +// Coreboot interface support. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // MAXDESCSIZE +#include "byteorder.h" // be32_to_cpu +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pci_probe_devices +#include "lzmadecode.h" // LzmaDecode +#include "malloc.h" // free +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // PlatformRunningOn +#include "romfile.h" // romfile_findprefix +#include "stacks.h" // yield +#include "string.h" // memset +#include "util.h" // coreboot_preinit + + +/**************************************************************** + * Memory map + ****************************************************************/ + +struct cb_header { + u32 signature; + u32 header_bytes; + u32 header_checksum; + u32 table_bytes; + u32 table_checksum; + u32 table_entries; +}; + +#define CB_SIGNATURE 0x4f49424C // "LBIO" + +struct cb_memory_range { + u64 start; + u64 size; + u32 type; +}; + +#define CB_MEM_TABLE 16 + +struct cb_memory { + u32 tag; + u32 size; + struct cb_memory_range map[0]; +}; + +#define CB_TAG_MEMORY 0x01 + +#define MEM_RANGE_COUNT(_rec) \ + (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0])) + +struct cb_mainboard { + u32 tag; + u32 size; + u8 vendor_idx; + u8 part_idx; + char strings[0]; +}; + +#define CB_TAG_MAINBOARD 0x0003 + +struct cb_forward { + u32 tag; + u32 size; + u64 forward; +}; + +#define CB_TAG_FORWARD 0x11 + +struct cb_cbmem_ref { + u32 tag; + u32 size; + u64 cbmem_addr; +}; + +#define CB_TAG_CBMEM_CONSOLE 0x17 + +struct cbmem_console { + u32 buffer_size; + u32 buffer_cursor; + u8 buffer_body[0]; +} PACKED; +static struct cbmem_console *cbcon = NULL; + +static u16 +ipchksum(char *buf, int count) +{ + u16 *p = (u16*)buf; + u32 sum = 0; + while (count > 1) { + sum += GET_FARVAR(0, *p); + p++; + count -= 2; + } + if (count) + sum += GET_FARVAR(0, *(u8*)p); + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return ~sum; +} + +// Try to locate the coreboot header in a given address range. +static struct cb_header * +find_cb_header(u32 addr, int len) +{ + u32 end = addr + len; + for (; addr < end; addr += 16) { + struct cb_header *cbh = (void*)addr; + if (GET_FARVAR(0, cbh->signature) != CB_SIGNATURE) + continue; + u32 tsize = GET_FARVAR(0, cbh->table_bytes); + if (! tsize) + continue; + if (ipchksum((void*)addr, sizeof(*cbh)) != 0) + continue; + if (ipchksum((void*)addr + sizeof(*cbh), tsize) + != GET_FARVAR(0, cbh->table_checksum)) + continue; + return cbh; + } + return NULL; +} + +// Try to find the coreboot memory table in the given coreboot table. +void * +find_cb_subtable(struct cb_header *cbh, u32 tag) +{ + char *tbl = (char *)cbh + sizeof(*cbh); + u32 count = GET_FARVAR(0, cbh->table_entries); + int i; + for (i=0; i<count; i++) { + struct cb_memory *cbm = (void*)tbl; + tbl += GET_FARVAR(0, cbm->size); + if (GET_FARVAR(0, cbm->tag) == tag) + return cbm; + } + return NULL; +} + +struct cb_header * +find_cb_table(void) +{ + struct cb_header *cbh = find_cb_header(0, 0x1000); + if (!cbh) + return NULL; + struct cb_forward *cbf = find_cb_subtable(cbh, CB_TAG_FORWARD); + if (cbf) { + dprintf(3, "Found coreboot table forwarder.\n"); + cbh = find_cb_header(GET_FARVAR(0, cbf->forward), 0x100); + if (!cbh) + return NULL; + } + return cbh; +} + +static struct cb_memory *CBMemTable; +const char *CBvendor = "", *CBpart = ""; + +// Populate max ram and e820 map info by scanning for a coreboot table. +void +coreboot_preinit(void) +{ + if (!CONFIG_COREBOOT) + return; + + dprintf(3, "Attempting to find coreboot table\n"); + + // Find coreboot table. + struct cb_header *cbh = find_cb_table(); + if (!cbh) + goto fail; + dprintf(3, "Now attempting to find coreboot memory map\n"); + struct cb_memory *cbm = CBMemTable = find_cb_subtable(cbh, CB_TAG_MEMORY); + if (!cbm) + goto fail; + + int i, count = MEM_RANGE_COUNT(cbm); + for (i=0; i<count; i++) { + struct cb_memory_range *m = &cbm->map[i]; + u32 type = m->type; + if (type == CB_MEM_TABLE) + type = E820_RESERVED; + add_e820(m->start, m->size, type); + } + + // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this + // confuses grub. So, override it. + add_e820(0, 16*1024, E820_RAM); + + struct cb_cbmem_ref *cbref = find_cb_subtable(cbh, CB_TAG_CBMEM_CONSOLE); + if (cbref) { + cbcon = (void*)(u32)cbref->cbmem_addr; + debug_banner(); + dprintf(1, "Found coreboot cbmem console @ %llx\n", cbref->cbmem_addr); + } + + struct cb_mainboard *cbmb = find_cb_subtable(cbh, CB_TAG_MAINBOARD); + if (cbmb) { + CBvendor = &cbmb->strings[cbmb->vendor_idx]; + CBpart = &cbmb->strings[cbmb->part_idx]; + dprintf(1, "Found mainboard %s %s\n", CBvendor, CBpart); + } + + return; + +fail: + // No table found.. Use 16Megs as a dummy value. + dprintf(1, "Unable to find coreboot table!\n"); + add_e820(0, 16*1024*1024, E820_RAM); + return; +} + +void coreboot_debug_putc(char c) +{ + if (!CONFIG_DEBUG_COREBOOT) + return; + if (!cbcon) + return; + u32 cursor = cbcon->buffer_cursor++; + if (cursor < cbcon->buffer_size) + cbcon->buffer_body[cursor] = c; +} + +/**************************************************************** + * BIOS table copying + ****************************************************************/ + +// Attempt to find (and relocate) any standard bios tables found in a +// given address range. +static void +scan_tables(u32 start, u32 size) +{ + void *p = (void*)ALIGN(start, 16); + void *end = (void*)start + size; + for (; p<end; p += 16) + copy_table(p); +} + +void +coreboot_platform_setup(void) +{ + if (!CONFIG_COREBOOT) + return; + pci_probe_devices(); + + struct cb_memory *cbm = CBMemTable; + if (!cbm) + return; + + dprintf(3, "Relocating coreboot bios tables\n"); + + // Scan CB_MEM_TABLE areas for bios tables. + int i, count = MEM_RANGE_COUNT(cbm); + for (i=0; i<count; i++) { + struct cb_memory_range *m = &cbm->map[i]; + if (m->type == CB_MEM_TABLE) + scan_tables(m->start, m->size); + } + + find_acpi_features(); +} + + +/**************************************************************** + * ulzma + ****************************************************************/ + +// Uncompress data in flash to an area of memory. +static int +ulzma(u8 *dst, u32 maxlen, const u8 *src, u32 srclen) +{ + dprintf(3, "Uncompressing data %d@%p to %d@%p\n", srclen, src, maxlen, dst); + CLzmaDecoderState state; + int ret = LzmaDecodeProperties(&state.Properties, src, LZMA_PROPERTIES_SIZE); + if (ret != LZMA_RESULT_OK) { + dprintf(1, "LzmaDecodeProperties error - %d\n", ret); + return -1; + } + u8 scratch[15980]; + int need = (LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); + if (need > sizeof(scratch)) { + dprintf(1, "LzmaDecode need %d have %d\n", need, (unsigned int)sizeof(scratch)); + return -1; + } + state.Probs = (CProb *)scratch; + + u32 dstlen = *(u32*)(src + LZMA_PROPERTIES_SIZE); + if (dstlen > maxlen) { + dprintf(1, "LzmaDecode too large (max %d need %d)\n", maxlen, dstlen); + return -1; + } + u32 inProcessed, outProcessed; + ret = LzmaDecode(&state, src + LZMA_PROPERTIES_SIZE + 8, srclen + , &inProcessed, dst, dstlen, &outProcessed); + if (ret) { + dprintf(1, "LzmaDecode returned %d\n", ret); + return -1; + } + return dstlen; +} + + +/**************************************************************** + * Coreboot flash format + ****************************************************************/ + +#define CBFS_HEADER_MAGIC 0x4F524243 +#define CBFS_VERSION1 0x31313131 + +struct cbfs_header { + u32 magic; + u32 version; + u32 romsize; + u32 bootblocksize; + u32 align; + u32 offset; + u32 pad[2]; +} PACKED; + +#define CBFS_FILE_MAGIC 0x455649484352414cLL // LARCHIVE + +struct cbfs_file { + u64 magic; + u32 len; + u32 type; + u32 checksum; + u32 offset; + char filename[0]; +} PACKED; + +struct cbfs_romfile_s { + struct romfile_s file; + struct cbfs_file *fhdr; + void *data; + u32 rawsize, flags; +}; + +// Copy a file to memory (uncompressing if necessary) +static int +cbfs_copyfile(struct romfile_s *file, void *dst, u32 maxlen) +{ + if (!CONFIG_COREBOOT_FLASH) + return -1; + + struct cbfs_romfile_s *cfile; + cfile = container_of(file, struct cbfs_romfile_s, file); + u32 size = cfile->rawsize; + void *src = cfile->data; + if (cfile->flags) { + // Compressed - copy to temp ram and uncompress it. + void *temp = malloc_tmphigh(size); + if (!temp) { + warn_noalloc(); + return -1; + } + iomemcpy(temp, src, size); + int ret = ulzma(dst, maxlen, temp, size); + yield(); + free(temp); + return ret; + } + + // Not compressed. + dprintf(3, "Copying data %d@%p to %d@%p\n", size, src, maxlen, dst); + if (size > maxlen) { + warn_noalloc(); + return -1; + } + iomemcpy(dst, src, size); + return size; +} + +// Process CBFS links file. The links file is a newline separated +// file where each line has a "link name" and a "destination name" +// separated by a space character. +static void +process_links_file(void) +{ + char *links = romfile_loadfile("links", NULL), *next = links; + while (next) { + // Parse out linkname and destname + char *linkname = next; + next = strchr(linkname, '\n'); + if (next) + *next++ = '\0'; + char *comment = strchr(linkname, '#'); + if (comment) + *comment = '\0'; + linkname = nullTrailingSpace(linkname); + char *destname = strchr(linkname, ' '); + if (!destname) + continue; + *destname++ = '\0'; + destname = nullTrailingSpace(destname); + // Lookup destname and create new romfile entry for linkname + struct romfile_s *ufile = romfile_find(destname); + if (!ufile) + continue; + struct cbfs_romfile_s *cufile + = container_of(ufile, struct cbfs_romfile_s, file); + struct cbfs_romfile_s *cfile = malloc_tmp(sizeof(*cfile)); + if (!cfile) { + warn_noalloc(); + break; + } + memcpy(cfile, cufile, sizeof(*cfile)); + strtcpy(cfile->file.name, linkname, sizeof(cfile->file.name)); + romfile_add(&cfile->file); + } + free(links); +} + +void +coreboot_cbfs_init(void) +{ + if (!CONFIG_COREBOOT_FLASH) + return; + + struct cbfs_header *hdr = *(void **)(CONFIG_CBFS_LOCATION - 4); + if (hdr->magic != cpu_to_be32(CBFS_HEADER_MAGIC)) { + dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n" + , hdr, hdr->magic, cpu_to_be32(CBFS_HEADER_MAGIC)); + return; + } + dprintf(1, "Found CBFS header at %p\n", hdr); + + u32 romsize = be32_to_cpu(hdr->romsize); + u32 romstart = CONFIG_CBFS_LOCATION - romsize; + struct cbfs_file *fhdr = (void*)romstart + be32_to_cpu(hdr->offset); + for (;;) { + if ((u32)fhdr - romstart > romsize) + break; + u64 magic = fhdr->magic; + if (magic != CBFS_FILE_MAGIC) + break; + struct cbfs_romfile_s *cfile = malloc_tmp(sizeof(*cfile)); + if (!cfile) { + warn_noalloc(); + break; + } + memset(cfile, 0, sizeof(*cfile)); + strtcpy(cfile->file.name, fhdr->filename, sizeof(cfile->file.name)); + cfile->file.size = cfile->rawsize = be32_to_cpu(fhdr->len); + cfile->fhdr = fhdr; + cfile->file.copy = cbfs_copyfile; + cfile->data = (void*)fhdr + be32_to_cpu(fhdr->offset); + int len = strlen(cfile->file.name); + if (len > 5 && strcmp(&cfile->file.name[len-5], ".lzma") == 0) { + // Using compression. + cfile->flags = 1; + cfile->file.name[len-5] = '\0'; + cfile->file.size = *(u32*)(cfile->data + LZMA_PROPERTIES_SIZE); + } + romfile_add(&cfile->file); + + fhdr = (void*)ALIGN((u32)cfile->data + cfile->rawsize + , be32_to_cpu(hdr->align)); + } + + process_links_file(); +} + +struct cbfs_payload_segment { + u32 type; + u32 compression; + u32 offset; + u64 load_addr; + u32 len; + u32 mem_len; +} PACKED; + +#define PAYLOAD_SEGMENT_BSS 0x20535342 +#define PAYLOAD_SEGMENT_ENTRY 0x52544E45 + +#define CBFS_COMPRESS_NONE 0 +#define CBFS_COMPRESS_LZMA 1 + +struct cbfs_payload { + struct cbfs_payload_segment segments[1]; +}; + +void +cbfs_run_payload(struct cbfs_file *fhdr) +{ + if (!CONFIG_COREBOOT_FLASH || !fhdr) + return; + dprintf(1, "Run %s\n", fhdr->filename); + struct cbfs_payload *pay = (void*)fhdr + be32_to_cpu(fhdr->offset); + struct cbfs_payload_segment *seg = pay->segments; + for (;;) { + void *src = (void*)pay + be32_to_cpu(seg->offset); + void *dest = (void*)(u32)be64_to_cpu(seg->load_addr); + u32 src_len = be32_to_cpu(seg->len); + u32 dest_len = be32_to_cpu(seg->mem_len); + switch (seg->type) { + case PAYLOAD_SEGMENT_BSS: + dprintf(3, "BSS segment %d@%p\n", dest_len, dest); + memset(dest, 0, dest_len); + break; + case PAYLOAD_SEGMENT_ENTRY: { + dprintf(1, "Calling addr %p\n", dest); + void (*func)() = dest; + func(); + return; + } + default: + dprintf(3, "Segment %x %d@%p -> %d@%p\n" + , seg->type, src_len, src, dest_len, dest); + if (seg->compression == cpu_to_be32(CBFS_COMPRESS_NONE)) { + if (src_len > dest_len) + src_len = dest_len; + memcpy(dest, src, src_len); + } else if (CONFIG_LZMA + && seg->compression == cpu_to_be32(CBFS_COMPRESS_LZMA)) { + int ret = ulzma(dest, dest_len, src, src_len); + if (ret < 0) + return; + src_len = ret; + } else { + dprintf(1, "No support for compression type %x\n" + , seg->compression); + return; + } + if (dest_len > src_len) + memset(dest + src_len, 0, dest_len - src_len); + break; + } + seg++; + } +} + +// Register payloads in "img/" directory with boot system. +void +cbfs_payload_setup(void) +{ + if (!CONFIG_COREBOOT_FLASH) + return; + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix("img/", file); + if (!file) + break; + struct cbfs_romfile_s *cfile; + cfile = container_of(file, struct cbfs_romfile_s, file); + const char *filename = file->name; + char *desc = znprintf(MAXDESCSIZE, "Payload [%s]", &filename[4]); + boot_add_cbfs(cfile->fhdr, desc, bootprio_find_named_rom(filename, 0)); + } +} diff --git a/qemu/roms/seabios/src/fw/csm.c b/qemu/roms/seabios/src/fw/csm.c new file mode 100644 index 000000000..7cdb398f2 --- /dev/null +++ b/qemu/roms/seabios/src/fw/csm.c @@ -0,0 +1,344 @@ +// Compatibility Support Module (CSM) for UEFI / EDK-II +// +// Copyright © 2013 Intel Corporation +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" +#include "config.h" // CONFIG_* +#include "farptr.h" // MAKE_FLATPTR +#include "hw/pci.h" +#include "hw/pic.h" +#include "malloc.h" // csm_malloc_preinit +#include "memmap.h" +#include "output.h" // dprintf +#include "stacks.h" // wait_threads +#include "std/acpi.h" // RSDP_SIGNATURE +#include "std/bda.h" // struct bios_data_area_s +#include "std/optionrom.h" // struct rom_header +#include "util.h" // copy_smbios +#include "paravirt.h" // qemu_preinit + +#define UINT8 u8 +#define UINT16 u16 +#define UINT32 u32 +#include "std/LegacyBios.h" + +struct rsdp_descriptor csm_rsdp VARFSEG __aligned(16); + +EFI_COMPATIBILITY16_TABLE csm_compat_table VARFSEG __aligned(16) = { + .Signature = 0x24454649, + .TableChecksum = 0 /* Filled in by checkrom.py */, + .TableLength = sizeof(csm_compat_table), + .Compatibility16CallSegment = SEG_BIOS, + .Compatibility16CallOffset = 0 /* Filled in by checkrom.py */, + .OemIdStringPointer = (u32)"SeaBIOS", + .AcpiRsdPtrPointer = (u32)&csm_rsdp, +}; + +EFI_TO_COMPATIBILITY16_INIT_TABLE *csm_init_table; +EFI_TO_COMPATIBILITY16_BOOT_TABLE *csm_boot_table; + +static u16 PICMask = PIC_IRQMASK_DEFAULT; + +extern void __csm_return(struct bregs *regs) __noreturn; + +static void +csm_return(struct bregs *regs) +{ + u32 rommax = rom_get_max(); + extern u8 final_readonly_start[]; + + dprintf(3, "handle_csm returning AX=%04x\n", regs->ax); + + csm_compat_table.UmaAddress = rommax; + csm_compat_table.UmaSize = (u32)final_readonly_start - rommax; + + PICMask = pic_irqmask_read(); + __csm_return(regs); +} + +static void +csm_maininit(struct bregs *regs) +{ + interface_init(); + pci_probe_devices(); + + csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS; + csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset(); + + regs->ax = 0; + + csm_return(regs); +} + +/* Legacy16InitializeYourself */ +static void +handle_csm_0000(struct bregs *regs) +{ + qemu_preinit(); + + dprintf(3, "Legacy16InitializeYourself table %04x:%04x\n", regs->es, + regs->bx); + + csm_init_table = MAKE_FLATPTR(regs->es, regs->bx); + + dprintf(3, "BiosLessThan1MB %08x\n", csm_init_table->BiosLessThan1MB); + dprintf(3, "HiPmmMemory %08x\n", csm_init_table->HiPmmMemory); + dprintf(3, "HiPmmMemorySize %08x\n", csm_init_table->HiPmmMemorySizeInBytes); + dprintf(3, "ReverseThunk %04x:%04x\n", csm_init_table->ReverseThunkCallSegment, + csm_init_table->ReverseThunkCallOffset); + dprintf(3, "NumE820Entries %08x\n", csm_init_table->NumberE820Entries); + dprintf(3, "OsMemoryAbove1M %08x\n", csm_init_table->OsMemoryAbove1Mb); + dprintf(3, "ThunkStart %08x\n", csm_init_table->ThunkStart); + dprintf(3, "ThunkSize %08x\n", csm_init_table->ThunkSizeInBytes); + dprintf(3, "LoPmmMemory %08x\n", csm_init_table->LowPmmMemory); + dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes); + + csm_malloc_preinit(csm_init_table->LowPmmMemory, + csm_init_table->LowPmmMemorySizeInBytes, + csm_init_table->HiPmmMemory, + csm_init_table->HiPmmMemorySizeInBytes); + reloc_preinit(csm_maininit, regs); +} + +/* Legacy16UpdateBbs */ +static void +handle_csm_0001(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "Legacy16UpdateBbs table %04x:%04x\n", regs->es, regs->bx); + + csm_boot_table = MAKE_FLATPTR(regs->es, regs->bx); + dprintf(3, "MajorVersion %04x\n", csm_boot_table->MajorVersion); + dprintf(3, "MinorVersion %04x\n", csm_boot_table->MinorVersion); + dprintf(3, "AcpiTable %08x\n", csm_boot_table->AcpiTable); + dprintf(3, "SmbiosTable %08x\n", csm_boot_table->SmbiosTable); + dprintf(3, "SmbiosTableLength %08x\n", csm_boot_table->SmbiosTableLength); +// dprintf(3, "SioData %08x\n", csm_boot_table->SioData); + dprintf(3, "DevicePathType %04x\n", csm_boot_table->DevicePathType); + dprintf(3, "PciIrqMask %04x\n", csm_boot_table->PciIrqMask); + dprintf(3, "NumberE820Entries %08x\n", csm_boot_table->NumberE820Entries); +// dprintf(3, "HddInfo %08x\n", csm_boot_table->HddInfo); + dprintf(3, "NumberBbsEntries %08x\n", csm_boot_table->NumberBbsEntries); + dprintf(3, "BBsTable %08x\n", csm_boot_table->BbsTable); + dprintf(3, "SmmTable %08x\n", csm_boot_table->SmmTable); + dprintf(3, "OsMemoryAbove1Mb %08x\n", csm_boot_table->OsMemoryAbove1Mb); + dprintf(3, "UnconventionalDeviceTable %08x\n", csm_boot_table->UnconventionalDeviceTable); + + regs->ax = 0; +} + +/* PrepareToBoot */ +static void +handle_csm_0002(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "PrepareToBoot table %04x:%04x\n", regs->es, regs->bx); + + struct e820entry *p = (void *)csm_compat_table.E820Pointer; + int i; + for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++) + add_e820(p[i].start, p[i].size, p[i].type); + + if (csm_init_table->HiPmmMemorySizeInBytes > BUILD_MAX_HIGHTABLE) { + u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes; + add_e820(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED); + } + + // For PCIBIOS 1ab10e + if (csm_compat_table.IrqRoutingTablePointer && + csm_compat_table.IrqRoutingTableLength) { + PirAddr = (void *)csm_compat_table.IrqRoutingTablePointer; + dprintf(3, "CSM PIRQ table at %p\n", PirAddr); + } + + // For find_resume_vector()... and find_acpi_features() + if (csm_rsdp.signature == RSDP_SIGNATURE) { + RsdpAddr = &csm_rsdp; + dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr); + + find_acpi_features(); + } + + // SMBIOS table needs to be copied into the f-seg + // XX: OVMF doesn't seem to set SmbiosTableLength so don't check it + if (csm_boot_table->SmbiosTable && !SMBiosAddr) + copy_smbios((void *)csm_boot_table->SmbiosTable); + + // MPTABLE is just there; we don't care where. + + // EFI may have reinitialised the video using its *own* driver. + enable_vga_console(); + + // EFI fills this in for us. Zero it for now... + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + bda->hdcount = 0; + + mathcp_setup(); + timer_setup(); + clock_setup(); + device_hardware_setup(); + wait_threads(); + interactive_bootmenu(); + + prepareboot(); + + regs->ax = 0; +} + +/* Boot */ +static void +handle_csm_0003(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "Boot\n"); + + startBoot(); + + regs->ax = 1; +} + +/* Legacy16DispatchOprom */ +static void +handle_csm_0005(struct bregs *regs) +{ + EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx); + struct rom_header *rom; + u16 bdf; + + if (!CONFIG_OPTIONROMS) { + regs->ax = 1; + return; + } + + dprintf(3, "Legacy16DispatchOprom rom %p\n", table); + + dprintf(3, "OpromSegment %04x\n", table->OpromSegment); + dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); + dprintf(3, "PnPInstallationCheck %04x:%04x\n", + table->PnPInstallationCheckSegment, + table->PnPInstallationCheckOffset); + dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); + + rom = MAKE_FLATPTR(table->OpromSegment, 0); + bdf = pci_bus_devfn_to_bdf(table->PciBus, table->PciDeviceFunction); + + rom_reserve(rom->size * 512); + + // XX PnP seg/ofs should never be other than default + callrom(rom, bdf); + + rom_confirm(rom->size * 512); + + regs->bx = 0; // FIXME + regs->ax = 0; +} + +/* Legacy16GetTableAddress */ +static void +handle_csm_0006(struct bregs *regs) +{ + u16 size = regs->cx; + u16 align = regs->dx; + u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either) + void *chunk = NULL; + + if (!region) + region = 3; + + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n", + size, align, region); + + if (region & 2) + chunk = _malloc(&ZoneLow, size, align); + if (!chunk && (region & 1)) + chunk = _malloc(&ZoneFSeg, size, align); + + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d yields %p\n", + size, align, region, chunk); + if (chunk) { + regs->ds = FLATPTR_TO_SEG(chunk); + regs->bx = FLATPTR_TO_OFFSET(chunk); + regs->ax = 0; + } else { + regs->ax = 1; + } +} + +void VISIBLE32INIT +handle_csm(struct bregs *regs) +{ + ASSERT32FLAT(); + + if (!CONFIG_CSM) + return; + + dprintf(3, "handle_csm regs %p AX=%04x\n", regs, regs->ax); + + pic_irqmask_write(PICMask); + + switch(regs->ax) { + case 0000: handle_csm_0000(regs); break; + case 0001: handle_csm_0001(regs); break; + case 0002: handle_csm_0002(regs); break; + case 0003: handle_csm_0003(regs); break; +// case 0004: handle_csm_0004(regs); break; + case 0005: handle_csm_0005(regs); break; + case 0006: handle_csm_0006(regs); break; +// case 0007: handle_csm_0007(regs); break; +// case 0008: hamdle_csm_0008(regs); break; + default: regs->al = 1; + } + + csm_return(regs); +} + +int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + int index = 1 + (chanid * 2) + slave; + dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave, + index, bbs[index].BootPriority); + return bbs[index].BootPriority; +} + +int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority); + return bbs[0].BootPriority; +} + +int csm_bootprio_pci(struct pci_device *pci) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + int i; + + for (i = 5; i < csm_boot_table->NumberBbsEntries; i++) { + if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) { + dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus, + bbs[i].Device, bbs[i].Function, bbs[i].BootPriority); + return bbs[i].BootPriority; + } + } + return -1; +} diff --git a/qemu/roms/seabios/src/fw/dev-piix.h b/qemu/roms/seabios/src/fw/dev-piix.h new file mode 100644 index 000000000..c389f171a --- /dev/null +++ b/qemu/roms/seabios/src/fw/dev-piix.h @@ -0,0 +1,29 @@ +#ifndef __DEV_PIIX_H +#define __DEV_PIIX_H + +#define I440FX_PAM0 0x59 +#define I440FX_SMRAM 0x72 + +#define PIIX_PMBASE 0x40 +#define PIIX_PMREGMISC 0x80 +#define PIIX_SMBHSTBASE 0x90 +#define PIIX_SMBHSTCFG 0xd2 +#define PIIX_DEVACTB 0x58 +#define PIIX_DEVACTB_APMC_EN (1 << 25) + +#define PIIX_PORT_ELCR1 0x4d0 +#define PIIX_PORT_ELCR2 0x4d1 + +/* ICH9 PM I/O registers */ +#define PIIX_GPE0_BLK 0xafe0 +#define PIIX_GPE0_BLK_LEN 4 +#define PIIX_PMIO_GLBCTL 0x28 +#define PIIX_PMIO_GLBCTL_SMI_EN 1 + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define PIIX_ACPI_ENABLE 0xf1 +#define PIIX_ACPI_DISABLE 0xf0 + +#define PIIX_PM_INTRRUPT 9 // irq 9 + +#endif // dev-piix.h diff --git a/qemu/roms/seabios/src/fw/dev-q35.h b/qemu/roms/seabios/src/fw/dev-q35.h new file mode 100644 index 000000000..c6f8bd9e7 --- /dev/null +++ b/qemu/roms/seabios/src/fw/dev-q35.h @@ -0,0 +1,49 @@ +#ifndef __DEV_Q35_H +#define __DEV_Q35_H + +#include "types.h" // u16 + +#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 +#define Q35_HOST_BRIDGE_PAM0 0x90 +#define Q35_HOST_BRIDGE_SMRAM 0x9d +#define Q35_HOST_BRIDGE_PCIEXBAR 0x60 +#define Q35_HOST_BRIDGE_PCIEXBAR_SIZE (256 * 1024 * 1024) +#define Q35_HOST_BRIDGE_PCIEXBAR_ADDR 0xb0000000 +#define Q35_HOST_BRIDGE_PCIEXBAREN ((u64)1) +#define Q35_HOST_PCIE_PCI_SEGMENT 0 +#define Q35_HOST_PCIE_START_BUS_NUMBER 0 +#define Q35_HOST_PCIE_END_BUS_NUMBER 255 + +#define PCI_DEVICE_ID_INTEL_ICH9_LPC 0x2918 +#define ICH9_LPC_PMBASE 0x40 +#define ICH9_LPC_PMBASE_RTE 0x1 + +#define ICH9_LPC_ACPI_CTRL 0x44 +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 +#define ICH9_LPC_PIRQA_ROUT 0x60 +#define ICH9_LPC_PIRQE_ROUT 0x68 +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 +#define ICH9_LPC_GEN_PMCON_1 0xa0 +#define ICH9_LPC_GEN_PMCON_1_SMI_LOCK (1 << 4) +#define ICH9_LPC_PORT_ELCR1 0x4d0 +#define ICH9_LPC_PORT_ELCR2 0x4d1 +#define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930 +#define ICH9_SMB_SMB_BASE 0x20 +#define ICH9_SMB_HOSTC 0x40 +#define ICH9_SMB_HOSTC_HST_EN 0x01 + +#define ICH9_ACPI_ENABLE 0x2 +#define ICH9_ACPI_DISABLE 0x3 + +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ +#define ICH9_PMIO_GPE0_STS 0x20 +#define ICH9_PMIO_GPE0_BLK_LEN 0x10 +#define ICH9_PMIO_SMI_EN 0x30 +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) +#define ICH9_PMIO_SMI_EN_GLB_SMI_EN (1 << 0) + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define ICH9_APM_ACPI_ENABLE 0x2 +#define ICH9_APM_ACPI_DISABLE 0x3 + +#endif // dev-q35.h diff --git a/qemu/roms/seabios/src/fw/lzmadecode.c b/qemu/roms/seabios/src/fw/lzmadecode.c new file mode 100644 index 000000000..65819b53c --- /dev/null +++ b/qemu/roms/seabios/src/fw/lzmadecode.c @@ -0,0 +1,398 @@ +/* + LzmaDecode.c + LZMA Decoder (optimized for Speed version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this Code, expressly permits you to + statically or dynamically link your Code (or bind by name) to the + interfaces of this file without subjecting your linked Code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "lzmadecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \ + { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }} + + +#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; } + +#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2 + + +#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; + +#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \ + { UpdateBit0(p); mi <<= 1; A0; } else \ + { UpdateBit1(p); mi = (mi + mi) + 1; A1; } + +#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;) + +#define RangeDecoderBitTreeDecode(probs, numLevels, res) \ + { int i = numLevels; res = 1; \ + do { CProb *cp = probs + res; RC_GET_BIT(cp, res) } while(--i != 0); \ + res -= (1 << numLevels); } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ + unsigned char prop0; + if (size < LZMA_PROPERTIES_SIZE) + return LZMA_RESULT_DATA_ERROR; + prop0 = propsData[0]; + if (prop0 >= (9 * 5 * 5)) + return LZMA_RESULT_DATA_ERROR; + { + for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); + for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); + propsRes->lc = prop0; + /* + unsigned char remainder = (unsigned char)(prop0 / 9); + propsRes->lc = prop0 % 9; + propsRes->pb = remainder / 5; + propsRes->lp = remainder % 5; + */ + } + + return LZMA_RESULT_OK; +} + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecode(CLzmaDecoderState *vs, + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed) +{ + CProb *p = vs->Probs; + SizeT nowPos = 0; + Byte previousByte = 0; + UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; + UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; + int lc = vs->Properties.lc; + + + int state = 0; + UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + int len = 0; + const Byte *Buffer; + const Byte *BufferLim; + UInt32 Range; + UInt32 Code; + + *inSizeProcessed = 0; + *outSizeProcessed = 0; + + { + UInt32 i; + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + } + + RC_INIT(inStream, inSize); + + + while(nowPos < outSize) + { + CProb *prob; + UInt32 bound; + int posState = (int)( + (nowPos + ) + & posStateMask); + + prob = p + IsMatch + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + int symbol = 1; + UpdateBit0(prob) + prob = p + Literal + (LZMA_LIT_SIZE * + ((( + (nowPos + ) + & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state >= kNumLitStates) + { + int matchByte; + matchByte = outStream[nowPos - rep0]; + do + { + int bit; + CProb *probLit; + matchByte <<= 1; + bit = (matchByte & 0x100); + probLit = prob + 0x100 + bit + symbol; + RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break) + } + while (symbol < 0x100); + } + while (symbol < 0x100) + { + CProb *probLit = prob + symbol; + RC_GET_BIT(probLit, symbol) + } + previousByte = (Byte)symbol; + + outStream[nowPos++] = previousByte; + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + } + else + { + UpdateBit1(prob); + prob = p + IsRep + state; + IfBit0(prob) + { + UpdateBit0(prob); + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < kNumLitStates ? 0 : 3; + prob = p + LenCoder; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG0 + state; + IfBit0(prob) + { + UpdateBit0(prob); + prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + UpdateBit0(prob); + + if (nowPos == 0) + return LZMA_RESULT_DATA_ERROR; + + state = state < kNumLitStates ? 9 : 11; + previousByte = outStream[nowPos - rep0]; + outStream[nowPos++] = previousByte; + + continue; + } + else + { + UpdateBit1(prob); + } + } + else + { + UInt32 distance; + UpdateBit1(prob); + prob = p + IsRepG1 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep1; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG2 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep2; + } + else + { + UpdateBit1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = p + RepLenCoder; + } + { + int numBits, offset; + CProb *probLen = prob + LenChoice; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + numBits = kLenNumLowBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenChoice2; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + numBits = kLenNumMidBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + numBits = kLenNumHighBits; + } + } + RangeDecoderBitTreeDecode(probLen, numBits, len); + len += offset; + } + + if (state < 4) + { + int posSlot; + state += kNumLitStates; + prob = p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = (2 | ((UInt32)posSlot & 1)); + if (posSlot < kEndPosModelIndex) + { + rep0 <<= numDirectBits; + prob = p + SpecPos + rep0 - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + RC_NORMALIZE + Range >>= 1; + rep0 <<= 1; + if (Code >= Range) + { + Code -= Range; + rep0 |= 1; + } + } + while (--numDirectBits != 0); + prob = p + Align; + rep0 <<= kNumAlignBits; + numDirectBits = kNumAlignBits; + } + { + int i = 1; + int mi = 1; + do + { + CProb *prob3 = prob + mi; + RC_GET_BIT2(prob3, mi, ; , rep0 |= i); + i <<= 1; + } + while(--numDirectBits != 0); + } + } + else + rep0 = posSlot; + if (++rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = kLzmaStreamWasFinishedId; + break; + } + } + + len += kMatchMinLen; + if (rep0 > nowPos) + return LZMA_RESULT_DATA_ERROR; + + + do + { + previousByte = outStream[nowPos - rep0]; + len--; + outStream[nowPos++] = previousByte; + } + while(len != 0 && nowPos < outSize); + } + } + RC_NORMALIZE; + + + *inSizeProcessed = (SizeT)(Buffer - inStream); + *outSizeProcessed = nowPos; + return LZMA_RESULT_OK; +} diff --git a/qemu/roms/seabios/src/fw/lzmadecode.h b/qemu/roms/seabios/src/fw/lzmadecode.h new file mode 100644 index 000000000..dedde0de6 --- /dev/null +++ b/qemu/roms/seabios/src/fw/lzmadecode.h @@ -0,0 +1,67 @@ +/* + LzmaDecode.h + LZMA Decoder interface + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMADECODE_H +#define __LZMADECODE_H + +typedef unsigned char Byte; +typedef unsigned short UInt16; +typedef unsigned int UInt32; +typedef UInt32 SizeT; + +#define CProb UInt16 + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LZMA_PROPERTIES_SIZE 5 + +typedef struct _CLzmaProperties +{ + int lc; + int lp; + int pb; +}CLzmaProperties; + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size); + +#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp))) + +#define kLzmaNeedInitId (-2) + +typedef struct _CLzmaDecoderState +{ + CLzmaProperties Properties; + CProb *Probs; + + +} CLzmaDecoderState; + + +int LzmaDecode(CLzmaDecoderState *vs, + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed); + +#endif diff --git a/qemu/roms/seabios/src/fw/mptable.c b/qemu/roms/seabios/src/fw/mptable.c new file mode 100644 index 000000000..8e01e0043 --- /dev/null +++ b/qemu/roms/seabios/src/fw/mptable.c @@ -0,0 +1,196 @@ +// MPTable generation (on emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/pci.h" +#include "hw/pci_regs.h" +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "std/mptable.h" // MPTABLE_SIGNATURE +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // cpuid + +void +mptable_setup(void) +{ + if (! CONFIG_MPTABLE) + return; + + dprintf(3, "init MPTable\n"); + + // Config structure in temp area. + struct mptable_config_s *config = malloc_tmp(32*1024); + if (!config) { + warn_noalloc(); + return; + } + memset(config, 0, sizeof(*config)); + config->signature = MPCONFIG_SIGNATURE; + config->spec = 4; + memcpy(config->oemid, BUILD_CPUNAME8, sizeof(config->oemid)); + memcpy(config->productid, "0.1 ", sizeof(config->productid)); + config->lapic = BUILD_APIC_ADDR; + + // Detect cpu info + u32 cpuid_signature, ebx, ecx, cpuid_features; + cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); + if (! cpuid_signature) { + // Use default values. + cpuid_signature = 0x600; + cpuid_features = 0x201; + } + int pkgcpus = 1; + if (cpuid_features & (1 << 28)) { + /* Only populate the MPS tables with the first logical CPU in + each package */ + pkgcpus = (ebx >> 16) & 0xff; + pkgcpus = 1 << (__fls(pkgcpus - 1) + 1); /* round up to power of 2 */ + } + u8 apic_version = readl((u8*)BUILD_APIC_ADDR + 0x30) & 0xff; + + // CPU definitions. + struct mpt_cpu *cpus = (void*)&config[1], *cpu = cpus; + int i; + for (i = 0; i < MaxCountCPUs; i+=pkgcpus) { + memset(cpu, 0, sizeof(*cpu)); + cpu->type = MPT_TYPE_CPU; + cpu->apicid = i; + cpu->apicver = apic_version; + /* cpu flags: enabled, bootstrap cpu */ + cpu->cpuflag = (apic_id_is_present(i) ? 0x01 : 0x00) | ((i==0) ? 0x02 : 0x00); + cpu->cpusignature = cpuid_signature; + cpu->featureflag = cpuid_features; + cpu++; + } + int entrycount = cpu - cpus; + + // PCI bus + struct mpt_bus *buses = (void*)cpu, *bus = buses; + if (!hlist_empty(&PCIDevices)) { + memset(bus, 0, sizeof(*bus)); + bus->type = MPT_TYPE_BUS; + bus->busid = 0; + memcpy(bus->bustype, "PCI ", sizeof(bus->bustype)); + bus++; + entrycount++; + } + + /* isa bus */ + int isabusid = bus - buses; + memset(bus, 0, sizeof(*bus)); + bus->type = MPT_TYPE_BUS; + bus->busid = isabusid; + memcpy(bus->bustype, "ISA ", sizeof(bus->bustype)); + bus++; + entrycount++; + + /* ioapic */ + u8 ioapic_id = BUILD_IOAPIC_ID; + struct mpt_ioapic *ioapic = (void*)bus; + memset(ioapic, 0, sizeof(*ioapic)); + ioapic->type = MPT_TYPE_IOAPIC; + ioapic->apicid = ioapic_id; + ioapic->apicver = 0x11; + ioapic->flags = 1; // enable + ioapic->apicaddr = BUILD_IOAPIC_ADDR; + entrycount++; + + /* irqs */ + struct mpt_intsrc *intsrcs = (void*)&ioapic[1], *intsrc = intsrcs; + int dev = -1; + unsigned short pinmask = 0; + + struct pci_device *pci; + foreachpci(pci) { + u16 bdf = pci->bdf; + if (pci_bdf_to_bus(bdf) != 0) + break; + int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); + int irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + if (pin == 0) + continue; + if (dev != pci_bdf_to_busdev(bdf)) { + dev = pci_bdf_to_busdev(bdf); + pinmask = 0; + } + if (pinmask & (1 << pin)) /* pin was seen already */ + continue; + pinmask |= (1 << pin); + memset(intsrc, 0, sizeof(*intsrc)); + intsrc->type = MPT_TYPE_INTSRC; + intsrc->irqtype = 0; /* INT */ + intsrc->irqflag = 1; /* active high */ + intsrc->srcbus = pci_bdf_to_bus(bdf); /* PCI bus */ + intsrc->srcbusirq = (pci_bdf_to_dev(bdf) << 2) | (pin - 1); + intsrc->dstapic = ioapic_id; + intsrc->dstirq = irq; + intsrc++; + } + + int irq0_override = romfile_loadint("etc/irq0-override", 0); + for (i = 0; i < 16; i++) { + memset(intsrc, 0, sizeof(*intsrc)); + if (BUILD_PCI_IRQS & (1 << i)) + continue; + intsrc->type = MPT_TYPE_INTSRC; + intsrc->irqtype = 0; /* INT */ + intsrc->irqflag = 0; /* conform to bus spec */ + intsrc->srcbus = isabusid; /* ISA bus */ + intsrc->srcbusirq = i; + intsrc->dstapic = ioapic_id; + intsrc->dstirq = i; + if (irq0_override) { + /* Destination 2 is covered by irq0->inti2 override (i == + 0). Source IRQ 2 is unused */ + if (i == 0) + intsrc->dstirq = 2; + else if (i == 2) + intsrc--; + } + intsrc++; + } + + /* Local interrupt assignment */ + intsrc->type = MPT_TYPE_LOCAL_INT; + intsrc->irqtype = 3; /* ExtINT */ + intsrc->irqflag = 0; /* PO, EL default */ + intsrc->srcbus = isabusid; /* ISA */ + intsrc->srcbusirq = 0; + intsrc->dstapic = 0; /* BSP == APIC #0 */ + intsrc->dstirq = 0; /* LINTIN0 */ + intsrc++; + + intsrc->type = MPT_TYPE_LOCAL_INT; + intsrc->irqtype = 1; /* NMI */ + intsrc->irqflag = 0; /* PO, EL default */ + intsrc->srcbus = isabusid; /* ISA */ + intsrc->srcbusirq = 0; + intsrc->dstapic = 0xff; /* to all local APICs */ + intsrc->dstirq = 1; /* LINTIN1 */ + intsrc++; + entrycount += intsrc - intsrcs; + + // Finalize config structure. + int length = (void*)intsrc - (void*)config; + config->entrycount = entrycount; + config->length = length; + config->checksum -= checksum(config, length); + + // floating pointer structure + struct mptable_floating_s floating; + memset(&floating, 0, sizeof(floating)); + floating.signature = MPTABLE_SIGNATURE; + floating.physaddr = (u32)config; + floating.length = 1; + floating.spec_rev = 4; + floating.checksum -= checksum(&floating, sizeof(floating)); + copy_mptable(&floating); + free(config); +} diff --git a/qemu/roms/seabios/src/fw/mtrr.c b/qemu/roms/seabios/src/fw/mtrr.c new file mode 100644 index 000000000..913580e7d --- /dev/null +++ b/qemu/roms/seabios/src/fw/mtrr.c @@ -0,0 +1,106 @@ +// Initialize MTRRs - mostly useful on KVM. +// +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pcimem_start +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "util.h" // mtrr_setup +#include "x86.h" // cpuid + +#define MSR_MTRRcap 0x000000fe +#define MSR_MTRRfix64K_00000 0x00000250 +#define MSR_MTRRfix16K_80000 0x00000258 +#define MSR_MTRRfix16K_A0000 0x00000259 +#define MSR_MTRRfix4K_C0000 0x00000268 +#define MSR_MTRRfix4K_C8000 0x00000269 +#define MSR_MTRRfix4K_D0000 0x0000026a +#define MSR_MTRRfix4K_D8000 0x0000026b +#define MSR_MTRRfix4K_E0000 0x0000026c +#define MSR_MTRRfix4K_E8000 0x0000026d +#define MSR_MTRRfix4K_F0000 0x0000026e +#define MSR_MTRRfix4K_F8000 0x0000026f +#define MSR_MTRRdefType 0x000002ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define MTRR_MEMTYPE_UC 0 +#define MTRR_MEMTYPE_WC 1 +#define MTRR_MEMTYPE_WT 4 +#define MTRR_MEMTYPE_WP 5 +#define MTRR_MEMTYPE_WB 6 + +void mtrr_setup(void) +{ + if (!CONFIG_MTRR_INIT) + return; + + u32 eax, ebx, ecx, edx, cpuid_features; + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + if (!(cpuid_features & CPUID_MTRR)) + return; + if (!(cpuid_features & CPUID_MSR)) + return; + + dprintf(3, "init mtrr\n"); + + u32 mtrr_cap = rdmsr(MSR_MTRRcap); + int vcnt = mtrr_cap & 0xff; + int fix = mtrr_cap & 0x100; + if (!vcnt || !fix) + return; + + // Disable MTRRs + wrmsr_smp(MSR_MTRRdefType, 0); + + // Set fixed MTRRs + union u64b { + u8 valb[8]; + u64 val; + } u; + u.val = 0; + int i; + for (i = 0; i < 8; i++) + if (RamSize >= 65536 * (i + 1)) + u.valb[i] = MTRR_MEMTYPE_WB; + wrmsr_smp(MSR_MTRRfix64K_00000, u.val); + u.val = 0; + for (i = 0; i < 8; i++) + if (RamSize >= 0x80000 + 16384 * (i + 1)) + u.valb[i] = MTRR_MEMTYPE_WB; + wrmsr_smp(MSR_MTRRfix16K_80000, u.val); + wrmsr_smp(MSR_MTRRfix16K_A0000, 0); // 0xA0000-0xC0000 is uncached + int j; + for (j = 0; j < 8; j++) { + u.val = 0; + for (i = 0; i < 8; i++) + if (RamSize >= 0xC0000 + j * 0x8000 + 4096 * (i + 1)) + u.valb[i] = MTRR_MEMTYPE_WP; + wrmsr_smp(MSR_MTRRfix4K_C0000 + j, u.val); + } + + // Set variable MTRRs + int phys_bits = 36; + cpuid(0x80000000u, &eax, &ebx, &ecx, &edx); + if (eax >= 0x80000008) { + /* Get physical bits from leaf 0x80000008 (if available) */ + cpuid(0x80000008u, &eax, &ebx, &ecx, &edx); + phys_bits = eax & 0xff; + } + u64 phys_mask = ((1ull << phys_bits) - 1); + for (i=0; i<vcnt; i++) { + wrmsr_smp(MTRRphysBase_MSR(i), 0); + wrmsr_smp(MTRRphysMask_MSR(i), 0); + } + /* Mark 3.5-4GB as UC, anything not specified defaults to WB */ + wrmsr_smp(MTRRphysBase_MSR(0), pcimem_start | MTRR_MEMTYPE_UC); + wrmsr_smp(MTRRphysMask_MSR(0) + , (-((1ull<<32)-pcimem_start) & phys_mask) | 0x800); + + // Enable fixed and variable MTRRs; set default type. + wrmsr_smp(MSR_MTRRdefType, 0xc00 | MTRR_MEMTYPE_WB); +} diff --git a/qemu/roms/seabios/src/fw/paravirt.c b/qemu/roms/seabios/src/fw/paravirt.c new file mode 100644 index 000000000..db22ae8fc --- /dev/null +++ b/qemu/roms/seabios/src/fw/paravirt.c @@ -0,0 +1,448 @@ +// Paravirtualization support. +// +// Copyright (C) 2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2009 Red Hat Inc. +// +// Authors: +// Gleb Natapov <gnatapov@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // be32_to_cpu +#include "config.h" // CONFIG_QEMU +#include "hw/pci.h" // create_pirtable +#include "hw/pci_regs.h" // PCI_DEVICE_ID +#include "hw/rtc.h" // CMOS_* +#include "malloc.h" // malloc_tmp +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // qemu_cfg_preinit +#include "romfile.h" // romfile_loadint +#include "romfile_loader.h" // romfile_loader_execute +#include "string.h" // memset +#include "util.h" // pci_setup +#include "x86.h" // cpuid +#include "xen.h" // xen_biostable_setup + +// Amount of continuous ram under 4Gig +u32 RamSize; +// Amount of continuous ram >4Gig +u64 RamSizeOver4G; +// Type of emulator platform. +int PlatformRunningOn VARFSEG; + +/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It + * should be used to determine that a VM is running under KVM. + */ +#define KVM_CPUID_SIGNATURE 0x40000000 + +static void kvm_detect(void) +{ + unsigned int eax, ebx, ecx, edx; + char signature[13]; + + cpuid(KVM_CPUID_SIGNATURE, &eax, &ebx, &ecx, &edx); + memcpy(signature + 0, &ebx, 4); + memcpy(signature + 4, &ecx, 4); + memcpy(signature + 8, &edx, 4); + signature[12] = 0; + + if (strcmp(signature, "KVMKVMKVM") == 0) { + dprintf(1, "Running on KVM\n"); + PlatformRunningOn |= PF_KVM; + } +} + +static void qemu_detect(void) +{ + if (!CONFIG_QEMU_HARDWARE) + return; + + // check northbridge @ 00:00.0 + u16 v = pci_config_readw(0, PCI_VENDOR_ID); + if (v == 0x0000 || v == 0xffff) + return; + u16 d = pci_config_readw(0, PCI_DEVICE_ID); + u16 sv = pci_config_readw(0, PCI_SUBSYSTEM_VENDOR_ID); + u16 sd = pci_config_readw(0, PCI_SUBSYSTEM_ID); + + if (sv != 0x1af4 || /* Red Hat, Inc */ + sd != 0x1100) /* Qemu virtual machine */ + return; + + PlatformRunningOn |= PF_QEMU; + switch (d) { + case 0x1237: + dprintf(1, "Running on QEMU (i440fx)\n"); + break; + case 0x29c0: + dprintf(1, "Running on QEMU (q35)\n"); + break; + default: + dprintf(1, "Running on QEMU (unknown nb: %04x:%04x)\n", v, d); + break; + } + kvm_detect(); +} + +void +qemu_preinit(void) +{ + qemu_detect(); + + if (!CONFIG_QEMU) + return; + + if (runningOnXen()) { + xen_ramsize_preinit(); + return; + } + + if (!runningOnQEMU()) { + dprintf(1, "Warning: No QEMU Northbridge found (isapc?)\n"); + PlatformRunningOn |= PF_QEMU; + kvm_detect(); + } + + // On emulators, get memory size from nvram. + u32 rs = ((rtc_read(CMOS_MEM_EXTMEM2_LOW) << 16) + | (rtc_read(CMOS_MEM_EXTMEM2_HIGH) << 24)); + if (rs) + rs += 16 * 1024 * 1024; + else + rs = (((rtc_read(CMOS_MEM_EXTMEM_LOW) << 10) + | (rtc_read(CMOS_MEM_EXTMEM_HIGH) << 18)) + + 1 * 1024 * 1024); + RamSize = rs; + add_e820(0, rs, E820_RAM); + + /* reserve 256KB BIOS area at the end of 4 GB */ + add_e820(0xfffc0000, 256*1024, E820_RESERVED); + + dprintf(1, "RamSize: 0x%08x [cmos]\n", RamSize); +} + +void +qemu_platform_setup(void) +{ + if (!CONFIG_QEMU) + return; + + if (runningOnXen()) { + pci_probe_devices(); + xen_hypercall_setup(); + xen_biostable_setup(); + return; + } + + // Initialize pci + pci_setup(); + smm_device_setup(); + smm_setup(); + + // Initialize mtrr and smp + mtrr_setup(); + smp_setup(); + + // Create bios tables + pirtable_setup(); + mptable_setup(); + smbios_setup(); + + if (CONFIG_FW_ROMFILE_LOAD) { + int loader_err; + + dprintf(3, "load ACPI tables\n"); + + loader_err = romfile_loader_execute("etc/table-loader"); + + RsdpAddr = find_acpi_rsdp(); + + if (RsdpAddr) + return; + + /* If present, loader should have installed an RSDP. + * Not installed? We might still be able to continue + * using the builtin RSDP. + */ + if (!loader_err) + warn_internalerror(); + } + + acpi_setup(); +} + + +/**************************************************************** + * QEMU firmware config (fw_cfg) interface + ****************************************************************/ + +// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content +// should be passed via the fw_cfg "file" interface.) +#define QEMU_CFG_SIGNATURE 0x00 +#define QEMU_CFG_ID 0x01 +#define QEMU_CFG_UUID 0x02 +#define QEMU_CFG_NUMA 0x0d +#define QEMU_CFG_BOOT_MENU 0x0e +#define QEMU_CFG_MAX_CPUS 0x0f +#define QEMU_CFG_FILE_DIR 0x19 +#define QEMU_CFG_ARCH_LOCAL 0x8000 +#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) +#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) +#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) +#define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3) + +static void +qemu_cfg_select(u16 f) +{ + outw(f, PORT_QEMU_CFG_CTL); +} + +static void +qemu_cfg_read(void *buf, int len) +{ + insb(PORT_QEMU_CFG_DATA, buf, len); +} + +static void +qemu_cfg_skip(int len) +{ + while (len--) + inb(PORT_QEMU_CFG_DATA); +} + +static void +qemu_cfg_read_entry(void *buf, int e, int len) +{ + qemu_cfg_select(e); + qemu_cfg_read(buf, len); +} + +struct qemu_romfile_s { + struct romfile_s file; + int select, skip; +}; + +static int +qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen) +{ + if (file->size > maxlen) + return -1; + struct qemu_romfile_s *qfile; + qfile = container_of(file, struct qemu_romfile_s, file); + qemu_cfg_select(qfile->select); + qemu_cfg_skip(qfile->skip); + qemu_cfg_read(dst, file->size); + return file->size; +} + +static void +qemu_romfile_add(char *name, int select, int skip, int size) +{ + struct qemu_romfile_s *qfile = malloc_tmp(sizeof(*qfile)); + if (!qfile) { + warn_noalloc(); + return; + } + memset(qfile, 0, sizeof(*qfile)); + strtcpy(qfile->file.name, name, sizeof(qfile->file.name)); + qfile->file.size = size; + qfile->select = select; + qfile->skip = skip; + qfile->file.copy = qemu_cfg_read_file; + romfile_add(&qfile->file); +} + +struct e820_reservation { + u64 address; + u64 length; + u32 type; +}; + +#define SMBIOS_FIELD_ENTRY 0 +#define SMBIOS_TABLE_ENTRY 1 + +struct qemu_smbios_header { + u16 length; + u8 headertype; + u8 tabletype; + u16 fieldoffset; +} PACKED; + +static void +qemu_cfg_e820(void) +{ + struct e820_reservation *table; + int i, size; + + if (!CONFIG_QEMU) + return; + + // "etc/e820" has both ram and reservations + table = romfile_loadfile("etc/e820", &size); + if (table) { + for (i = 0; i < size / sizeof(struct e820_reservation); i++) { + switch (table[i].type) { + case E820_RAM: + dprintf(1, "RamBlock: addr 0x%016llx len 0x%016llx [e820]\n", + table[i].address, table[i].length); + if (table[i].address < RamSize) + // ignore, preinit got it from cmos already and + // adding this again would ruin any reservations + // done so far + continue; + if (table[i].address < 0x100000000LL) { + // below 4g -- adjust RamSize to mark highest lowram addr + if (RamSize < table[i].address + table[i].length) + RamSize = table[i].address + table[i].length; + } else { + // above 4g -- adjust RamSizeOver4G to mark highest ram addr + if (0x100000000LL + RamSizeOver4G < table[i].address + table[i].length) + RamSizeOver4G = table[i].address + table[i].length - 0x100000000LL; + } + /* fall through */ + case E820_RESERVED: + add_e820(table[i].address, table[i].length, table[i].type); + break; + default: + /* + * Qemu 1.7 uses RAM + RESERVED only. Ignore + * everything else, so we have the option to + * extend this in the future without breakage. + */ + break; + } + } + return; + } + + // QEMU_CFG_E820_TABLE has reservations only + u32 count32; + qemu_cfg_read_entry(&count32, QEMU_CFG_E820_TABLE, sizeof(count32)); + if (count32) { + struct e820_reservation entry; + int i; + for (i = 0; i < count32; i++) { + qemu_cfg_read(&entry, sizeof(entry)); + add_e820(entry.address, entry.length, entry.type); + } + } else if (runningOnKVM()) { + // Backwards compatibility - provide hard coded range. + // 4 pages before the bios, 3 pages for vmx tss pages, the + // other page for EPT real mode pagetable + add_e820(0xfffbc000, 4*4096, E820_RESERVED); + } + + // Check for memory over 4Gig in cmos + u64 high = ((rtc_read(CMOS_MEM_HIGHMEM_LOW) << 16) + | ((u32)rtc_read(CMOS_MEM_HIGHMEM_MID) << 24) + | ((u64)rtc_read(CMOS_MEM_HIGHMEM_HIGH) << 32)); + RamSizeOver4G = high; + add_e820(0x100000000ull, high, E820_RAM); + dprintf(1, "RamSizeOver4G: 0x%016llx [cmos]\n", RamSizeOver4G); +} + +// Populate romfile entries for legacy fw_cfg ports (that predate the +// "file" interface). +static void +qemu_cfg_legacy(void) +{ + if (!CONFIG_QEMU) + return; + + // Misc config items. + qemu_romfile_add("etc/show-boot-menu", QEMU_CFG_BOOT_MENU, 0, 2); + qemu_romfile_add("etc/irq0-override", QEMU_CFG_IRQ0_OVERRIDE, 0, 1); + qemu_romfile_add("etc/max-cpus", QEMU_CFG_MAX_CPUS, 0, 2); + + // NUMA data + u64 numacount; + qemu_cfg_read_entry(&numacount, QEMU_CFG_NUMA, sizeof(numacount)); + int max_cpu = romfile_loadint("etc/max-cpus", 0); + qemu_romfile_add("etc/numa-cpu-map", QEMU_CFG_NUMA, sizeof(numacount) + , max_cpu*sizeof(u64)); + qemu_romfile_add("etc/numa-nodes", QEMU_CFG_NUMA + , sizeof(numacount) + max_cpu*sizeof(u64) + , numacount*sizeof(u64)); + + // ACPI tables + char name[128]; + u16 cnt; + qemu_cfg_read_entry(&cnt, QEMU_CFG_ACPI_TABLES, sizeof(cnt)); + int i, offset = sizeof(cnt); + for (i = 0; i < cnt; i++) { + u16 len; + qemu_cfg_read(&len, sizeof(len)); + offset += sizeof(len); + snprintf(name, sizeof(name), "acpi/table%d", i); + qemu_romfile_add(name, QEMU_CFG_ACPI_TABLES, offset, len); + qemu_cfg_skip(len); + offset += len; + } + + // SMBIOS info + qemu_cfg_read_entry(&cnt, QEMU_CFG_SMBIOS_ENTRIES, sizeof(cnt)); + offset = sizeof(cnt); + for (i = 0; i < cnt; i++) { + struct qemu_smbios_header header; + qemu_cfg_read(&header, sizeof(header)); + if (header.headertype == SMBIOS_FIELD_ENTRY) { + snprintf(name, sizeof(name), "smbios/field%d-%d" + , header.tabletype, header.fieldoffset); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + sizeof(header) + , header.length - sizeof(header)); + } else { + snprintf(name, sizeof(name), "smbios/table%d-%d" + , header.tabletype, i); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + 3, header.length - 3); + } + qemu_cfg_skip(header.length - sizeof(header)); + offset += header.length; + } +} + +struct QemuCfgFile { + u32 size; /* file size */ + u16 select; /* write this to 0x510 to read it */ + u16 reserved; + char name[56]; +}; + +void qemu_cfg_init(void) +{ + if (!runningOnQEMU()) + return; + + // Detect fw_cfg interface. + qemu_cfg_select(QEMU_CFG_SIGNATURE); + char *sig = "QEMU"; + int i; + for (i = 0; i < 4; i++) + if (inb(PORT_QEMU_CFG_DATA) != sig[i]) + return; + dprintf(1, "Found QEMU fw_cfg\n"); + + // Populate romfiles for legacy fw_cfg entries + qemu_cfg_legacy(); + + // Load files found in the fw_cfg file directory + u32 count; + qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count)); + count = be32_to_cpu(count); + u32 e; + for (e = 0; e < count; e++) { + struct QemuCfgFile qfile; + qemu_cfg_read(&qfile, sizeof(qfile)); + qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select) + , 0, be32_to_cpu(qfile.size)); + } + + qemu_cfg_e820(); + + if (romfile_find("etc/table-loader")) { + acpi_pm_base = 0x0600; + dprintf(1, "Moving pm_base to 0x%x\n", acpi_pm_base); + } +} diff --git a/qemu/roms/seabios/src/fw/paravirt.h b/qemu/roms/seabios/src/fw/paravirt.h new file mode 100644 index 000000000..95ffb92ad --- /dev/null +++ b/qemu/roms/seabios/src/fw/paravirt.h @@ -0,0 +1,37 @@ +#ifndef __PV_H +#define __PV_H + +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL + +// Types of paravirtualized platforms. +#define PF_QEMU (1<<0) +#define PF_XEN (1<<1) +#define PF_KVM (1<<2) + +extern u32 RamSize; +extern u64 RamSizeOver4G; +extern int PlatformRunningOn; + +static inline int runningOnQEMU(void) { + return CONFIG_QEMU || ( + CONFIG_QEMU_HARDWARE && GET_GLOBAL(PlatformRunningOn) & PF_QEMU); +} +static inline int runningOnXen(void) { + return CONFIG_XEN && GET_GLOBAL(PlatformRunningOn) & PF_XEN; +} +static inline int runningOnKVM(void) { + return CONFIG_QEMU && GET_GLOBAL(PlatformRunningOn) & PF_KVM; +} + +// Common paravirt ports. +#define PORT_SMI_CMD 0x00b2 +#define PORT_SMI_STATUS 0x00b3 +#define PORT_QEMU_CFG_CTL 0x0510 +#define PORT_QEMU_CFG_DATA 0x0511 + +void qemu_preinit(void); +void qemu_platform_setup(void); +void qemu_cfg_init(void); + +#endif diff --git a/qemu/roms/seabios/src/fw/pciinit.c b/qemu/roms/seabios/src/fw/pciinit.c new file mode 100644 index 000000000..46ae7090e --- /dev/null +++ b/qemu/roms/seabios/src/fw/pciinit.c @@ -0,0 +1,954 @@ +// Initialize PCI devices (on emulators) +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // le64_to_cpu +#include "config.h" // CONFIG_* +#include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR +#include "dev-piix.h" // PIIX_* +#include "hw/ata.h" // PORT_ATA1_CMD_BASE +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_COMMAND +#include "list.h" // struct hlist_node +#include "malloc.h" // free +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_loadint +#include "string.h" // memset +#include "util.h" // pci_setup +#include "x86.h" // outb + +#define PCI_DEVICE_MEM_MIN (1<<12) // 4k == page size +#define PCI_BRIDGE_MEM_MIN (1<<21) // 2M == hugepage size +#define PCI_BRIDGE_IO_MIN 0x1000 // mandated by pci bridge spec + +static const char *region_type_name[] = { + [ PCI_REGION_TYPE_IO ] = "io", + [ PCI_REGION_TYPE_MEM ] = "mem", + [ PCI_REGION_TYPE_PREFMEM ] = "prefmem", +}; + +u64 pcimem_start = BUILD_PCIMEM_START; +u64 pcimem_end = BUILD_PCIMEM_END; +u64 pcimem64_start = BUILD_PCIMEM64_START; +u64 pcimem64_end = BUILD_PCIMEM64_END; +u64 pci_io_low_end = 0xa000; + +struct pci_region_entry { + struct pci_device *dev; + int bar; + u64 size; + u64 align; + int is64; + enum pci_region_type type; + struct hlist_node node; +}; + +struct pci_region { + /* pci region assignments */ + u64 base; + struct hlist_head list; +}; + +struct pci_bus { + struct pci_region r[PCI_REGION_TYPE_COUNT]; + struct pci_device *bus_dev; +}; + +static u32 pci_bar(struct pci_device *pci, int region_num) +{ + if (region_num != PCI_ROM_SLOT) { + return PCI_BASE_ADDRESS_0 + region_num * 4; + } + +#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 + u8 type = pci->header_type & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; +} + +static void +pci_set_io_region_addr(struct pci_device *pci, int bar, u64 addr, int is64) +{ + u32 ofs = pci_bar(pci, bar); + pci_config_writel(pci->bdf, ofs, addr); + if (is64) + pci_config_writel(pci->bdf, ofs + 4, addr >> 32); +} + + +/**************************************************************** + * Misc. device init + ****************************************************************/ + +/* host irqs corresponding to PCI irqs A-D */ +const u8 pci_irqs[4] = { + 10, 10, 11, 11 +}; + +static int dummy_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + dprintf(1, "pci_slot_get_irq called with unknown routing\n"); + + return 0xff; /* PCI defined "unknown" or "no connection" for x86 */ +} + +static int (*pci_slot_get_irq)(struct pci_device *pci, int pin) = + dummy_pci_slot_get_irq; + +// Return the global irq number corresponding to a host bus device irq pin. +static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + int slot_addend = 0; + + while (pci->parent != NULL) { + slot_addend += pci_bdf_to_dev(pci->bdf); + pci = pci->parent; + } + slot_addend += pci_bdf_to_dev(pci->bdf) - 1; + return pci_irqs[(pin - 1 + slot_addend) & 3]; +} + +static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + int pin_addend = 0; + while (pci->parent != NULL) { + pin_addend += pci_bdf_to_dev(pci->bdf); + pci = pci->parent; + } + u8 slot = pci_bdf_to_dev(pci->bdf); + if (slot <= 24) + /* Slots 0-24 rotate slot:pin mapping similar to piix above, but + with a different starting index - see q35-acpi-dsdt.dsl */ + return pci_irqs[(pin - 1 + pin_addend + slot) & 3]; + /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ + return pci_irqs[(pin - 1 + pin_addend) & 3]; +} + +/* PIIX3/PIIX4 PCI to ISA bridge */ +static void piix_isa_bridge_setup(struct pci_device *pci, void *arg) +{ + int i, irq; + u8 elcr[2]; + + elcr[0] = 0x00; + elcr[1] = 0x00; + for (i = 0; i < 4; i++) { + irq = pci_irqs[i]; + /* set to trigger level */ + elcr[irq >> 3] |= (1 << (irq & 7)); + /* activate irq remapping in PIIX */ + pci_config_writeb(pci->bdf, 0x60 + i, irq); + } + outb(elcr[0], PIIX_PORT_ELCR1); + outb(elcr[1], PIIX_PORT_ELCR2); + dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); +} + +/* ICH9 LPC PCI to ISA bridge */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +static void mch_isa_bridge_setup(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + int i, irq; + u8 elcr[2]; + + elcr[0] = 0x00; + elcr[1] = 0x00; + + for (i = 0; i < 4; i++) { + irq = pci_irqs[i]; + /* set to trigger level */ + elcr[irq >> 3] |= (1 << (irq & 7)); + + /* activate irq remapping in LPC */ + + /* PIRQ[A-D] routing */ + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT + i, irq); + /* PIRQ[E-H] routing */ + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, irq); + } + outb(elcr[0], ICH9_LPC_PORT_ELCR1); + outb(elcr[1], ICH9_LPC_PORT_ELCR2); + dprintf(1, "Q35 LPC init: elcr=%02x %02x\n", elcr[0], elcr[1]); + + /* pm io base */ + pci_config_writel(bdf, ICH9_LPC_PMBASE, + acpi_pm_base | ICH9_LPC_PMBASE_RTE); + + /* acpi enable, SCI: IRQ9 000b = irq9*/ + pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN); + + acpi_pm1a_cnt = acpi_pm_base + 0x04; + pmtimer_setup(acpi_pm_base + 0x08); +} + +static void storage_ide_setup(struct pci_device *pci, void *arg) +{ + /* IDE: we map it as in ISA mode */ + pci_set_io_region_addr(pci, 0, PORT_ATA1_CMD_BASE, 0); + pci_set_io_region_addr(pci, 1, PORT_ATA1_CTRL_BASE, 0); + pci_set_io_region_addr(pci, 2, PORT_ATA2_CMD_BASE, 0); + pci_set_io_region_addr(pci, 3, PORT_ATA2_CTRL_BASE, 0); +} + +/* PIIX3/PIIX4 IDE */ +static void piix_ide_setup(struct pci_device *pci, void *arg) +{ + u16 bdf = pci->bdf; + pci_config_writew(bdf, 0x40, 0x8000); // enable IDE0 + pci_config_writew(bdf, 0x42, 0x8000); // enable IDE1 +} + +static void pic_ibm_setup(struct pci_device *pci, void *arg) +{ + /* PIC, IBM, MPIC & MPIC2 */ + pci_set_io_region_addr(pci, 0, 0x80800000 + 0x00040000, 0); +} + +static void apple_macio_setup(struct pci_device *pci, void *arg) +{ + /* macio bridge */ + pci_set_io_region_addr(pci, 0, 0x80800000, 0); +} + +static void piix4_pm_config_setup(u16 bdf) +{ + // acpi sci is hardwired to 9 + pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9); + + pci_config_writel(bdf, PIIX_PMBASE, acpi_pm_base | 1); + pci_config_writeb(bdf, PIIX_PMREGMISC, 0x01); /* enable PM io space */ + pci_config_writel(bdf, PIIX_SMBHSTBASE, (acpi_pm_base + 0x100) | 1); + pci_config_writeb(bdf, PIIX_SMBHSTCFG, 0x09); /* enable SMBus io space */ +} + +static int PiixPmBDF = -1; + +/* PIIX4 Power Management device (for ACPI) */ +static void piix4_pm_setup(struct pci_device *pci, void *arg) +{ + PiixPmBDF = pci->bdf; + piix4_pm_config_setup(pci->bdf); + + acpi_pm1a_cnt = acpi_pm_base + 0x04; + pmtimer_setup(acpi_pm_base + 0x08); +} + +/* ICH9 SMBUS */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_SMBUS */ +static void ich9_smbus_setup(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + /* map smbus into io space */ + pci_config_writel(bdf, ICH9_SMB_SMB_BASE, + (acpi_pm_base + 0x100) | PCI_BASE_ADDRESS_SPACE_IO); + + /* enable SMBus */ + pci_config_writeb(bdf, ICH9_SMB_HOSTC, ICH9_SMB_HOSTC_HST_EN); +} + +static const struct pci_device_id pci_device_tbl[] = { + /* PIIX3/PIIX4 PCI to ISA bridge */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, + piix_isa_bridge_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, + piix_isa_bridge_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + mch_isa_bridge_setup), + + /* STORAGE IDE */ + PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, + PCI_CLASS_STORAGE_IDE, piix_ide_setup), + PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, + PCI_CLASS_STORAGE_IDE, piix_ide_setup), + PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE, + storage_ide_setup), + + /* PIC, IBM, MIPC & MPIC2 */ + PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0x0046, PCI_CLASS_SYSTEM_PIC, + pic_ibm_setup), + PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0xFFFF, PCI_CLASS_SYSTEM_PIC, + pic_ibm_setup), + + /* PIIX4 Power Management device (for ACPI) */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, + piix4_pm_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_SMBUS, + ich9_smbus_setup), + + /* 0xff00 */ + PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_setup), + PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_setup), + + PCI_DEVICE_END, +}; + +void pci_resume(void) +{ + if (!CONFIG_QEMU) { + return; + } + + if (PiixPmBDF >= 0) { + piix4_pm_config_setup(PiixPmBDF); + } +} + +static void pci_bios_init_device(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "PCI: init bdf=%02x:%02x.%x id=%04x:%04x\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf) + , pci->vendor, pci->device); + + /* map the interrupt */ + int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); + if (pin != 0) + pci_config_writeb(bdf, PCI_INTERRUPT_LINE, pci_slot_get_irq(pci, pin)); + + pci_init_device(pci_device_tbl, pci, NULL); + + /* enable memory mappings */ + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR); +} + +static void pci_bios_init_devices(void) +{ + struct pci_device *pci; + foreachpci(pci) { + pci_bios_init_device(pci); + } +} + +static void pci_enable_default_vga(void) +{ + struct pci_device *pci; + + foreachpci(pci) { + if (is_pci_vga(pci)) { + dprintf(1, "PCI: Using %02x:%02x.%x for primary VGA\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); + return; + } + } + + pci = pci_find_class(PCI_CLASS_DISPLAY_VGA); + if (!pci) { + dprintf(1, "PCI: No VGA devices found\n"); + return; + } + + dprintf(1, "PCI: Enabling %02x:%02x.%x for primary VGA\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + while (pci->parent) { + pci = pci->parent; + + dprintf(1, "PCI: Setting VGA enable on bridge %02x:%02x.%x\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); + + pci_config_maskw(pci->bdf, PCI_BRIDGE_CONTROL, 0, PCI_BRIDGE_CTL_VGA); + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + } +} + +/**************************************************************** + * Platform device initialization + ****************************************************************/ + +static void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) +{ + if (RamSize <= 0x80000000) + pcimem_start = 0x80000000; + else if (RamSize <= 0xc0000000) + pcimem_start = 0xc0000000; + + pci_slot_get_irq = piix_pci_slot_get_irq; +} + +static void mch_mem_addr_setup(struct pci_device *dev, void *arg) +{ + u64 addr = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; + u32 size = Q35_HOST_BRIDGE_PCIEXBAR_SIZE; + + /* setup mmconfig */ + u16 bdf = dev->bdf; + u32 upper = addr >> 32; + u32 lower = (addr & 0xffffffff) | Q35_HOST_BRIDGE_PCIEXBAREN; + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); + add_e820(addr, size, E820_RESERVED); + + /* setup pci i/o window (above mmconfig) */ + pcimem_start = addr + size; + + pci_slot_get_irq = mch_pci_slot_get_irq; + + /* setup io address space */ + if (acpi_pm_base < 0x1000) + pci_io_low_end = 0x10000; + else + pci_io_low_end = acpi_pm_base; +} + +static const struct pci_device_id pci_platform_tbl[] = { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, + i440fx_mem_addr_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, + mch_mem_addr_setup), + PCI_DEVICE_END +}; + +static void pci_bios_init_platform(void) +{ + struct pci_device *pci; + foreachpci(pci) { + pci_init_device(pci_platform_tbl, pci, NULL); + } +} + + +/**************************************************************** + * Bus initialization + ****************************************************************/ + +static void +pci_bios_init_bus_rec(int bus, u8 *pci_bus) +{ + int bdf; + u16 class; + + dprintf(1, "PCI: %s bus = 0x%x\n", __func__, bus); + + /* prevent accidental access to unintended devices */ + foreachbdf(bdf, bus) { + class = pci_config_readw(bdf, PCI_CLASS_DEVICE); + if (class == PCI_CLASS_BRIDGE_PCI) { + pci_config_writeb(bdf, PCI_SECONDARY_BUS, 255); + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 0); + } + } + + foreachbdf(bdf, bus) { + class = pci_config_readw(bdf, PCI_CLASS_DEVICE); + if (class != PCI_CLASS_BRIDGE_PCI) { + continue; + } + dprintf(1, "PCI: %s bdf = 0x%x\n", __func__, bdf); + + u8 pribus = pci_config_readb(bdf, PCI_PRIMARY_BUS); + if (pribus != bus) { + dprintf(1, "PCI: primary bus = 0x%x -> 0x%x\n", pribus, bus); + pci_config_writeb(bdf, PCI_PRIMARY_BUS, bus); + } else { + dprintf(1, "PCI: primary bus = 0x%x\n", pribus); + } + + u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); + (*pci_bus)++; + if (*pci_bus != secbus) { + dprintf(1, "PCI: secondary bus = 0x%x -> 0x%x\n", + secbus, *pci_bus); + secbus = *pci_bus; + pci_config_writeb(bdf, PCI_SECONDARY_BUS, secbus); + } else { + dprintf(1, "PCI: secondary bus = 0x%x\n", secbus); + } + + /* set to max for access to all subordinate buses. + later set it to accurate value */ + u8 subbus = pci_config_readb(bdf, PCI_SUBORDINATE_BUS); + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 255); + + pci_bios_init_bus_rec(secbus, pci_bus); + + if (subbus != *pci_bus) { + dprintf(1, "PCI: subordinate bus = 0x%x -> 0x%x\n", + subbus, *pci_bus); + subbus = *pci_bus; + } else { + dprintf(1, "PCI: subordinate bus = 0x%x\n", subbus); + } + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, subbus); + } +} + +static void +pci_bios_init_bus(void) +{ + u8 extraroots = romfile_loadint("etc/extra-pci-roots", 0); + u8 pci_bus = 0; + + pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); + + if (extraroots) { + while (pci_bus < 0xff) { + pci_bus++; + pci_bios_init_bus_rec(pci_bus, &pci_bus); + } + } +} + + +/**************************************************************** + * Bus sizing + ****************************************************************/ + +static void +pci_bios_get_bar(struct pci_device *pci, int bar, + int *ptype, u64 *psize, int *pis64) +{ + u32 ofs = pci_bar(pci, bar); + u16 bdf = pci->bdf; + u32 old = pci_config_readl(bdf, ofs); + int is64 = 0, type = PCI_REGION_TYPE_MEM; + u64 mask; + + if (bar == PCI_ROM_SLOT) { + mask = PCI_ROM_ADDRESS_MASK; + pci_config_writel(bdf, ofs, mask); + } else { + if (old & PCI_BASE_ADDRESS_SPACE_IO) { + mask = PCI_BASE_ADDRESS_IO_MASK; + type = PCI_REGION_TYPE_IO; + } else { + mask = PCI_BASE_ADDRESS_MEM_MASK; + if (old & PCI_BASE_ADDRESS_MEM_PREFETCH) + type = PCI_REGION_TYPE_PREFMEM; + is64 = ((old & PCI_BASE_ADDRESS_MEM_TYPE_MASK) + == PCI_BASE_ADDRESS_MEM_TYPE_64); + } + pci_config_writel(bdf, ofs, ~0); + } + u64 val = pci_config_readl(bdf, ofs); + pci_config_writel(bdf, ofs, old); + if (is64) { + u32 hold = pci_config_readl(bdf, ofs + 4); + pci_config_writel(bdf, ofs + 4, ~0); + u32 high = pci_config_readl(bdf, ofs + 4); + pci_config_writel(bdf, ofs + 4, hold); + val |= ((u64)high << 32); + mask |= ((u64)0xffffffff << 32); + *psize = (~(val & mask)) + 1; + } else { + *psize = ((~(val & mask)) + 1) & 0xffffffff; + } + *ptype = type; + *pis64 = is64; +} + +static int pci_bios_bridge_region_is64(struct pci_region *r, + struct pci_device *pci, int type) +{ + if (type != PCI_REGION_TYPE_PREFMEM) + return 0; + u32 pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); + if (!pmem) { + pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0xfff0fff0); + pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); + pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0x0); + } + if ((pmem & PCI_PREF_RANGE_TYPE_MASK) != PCI_PREF_RANGE_TYPE_64) + return 0; + struct pci_region_entry *entry; + hlist_for_each_entry(entry, &r->list, node) { + if (!entry->is64) + return 0; + } + return 1; +} + +static u64 pci_region_align(struct pci_region *r) +{ + struct pci_region_entry *entry; + hlist_for_each_entry(entry, &r->list, node) { + // The first entry in the sorted list has the largest alignment + return entry->align; + } + return 1; +} + +static u64 pci_region_sum(struct pci_region *r) +{ + u64 sum = 0; + struct pci_region_entry *entry; + hlist_for_each_entry(entry, &r->list, node) { + sum += entry->size; + } + return sum; +} + +static void pci_region_migrate_64bit_entries(struct pci_region *from, + struct pci_region *to) +{ + struct hlist_node *n, **last = &to->list.first; + struct pci_region_entry *entry; + hlist_for_each_entry_safe(entry, n, &from->list, node) { + if (!entry->is64) + continue; + if (entry->dev->class == PCI_CLASS_SERIAL_USB) + continue; + // Move from source list to destination list. + hlist_del(&entry->node); + hlist_add(&entry->node, last); + last = &entry->node.next; + } +} + +static struct pci_region_entry * +pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev, + int bar, u64 size, u64 align, int type, int is64) +{ + struct pci_region_entry *entry = malloc_tmp(sizeof(*entry)); + if (!entry) { + warn_noalloc(); + return NULL; + } + memset(entry, 0, sizeof(*entry)); + entry->dev = dev; + entry->bar = bar; + entry->size = size; + entry->align = align; + entry->is64 = is64; + entry->type = type; + // Insert into list in sorted order. + struct hlist_node **pprev; + struct pci_region_entry *pos; + hlist_for_each_entry_pprev(pos, pprev, &bus->r[type].list, node) { + if (pos->align < align || (pos->align == align && pos->size < size)) + break; + } + hlist_add(&entry->node, pprev); + return entry; +} + +static int pci_bus_hotplug_support(struct pci_bus *bus) +{ + u8 pcie_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_EXP); + u8 shpc_cap; + + if (pcie_cap) { + u16 pcie_flags = pci_config_readw(bus->bus_dev->bdf, + pcie_cap + PCI_EXP_FLAGS); + u8 port_type = ((pcie_flags & PCI_EXP_FLAGS_TYPE) >> + (__builtin_ffs(PCI_EXP_FLAGS_TYPE) - 1)); + u8 downstream_port = (port_type == PCI_EXP_TYPE_DOWNSTREAM) || + (port_type == PCI_EXP_TYPE_ROOT_PORT); + /* + * PCI Express SPEC, 7.8.2: + * Slot Implemented – When Set, this bit indicates that the Link + * HwInit associated with this Port is connected to a slot (as + * compared to being connected to a system-integrated device or + * being disabled). + * This bit is valid for Downstream Ports. This bit is undefined + * for Upstream Ports. + */ + u16 slot_implemented = pcie_flags & PCI_EXP_FLAGS_SLOT; + + return downstream_port && slot_implemented; + } + + shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC); + return !!shpc_cap; +} + +static int pci_bios_check_devices(struct pci_bus *busses) +{ + dprintf(1, "PCI: check devices\n"); + + // Calculate resources needed for regular (non-bus) devices. + struct pci_device *pci; + foreachpci(pci) { + if (pci->class == PCI_CLASS_BRIDGE_PCI) + busses[pci->secondary_bus].bus_dev = pci; + + struct pci_bus *bus = &busses[pci_bdf_to_bus(pci->bdf)]; + if (!bus->bus_dev) + /* + * Resources for all root busses go in busses[0] + */ + bus = &busses[0]; + int i; + for (i = 0; i < PCI_NUM_REGIONS; i++) { + if ((pci->class == PCI_CLASS_BRIDGE_PCI) && + (i >= PCI_BRIDGE_NUM_REGIONS && i < PCI_ROM_SLOT)) + continue; + int type, is64; + u64 size; + pci_bios_get_bar(pci, i, &type, &size, &is64); + if (size == 0) + continue; + + if (type != PCI_REGION_TYPE_IO && size < PCI_DEVICE_MEM_MIN) + size = PCI_DEVICE_MEM_MIN; + struct pci_region_entry *entry = pci_region_create_entry( + bus, pci, i, size, size, type, is64); + if (!entry) + return -1; + + if (is64) + i++; + } + } + + // Propagate required bus resources to parent busses. + int secondary_bus; + for (secondary_bus=MaxPCIBus; secondary_bus>0; secondary_bus--) { + struct pci_bus *s = &busses[secondary_bus]; + if (!s->bus_dev) + continue; + struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)]; + if (!parent->bus_dev) + /* + * Resources for all root busses go in busses[0] + */ + parent = &busses[0]; + int type; + int hotplug_support = pci_bus_hotplug_support(s); + for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { + u64 align = (type == PCI_REGION_TYPE_IO) ? + PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; + if (!pci_bridge_has_region(s->bus_dev, type)) + continue; + if (pci_region_align(&s->r[type]) > align) + align = pci_region_align(&s->r[type]); + u64 sum = pci_region_sum(&s->r[type]); + if (!sum && hotplug_support) + sum = align; /* reserve min size for hot-plug */ + u64 size = ALIGN(sum, align); + int is64 = pci_bios_bridge_region_is64(&s->r[type], + s->bus_dev, type); + // entry->bar is -1 if the entry represents a bridge region + struct pci_region_entry *entry = pci_region_create_entry( + parent, s->bus_dev, -1, size, align, type, is64); + if (!entry) + return -1; + dprintf(1, "PCI: secondary bus %d size %08llx type %s\n", + entry->dev->secondary_bus, size, + region_type_name[entry->type]); + } + } + return 0; +} + + +/**************************************************************** + * BAR assignment + ****************************************************************/ + +// Setup region bases (given the regions' size and alignment) +static int pci_bios_init_root_regions_io(struct pci_bus *bus) +{ + /* + * QEMU I/O address space usage: + * 0000 - 0fff legacy isa, pci config, pci root bus, ... + * 1000 - 9fff free + * a000 - afff hotplug (cpu, pci via acpi, i440fx/piix only) + * b000 - bfff power management (PORT_ACPI_PM_BASE) + * [ qemu 1.4+ implements pci config registers + * properly so guests can place the registers + * where they want, on older versions its fixed ] + * c000 - ffff free, traditionally used for pci io + */ + struct pci_region *r_io = &bus->r[PCI_REGION_TYPE_IO]; + u64 sum = pci_region_sum(r_io); + if (sum < 0x4000) { + /* traditional region is big enougth, use it */ + r_io->base = 0xc000; + } else if (sum < pci_io_low_end - 0x1000) { + /* use the larger region at 0x1000 */ + r_io->base = 0x1000; + } else { + /* not enouth io address space -> error out */ + return -1; + } + dprintf(1, "PCI: IO: %4llx - %4llx\n", r_io->base, r_io->base + sum - 1); + return 0; +} + +static int pci_bios_init_root_regions_mem(struct pci_bus *bus) +{ + struct pci_region *r_end = &bus->r[PCI_REGION_TYPE_PREFMEM]; + struct pci_region *r_start = &bus->r[PCI_REGION_TYPE_MEM]; + + if (pci_region_align(r_start) < pci_region_align(r_end)) { + // Swap regions to improve alignment. + r_end = r_start; + r_start = &bus->r[PCI_REGION_TYPE_PREFMEM]; + } + u64 sum = pci_region_sum(r_end); + u64 align = pci_region_align(r_end); + r_end->base = ALIGN_DOWN((pcimem_end - sum), align); + sum = pci_region_sum(r_start); + align = pci_region_align(r_start); + r_start->base = ALIGN_DOWN((r_end->base - sum), align); + + if ((r_start->base < pcimem_start) || + (r_start->base > pcimem_end)) + // Memory range requested is larger than available. + return -1; + return 0; +} + +#define PCI_IO_SHIFT 8 +#define PCI_MEMORY_SHIFT 16 +#define PCI_PREF_MEMORY_SHIFT 16 + +static void +pci_region_map_one_entry(struct pci_region_entry *entry, u64 addr) +{ + u16 bdf = entry->dev->bdf; + if (entry->bar >= 0) { + dprintf(1, "PCI: map device bdf=%02x:%02x.%x" + " bar %d, addr %08llx, size %08llx [%s]\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf), + entry->bar, addr, entry->size, region_type_name[entry->type]); + + pci_set_io_region_addr(entry->dev, entry->bar, addr, entry->is64); + return; + } + + u64 limit = addr + entry->size - 1; + if (entry->type == PCI_REGION_TYPE_IO) { + pci_config_writeb(bdf, PCI_IO_BASE, addr >> PCI_IO_SHIFT); + pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); + pci_config_writeb(bdf, PCI_IO_LIMIT, limit >> PCI_IO_SHIFT); + pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0); + } + if (entry->type == PCI_REGION_TYPE_MEM) { + pci_config_writew(bdf, PCI_MEMORY_BASE, addr >> PCI_MEMORY_SHIFT); + pci_config_writew(bdf, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT); + } + if (entry->type == PCI_REGION_TYPE_PREFMEM) { + pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, addr >> PCI_PREF_MEMORY_SHIFT); + pci_config_writew(bdf, PCI_PREF_MEMORY_LIMIT, limit >> PCI_PREF_MEMORY_SHIFT); + pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, addr >> 32); + pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, limit >> 32); + } +} + +static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) +{ + struct hlist_node *n; + struct pci_region_entry *entry; + hlist_for_each_entry_safe(entry, n, &r->list, node) { + u64 addr = r->base; + r->base += entry->size; + if (entry->bar == -1) + // Update bus base address if entry is a bridge region + busses[entry->dev->secondary_bus].r[entry->type].base = addr; + pci_region_map_one_entry(entry, addr); + hlist_del(&entry->node); + free(entry); + } +} + +static void pci_bios_map_devices(struct pci_bus *busses) +{ + if (pci_bios_init_root_regions_io(busses)) + panic("PCI: out of I/O address space\n"); + + dprintf(1, "PCI: 32: %016llx - %016llx\n", pcimem_start, pcimem_end); + if (pci_bios_init_root_regions_mem(busses)) { + struct pci_region r64_mem, r64_pref; + r64_mem.list.first = NULL; + r64_pref.list.first = NULL; + pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM], + &r64_mem); + pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_PREFMEM], + &r64_pref); + + if (pci_bios_init_root_regions_mem(busses)) + panic("PCI: out of 32bit address space\n"); + + u64 sum_mem = pci_region_sum(&r64_mem); + u64 sum_pref = pci_region_sum(&r64_pref); + u64 align_mem = pci_region_align(&r64_mem); + u64 align_pref = pci_region_align(&r64_pref); + + r64_mem.base = le64_to_cpu(romfile_loadint("etc/reserved-memory-end", 0)); + if (r64_mem.base < 0x100000000LL + RamSizeOver4G) + r64_mem.base = 0x100000000LL + RamSizeOver4G; + r64_mem.base = ALIGN(r64_mem.base, align_mem); + r64_mem.base = ALIGN(r64_mem.base, (1LL<<30)); // 1G hugepage + r64_pref.base = r64_mem.base + sum_mem; + r64_pref.base = ALIGN(r64_pref.base, align_pref); + r64_pref.base = ALIGN(r64_pref.base, (1LL<<30)); // 1G hugepage + pcimem64_start = r64_mem.base; + pcimem64_end = r64_pref.base + sum_pref; + pcimem64_end = ALIGN(pcimem64_end, (1LL<<30)); // 1G hugepage + dprintf(1, "PCI: 64: %016llx - %016llx\n", pcimem64_start, pcimem64_end); + + pci_region_map_entries(busses, &r64_mem); + pci_region_map_entries(busses, &r64_pref); + } else { + // no bars mapped high -> drop 64bit window (see dsdt) + pcimem64_start = 0; + } + // Map regions on each device. + int bus; + for (bus = 0; bus<=MaxPCIBus; bus++) { + int type; + for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) + pci_region_map_entries(busses, &busses[bus].r[type]); + } +} + + +/**************************************************************** + * Main setup code + ****************************************************************/ + +void +pci_setup(void) +{ + if (!CONFIG_QEMU) + return; + + dprintf(3, "pci setup\n"); + + dprintf(1, "=== PCI bus & bridge init ===\n"); + if (pci_probe_host() != 0) { + return; + } + pci_bios_init_bus(); + + dprintf(1, "=== PCI device probing ===\n"); + pci_probe_devices(); + + pcimem_start = RamSize; + pci_bios_init_platform(); + + dprintf(1, "=== PCI new allocation pass #1 ===\n"); + struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); + if (!busses) { + warn_noalloc(); + return; + } + memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); + if (pci_bios_check_devices(busses)) + return; + + dprintf(1, "=== PCI new allocation pass #2 ===\n"); + pci_bios_map_devices(busses); + + pci_bios_init_devices(); + + free(busses); + + pci_enable_default_vga(); +} diff --git a/qemu/roms/seabios/src/fw/pirtable.c b/qemu/roms/seabios/src/fw/pirtable.c new file mode 100644 index 000000000..a49440808 --- /dev/null +++ b/qemu/roms/seabios/src/fw/pirtable.c @@ -0,0 +1,103 @@ +// PIR table generation (for emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "std/pirtable.h" // struct pir_header +#include "string.h" // checksum +#include "util.h" // PirAddr + +struct pir_table { + struct pir_header pir; + struct pir_slot slots[6]; +} PACKED; + +static struct pir_table PIR_TABLE = { + .pir = { + .version = 0x0100, + .size = sizeof(struct pir_table), + .router_devfunc = 0x08, + .compatible_devid = 0x122e8086, + }, + .slots = { + { + // first slot entry PCI-to-ISA (embedded) + .dev = 1<<3, + .links = { + {.link = 0x60, .bitmap = 0xdef8}, // INTA# + {.link = 0x61, .bitmap = 0xdef8}, // INTB# + {.link = 0x62, .bitmap = 0xdef8}, // INTC# + {.link = 0x63, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 0, // embedded + }, { + // second slot entry: 1st PCI slot + .dev = 2<<3, + .links = { + {.link = 0x61, .bitmap = 0xdef8}, // INTA# + {.link = 0x62, .bitmap = 0xdef8}, // INTB# + {.link = 0x63, .bitmap = 0xdef8}, // INTC# + {.link = 0x60, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 1, + }, { + // third slot entry: 2nd PCI slot + .dev = 3<<3, + .links = { + {.link = 0x62, .bitmap = 0xdef8}, // INTA# + {.link = 0x63, .bitmap = 0xdef8}, // INTB# + {.link = 0x60, .bitmap = 0xdef8}, // INTC# + {.link = 0x61, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 2, + }, { + // 4th slot entry: 3rd PCI slot + .dev = 4<<3, + .links = { + {.link = 0x63, .bitmap = 0xdef8}, // INTA# + {.link = 0x60, .bitmap = 0xdef8}, // INTB# + {.link = 0x61, .bitmap = 0xdef8}, // INTC# + {.link = 0x62, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 3, + }, { + // 5th slot entry: 4rd PCI slot + .dev = 5<<3, + .links = { + {.link = 0x60, .bitmap = 0xdef8}, // INTA# + {.link = 0x61, .bitmap = 0xdef8}, // INTB# + {.link = 0x62, .bitmap = 0xdef8}, // INTC# + {.link = 0x63, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 4, + }, { + // 6th slot entry: 5rd PCI slot + .dev = 6<<3, + .links = { + {.link = 0x61, .bitmap = 0xdef8}, // INTA# + {.link = 0x62, .bitmap = 0xdef8}, // INTB# + {.link = 0x63, .bitmap = 0xdef8}, // INTC# + {.link = 0x60, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 5, + }, + } +}; + +void +pirtable_setup(void) +{ + if (! CONFIG_PIRTABLE) + return; + + dprintf(3, "init PIR table\n"); + + PIR_TABLE.pir.signature = PIR_SIGNATURE; + PIR_TABLE.pir.checksum -= checksum(&PIR_TABLE, sizeof(PIR_TABLE)); + copy_pir(&PIR_TABLE); +} diff --git a/qemu/roms/seabios/src/fw/q35-acpi-dsdt.dsl b/qemu/roms/seabios/src/fw/q35-acpi-dsdt.dsl new file mode 100644 index 000000000..5dec54146 --- /dev/null +++ b/qemu/roms/seabios/src/fw/q35-acpi-dsdt.dsl @@ -0,0 +1,450 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/* + * Copyright (c) 2010 Isaku Yamahata + * yamahata at valinux co jp + * Based on acpi-dsdt.dsl, but heavily modified for q35 chipset. + */ + +DefinitionBlock ( + "q35-acpi-dsdt.aml",// Output Filename + "DSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXDSDT", // TABLE ID + 0x2 // OEM Revision + ) +{ + +#include "acpi-dsdt-dbug.dsl" + + Scope(\_SB) { + OperationRegion(PCST, SystemIO, 0xae00, 0x0c) + OperationRegion(PCSB, SystemIO, 0xae0c, 0x01) + Field(PCSB, AnyAcc, NoLock, WriteAsZeros) { + PCIB, 8, + } + } + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + + Scope(\_SB) { + Device(PCI0) { + Name(_HID, EisaId("PNP0A08")) + Name(_CID, EisaId("PNP0A03")) + Name(_ADR, 0x00) + Name(_UID, 1) + + // _OSC: based on sample of ACPI3.0b spec + Name(SUPP, 0) // PCI _OSC Support Field value + Name(CTRL, 0) // PCI _OSC Control Field value + Method(_OSC, 4) { + // Create DWORD-addressable fields from the Capabilities Buffer + CreateDWordField(Arg3, 0, CDW1) + + // Check for proper UUID + If (LEqual(Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) { + // Create DWORD-addressable fields from the Capabilities Buffer + CreateDWordField(Arg3, 4, CDW2) + CreateDWordField(Arg3, 8, CDW3) + + // Save Capabilities DWORD2 & 3 + Store(CDW2, SUPP) + Store(CDW3, CTRL) + + // Always allow native PME, AER (no dependencies) + // Never allow SHPC (no SHPC controller in this system) + And(CTRL, 0x1D, CTRL) + +#if 0 // For now, nothing to do + If (Not(And(CDW1, 1))) { // Query flag clear? + // Disable GPEs for features granted native control. + If (And(CTRL, 0x01)) { // Hot plug control granted? + Store(0, HPCE) // clear the hot plug SCI enable bit + Store(1, HPCS) // clear the hot plug SCI status bit + } + If (And(CTRL, 0x04)) { // PME control granted? + Store(0, PMCE) // clear the PME SCI enable bit + Store(1, PMCS) // clear the PME SCI status bit + } + If (And(CTRL, 0x10)) { // OS restoring PCI Express cap structure? + // Set status to not restore PCI Express cap structure + // upon resume from S3 + Store(1, S3CR) + } + } +#endif + If (LNotEqual(Arg1, One)) { + // Unknown revision + Or(CDW1, 0x08, CDW1) + } + If (LNotEqual(CDW3, CTRL)) { + // Capabilities bits were masked + Or(CDW1, 0x10, CDW1) + } + // Update DWORD3 in the buffer + Store(CTRL, CDW3) + } Else { + Or(CDW1, 4, CDW1) // Unrecognized UUID + } + Return (Arg3) + } + } + } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(VGA) { + Name(_ADR, 0x00010000) + Method(_S1D, 0, NotSerialized) { + Return (0x00) + } + Method(_S2D, 0, NotSerialized) { + Return (0x00) + } + Method(_S3D, 0, NotSerialized) { + Return (0x00) + } + } + } + + +/**************************************************************** + * LPC ISA bridge + ****************************************************************/ + + Scope(\_SB.PCI0) { + /* PCI D31:f0 LPC ISA bridge */ + Device(ISA) { + /* PCI D31:f0 */ + Name(_ADR, 0x001f0000) + + /* ICH9 PCI to ISA irq remapping */ + OperationRegion(PIRQ, PCI_Config, 0x60, 0x0C) + + OperationRegion(LPCD, PCI_Config, 0x80, 0x2) + Field(LPCD, AnyAcc, NoLock, Preserve) { + COMA, 3, + , 1, + COMB, 3, + + Offset(0x01), + LPTD, 2, + , 2, + FDCD, 2 + } + OperationRegion(LPCE, PCI_Config, 0x82, 0x2) + Field(LPCE, AnyAcc, NoLock, Preserve) { + CAEN, 1, + CBEN, 1, + LPEN, 1, + FDEN, 1 + } + } + } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + + /* Zero => PIC mode, One => APIC Mode */ + Name(\PICF, Zero) + Method(\_PIC, 1, NotSerialized) { + Store(Arg0, \PICF) + } + + Scope(\_SB) { + Scope(PCI0) { +#define prt_slot_lnk(nr, lnk0, lnk1, lnk2, lnk3) \ + Package() { nr##ffff, 0, lnk0, 0 }, \ + Package() { nr##ffff, 1, lnk1, 0 }, \ + Package() { nr##ffff, 2, lnk2, 0 }, \ + Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot_lnkA(nr) prt_slot_lnk(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot_lnkB(nr) prt_slot_lnk(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot_lnkC(nr) prt_slot_lnk(nr, LNKC, LNKD, LNKA, LNKB) +#define prt_slot_lnkD(nr) prt_slot_lnk(nr, LNKD, LNKA, LNKB, LNKC) + +#define prt_slot_lnkE(nr) prt_slot_lnk(nr, LNKE, LNKF, LNKG, LNKH) +#define prt_slot_lnkF(nr) prt_slot_lnk(nr, LNKF, LNKG, LNKH, LNKE) +#define prt_slot_lnkG(nr) prt_slot_lnk(nr, LNKG, LNKH, LNKE, LNKF) +#define prt_slot_lnkH(nr) prt_slot_lnk(nr, LNKH, LNKE, LNKF, LNKG) + + Name(PRTP, package() { + prt_slot_lnkE(0x0000), + prt_slot_lnkF(0x0001), + prt_slot_lnkG(0x0002), + prt_slot_lnkH(0x0003), + prt_slot_lnkE(0x0004), + prt_slot_lnkF(0x0005), + prt_slot_lnkG(0x0006), + prt_slot_lnkH(0x0007), + prt_slot_lnkE(0x0008), + prt_slot_lnkF(0x0009), + prt_slot_lnkG(0x000a), + prt_slot_lnkH(0x000b), + prt_slot_lnkE(0x000c), + prt_slot_lnkF(0x000d), + prt_slot_lnkG(0x000e), + prt_slot_lnkH(0x000f), + prt_slot_lnkE(0x0010), + prt_slot_lnkF(0x0011), + prt_slot_lnkG(0x0012), + prt_slot_lnkH(0x0013), + prt_slot_lnkE(0x0014), + prt_slot_lnkF(0x0015), + prt_slot_lnkG(0x0016), + prt_slot_lnkH(0x0017), + prt_slot_lnkE(0x0018), + + /* INTA -> PIRQA for slot 25 - 31 + see the default value of D<N>IR */ + prt_slot_lnkA(0x0019), + prt_slot_lnkA(0x001a), + prt_slot_lnkA(0x001b), + prt_slot_lnkA(0x001c), + prt_slot_lnkA(0x001d), + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + prt_slot_lnkE(0x001e), + + prt_slot_lnkA(0x001f) + }) + +#define prt_slot_gsi(nr, gsi0, gsi1, gsi2, gsi3) \ + Package() { nr##ffff, 0, gsi0, 0 }, \ + Package() { nr##ffff, 1, gsi1, 0 }, \ + Package() { nr##ffff, 2, gsi2, 0 }, \ + Package() { nr##ffff, 3, gsi3, 0 } + +#define prt_slot_gsiA(nr) prt_slot_gsi(nr, GSIA, GSIB, GSIC, GSID) +#define prt_slot_gsiB(nr) prt_slot_gsi(nr, GSIB, GSIC, GSID, GSIA) +#define prt_slot_gsiC(nr) prt_slot_gsi(nr, GSIC, GSID, GSIA, GSIB) +#define prt_slot_gsiD(nr) prt_slot_gsi(nr, GSID, GSIA, GSIB, GSIC) + +#define prt_slot_gsiE(nr) prt_slot_gsi(nr, GSIE, GSIF, GSIG, GSIH) +#define prt_slot_gsiF(nr) prt_slot_gsi(nr, GSIF, GSIG, GSIH, GSIE) +#define prt_slot_gsiG(nr) prt_slot_gsi(nr, GSIG, GSIH, GSIE, GSIF) +#define prt_slot_gsiH(nr) prt_slot_gsi(nr, GSIH, GSIE, GSIF, GSIG) + + Name(PRTA, package() { + prt_slot_gsiE(0x0000), + prt_slot_gsiF(0x0001), + prt_slot_gsiG(0x0002), + prt_slot_gsiH(0x0003), + prt_slot_gsiE(0x0004), + prt_slot_gsiF(0x0005), + prt_slot_gsiG(0x0006), + prt_slot_gsiH(0x0007), + prt_slot_gsiE(0x0008), + prt_slot_gsiF(0x0009), + prt_slot_gsiG(0x000a), + prt_slot_gsiH(0x000b), + prt_slot_gsiE(0x000c), + prt_slot_gsiF(0x000d), + prt_slot_gsiG(0x000e), + prt_slot_gsiH(0x000f), + prt_slot_gsiE(0x0010), + prt_slot_gsiF(0x0011), + prt_slot_gsiG(0x0012), + prt_slot_gsiH(0x0013), + prt_slot_gsiE(0x0014), + prt_slot_gsiF(0x0015), + prt_slot_gsiG(0x0016), + prt_slot_gsiH(0x0017), + prt_slot_gsiE(0x0018), + + /* INTA -> PIRQA for slot 25 - 31, but 30 + see the default value of D<N>IR */ + prt_slot_gsiA(0x0019), + prt_slot_gsiA(0x001a), + prt_slot_gsiA(0x001b), + prt_slot_gsiA(0x001c), + prt_slot_gsiA(0x001d), + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + prt_slot_gsiE(0x001e), + + prt_slot_gsiA(0x001f) + }) + + Method(_PRT, 0, NotSerialized) { + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + If (LEqual(\PICF, Zero)) { + Return (PRTP) + } Else { + Return (PRTA) + } + } + } + + Field(PCI0.ISA.PIRQ, ByteAcc, NoLock, Preserve) { + PRQA, 8, + PRQB, 8, + PRQC, 8, + PRQD, 8, + + Offset(0x08), + PRQE, 8, + PRQF, 8, + PRQG, 8, + PRQH, 8 + } + + Method(IQST, 1, NotSerialized) { + // _STA method - get status + If (And(0x80, Arg0)) { + Return (0x09) + } + Return (0x0B) + } + Method(IQCR, 1, Serialized) { + // _CRS method - get current settings + Name(PRR0, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 0 } + }) + CreateDWordField(PRR0, 0x05, PRRI) + Store(And(Arg0, 0x0F), PRRI) + Return (PRR0) + } + +#define define_link(link, uid, reg) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + 5, 10, 11 \ + } \ + }) \ + Method(_STA, 0, NotSerialized) { \ + Return (IQST(reg)) \ + } \ + Method(_DIS, 0, NotSerialized) { \ + Or(reg, 0x80, reg) \ + } \ + Method(_CRS, 0, NotSerialized) { \ + Return (IQCR(reg)) \ + } \ + Method(_SRS, 1, NotSerialized) { \ + CreateDWordField(Arg0, 0x05, PRRI) \ + Store(PRRI, reg) \ + } \ + } + + define_link(LNKA, 0, PRQA) + define_link(LNKB, 1, PRQB) + define_link(LNKC, 2, PRQC) + define_link(LNKD, 3, PRQD) + define_link(LNKE, 4, PRQE) + define_link(LNKF, 5, PRQF) + define_link(LNKG, 6, PRQG) + define_link(LNKH, 7, PRQH) + +#define define_gsi_link(link, uid, gsi) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + gsi \ + } \ + }) \ + Name(_CRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + gsi \ + } \ + }) \ + Method(_SRS, 1, NotSerialized) { \ + } \ + } + + define_gsi_link(GSIA, 0, 0x10) + define_gsi_link(GSIB, 0, 0x11) + define_gsi_link(GSIC, 0, 0x12) + define_gsi_link(GSID, 0, 0x13) + define_gsi_link(GSIE, 0, 0x14) + define_gsi_link(GSIF, 0, 0x15) + define_gsi_link(GSIG, 0, 0x16) + define_gsi_link(GSIH, 0, 0x17) + } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + + Scope(\_GPE) { + Name(_HID, "ACPI0006") + + Method(_L00) { + } + Method(_L01) { + // CPU hotplug event + \_SB.PRSC() + } + Method(_L02) { + } + Method(_L03) { + } + Method(_L04) { + } + Method(_L05) { + } + Method(_L06) { + } + Method(_L07) { + } + Method(_L08) { + } + Method(_L09) { + } + Method(_L0A) { + } + Method(_L0B) { + } + Method(_L0C) { + } + Method(_L0D) { + } + Method(_L0E) { + } + Method(_L0F) { + } + } +} diff --git a/qemu/roms/seabios/src/fw/romfile_loader.c b/qemu/roms/seabios/src/fw/romfile_loader.c new file mode 100644 index 000000000..f4b17ff90 --- /dev/null +++ b/qemu/roms/seabios/src/fw/romfile_loader.c @@ -0,0 +1,177 @@ +#include "romfile_loader.h" +#include "byteorder.h" // leXX_to_cpu/cpu_to_leXX +#include "util.h" // checksum +#include "string.h" // strcmp +#include "romfile.h" // struct romfile_s +#include "malloc.h" // Zone*, _malloc +#include "output.h" // warn_* + +struct romfile_loader_file { + struct romfile_s *file; + void *data; +}; +struct romfile_loader_files { + int nfiles; + struct romfile_loader_file files[]; +}; + +static struct romfile_loader_file * +romfile_loader_find(const char *name, + struct romfile_loader_files *files) +{ + int i; + if (name[ROMFILE_LOADER_FILESZ - 1]) + return NULL; + for (i = 0; i < files->nfiles; ++i) + if (!strcmp(files->files[i].file->name, name)) + return &files->files[i]; + return NULL; +} + +static void romfile_loader_allocate(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct zone_s *zone; + struct romfile_loader_file *file = &files->files[files->nfiles]; + void *data; + int ret; + unsigned alloc_align = le32_to_cpu(entry->alloc_align); + + if (alloc_align & (alloc_align - 1)) + goto err; + + switch (entry->alloc_zone) { + case ROMFILE_LOADER_ALLOC_ZONE_HIGH: + zone = &ZoneHigh; + break; + case ROMFILE_LOADER_ALLOC_ZONE_FSEG: + zone = &ZoneFSeg; + break; + default: + goto err; + } + if (alloc_align < MALLOC_MIN_ALIGN) + alloc_align = MALLOC_MIN_ALIGN; + if (entry->alloc_file[ROMFILE_LOADER_FILESZ - 1]) + goto err; + file->file = romfile_find(entry->alloc_file); + if (!file->file || !file->file->size) + return; + data = _malloc(zone, file->file->size, alloc_align); + if (!data) { + warn_noalloc(); + return; + } + ret = file->file->copy(file->file, data, file->file->size); + if (ret != file->file->size) + goto file_err; + file->data = data; + files->nfiles++; + return; + +file_err: + free(data); +err: + warn_internalerror(); +} + +static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct romfile_loader_file *dest_file; + struct romfile_loader_file *src_file; + unsigned offset = le32_to_cpu(entry->pointer_offset); + u64 pointer = 0; + + dest_file = romfile_loader_find(entry->pointer_dest_file, files); + src_file = romfile_loader_find(entry->pointer_src_file, files); + + if (!dest_file || !src_file || !dest_file->data || !src_file->data || + offset + entry->pointer_size < offset || + offset + entry->pointer_size > dest_file->file->size || + entry->pointer_size < 1 || entry->pointer_size > 8 || + entry->pointer_size & (entry->pointer_size - 1)) + goto err; + + memcpy(&pointer, dest_file->data + offset, entry->pointer_size); + pointer = le64_to_cpu(pointer); + pointer += (unsigned long)src_file->data; + pointer = cpu_to_le64(pointer); + memcpy(dest_file->data + offset, &pointer, entry->pointer_size); + + return; +err: + warn_internalerror(); +} + +static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct romfile_loader_file *file; + unsigned offset = le32_to_cpu(entry->cksum_offset); + unsigned start = le32_to_cpu(entry->cksum_start); + unsigned len = le32_to_cpu(entry->cksum_length); + u8 *data; + + file = romfile_loader_find(entry->cksum_file, files); + + if (!file || !file->data || offset >= file->file->size || + start + len < start || start + len > file->file->size) + goto err; + + data = file->data + offset; + *data -= checksum(file->data + start, len); + + return; +err: + warn_internalerror(); +} + +int romfile_loader_execute(const char *name) +{ + struct romfile_loader_entry_s *entry; + int size, offset = 0, nfiles; + struct romfile_loader_files *files; + void *data = romfile_loadfile(name, &size); + if (!data) + return -1; + + if (size % sizeof(*entry)) { + warn_internalerror(); + goto err; + } + + /* (over)estimate the number of files to load. */ + nfiles = size / sizeof(*entry); + files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0])); + if (!files) { + warn_noalloc(); + goto err; + } + files->nfiles = 0; + + for (offset = 0; offset < size; offset += sizeof(*entry)) { + entry = data + offset; + switch (le32_to_cpu(entry->command)) { + case ROMFILE_LOADER_COMMAND_ALLOCATE: + romfile_loader_allocate(entry, files); + break; + case ROMFILE_LOADER_COMMAND_ADD_POINTER: + romfile_loader_add_pointer(entry, files); + break; + case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM: + romfile_loader_add_checksum(entry, files); + default: + /* Skip commands that we don't recognize. */ + break; + } + } + + free(files); + free(data); + return 0; + +err: + free(data); + return -1; +} diff --git a/qemu/roms/seabios/src/fw/romfile_loader.h b/qemu/roms/seabios/src/fw/romfile_loader.h new file mode 100644 index 000000000..15eab2ac6 --- /dev/null +++ b/qemu/roms/seabios/src/fw/romfile_loader.h @@ -0,0 +1,72 @@ +#ifndef __ROMFILE_LOADER_H +#define __ROMFILE_LOADER_H + +#include "types.h" // u8 +#include "util.h" // romfile_s + +#define ROMFILE_LOADER_FILESZ 56 + +/* ROM file linker/loader interface. Linker uses little endian format */ +struct romfile_loader_entry_s { + u32 command; + union { + /* + * COMMAND_ALLOCATE - allocate a table from @alloc_file + * subject to @alloc_align alignment (must be power of 2) + * and @alloc_zone (can be HIGH or FSEG) requirements. + * + * Must appear exactly once for each file, and before + * this file is referenced by any other command. + */ + struct { + char alloc_file[ROMFILE_LOADER_FILESZ]; + u32 alloc_align; + u8 alloc_zone; + }; + + /* + * COMMAND_ADD_POINTER - patch the table (originating from + * @dest_file) at @pointer_offset, by adding a pointer to the table + * originating from @src_file. 1,2,4 or 8 byte unsigned + * addition is used depending on @pointer_size. + */ + struct { + char pointer_dest_file[ROMFILE_LOADER_FILESZ]; + char pointer_src_file[ROMFILE_LOADER_FILESZ]; + u32 pointer_offset; + u8 pointer_size; + }; + + /* + * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by + * @cksum_start and @cksum_length fields, + * and then add the value at @cksum_offset. + * Checksum simply sums -X for each byte X in the range + * using 8-bit math. + */ + struct { + char cksum_file[ROMFILE_LOADER_FILESZ]; + u32 cksum_offset; + u32 cksum_start; + u32 cksum_length; + }; + + /* padding */ + char pad[124]; + }; +}; + +enum { + ROMFILE_LOADER_COMMAND_ALLOCATE = 0x1, + ROMFILE_LOADER_COMMAND_ADD_POINTER = 0x2, + ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3, +}; + +enum { + ROMFILE_LOADER_ALLOC_ZONE_HIGH = 0x1, + ROMFILE_LOADER_ALLOC_ZONE_FSEG = 0x2, +}; + +int romfile_loader_execute(const char *name); + +#endif diff --git a/qemu/roms/seabios/src/fw/shadow.c b/qemu/roms/seabios/src/fw/shadow.c new file mode 100644 index 000000000..4f00006bf --- /dev/null +++ b/qemu/roms/seabios/src/fw/shadow.c @@ -0,0 +1,171 @@ +// Support for enabling/disabling BIOS ram shadowing. +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "dev-q35.h" // PCI_VENDOR_ID_INTEL +#include "dev-piix.h" // I440FX_PAM0 +#include "hw/pci.h" // pci_config_writeb +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "malloc.h" // rom_get_last +#include "output.h" // dprintf +#include "paravirt.h" // runningOnXen +#include "string.h" // memset +#include "util.h" // make_bios_writable +#include "x86.h" // wbinvd + +// On the emulators, the bios at 0xf0000 is also at 0xffff0000 +#define BIOS_SRC_OFFSET 0xfff00000 + +// Enable shadowing and copy bios. +static void +__make_bios_writable_intel(u16 bdf, u32 pam0) +{ + // Make ram from 0xc0000-0xf0000 writable + int clear = 0; + int i; + for (i=0; i<6; i++) { + u32 pam = pam0 + 1 + i; + int reg = pci_config_readb(bdf, pam); + if (CONFIG_OPTIONROMS_DEPLOYED && (reg & 0x11) != 0x11) { + // Need to copy optionroms to work around qemu implementation + void *mem = (void*)(BUILD_ROM_START + i * 32*1024); + memcpy((void*)BUILD_BIOS_TMP_ADDR, mem, 32*1024); + pci_config_writeb(bdf, pam, 0x33); + memcpy(mem, (void*)BUILD_BIOS_TMP_ADDR, 32*1024); + clear = 1; + } else { + pci_config_writeb(bdf, pam, 0x33); + } + } + if (clear) + memset((void*)BUILD_BIOS_TMP_ADDR, 0, 32*1024); + + // Make ram from 0xf0000-0x100000 writable + int reg = pci_config_readb(bdf, pam0); + pci_config_writeb(bdf, pam0, 0x30); + if (reg & 0x10) + // Ram already present. + return; + + // Copy bios. + extern u8 code32flat_start[], code32flat_end[]; + memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET + , code32flat_end - code32flat_start); +} + +static void +make_bios_writable_intel(u16 bdf, u32 pam0) +{ + int reg = pci_config_readb(bdf, pam0); + if (!(reg & 0x10)) { + // QEMU doesn't fully implement the piix shadow capabilities - + // if ram isn't backing the bios segment when shadowing is + // disabled, the code itself wont be in memory. So, run the + // code from the high-memory flash location. + u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET; + void (*func)(u16 bdf, u32 pam0) = (void*)pos; + func(bdf, pam0); + return; + } + // Ram already present - just enable writes + __make_bios_writable_intel(bdf, pam0); +} + +static void +make_bios_readonly_intel(u16 bdf, u32 pam0) +{ + // Flush any pending writes before locking memory. + wbinvd(); + + // Write protect roms from 0xc0000-0xf0000 + u32 romlast = BUILD_BIOS_ADDR, rommax = BUILD_BIOS_ADDR; + if (CONFIG_WRITABLE_UPPERMEMORY) + romlast = rom_get_last(); + if (CONFIG_MALLOC_UPPERMEMORY) + rommax = rom_get_max(); + int i; + for (i=0; i<6; i++) { + u32 mem = BUILD_ROM_START + i * 32*1024; + u32 pam = pam0 + 1 + i; + if (romlast < mem + 16*1024 || rommax < mem + 32*1024) { + if (romlast >= mem && rommax >= mem + 16*1024) + pci_config_writeb(bdf, pam, 0x31); + break; + } + pci_config_writeb(bdf, pam, 0x11); + } + + // Write protect 0xf0000-0x100000 + pci_config_writeb(bdf, pam0, 0x10); +} + +static int ShadowBDF = -1; + +// Make the 0xc0000-0x100000 area read/writable. +void +make_bios_writable(void) +{ + if (!CONFIG_QEMU || runningOnXen()) + return; + + dprintf(3, "enabling shadow ram\n"); + + // At this point, statically allocated variables can't be written, + // so do this search manually. + int bdf; + foreachbdf(bdf, 0) { + u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); + u16 vendor = vendev & 0xffff, device = vendev >> 16; + if (vendor == PCI_VENDOR_ID_INTEL + && device == PCI_DEVICE_ID_INTEL_82441) { + make_bios_writable_intel(bdf, I440FX_PAM0); + ShadowBDF = bdf; + return; + } + if (vendor == PCI_VENDOR_ID_INTEL + && device == PCI_DEVICE_ID_INTEL_Q35_MCH) { + make_bios_writable_intel(bdf, Q35_HOST_BRIDGE_PAM0); + ShadowBDF = bdf; + return; + } + } + dprintf(1, "Unable to unlock ram - bridge not found\n"); +} + +// Make the BIOS code segment area (0xf0000) read-only. +void +make_bios_readonly(void) +{ + if (!CONFIG_QEMU || runningOnXen()) + return; + dprintf(3, "locking shadow ram\n"); + + if (ShadowBDF < 0) { + dprintf(1, "Unable to lock ram - bridge not found\n"); + return; + } + + u16 device = pci_config_readw(ShadowBDF, PCI_DEVICE_ID); + if (device == PCI_DEVICE_ID_INTEL_82441) + make_bios_readonly_intel(ShadowBDF, I440FX_PAM0); + else + make_bios_readonly_intel(ShadowBDF, Q35_HOST_BRIDGE_PAM0); +} + +void +qemu_prep_reset(void) +{ + if (!CONFIG_QEMU || runningOnXen()) + return; + // QEMU doesn't map 0xc0000-0xfffff back to the original rom on a + // reset, so do that manually before invoking a hard reset. + make_bios_writable(); + extern u8 code32flat_start[], code32flat_end[]; + memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET + , code32flat_end - code32flat_start); +} diff --git a/qemu/roms/seabios/src/fw/smbios.c b/qemu/roms/seabios/src/fw/smbios.c new file mode 100644 index 000000000..dba054133 --- /dev/null +++ b/qemu/roms/seabios/src/fw/smbios.c @@ -0,0 +1,585 @@ +// smbios table generation (on emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_findprefix +#include "std/smbios.h" // struct smbios_entry_point +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // cpuid + +static void +smbios_entry_point_setup(u16 max_structure_size, + u16 structure_table_length, + void *structure_table_address, + u16 number_of_structures) +{ + void *finaltable; + if (structure_table_length <= BUILD_MAX_SMBIOS_FSEG) + // Table is small enough for f-seg - allocate there. This + // works around a bug in JunOS (at least for small SMBIOS tables). + finaltable = malloc_fseg(structure_table_length); + else + finaltable = malloc_high(structure_table_length); + if (!finaltable) { + warn_noalloc(); + return; + } + memcpy(finaltable, structure_table_address, structure_table_length); + + struct smbios_entry_point ep; + memset(&ep, 0, sizeof(ep)); + memcpy(ep.anchor_string, "_SM_", 4); + ep.length = 0x1f; + ep.smbios_major_version = 2; + ep.smbios_minor_version = 4; + ep.max_structure_size = max_structure_size; + memcpy(ep.intermediate_anchor_string, "_DMI_", 5); + + ep.structure_table_length = structure_table_length; + ep.structure_table_address = (u32)finaltable; + ep.number_of_structures = number_of_structures; + ep.smbios_bcd_revision = 0x24; + + ep.checksum -= checksum(&ep, 0x10); + + ep.intermediate_checksum -= checksum((void*)&ep + 0x10, ep.length - 0x10); + + copy_smbios(&ep); +} + +static int +get_field(int type, int offset, void *dest) +{ + char name[128]; + snprintf(name, sizeof(name), "smbios/field%d-%d", type, offset); + struct romfile_s *file = romfile_find(name); + if (!file) + return 0; + file->copy(file, dest, file->size); + return file->size; +} + +static int +get_external(int type, char **p, unsigned *nr_structs, + unsigned *max_struct_size, char *end) +{ + static u64 used_bitmap[4] = { 0 }; + char *start = *p; + + /* Check if we've already reported these tables */ + if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f))) + return 1; + + /* Don't introduce spurious end markers */ + if (type == 127) + return 0; + + char prefix[128]; + snprintf(prefix, sizeof(prefix), "smbios/table%d-", type); + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix(prefix, file); + if (!file) + break; + + if (end - *p < file->size) { + warn_noalloc(); + break; + } + + struct smbios_structure_header *header = (void*)*p; + file->copy(file, header, file->size); + *p += file->size; + + /* Entries end with a double NULL char, if there's a string at + * the end (length is greater than formatted length), the string + * terminator provides the first NULL. */ + *((u8*)*p) = 0; + (*p)++; + if (header->length >= file->size) { + *((u8*)*p) = 0; + (*p)++; + } + + (*nr_structs)++; + if (*p - (char*)header > *max_struct_size) + *max_struct_size = *p - (char*)header; + } + + if (start == *p) + return 0; + + /* Mark that we've reported on this type */ + used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f)); + return 1; +} + +#define load_str_field_with_default(type, field, def) \ + do { \ + size = get_field(type, offsetof(struct smbios_type_##type, \ + field), end); \ + if (size == 1) { \ + /* zero-length string, skip to avoid bogus end marker */ \ + p->field = 0; \ + } else if (size > 1) { \ + end += size; \ + p->field = ++str_index; \ + } else { \ + memcpy(end, def, sizeof(def)); \ + end += sizeof(def); \ + p->field = ++str_index; \ + } \ + } while (0) + +#define load_str_field_or_skip(type, field) \ + do { \ + size = get_field(type, offsetof(struct smbios_type_##type, \ + field), end); \ + if (size > 1) { \ + end += size; \ + p->field = ++str_index; \ + } else { \ + p->field = 0; \ + } \ + } while (0) + +#define set_field_with_default(type, field, def) \ + do { \ + if (!get_field(type, offsetof(struct smbios_type_##type, \ + field), &p->field)) { \ + p->field = def; \ + } \ + } while (0) + +/* Type 0 -- BIOS Information */ +#define RELEASE_DATE_STR "01/01/2011" +static void * +smbios_init_type_0(void *start) +{ + struct smbios_type_0 *p = (struct smbios_type_0 *)start; + char *end = (char *)start + sizeof(struct smbios_type_0); + size_t size; + int str_index = 0; + + p->header.type = 0; + p->header.length = sizeof(struct smbios_type_0); + p->header.handle = 0; + + load_str_field_with_default(0, vendor_str, BUILD_APPNAME); + load_str_field_with_default(0, bios_version_str, BUILD_APPNAME); + + p->bios_starting_address_segment = 0xe800; + + load_str_field_with_default(0, bios_release_date_str, RELEASE_DATE_STR); + + p->bios_rom_size = 0; /* FIXME */ + + if (!get_field(0, offsetof(struct smbios_type_0, bios_characteristics), + &p->bios_characteristics)) { + memset(p->bios_characteristics, 0, 8); + /* BIOS characteristics not supported */ + p->bios_characteristics[0] = 0x08; + } + + if (!get_field(0, offsetof(struct smbios_type_0, + bios_characteristics_extension_bytes), + &p->bios_characteristics_extension_bytes)) { + p->bios_characteristics_extension_bytes[0] = 0; + /* Enable targeted content distribution. Needed for SVVP */ + p->bios_characteristics_extension_bytes[1] = 4; + } + + set_field_with_default(0, system_bios_major_release, 1); + set_field_with_default(0, system_bios_minor_release, 0); + set_field_with_default(0, embedded_controller_major_release, 0xff); + set_field_with_default(0, embedded_controller_minor_release, 0xff); + + *end = 0; + end++; + + return end; +} + +/* Type 1 -- System Information */ +static void * +smbios_init_type_1(void *start) +{ + struct smbios_type_1 *p = (struct smbios_type_1 *)start; + char *end = (char *)start + sizeof(struct smbios_type_1); + size_t size; + int str_index = 0; + + p->header.type = 1; + p->header.length = sizeof(struct smbios_type_1); + p->header.handle = 0x100; + + load_str_field_with_default(1, manufacturer_str, BUILD_APPNAME); + load_str_field_with_default(1, product_name_str, BUILD_APPNAME); + load_str_field_or_skip(1, version_str); + load_str_field_or_skip(1, serial_number_str); + + if (!get_field(1, offsetof(struct smbios_type_1, uuid), &p->uuid)) + memset(p->uuid, 0, 16); + + set_field_with_default(1, wake_up_type, 0x06); /* power switch */ + + load_str_field_or_skip(1, sku_number_str); + load_str_field_or_skip(1, family_str); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 3 -- System Enclosure */ +static void * +smbios_init_type_3(void *start) +{ + struct smbios_type_3 *p = (struct smbios_type_3 *)start; + char *end = (char *)start + sizeof(struct smbios_type_3); + size_t size; + int str_index = 0; + + p->header.type = 3; + p->header.length = sizeof(struct smbios_type_3); + p->header.handle = 0x300; + + load_str_field_with_default(3, manufacturer_str, BUILD_APPNAME); + set_field_with_default(3, type, 0x01); /* other */ + + load_str_field_or_skip(3, version_str); + load_str_field_or_skip(3, serial_number_str); + load_str_field_or_skip(3, asset_tag_number_str); + + set_field_with_default(3, boot_up_state, 0x03); /* safe */ + set_field_with_default(3, power_supply_state, 0x03); /* safe */ + set_field_with_default(3, thermal_state, 0x03); /* safe */ + set_field_with_default(3, security_status, 0x02); /* unknown */ + + set_field_with_default(3, oem_defined, 0); + set_field_with_default(3, height, 0); + set_field_with_default(3, number_of_power_cords, 0); + set_field_with_default(3, contained_element_count, 0); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 4 -- Processor Information */ +static void * +smbios_init_type_4(void *start, unsigned int cpu_number) +{ + struct smbios_type_4 *p = (struct smbios_type_4 *)start; + char *end = (char *)start + sizeof(struct smbios_type_4); + size_t size; + int str_index = 0; + char name[1024]; + + p->header.type = 4; + p->header.length = sizeof(struct smbios_type_4); + p->header.handle = 0x400 + cpu_number; + + size = get_field(4, offsetof(struct smbios_type_4, socket_designation_str), + name); + if (size) + snprintf(name + size - 1, sizeof(name) - size, "%2x", cpu_number); + else + snprintf(name, sizeof(name), "CPU%2x", cpu_number); + + memcpy(end, name, strlen(name) + 1); + end += strlen(name) + 1; + p->socket_designation_str = ++str_index; + + set_field_with_default(4, processor_type, 0x03); /* CPU */ + set_field_with_default(4, processor_family, 0x01); /* other */ + + load_str_field_with_default(4, processor_manufacturer_str, BUILD_APPNAME); + + if (!get_field(4, offsetof(struct smbios_type_4, processor_id) + , p->processor_id)) { + u32 cpuid_signature, ebx, ecx, cpuid_features; + cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); + p->processor_id[0] = cpuid_signature; + p->processor_id[1] = cpuid_features; + } + + load_str_field_or_skip(4, processor_version_str); + set_field_with_default(4, voltage, 0); + set_field_with_default(4, external_clock, 0); + + set_field_with_default(4, max_speed, 2000); + set_field_with_default(4, current_speed, 2000); + + set_field_with_default(4, status, 0x41); /* socket populated, CPU enabled */ + set_field_with_default(4, processor_upgrade, 0x01); /* other */ + + /* cache information structure not provided */ + p->l1_cache_handle = 0xffff; + p->l2_cache_handle = 0xffff; + p->l3_cache_handle = 0xffff; + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 16 -- Physical Memory Array */ +static void * +smbios_init_type_16(void *start, u32 memory_size_mb, int nr_mem_devs) +{ + struct smbios_type_16 *p = (struct smbios_type_16*)start; + + p->header.type = 16; + p->header.length = sizeof(struct smbios_type_16); + p->header.handle = 0x1000; + + set_field_with_default(16, location, 0x01); /* other */ + set_field_with_default(16, use, 0x03); /* system memory */ + /* Multi-bit ECC to make Microsoft happy */ + set_field_with_default(16, error_correction, 0x06); + /* 0x80000000 = unknown, accept sizes < 2TB - TODO multiple arrays */ + p->maximum_capacity = memory_size_mb < 2 << 20 ? + memory_size_mb << 10 : 0x80000000; + p->memory_error_information_handle = 0xfffe; /* none provided */ + p->number_of_memory_devices = nr_mem_devs; + + start += sizeof(struct smbios_type_16); + *((u16 *)start) = 0; + + return start + 2; +} + +/* Type 17 -- Memory Device */ +static void * +smbios_init_type_17(void *start, u32 size_mb, int instance) +{ + struct smbios_type_17 *p = (struct smbios_type_17 *)start; + char *end = (char *)start + sizeof(struct smbios_type_17); + size_t size; + int str_index = 0; + char name[1024]; + + p->header.type = 17; + p->header.length = sizeof(struct smbios_type_17); + p->header.handle = 0x1100 + instance; + + p->physical_memory_array_handle = 0x1000; + set_field_with_default(17, total_width, 64); + set_field_with_default(17, data_width, 64); +/* TODO: should assert in case something is wrong ASSERT((memory_size_mb & ~0x7fff) == 0); */ + p->size = size_mb; + set_field_with_default(17, form_factor, 0x09); /* DIMM */ + p->device_set = 0; + + size = get_field(17, offsetof(struct smbios_type_17, device_locator_str), + name); + if (size) + snprintf(name + size - 1, sizeof(name) - size, "%d", instance); + else + snprintf(name, sizeof(name), "DIMM %d", instance); + + memcpy(end, name, strlen(name) + 1); + end += strlen(name) + 1; + p->device_locator_str = ++str_index; + + load_str_field_or_skip(17, bank_locator_str); + set_field_with_default(17, memory_type, 0x07); /* RAM */ + set_field_with_default(17, type_detail, 0); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 19 -- Memory Array Mapped Address */ +static void * +smbios_init_type_19(void *start, u32 start_mb, u32 size_mb, int instance) +{ + struct smbios_type_19 *p = (struct smbios_type_19 *)start; + + p->header.type = 19; + p->header.length = sizeof(struct smbios_type_19); + p->header.handle = 0x1300 + instance; + + p->starting_address = start_mb << 10; + p->ending_address = p->starting_address + (size_mb << 10) - 1; + p->memory_array_handle = 0x1000; + p->partition_width = 1; + + start += sizeof(struct smbios_type_19); + *((u16 *)start) = 0; + + return start + 2; +} + +/* Type 20 -- Memory Device Mapped Address */ +static void * +smbios_init_type_20(void *start, u32 start_mb, u32 size_mb, int instance, + int dev_handle, int array_handle) +{ + struct smbios_type_20 *p = (struct smbios_type_20 *)start; + + p->header.type = 20; + p->header.length = sizeof(struct smbios_type_20); + p->header.handle = 0x1400 + instance; + + p->starting_address = start_mb << 10; + p->ending_address = p->starting_address + (size_mb << 10) - 1; + p->memory_device_handle = 0x1100 + dev_handle; + p->memory_array_mapped_address_handle = 0x1300 + array_handle; + p->partition_row_position = 1; + p->interleave_position = 0; + p->interleaved_data_depth = 0; + + start += sizeof(struct smbios_type_20); + + *((u16 *)start) = 0; + return start+2; +} + +/* Type 32 -- System Boot Information */ +static void * +smbios_init_type_32(void *start) +{ + struct smbios_type_32 *p = (struct smbios_type_32 *)start; + + p->header.type = 32; + p->header.length = sizeof(struct smbios_type_32); + p->header.handle = 0x2000; + memset(p->reserved, 0, 6); + set_field_with_default(32, boot_status, 0); /* no errors detected */ + + start += sizeof(struct smbios_type_32); + *((u16 *)start) = 0; + + return start+2; +} + +/* Type 127 -- End of Table */ +static void * +smbios_init_type_127(void *start) +{ + struct smbios_type_127 *p = (struct smbios_type_127 *)start; + + p->header.type = 127; + p->header.length = sizeof(struct smbios_type_127); + p->header.handle = 0x7f00; + + start += sizeof(struct smbios_type_127); + *((u16 *)start) = 0; + + return start + 2; +} + +#define TEMPSMBIOSSIZE (32 * 1024) + +void +smbios_legacy_setup(void) +{ + if (! CONFIG_SMBIOS) + return; + + dprintf(3, "init SMBIOS tables\n"); + + char *start = malloc_tmphigh(TEMPSMBIOSSIZE); + if (! start) { + warn_noalloc(); + return; + } + memset(start, 0, TEMPSMBIOSSIZE); + + u32 nr_structs = 0, max_struct_size = 0; + char *q, *p = start; + char *end = start + TEMPSMBIOSSIZE - sizeof(struct smbios_type_127); + +#define add_struct(type, args...) \ + do { \ + if (!get_external(type, &p, &nr_structs, &max_struct_size, end)) { \ + q = smbios_init_type_##type(args); \ + nr_structs++; \ + if ((q - p) > max_struct_size) \ + max_struct_size = q - p; \ + p = q; \ + } \ + } while (0) + + add_struct(0, p); + add_struct(1, p); + add_struct(3, p); + + int cpu_num; + for (cpu_num = 1; cpu_num <= MaxCountCPUs; cpu_num++) + add_struct(4, p, cpu_num); + + int ram_mb = (RamSize + RamSizeOver4G) >> 20; + int nr_mem_devs = (ram_mb + 0x3fff) >> 14; + add_struct(16, p, ram_mb, nr_mem_devs); + + int i, j; + for (i = 0; i < nr_mem_devs; i++) { + u32 dev_mb = ((i == (nr_mem_devs - 1)) + ? (((ram_mb - 1) & 0x3fff) + 1) + : 16384); + add_struct(17, p, dev_mb, i); + } + + add_struct(19, p, 0, RamSize >> 20, 0); + if (RamSizeOver4G) + add_struct(19, p, 4096, RamSizeOver4G >> 20, 1); + + add_struct(20, p, 0, RamSize >> 20, 0, 0, 0); + if (RamSizeOver4G) { + u32 start_mb = 4096; + for (j = 1, i = 0; i < nr_mem_devs; i++, j++) { + u32 dev_mb = ((i == (nr_mem_devs - 1)) + ? (((ram_mb - 1) & 0x3fff) + 1) + : 16384); + if (i == 0) + dev_mb -= RamSize >> 20; + + add_struct(20, p, start_mb, dev_mb, j, i, 1); + start_mb += dev_mb; + } + } + + add_struct(32, p); + /* Add any remaining provided entries before the end marker */ + for (i = 0; i < 256; i++) + get_external(i, &p, &nr_structs, &max_struct_size, end); + add_struct(127, p); + +#undef add_struct + + smbios_entry_point_setup(max_struct_size, p - start, start, nr_structs); + free(start); +} diff --git a/qemu/roms/seabios/src/fw/smm.c b/qemu/roms/seabios/src/fw/smm.c new file mode 100644 index 000000000..6cb484e7e --- /dev/null +++ b/qemu/roms/seabios/src/fw/smm.c @@ -0,0 +1,259 @@ +// System Management Mode support (on emulators) +// +// Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "dev-q35.h" +#include "dev-piix.h" +#include "hw/pci.h" // pci_config_writel +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_DEVICE_ID +#include "output.h" // dprintf +#include "paravirt.h" // PORT_SMI_STATUS +#include "stacks.h" // HaveSmmCall32 +#include "string.h" // memcpy +#include "util.h" // smm_setup +#include "x86.h" // wbinvd + +/* + * Check SMM state save area format (bits 0-15) and require support + * for SMBASE relocation. + */ +#define SMM_REV_MASK 0x0002ffff + +#define SMM_REV_I32 0x00020000 +#define SMM_REV_I64 0x00020064 + +struct smm_state { + union { + struct { + u8 pad_000[0xf8]; + u32 smm_base; + u32 smm_rev; + u8 pad_100[0xd0]; + u32 eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags; + u8 pad_1f8[0x08]; + } i32; + struct { + u8 pad_000[0xfc]; + u32 smm_rev; + u32 smm_base; + u8 pad_104[0x6c]; + u64 rflags, rip, r15, r14, r13, r12, r11, r10, r9, r8; + u64 rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; + } i64; + }; +}; + +struct smm_layout { + struct smm_state backup1; + struct smm_state backup2; + u8 stack[0x7c00]; + u64 codeentry; + u8 pad_8008[0x7df8]; + struct smm_state cpu; +}; + +void VISIBLE32FLAT +handle_smi(u16 cs) +{ + if (!CONFIG_USE_SMM) + return; + u8 cmd = inb(PORT_SMI_CMD); + struct smm_layout *smm = MAKE_FLATPTR(cs, 0); + dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm); + + if (smm == (void*)BUILD_SMM_INIT_ADDR) { + // relocate SMBASE to 0xa0000 + u32 rev = smm->cpu.i32.smm_rev & SMM_REV_MASK; + if (rev == SMM_REV_I32) { + smm->cpu.i32.smm_base = BUILD_SMM_ADDR; + } else if (rev == SMM_REV_I64) { + smm->cpu.i64.smm_base = BUILD_SMM_ADDR; + } else { + warn_internalerror(); + return; + } + // indicate to smm_relocate_and_restore() that the SMM code was executed + outb(0x00, PORT_SMI_STATUS); + + if (CONFIG_CALL32_SMM) { + // Backup current cpu state for SMM trampolining + struct smm_layout *newsmm = (void*)BUILD_SMM_ADDR; + memcpy(&newsmm->backup1, &smm->cpu, sizeof(newsmm->backup1)); + memcpy(&newsmm->backup2, &smm->cpu, sizeof(newsmm->backup2)); + HaveSmmCall32 = 1; + } + + return; + } + + if (CONFIG_CALL32_SMM && cmd == CALL32SMM_CMDID) { + if (smm->cpu.i32.smm_rev == SMM_REV_I32) { + u32 regs[8]; + memcpy(regs, &smm->cpu.i32.eax, sizeof(regs)); + if (smm->cpu.i32.ecx == CALL32SMM_ENTERID) { + dprintf(9, "smm cpu call pc=%x esp=%x\n", regs[3], regs[4]); + memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); + memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); + memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); + smm->cpu.i32.eip = regs[3]; + } else if (smm->cpu.i32.ecx == CALL32SMM_RETURNID) { + dprintf(9, "smm cpu ret %x esp=%x\n", regs[3], regs[4]); + memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); + memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); + smm->cpu.i32.eip = regs[3]; + } + } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) { + u64 regs[8]; + memcpy(regs, &smm->cpu.i64.rdi, sizeof(regs)); + if ((u32)smm->cpu.i64.rcx == CALL32SMM_ENTERID) { + memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); + memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); + memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); + smm->cpu.i64.rip = (u32)regs[4]; + } else if ((u32)smm->cpu.i64.rcx == CALL32SMM_RETURNID) { + memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); + memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); + smm->cpu.i64.rip = (u32)regs[4]; + } + } + } +} + +extern void entry_smi(void); +// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR) +#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \ + | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24)) + +static void +smm_save_and_copy(void) +{ + // save original memory content + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&smm->cpu, &initsmm->cpu, sizeof(smm->cpu)); + memcpy(&smm->codeentry, &initsmm->codeentry, sizeof(smm->codeentry)); + + // Setup code entry point. + initsmm->codeentry = SMI_INSN; +} + +static void +smm_relocate_and_restore(void) +{ + /* init APM status port */ + outb(0x01, PORT_SMI_STATUS); + + /* raise an SMI interrupt */ + outb(0x00, PORT_SMI_CMD); + + /* wait until SMM code executed */ + while (inb(PORT_SMI_STATUS) != 0x00) + ; + + /* restore original memory content */ + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&initsmm->cpu, &smm->cpu, sizeof(initsmm->cpu)); + memcpy(&initsmm->codeentry, &smm->codeentry, sizeof(initsmm->codeentry)); + + // Setup code entry point. + smm->codeentry = SMI_INSN; + wbinvd(); +} + +// This code is hardcoded for PIIX4 Power Management device. +static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) +{ + /* check if SMM init is already done */ + u32 value = pci_config_readl(isabdf, PIIX_DEVACTB); + if (value & PIIX_DEVACTB_APMC_EN) + return; + + /* enable the SMM memory window */ + pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x48); + + smm_save_and_copy(); + + /* enable SMI generation when writing to the APMC register */ + pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN); + + /* enable SMI generation */ + value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL); + outl(acpi_pm_base + PIIX_PMIO_GLBCTL, value | PIIX_PMIO_GLBCTL_SMI_EN); + + smm_relocate_and_restore(); + + /* close the SMM memory window and enable normal SMM */ + pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x08); +} + +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf) +{ + /* check if SMM init is already done */ + u32 value = inl(acpi_pm_base + ICH9_PMIO_SMI_EN); + if (value & ICH9_PMIO_SMI_EN_APMC_EN) + return; + + /* enable the SMM memory window */ + pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x48); + + smm_save_and_copy(); + + /* enable SMI generation when writing to the APMC register */ + outl(value | ICH9_PMIO_SMI_EN_APMC_EN | ICH9_PMIO_SMI_EN_GLB_SMI_EN, + acpi_pm_base + ICH9_PMIO_SMI_EN); + + /* lock SMI generation */ + value = pci_config_readw(isabdf, ICH9_LPC_GEN_PMCON_1); + pci_config_writel(isabdf, ICH9_LPC_GEN_PMCON_1, + value | ICH9_LPC_GEN_PMCON_1_SMI_LOCK); + + smm_relocate_and_restore(); + + /* close the SMM memory window and enable normal SMM */ + pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x08); +} + +static int SMMISADeviceBDF = -1, SMMPMDeviceBDF = -1; + +void +smm_device_setup(void) +{ + if (!CONFIG_USE_SMM) + return; + + struct pci_device *isapci, *pmpci; + isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3); + pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441); + if (isapci && pmpci) { + SMMISADeviceBDF = isapci->bdf; + SMMPMDeviceBDF = pmpci->bdf; + return; + } + isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC); + pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH); + if (isapci && pmpci) { + SMMISADeviceBDF = isapci->bdf; + SMMPMDeviceBDF = pmpci->bdf; + } +} + +void +smm_setup(void) +{ + if (!CONFIG_USE_SMM || SMMISADeviceBDF < 0) + return; + + dprintf(3, "init smm\n"); + u16 device = pci_config_readw(SMMISADeviceBDF, PCI_DEVICE_ID); + if (device == PCI_DEVICE_ID_INTEL_82371AB_3) + piix4_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF); + else + ich9_lpc_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF); +} diff --git a/qemu/roms/seabios/src/fw/smp.c b/qemu/roms/seabios/src/fw/smp.c new file mode 100644 index 000000000..a466ea6e9 --- /dev/null +++ b/qemu/roms/seabios/src/fw/smp.c @@ -0,0 +1,153 @@ +// QEMU multi-CPU initialization code +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/rtc.h" // CMOS_BIOS_SMP_COUNT +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // smp_setup +#include "x86.h" // wrmsr + +#define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300) +#define APIC_SVR ((u8*)BUILD_APIC_ADDR + 0x0F0) +#define APIC_LINT0 ((u8*)BUILD_APIC_ADDR + 0x350) +#define APIC_LINT1 ((u8*)BUILD_APIC_ADDR + 0x360) + +#define APIC_ENABLED 0x0100 + +static struct { u32 index; u64 val; } smp_mtrr[32]; +static u32 smp_mtrr_count; + +void +wrmsr_smp(u32 index, u64 val) +{ + wrmsr(index, val); + if (smp_mtrr_count >= ARRAY_SIZE(smp_mtrr)) { + warn_noalloc(); + return; + } + smp_mtrr[smp_mtrr_count].index = index; + smp_mtrr[smp_mtrr_count].val = val; + smp_mtrr_count++; +} + +u32 MaxCountCPUs; +static u32 CountCPUs; +// 256 bits for the found APIC IDs +static u32 FoundAPICIDs[256/32]; + +int apic_id_is_present(u8 apic_id) +{ + return !!(FoundAPICIDs[apic_id/32] & (1ul << (apic_id % 32))); +} + +void VISIBLE32FLAT +handle_smp(void) +{ + if (!CONFIG_QEMU) + return; + + // Enable CPU caching + setcr0(getcr0() & ~(CR0_CD|CR0_NW)); + + // Detect apic_id + u32 eax, ebx, ecx, cpuid_features; + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + u8 apic_id = ebx>>24; + dprintf(DEBUG_HDL_smp, "handle_smp: apic_id=%d\n", apic_id); + + // MTRR setup + int i; + for (i=0; i<smp_mtrr_count; i++) + wrmsr(smp_mtrr[i].index, smp_mtrr[i].val); + + // Set bit on FoundAPICIDs + FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32)); + + CountCPUs++; +} + +// Atomic lock for shared stack across processors. +u32 SMPLock __VISIBLE; +u32 SMPStack __VISIBLE; + +// find and initialize the CPUs by launching a SIPI to them +void +smp_setup(void) +{ + if (!CONFIG_QEMU) + return; + + ASSERT32FLAT(); + u32 eax, ebx, ecx, cpuid_features; + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + if (eax < 1 || !(cpuid_features & CPUID_APIC)) { + // No apic - only the main cpu is present. + dprintf(1, "No apic - only the main cpu is present.\n"); + CountCPUs= 1; + MaxCountCPUs = 1; + return; + } + + // mark the BSP initial APIC ID as found, too: + u8 apic_id = ebx>>24; + FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32)); + CountCPUs = 1; + + // Setup jump trampoline to counter code. + u64 old = *(u64*)BUILD_AP_BOOT_ADDR; + // ljmpw $SEG_BIOS, $(entry_smp - BUILD_BIOS_ADDR) + extern void entry_smp(void); + u64 new = (0xea | ((u64)SEG_BIOS<<24) + | (((u32)entry_smp - BUILD_BIOS_ADDR) << 8)); + *(u64*)BUILD_AP_BOOT_ADDR = new; + + // enable local APIC + u32 val = readl(APIC_SVR); + writel(APIC_SVR, val | APIC_ENABLED); + + /* Set LINT0 as Ext_INT, level triggered */ + writel(APIC_LINT0, 0x8700); + + /* Set LINT1 as NMI, level triggered */ + writel(APIC_LINT1, 0x8400); + + // Init the lock. + writel(&SMPLock, 1); + + // broadcast SIPI + barrier(); + writel(APIC_ICR_LOW, 0x000C4500); + u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12; + writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector); + + // Wait for other CPUs to process the SIPI. + u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1; + while (cmos_smp_count != CountCPUs) + asm volatile( + // Release lock and allow other processors to use the stack. + " movl %%esp, %1\n" + " movl $0, %0\n" + // Reacquire lock and take back ownership of stack. + "1:rep ; nop\n" + " lock btsl $0, %0\n" + " jc 1b\n" + : "+m" (SMPLock), "+m" (SMPStack) + : : "cc", "memory"); + yield(); + + // Restore memory. + *(u64*)BUILD_AP_BOOT_ADDR = old; + + MaxCountCPUs = romfile_loadint("etc/max-cpus", 0); + if (!MaxCountCPUs || MaxCountCPUs < CountCPUs) + MaxCountCPUs = CountCPUs; + + dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", CountCPUs, + MaxCountCPUs); +} diff --git a/qemu/roms/seabios/src/fw/ssdt-misc.dsl b/qemu/roms/seabios/src/fw/ssdt-misc.dsl new file mode 100644 index 000000000..acc850e84 --- /dev/null +++ b/qemu/roms/seabios/src/fw/ssdt-misc.dsl @@ -0,0 +1,104 @@ +ACPI_EXTRACT_ALL_CODE ssdp_misc_aml + +DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1) +{ + +/**************************************************************** + * PCI memory ranges + ****************************************************************/ + + Scope(\) { + ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_start + Name(P0S, 0x12345678) + ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_end + Name(P0E, 0x12345678) + ACPI_EXTRACT_NAME_BYTE_CONST acpi_pci64_valid + Name(P1V, 0x12) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_start + Name(P1S, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_end + Name(P1E, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_length + Name(P1L, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + } + + +/**************************************************************** + * Suspend + ****************************************************************/ + + Scope(\) { + /* + * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes: + * must match piix4 emulation. + */ + + ACPI_EXTRACT_NAME_STRING acpi_s3_name + Name(_S3, Package(0x04) { + One, /* PM1a_CNT.SLP_TYP */ + One, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + ACPI_EXTRACT_NAME_STRING acpi_s4_name + ACPI_EXTRACT_PKG_START acpi_s4_pkg + Name(_S4, Package(0x04) { + 0x2, /* PM1a_CNT.SLP_TYP */ + 0x2, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + Name(_S5, Package(0x04) { + Zero, /* PM1a_CNT.SLP_TYP */ + Zero, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + } + + External(\_SB.PCI0, DeviceObj) + External(\_SB.PCI0.ISA, DeviceObj) + + Scope(\_SB.PCI0.ISA) { + Device(PEVT) { + Name(_HID, "QEMU0001") + /* PEST will be patched to be Zero if no such device */ + ACPI_EXTRACT_NAME_WORD_CONST ssdt_isa_pest + Name(PEST, 0xFFFF) + OperationRegion(PEOR, SystemIO, PEST, 0x01) + Field(PEOR, ByteAcc, NoLock, Preserve) { + PEPT, 8, + } + + Method(_STA, 0, NotSerialized) { + Store(PEST, Local0) + If (LEqual(Local0, Zero)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + + Method(RDPT, 0, NotSerialized) { + Store(PEPT, Local0) + Return (Local0) + } + + Method(WRPT, 1, NotSerialized) { + Store(Arg0, PEPT) + } + + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x00, 0x00, 0x01, 0x01, IO) + }) + + CreateWordField(_CRS, IO._MIN, IOMN) + CreateWordField(_CRS, IO._MAX, IOMX) + + Method(_INI, 0, NotSerialized) { + Store(PEST, IOMN) + Store(PEST, IOMX) + } + } + } +} diff --git a/qemu/roms/seabios/src/fw/ssdt-pcihp.dsl b/qemu/roms/seabios/src/fw/ssdt-pcihp.dsl new file mode 100644 index 000000000..cb24c11ce --- /dev/null +++ b/qemu/roms/seabios/src/fw/ssdt-pcihp.dsl @@ -0,0 +1,36 @@ +ACPI_EXTRACT_ALL_CODE ssdp_pcihp_aml + +DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1) +{ + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + + /* Objects supplied by DSDT */ + External(\_SB.PCI0, DeviceObj) + External(\_SB.PCI0.PCEJ, MethodObj) + + Scope(\_SB.PCI0) { + + /* Bulk generated PCI hotplug devices */ + ACPI_EXTRACT_DEVICE_START ssdt_pcihp_start + ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end + ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name + + // Method _EJ0 can be patched by BIOS to EJ0_ + // at runtime, if the slot is detected to not support hotplug. + // Extract the offset of the address dword and the + // _EJ0 name to allow this patching. + Device(SAA) { + ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id + Name(_SUN, 0xAA) + ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr + Name(_ADR, 0xAA0000) + ACPI_EXTRACT_METHOD_STRING ssdt_pcihp_ej0 + Method(_EJ0, 1) { + PCEJ(_SUN) + } + } + } +} diff --git a/qemu/roms/seabios/src/fw/ssdt-proc.dsl b/qemu/roms/seabios/src/fw/ssdt-proc.dsl new file mode 100644 index 000000000..407d61ead --- /dev/null +++ b/qemu/roms/seabios/src/fw/ssdt-proc.dsl @@ -0,0 +1,48 @@ +/* This file is the basis for the ssdt table generated in src/acpi.c. + * It defines the contents of the per-cpu Processor() object. At + * runtime, a dynamically generated SSDT will contain one copy of this + * AML snippet for every possible cpu in the system. The objects will + * be placed in the \_SB_ namespace. + * + * In addition to the aml code generated from this file, the + * src/acpi.c file creates a NTFY method with an entry for each cpu: + * Method(NTFY, 2) { + * If (LEqual(Arg0, 0x00)) { Notify(CP00, Arg1) } + * If (LEqual(Arg0, 0x01)) { Notify(CP01, Arg1) } + * ... + * } + * and a CPON array with the list of active and inactive cpus: + * Name(CPON, Package() { One, One, ..., Zero, Zero, ... }) + */ + +ACPI_EXTRACT_ALL_CODE ssdp_proc_aml + +DefinitionBlock ("ssdt-proc.aml", "SSDT", 0x01, "BXPC", "BXSSDT", 0x1) +{ + ACPI_EXTRACT_PROCESSOR_START ssdt_proc_start + ACPI_EXTRACT_PROCESSOR_END ssdt_proc_end + ACPI_EXTRACT_PROCESSOR_STRING ssdt_proc_name + Processor(CPAA, 0xAA, 0x0000b010, 0x06) { + ACPI_EXTRACT_NAME_BYTE_CONST ssdt_proc_id + Name(ID, 0xAA) +/* + * The src/acpi.c code requires the above ACP_EXTRACT tags so that it can update + * CPAA and 0xAA with the appropriate CPU id (see + * SD_OFFSET_CPUHEX/CPUID1/CPUID2). Don't change the above without + * also updating the C code. + */ + Name(_HID, "ACPI0007") + External(CPMA, MethodObj) + External(CPST, MethodObj) + External(CPEJ, MethodObj) + Method(_MAT, 0) { + Return (CPMA(ID)) + } + Method(_STA, 0) { + Return (CPST(ID)) + } + Method(_EJ0, 1, NotSerialized) { + CPEJ(ID, Arg0) + } + } +} diff --git a/qemu/roms/seabios/src/fw/xen.c b/qemu/roms/seabios/src/fw/xen.c new file mode 100644 index 000000000..dd8e8afd4 --- /dev/null +++ b/qemu/roms/seabios/src/fw/xen.c @@ -0,0 +1,147 @@ +// Xen HVM support +// +// Copyright (C) 2011 Citrix Systems. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" +#include "hw/serialio.h" // DebugOutputPort +#include "malloc.h" // memalign_high +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // PlatformRunningOn +#include "string.h" // memcpy +#include "util.h" // copy_acpi_rsdp +#include "x86.h" // cpuid +#include "xen.h" + +#define INFO_PHYSICAL_ADDRESS 0x00001000 + +u32 xen_cpuid_base = 0; +unsigned long xen_hypercall_page = 0; + +struct xen_seabios_info { + char signature[14]; /* XenHVMSeaBIOS\0 */ + u8 length; /* Length of this struct */ + u8 checksum; /* Set such that the sum over bytes 0..length == 0 */ + /* + * Physical address of an array of tables_nr elements. + * + * Each element is a 32 bit value contianing the physical address + * of a BIOS table. + */ + u32 tables; + u32 tables_nr; + /* + * Physical address of the e820 table, contains e820_nr entries. + */ + u32 e820; + u32 e820_nr; +} PACKED; + +static void validate_info(struct xen_seabios_info *t) +{ + if ( memcmp(t->signature, "XenHVMSeaBIOS", 14) ) + panic("Bad Xen info signature\n"); + + if ( t->length < sizeof(struct xen_seabios_info) ) + panic("Bad Xen info length\n"); + + if (checksum(t, t->length) != 0) + panic("Bad Xen info checksum\n"); +} + +void xen_preinit(void) +{ + u32 base, eax, ebx, ecx, edx; + char signature[13]; + + if (!CONFIG_XEN) + return; + + for (base = 0x40000000; base < 0x40010000; base += 0x100) { + cpuid(base, &eax, &ebx, &ecx, &edx); + memcpy(signature + 0, &ebx, 4); + memcpy(signature + 4, &ecx, 4); + memcpy(signature + 8, &edx, 4); + signature[12] = 0; + + dprintf(9, "Found hypervisor signature \"%s\" at %x\n", + signature, base); + if (strcmp(signature, "XenVMMXenVMM") == 0) { + /* Set debug_io_port first, so the following messages work. */ + DebugOutputPort = 0xe9; + debug_banner(); + dprintf(1, "\nFound Xen hypervisor signature at %x\n", base); + if ((eax - base) < 2) + panic("Insufficient Xen cpuid leaves. eax=%x at base %x\n", + eax, base); + xen_cpuid_base = base; + break; + } + } + if (!xen_cpuid_base) { + dprintf(1, "No Xen hypervisor found.\n"); + return; + } + PlatformRunningOn = PF_QEMU|PF_XEN; +} + +static int hypercall_xen_version( int cmd, void *arg) +{ + return _hypercall2(int, xen_version, cmd, arg); +} + +/* Fill in hypercall transfer pages. */ +void xen_hypercall_setup(void) +{ + u32 eax, ebx, ecx, edx; + xen_extraversion_t extraversion; + unsigned long i; + + if (!runningOnXen()) + return; + + cpuid(xen_cpuid_base + 2, &eax, &ebx, &ecx, &edx); + + xen_hypercall_page = (unsigned long)memalign_high(PAGE_SIZE, eax*PAGE_SIZE); + if (!xen_hypercall_page) + panic("unable to allocate Xen hypercall page\n"); + + dprintf(1, "Allocated Xen hypercall page at %lx\n", xen_hypercall_page); + for ( i = 0; i < eax; i++ ) + wrmsr(ebx, xen_hypercall_page + (i << 12) + i); + + /* Print version information. */ + cpuid(xen_cpuid_base + 1, &eax, &ebx, &ecx, &edx); + hypercall_xen_version(XENVER_extraversion, extraversion); + dprintf(1, "Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion); +} + +void xen_biostable_setup(void) +{ + struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS; + void **tables = (void*)info->tables; + int i; + + dprintf(1, "xen: copy BIOS tables...\n"); + for (i=0; i<info->tables_nr; i++) + copy_table(tables[i]); + + find_acpi_features(); +} + +void xen_ramsize_preinit(void) +{ + int i; + struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS; + struct e820entry *e820 = (struct e820entry *)info->e820; + validate_info(info); + + dprintf(1, "xen: copy e820...\n"); + + for (i = 0; i < info->e820_nr; i++) { + struct e820entry *e = &e820[i]; + add_e820(e->start, e->size, e->type); + } +} diff --git a/qemu/roms/seabios/src/fw/xen.h b/qemu/roms/seabios/src/fw/xen.h new file mode 100644 index 000000000..f00f8407d --- /dev/null +++ b/qemu/roms/seabios/src/fw/xen.h @@ -0,0 +1,125 @@ +#ifndef __XEN_H +#define __XEN_H + +void xen_preinit(void); +void xen_ramsize_preinit(void); +void xen_hypercall_setup(void); +void xen_biostable_setup(void); + +extern unsigned long xen_hypercall_page; + +#define _hypercall0(type, name) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res) \ + : "0" (__hentry) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall1(type, name, a1) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1) \ + : "0" (__hentry), "1" ((long)(a1)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall2(type, name, a1, a2) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall3(type, name, a1, a2, a3) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall4(type, name, a1, a2, a3, a4) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2, __ign3, __ign4; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall5(type, name, a1, a2, a3, a4, a5) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)), \ + "5" ((long)(a5)) \ + : "memory" ); \ + (type)__res; \ +}) + +/****************************************************************************** + * + * The following interface definitions are taken from Xen and have the + * following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* xen.h */ + +#define __HYPERVISOR_xen_version 17 + +/* version.h */ + +/* arg == xen_extraversion_t. */ +#define XENVER_extraversion 1 +typedef char xen_extraversion_t[16]; +#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t)) + +#endif diff --git a/qemu/roms/seabios/src/gen-defs.h b/qemu/roms/seabios/src/gen-defs.h new file mode 100644 index 000000000..dabf64cd9 --- /dev/null +++ b/qemu/roms/seabios/src/gen-defs.h @@ -0,0 +1,19 @@ +// Tool for building defintions accessible from assembler code. This +// is based on code from the Linux Kernel. +#ifndef __GEN_DEFS_H +#define __GEN_DEFS_H + + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() \ + asm volatile("\n->" : : ) + +#define OFFSET(sym, str, mem) \ + DEFINE(sym, offsetof(struct str, mem)) + +#define COMMENT(x) \ + asm volatile("\n->#" x) + +#endif // gen-defs.h diff --git a/qemu/roms/seabios/src/hw/ahci.c b/qemu/roms/seabios/src/hw/ahci.c new file mode 100644 index 000000000..3193d81a6 --- /dev/null +++ b/qemu/roms/seabios/src/hw/ahci.c @@ -0,0 +1,639 @@ +// Low level AHCI disk access +// +// Copyright (C) 2010 Gerd Hoffmann <kraxel@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ahci.h" // CDB_CMD_READ_10 +#include "ata.h" // ATA_CB_STAT +#include "biosvar.h" // GET_GLOBAL +#include "blockcmd.h" // CDB_CMD_READ_10 +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER +#include "pci_regs.h" // PCI_INTERRUPT_LINE +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc +#include "x86.h" // inb + +#define AHCI_REQUEST_TIMEOUT 32000 // 32 seconds max for IDE ops +#define AHCI_RESET_TIMEOUT 500 // 500 miliseconds +#define AHCI_LINK_TIMEOUT 10 // 10 miliseconds + +// prepare sata command fis +static void sata_prep_simple(struct sata_cmd_fis *fis, u8 command) +{ + memset_fl(fis, 0, sizeof(*fis)); + fis->command = command; +} + +static void sata_prep_readwrite(struct sata_cmd_fis *fis, + struct disk_op_s *op, int iswrite) +{ + u64 lba = op->lba; + u8 command; + + memset_fl(fis, 0, sizeof(*fis)); + + if (op->count >= (1<<8) || lba + op->count >= (1<<28)) { + fis->sector_count2 = op->count >> 8; + fis->lba_low2 = lba >> 24; + fis->lba_mid2 = lba >> 32; + fis->lba_high2 = lba >> 40; + lba &= 0xffffff; + command = (iswrite ? ATA_CMD_WRITE_DMA_EXT + : ATA_CMD_READ_DMA_EXT); + } else { + command = (iswrite ? ATA_CMD_WRITE_DMA + : ATA_CMD_READ_DMA); + } + fis->feature = 1; /* dma */ + fis->command = command; + fis->sector_count = op->count; + fis->lba_low = lba; + fis->lba_mid = lba >> 8; + fis->lba_high = lba >> 16; + fis->device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA; +} + +static void sata_prep_atapi(struct sata_cmd_fis *fis, u16 blocksize) +{ + memset_fl(fis, 0, sizeof(*fis)); + fis->command = ATA_CMD_PACKET; + fis->feature = 1; /* dma */ + fis->lba_mid = blocksize; + fis->lba_high = blocksize >> 8; +} + +// ahci register access helpers +static u32 ahci_ctrl_readl(struct ahci_ctrl_s *ctrl, u32 reg) +{ + u32 addr = ctrl->iobase + reg; + return readl((void*)addr); +} + +static void ahci_ctrl_writel(struct ahci_ctrl_s *ctrl, u32 reg, u32 val) +{ + u32 addr = ctrl->iobase + reg; + writel((void*)addr, val); +} + +static u32 ahci_port_to_ctrl(u32 pnr, u32 port_reg) +{ + u32 ctrl_reg = 0x100; + ctrl_reg += pnr * 0x80; + ctrl_reg += port_reg; + return ctrl_reg; +} + +static u32 ahci_port_readl(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg) +{ + u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg); + return ahci_ctrl_readl(ctrl, ctrl_reg); +} + +static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val) +{ + u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg); + ahci_ctrl_writel(ctrl, ctrl_reg, val); +} + +// submit ahci command + wait for result +static int ahci_command(struct ahci_port_s *port_gf, int iswrite, int isatapi, + void *buffer, u32 bsize) +{ + u32 val, status, success, flags, intbits, error; + struct ahci_ctrl_s *ctrl = port_gf->ctrl; + struct ahci_cmd_s *cmd = port_gf->cmd; + struct ahci_fis_s *fis = port_gf->fis; + struct ahci_list_s *list = port_gf->list; + u32 pnr = port_gf->pnr; + + cmd->fis.reg = 0x27; + cmd->fis.pmp_type = 1 << 7; /* cmd fis */ + cmd->prdt[0].base = (u32)buffer; + cmd->prdt[0].baseu = 0; + cmd->prdt[0].flags = bsize-1; + + flags = ((1 << 16) | /* one prd entry */ + (iswrite ? (1 << 6) : 0) | + (isatapi ? (1 << 5) : 0) | + (5 << 0)); /* fis length (dwords) */ + list[0].flags = flags; + list[0].bytes = 0; + list[0].base = (u32)(cmd); + list[0].baseu = 0; + + dprintf(8, "AHCI/%d: send cmd ...\n", pnr); + intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (intbits) + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); + ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1); + ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1); + + u32 end = timer_calc(AHCI_REQUEST_TIMEOUT); + do { + for (;;) { + intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (intbits) { + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); + if (intbits & 0x02) { + status = GET_LOWFLAT(fis->psfis[2]); + error = GET_LOWFLAT(fis->psfis[3]); + break; + } + if (intbits & 0x01) { + status = GET_LOWFLAT(fis->rfis[2]); + error = GET_LOWFLAT(fis->rfis[3]); + break; + } + } + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + dprintf(8, "AHCI/%d: ... intbits 0x%x, status 0x%x ...\n", + pnr, intbits, status); + } while (status & ATA_CB_STAT_BSY); + + success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | + ATA_CB_STAT_ERR)) && + ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY))); + if (success) { + dprintf(8, "AHCI/%d: ... finished, status 0x%x, OK\n", pnr, + status); + } else { + dprintf(2, "AHCI/%d: ... finished, status 0x%x, ERROR 0x%x\n", pnr, + status, error); + + // non-queued error recovery (AHCI 1.3 section 6.2.2.1) + // Clears PxCMD.ST to 0 to reset the PxCI register + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~PORT_CMD_START); + + // waits for PxCMD.CR to clear to 0 + while (1) { + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + if ((val & PORT_CMD_LIST_ON) == 0) + break; + yield(); + } + + // Clears any error bits in PxSERR to enable capturing new errors + val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR); + ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val); + + // Clears status bits in PxIS as appropriate + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); + + // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to 1, issue + // a COMRESET to the device to put it in an idle state + val = ahci_port_readl(ctrl, pnr, PORT_TFDATA); + if (val & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ)) { + dprintf(2, "AHCI/%d: issue comreset\n", pnr); + val = ahci_port_readl(ctrl, pnr, PORT_SCR_CTL); + // set Device Detection Initialization (DET) to 1 for 1 ms for comreset + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val | 1); + mdelay (1); + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val); + } + + // Sets PxCMD.ST to 1 to enable issuing new commands + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START); + } + return success ? 0 : -1; +} + +#define CDROM_CDB_SIZE 12 + +int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (! CONFIG_AHCI) + return 0; + + struct ahci_port_s *port_gf = container_of( + op->drive_gf, struct ahci_port_s, drive); + struct ahci_cmd_s *cmd = port_gf->cmd; + u8 *atapi = cdbcmd; + int i, rc; + + sata_prep_atapi(&cmd->fis, blocksize); + for (i = 0; i < CDROM_CDB_SIZE; i++) { + cmd->atapi[i] = atapi[i]; + } + rc = ahci_command(port_gf, 0, 1, op->buf_fl, + op->count * blocksize); + if (rc < 0) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// read/write count blocks from a harddrive, op->buf_fl must be word aligned +static int +ahci_disk_readwrite_aligned(struct disk_op_s *op, int iswrite) +{ + struct ahci_port_s *port_gf = container_of( + op->drive_gf, struct ahci_port_s, drive); + struct ahci_cmd_s *cmd = port_gf->cmd; + int rc; + + sata_prep_readwrite(&cmd->fis, op, iswrite); + rc = ahci_command(port_gf, iswrite, 0, op->buf_fl, + op->count * DISK_SECTOR_SIZE); + dprintf(8, "ahci disk %s, lba %6x, count %3x, buf %p, rc %d\n", + iswrite ? "write" : "read", (u32)op->lba, op->count, op->buf_fl, rc); + if (rc < 0) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// read/write count blocks from a harddrive. +static int +ahci_disk_readwrite(struct disk_op_s *op, int iswrite) +{ + // if caller's buffer is word aligned, use it directly + if (((u32) op->buf_fl & 1) == 0) + return ahci_disk_readwrite_aligned(op, iswrite); + + // Use a word aligned buffer for AHCI I/O + int rc; + struct disk_op_s localop = *op; + u8 *alignedbuf_fl = bounce_buf_fl; + u8 *position = op->buf_fl; + + localop.buf_fl = alignedbuf_fl; + localop.count = 1; + + if (iswrite) { + u16 block; + for (block = 0; block < op->count; block++) { + memcpy_fl (alignedbuf_fl, position, DISK_SECTOR_SIZE); + rc = ahci_disk_readwrite_aligned (&localop, 1); + if (rc) + return rc; + position += DISK_SECTOR_SIZE; + localop.lba++; + } + } else { // read + u16 block; + for (block = 0; block < op->count; block++) { + rc = ahci_disk_readwrite_aligned (&localop, 0); + if (rc) + return rc; + memcpy_fl (position, alignedbuf_fl, DISK_SECTOR_SIZE); + position += DISK_SECTOR_SIZE; + localop.lba++; + } + } + return DISK_RET_SUCCESS; +} + +// command demuxer +int VISIBLE32FLAT +process_ahci_op(struct disk_op_s *op) +{ + if (!CONFIG_AHCI) + return 0; + switch (op->command) { + case CMD_READ: + return ahci_disk_readwrite(op, 0); + case CMD_WRITE: + return ahci_disk_readwrite(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + dprintf(1, "AHCI: unknown disk command %d\n", op->command); + return DISK_RET_EPARAM; + } +} + +static void +ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr) +{ + u32 val; + + /* disable FIS + CMD */ + u32 end = timer_calc(AHCI_RESET_TIMEOUT); + for (;;) { + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + if (!(val & (PORT_CMD_FIS_RX | PORT_CMD_START | + PORT_CMD_FIS_ON | PORT_CMD_LIST_ON))) + break; + val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START); + ahci_port_writel(ctrl, pnr, PORT_CMD, val); + if (timer_check(end)) { + warn_timeout(); + break; + } + yield(); + } + + /* disable + clear IRQs */ + ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, 0); + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (val) + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); +} + +static struct ahci_port_s* +ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr) +{ + struct ahci_port_s *port = malloc_tmp(sizeof(*port)); + + if (!port) { + warn_noalloc(); + return NULL; + } + port->pnr = pnr; + port->ctrl = ctrl; + port->list = memalign_tmp(1024, 1024); + port->fis = memalign_tmp(256, 256); + port->cmd = memalign_tmp(256, 256); + if (port->list == NULL || port->fis == NULL || port->cmd == NULL) { + warn_noalloc(); + return NULL; + } + memset(port->list, 0, 1024); + memset(port->fis, 0, 256); + memset(port->cmd, 0, 256); + + ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list); + ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis); + return port; +} + +static void ahci_port_release(struct ahci_port_s *port) +{ + ahci_port_reset(port->ctrl, port->pnr); + free(port->list); + free(port->fis); + free(port->cmd); + free(port); +} + +static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port) +{ + struct ahci_port_s *tmp; + u32 cmd; + + tmp = malloc_fseg(sizeof(*port)); + if (!tmp) { + warn_noalloc(); + ahci_port_release(port); + return NULL; + } + *tmp = *port; + free(port); + port = tmp; + + ahci_port_reset(port->ctrl, port->pnr); + + free(port->list); + free(port->fis); + free(port->cmd); + port->list = memalign_high(1024, 1024); + port->fis = memalign_high(256, 256); + port->cmd = memalign_high(256, 256); + + ahci_port_writel(port->ctrl, port->pnr, PORT_LST_ADDR, (u32)port->list); + ahci_port_writel(port->ctrl, port->pnr, PORT_FIS_ADDR, (u32)port->fis); + + cmd = ahci_port_readl(port->ctrl, port->pnr, PORT_CMD); + cmd |= (PORT_CMD_FIS_RX|PORT_CMD_START); + ahci_port_writel(port->ctrl, port->pnr, PORT_CMD, cmd); + + return port; +} + +#define MAXMODEL 40 + +/* See ahci spec chapter 10.1 "Software Initialization of HBA" */ +static int ahci_port_setup(struct ahci_port_s *port) +{ + struct ahci_ctrl_s *ctrl = port->ctrl; + u32 pnr = port->pnr; + char model[MAXMODEL+1]; + u16 buffer[256]; + u32 cmd, stat, err, tf; + int rc; + + /* enable FIS recv */ + cmd = ahci_port_readl(ctrl, pnr, PORT_CMD); + cmd |= PORT_CMD_FIS_RX; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + + /* spin up */ + cmd |= PORT_CMD_SPIN_UP; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + u32 end = timer_calc(AHCI_LINK_TIMEOUT); + for (;;) { + stat = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT); + if ((stat & 0x07) == 0x03) { + dprintf(2, "AHCI/%d: link up\n", port->pnr); + break; + } + if (timer_check(end)) { + dprintf(2, "AHCI/%d: link down\n", port->pnr); + return -1; + } + yield(); + } + + /* clear error status */ + err = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR); + if (err) + ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, err); + + /* wait for device becoming ready */ + end = timer_calc(AHCI_REQUEST_TIMEOUT); + for (;;) { + tf = ahci_port_readl(ctrl, pnr, PORT_TFDATA); + if (!(tf & (ATA_CB_STAT_BSY | + ATA_CB_STAT_DRQ))) + break; + if (timer_check(end)) { + warn_timeout(); + dprintf(1, "AHCI/%d: device not ready (tf 0x%x)\n", port->pnr, tf); + return -1; + } + yield(); + } + + /* start device */ + cmd |= PORT_CMD_START; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + + sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE); + rc = ahci_command(port, 0, 0, buffer, sizeof(buffer)); + if (rc == 0) { + port->atapi = 1; + } else { + port->atapi = 0; + sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE); + rc = ahci_command(port, 0, 0, buffer, sizeof(buffer)); + if (rc < 0) + return -1; + } + + port->drive.cntl_id = pnr; + port->drive.removable = (buffer[0] & 0x80) ? 1 : 0; + + if (!port->atapi) { + // found disk (ata) + port->drive.type = DTYPE_AHCI; + port->drive.blksize = DISK_SECTOR_SIZE; + port->drive.pchs.cylinder = buffer[1]; + port->drive.pchs.head = buffer[3]; + port->drive.pchs.sector = buffer[6]; + + u64 sectors; + if (buffer[83] & (1 << 10)) // word 83 - lba48 support + sectors = *(u64*)&buffer[100]; // word 100-103 + else + sectors = *(u32*)&buffer[60]; // word 60 and word 61 + port->drive.sectors = sectors; + u64 adjsize = sectors >> 11; + char adjprefix = 'M'; + if (adjsize >= (1 << 16)) { + adjsize >>= 10; + adjprefix = 'G'; + } + port->desc = znprintf(MAXDESCSIZE + , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)" + , port->pnr + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (u32)adjsize, adjprefix); + port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0); + } else { + // found cdrom (atapi) + port->drive.type = DTYPE_AHCI_ATAPI; + port->drive.blksize = CDROM_SECTOR_SIZE; + port->drive.sectors = (u64)-1; + u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05; + if (!iscd) { + dprintf(1, "AHCI/%d: atapi device isn't a cdrom\n", port->pnr); + return -1; + } + port->desc = znprintf(MAXDESCSIZE + , "DVD/CD [AHCI/%d: %s ATAPI-%d DVD/CD]" + , port->pnr + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer)); + port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0); + } + return 0; +} + +// Detect any drives attached to a given controller. +static void +ahci_port_detect(void *data) +{ + struct ahci_port_s *port = data; + int rc; + + dprintf(2, "AHCI/%d: probing\n", port->pnr); + ahci_port_reset(port->ctrl, port->pnr); + rc = ahci_port_setup(port); + if (rc < 0) + ahci_port_release(port); + else { + port = ahci_port_realloc(port); + if (port == NULL) + return; + dprintf(1, "AHCI/%d: registering: \"%s\"\n", port->pnr, port->desc); + if (!port->atapi) { + // Register with bcv system. + boot_add_hd(&port->drive, port->desc, port->prio); + } else { + // fill cdidmap + boot_add_cd(&port->drive, port->desc, port->prio); + } + } +} + +// Initialize an ata controller and detect its drives. +static void +ahci_controller_setup(struct pci_device *pci) +{ + struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl)); + struct ahci_port_s *port; + u16 bdf = pci->bdf; + u32 val, pnr, max; + + if (!ctrl) { + warn_noalloc(); + return; + } + + if (create_bounce_buf() < 0) { + warn_noalloc(); + free(ctrl); + return; + } + + ctrl->pci_tmp = pci; + ctrl->pci_bdf = bdf; + ctrl->iobase = pci_config_readl(bdf, PCI_BASE_ADDRESS_5); + ctrl->irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + dprintf(1, "AHCI controller at %02x.%x, iobase %x, irq %d\n", + bdf >> 3, bdf & 7, ctrl->iobase, ctrl->irq); + + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + val = ahci_ctrl_readl(ctrl, HOST_CTL); + ahci_ctrl_writel(ctrl, HOST_CTL, val | HOST_CTL_AHCI_EN); + + ctrl->caps = ahci_ctrl_readl(ctrl, HOST_CAP); + ctrl->ports = ahci_ctrl_readl(ctrl, HOST_PORTS_IMPL); + dprintf(2, "AHCI: cap 0x%x, ports_impl 0x%x\n", + ctrl->caps, ctrl->ports); + + max = 0x1f; + for (pnr = 0; pnr <= max; pnr++) { + if (!(ctrl->ports & (1 << pnr))) + continue; + port = ahci_port_alloc(ctrl, pnr); + if (port == NULL) + continue; + run_thread(ahci_port_detect, port); + } +} + +// Locate and init ahci controllers. +static void +ahci_scan(void) +{ + // Scan PCI bus for ATA adapters + struct pci_device *pci; + foreachpci(pci) { + if (pci->class != PCI_CLASS_STORAGE_SATA) + continue; + if (pci->prog_if != 1 /* AHCI rev 1 */) + continue; + ahci_controller_setup(pci); + } +} + +void +ahci_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_AHCI) + return; + + dprintf(3, "init ahci\n"); + ahci_scan(); +} diff --git a/qemu/roms/seabios/src/hw/ahci.h b/qemu/roms/seabios/src/hw/ahci.h new file mode 100644 index 000000000..c8c755a3f --- /dev/null +++ b/qemu/roms/seabios/src/hw/ahci.h @@ -0,0 +1,202 @@ +#ifndef __AHCI_H +#define __AHCI_H + +#include "block.h" // struct drive_s +#include "types.h" // u32 + +struct sata_cmd_fis { + u8 reg; + u8 pmp_type; + u8 command; + u8 feature; + + u8 lba_low; + u8 lba_mid; + u8 lba_high; + u8 device; + + u8 lba_low2; + u8 lba_mid2; + u8 lba_high2; + u8 feature2; + + u8 sector_count; + u8 sector_count2; + u8 res_1; + u8 control; + + u8 res_2[64 - 16]; +}; + +struct ahci_ctrl_s { + struct pci_device *pci_tmp; + u16 pci_bdf; + u8 irq; + u32 iobase; + u32 caps; + u32 ports; +}; + +struct ahci_cmd_s { + struct sata_cmd_fis fis; + u8 atapi[0x20]; + u8 res[0x20]; + struct { + u32 base; + u32 baseu; + u32 res; + u32 flags; + } prdt[]; +}; + +/* command list */ +struct ahci_list_s { + u32 flags; + u32 bytes; + u32 base; + u32 baseu; + u32 res[4]; +}; + +struct ahci_fis_s { + u8 dsfis[0x1c]; /* dma setup */ + u8 res_1[0x04]; + u8 psfis[0x14]; /* pio setup */ + u8 res_2[0x0c]; + u8 rfis[0x14]; /* d2h register */ + u8 res_3[0x04]; + u8 sdbfis[0x08]; /* set device bits */ + u8 ufis[0x40]; /* unknown */ + u8 res_4[0x60]; +}; + +struct ahci_port_s { + struct drive_s drive; + struct ahci_ctrl_s *ctrl; + struct ahci_list_s *list; + struct ahci_fis_s *fis; + struct ahci_cmd_s *cmd; + u32 pnr; + u32 atapi; + char *desc; + int prio; +}; + +void ahci_setup(void); +int process_ahci_op(struct disk_op_s *op); +int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); + +#define AHCI_IRQ_ON_SG (1 << 31) +#define AHCI_CMD_ATAPI (1 << 5) +#define AHCI_CMD_WRITE (1 << 6) +#define AHCI_CMD_PREFETCH (1 << 7) +#define AHCI_CMD_RESET (1 << 8) +#define AHCI_CMD_CLR_BUSY (1 << 10) + +#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ +#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ +#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ + +/* global controller registers */ +#define HOST_CAP 0x00 /* host capabilities */ +#define HOST_CTL 0x04 /* global host control */ +#define HOST_IRQ_STAT 0x08 /* interrupt status */ +#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ +#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ + +/* HOST_CTL bits */ +#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ +#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ +#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */ + +/* HOST_CAP bits */ +#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ +#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ +#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ +#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ +#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ +#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */ + +/* registers for each SATA port */ +#define PORT_LST_ADDR 0x00 /* command list DMA addr */ +#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ +#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ +#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ +#define PORT_IRQ_STAT 0x10 /* interrupt status */ +#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ +#define PORT_CMD 0x18 /* port command */ +#define PORT_TFDATA 0x20 /* taskfile data */ +#define PORT_SIG 0x24 /* device TF signature */ +#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ +#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ +#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ +#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ +#define PORT_CMD_ISSUE 0x38 /* command issue */ +#define PORT_RESERVED 0x3c /* reserved */ + +/* PORT_IRQ_{STAT,MASK} bits */ +#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */ +#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ +#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ +#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ +#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ +#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ +#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ +#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ + +#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ +#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ +#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ +#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ +#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ +#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ +#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ +#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ +#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ + +#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ + PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ + PORT_IRQ_UNK_FIS) +#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ + PORT_IRQ_HBUS_DATA_ERR) +#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) + +/* PORT_CMD bits */ +#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ +#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ +#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ +#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ +#define PORT_CMD_CLO (1 << 3) /* Command list override */ +#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ +#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ +#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ + +#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */ +#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ +#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ +#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ + +#define PORT_IRQ_STAT_DHRS (1 << 0) /* Device to Host Register FIS */ +#define PORT_IRQ_STAT_PSS (1 << 1) /* PIO Setup FIS */ +#define PORT_IRQ_STAT_DSS (1 << 2) /* DMA Setup FIS */ +#define PORT_IRQ_STAT_SDBS (1 << 3) /* Set Device Bits */ +#define PORT_IRQ_STAT_UFS (1 << 4) /* Unknown FIS */ +#define PORT_IRQ_STAT_DPS (1 << 5) /* Descriptor Processed */ +#define PORT_IRQ_STAT_PCS (1 << 6) /* Port Connect Change Status */ +#define PORT_IRQ_STAT_DMPS (1 << 7) /* Device Mechanical Presence + Status */ +#define PORT_IRQ_STAT_PRCS (1 << 22) /* File Ready Status */ +#define PORT_IRQ_STAT_IPMS (1 << 23) /* Incorrect Port Multiplier + Status */ +#define PORT_IRQ_STAT_OFS (1 << 24) /* Overflow Status */ +#define PORT_IRQ_STAT_INFS (1 << 26) /* Interface Non-Fatal Error + Status */ +#define PORT_IRQ_STAT_IFS (1 << 27) /* Interface Fatal Error */ +#define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */ +#define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */ +#define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */ +#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */ + +#endif // ahci.h diff --git a/qemu/roms/seabios/src/hw/ata.c b/qemu/roms/seabios/src/hw/ata.c new file mode 100644 index 000000000..d805706dd --- /dev/null +++ b/qemu/roms/seabios/src/hw/ata.c @@ -0,0 +1,1043 @@ +// Low level ATA disk access +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ata.h" // ATA_CB_STAT +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // CDB_CMD_READ_10 +#include "byteorder.h" // be16_to_cpu +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER +#include "pci_regs.h" // PCI_INTERRUPT_LINE +#include "pic.h" // enable_hwirq +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc +#include "x86.h" // inb + +#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Wait for the specified ide state +static inline int +await_ide(u8 mask, u8 flags, u16 base, u16 timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + u8 status = inb(base+ATA_CB_STAT); + if ((status & mask) == flags) + return status; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +// Wait for the device to be not-busy. +static int +await_not_bsy(u16 base) +{ + return await_ide(ATA_CB_STAT_BSY, 0, base, IDE_TIMEOUT); +} + +// Wait for the device to be ready. +static int +await_rdy(u16 base) +{ + return await_ide(ATA_CB_STAT_RDY, ATA_CB_STAT_RDY, base, IDE_TIMEOUT); +} + +// Wait for ide state - pauses for one ata cycle first. +static inline int +pause_await_not_bsy(u16 iobase1, u16 iobase2) +{ + // Wait one PIO transfer cycle. + inb(iobase2 + ATA_CB_ASTAT); + + return await_not_bsy(iobase1); +} + +// Wait for ide state - pause for 400ns first. +static inline int +ndelay_await_not_bsy(u16 iobase1) +{ + ndelay(400); + return await_not_bsy(iobase1); +} + +// Reset a drive +static void +ata_reset(struct atadrive_s *adrive_gf) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + dprintf(6, "ata_reset drive=%p\n", &adrive_gf->drive); + // Pulse SRST + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC); + udelay(5); + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC); + msleep(2); + + // wait for device to become not busy. + int status = await_not_bsy(iobase1); + if (status < 0) + goto done; + if (slave) { + // Change device. + u32 end = timer_calc(IDE_TIMEOUT); + for (;;) { + outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH); + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + goto done; + if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1) + break; + // Change drive request failed to take effect - retry. + if (timer_check(end)) { + warn_timeout(); + goto done; + } + } + } else { + // QEMU doesn't reset dh on reset, so set it explicitly. + outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH); + } + + // On a user-reset request, wait for RDY if it is an ATA device. + u8 type=GET_GLOBALFLAT(adrive_gf->drive.type); + if (type == DTYPE_ATA) + status = await_rdy(iobase1); + +done: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + + dprintf(6, "ata_reset exit status=%x\n", status); +} + +// Check for drive RDY for 16bit interface command. +static int +isready(struct atadrive_s *adrive_gf) +{ + // Read the status from controller + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u8 status = inb(iobase1 + ATA_CB_STAT); + if ((status & (ATA_CB_STAT_BSY|ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY) + return DISK_RET_SUCCESS; + return DISK_RET_ENOTREADY; +} + + +/**************************************************************** + * ATA send command + ****************************************************************/ + +struct ata_pio_command { + u8 feature; + u8 sector_count; + u8 lba_low; + u8 lba_mid; + u8 lba_high; + u8 device; + u8 command; + + u8 feature2; + u8 sector_count2; + u8 lba_low2; + u8 lba_mid2; + u8 lba_high2; +}; + +// Send an ata command to the drive. +static int +send_cmd(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + + // Select device + int status = await_not_bsy(iobase1); + if (status < 0) + return status; + u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1) + | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)); + u8 olddh = inb(iobase1 + ATA_CB_DH); + outb(newdh, iobase1 + ATA_CB_DH); + if ((olddh ^ newdh) & (1<<4)) { + // Was a device change - wait for device to become not busy. + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + return status; + } + + // Check for ATA_CMD_(READ|WRITE)_(SECTORS|DMA)_EXT commands. + if ((cmd->command & ~0x11) == ATA_CMD_READ_SECTORS_EXT) { + outb(cmd->feature2, iobase1 + ATA_CB_FR); + outb(cmd->sector_count2, iobase1 + ATA_CB_SC); + outb(cmd->lba_low2, iobase1 + ATA_CB_SN); + outb(cmd->lba_mid2, iobase1 + ATA_CB_CL); + outb(cmd->lba_high2, iobase1 + ATA_CB_CH); + } + outb(cmd->feature, iobase1 + ATA_CB_FR); + outb(cmd->sector_count, iobase1 + ATA_CB_SC); + outb(cmd->lba_low, iobase1 + ATA_CB_SN); + outb(cmd->lba_mid, iobase1 + ATA_CB_CL); + outb(cmd->lba_high, iobase1 + ATA_CB_CH); + outb(cmd->command, iobase1 + ATA_CB_CMD); + + return 0; +} + +// Wait for data after calling 'send_cmd'. +static int +ata_wait_data(u16 iobase1) +{ + int status = ndelay_await_not_bsy(iobase1); + if (status < 0) + return status; + + if (status & ATA_CB_STAT_ERR) { + dprintf(6, "send_cmd : read error (status=%02x err=%02x)\n" + , status, inb(iobase1 + ATA_CB_ERR)); + return -4; + } + if (!(status & ATA_CB_STAT_DRQ)) { + dprintf(6, "send_cmd : DRQ not set (status %02x)\n", status); + return -5; + } + + return 0; +} + +// Send an ata command that does not transfer any further data. +int +ata_cmd_nondata(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, cmd); + if (ret) + goto fail; + ret = ndelay_await_not_bsy(iobase1); + if (ret < 0) + goto fail; + + if (ret & ATA_CB_STAT_ERR) { + dprintf(6, "nondata cmd : read error (status=%02x err=%02x)\n" + , ret, inb(iobase1 + ATA_CB_ERR)); + ret = -4; + goto fail; + } + if (ret & ATA_CB_STAT_DRQ) { + dprintf(6, "nondata cmd : DRQ set (status %02x)\n", ret); + ret = -5; + goto fail; + } + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + + return ret; +} + + +/**************************************************************** + * ATA PIO transfers + ****************************************************************/ + +// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive +// 'op->drive_gf'. +static int +ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize) +{ + dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n" + , op->drive_gf, iswrite, op->count, blocksize, op->buf_fl); + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + int count = op->count; + void *buf_fl = op->buf_fl; + int status; + for (;;) { + if (iswrite) { + // Write data to controller + dprintf(16, "Write sector id=%p dest=%p\n", op->drive_gf, buf_fl); + if (CONFIG_ATA_PIO32) + outsl_fl(iobase1, buf_fl, blocksize / 4); + else + outsw_fl(iobase1, buf_fl, blocksize / 2); + } else { + // Read data from controller + dprintf(16, "Read sector id=%p dest=%p\n", op->drive_gf, buf_fl); + if (CONFIG_ATA_PIO32) + insl_fl(iobase1, buf_fl, blocksize / 4); + else + insw_fl(iobase1, buf_fl, blocksize / 2); + } + buf_fl += blocksize; + + status = pause_await_not_bsy(iobase1, iobase2); + if (status < 0) { + // Error + op->count -= count; + return status; + } + + count--; + if (!count) + break; + status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR); + if (status != ATA_CB_STAT_DRQ) { + dprintf(6, "ata_pio_transfer : more sectors left (status %02x)\n" + , status); + op->count -= count; + return -6; + } + } + + status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ + | ATA_CB_STAT_ERR); + if (!iswrite) + status &= ~ATA_CB_STAT_DF; + if (status != 0) { + dprintf(6, "ata_pio_transfer : no sectors left (status %02x)\n", status); + return -7; + } + + return 0; +} + + +/**************************************************************** + * ATA DMA transfers + ****************************************************************/ + +#define BM_CMD 0 +#define BM_CMD_MEMWRITE 0x08 +#define BM_CMD_START 0x01 +#define BM_STATUS 2 +#define BM_STATUS_IRQ 0x04 +#define BM_STATUS_ERROR 0x02 +#define BM_STATUS_ACTIVE 0x01 +#define BM_TABLE 4 + +struct sff_dma_prd { + u32 buf_fl; + u32 count; +}; + +// Check if DMA available and setup transfer if so. +static int +ata_try_dma(struct disk_op_s *op, int iswrite, int blocksize) +{ + ASSERT16(); + if (! CONFIG_ATA_DMA) + return -1; + u32 dest = (u32)op->buf_fl; + if (dest & 1) + // Need minimum alignment of 1. + return -1; + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster); + if (! iomaster) + return -1; + u32 bytes = op->count * blocksize; + if (! bytes) + return -1; + + // Build PRD dma structure. + struct sff_dma_prd *dma = MAKE_FLATPTR(SEG_LOW, ExtraStack); + struct sff_dma_prd *origdma = dma; + while (bytes) { + if (dma >= &origdma[16]) + // Too many descriptors.. + return -1; + u32 count = bytes; + u32 max = 0x10000 - (dest & 0xffff); + if (count > max) + count = max; + + SET_LOWFLAT(dma->buf_fl, dest); + bytes -= count; + if (!bytes) + // Last descriptor. + count |= 1<<31; + dprintf(16, "dma@%p: %08x %08x\n", dma, dest, count); + dest += count; + SET_LOWFLAT(dma->count, count); + dma++; + } + + // Program bus-master controller. + outl((u32)origdma, iomaster + BM_TABLE); + u8 oldcmd = inb(iomaster + BM_CMD) & ~(BM_CMD_MEMWRITE|BM_CMD_START); + outb(oldcmd | (iswrite ? 0x00 : BM_CMD_MEMWRITE), iomaster + BM_CMD); + outb(BM_STATUS_ERROR|BM_STATUS_IRQ, iomaster + BM_STATUS); + + return 0; +} + +// Transfer data using DMA. +static int +ata_dma_transfer(struct disk_op_s *op) +{ + if (! CONFIG_ATA_DMA) + return -1; + dprintf(16, "ata_dma_transfer id=%p buf=%p\n", op->drive_gf, op->buf_fl); + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster); + + // Start bus-master controller. + u8 oldcmd = inb(iomaster + BM_CMD); + outb(oldcmd | BM_CMD_START, iomaster + BM_CMD); + + u32 end = timer_calc(IDE_TIMEOUT); + u8 status; + for (;;) { + status = inb(iomaster + BM_STATUS); + if (status & BM_STATUS_IRQ) + break; + // Transfer in progress + if (timer_check(end)) { + // Timeout. + warn_timeout(); + break; + } + yield(); + } + outb(oldcmd & ~BM_CMD_START, iomaster + BM_CMD); + + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + int idestatus = pause_await_not_bsy(iobase1, iobase2); + + if ((status & (BM_STATUS_IRQ|BM_STATUS_ACTIVE)) == BM_STATUS_IRQ + && idestatus >= 0x00 + && (idestatus & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ + | ATA_CB_STAT_ERR)) == 0x00) + // Success. + return 0; + + dprintf(6, "IDE DMA error (dma=%x ide=%x/%x/%x)\n", status, idestatus + , inb(iobase2 + ATA_CB_ASTAT), inb(iobase1 + ATA_CB_ERR)); + return -1; +} + + +/**************************************************************** + * ATA hard drive functions + ****************************************************************/ + +// Transfer data to harddrive using PIO protocol. +static int +ata_pio_cmd_data(struct disk_op_s *op, int iswrite, struct ata_pio_command *cmd) +{ + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, cmd); + if (ret) + goto fail; + ret = ata_wait_data(iobase1); + if (ret) + goto fail; + ret = ata_pio_transfer(op, iswrite, DISK_SECTOR_SIZE); + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + return ret; +} + +// Transfer data to harddrive using DMA protocol. +static int +ata_dma_cmd_data(struct disk_op_s *op, struct ata_pio_command *cmd) +{ + if (! CONFIG_ATA_DMA) + return -1; + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + int ret = send_cmd(adrive_gf, cmd); + if (ret) + return ret; + return ata_dma_transfer(op); +} + +// Read/write count blocks from a harddrive. +static int +ata_readwrite(struct disk_op_s *op, int iswrite) +{ + u64 lba = op->lba; + + int usepio = ata_try_dma(op, iswrite, DISK_SECTOR_SIZE); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + if (op->count >= (1<<8) || lba + op->count >= (1<<28)) { + cmd.sector_count2 = op->count >> 8; + cmd.lba_low2 = lba >> 24; + cmd.lba_mid2 = lba >> 32; + cmd.lba_high2 = lba >> 40; + lba &= 0xffffff; + + if (usepio) + cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS_EXT + : ATA_CMD_READ_SECTORS_EXT); + else + cmd.command = (iswrite ? ATA_CMD_WRITE_DMA_EXT + : ATA_CMD_READ_DMA_EXT); + } else { + if (usepio) + cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS + : ATA_CMD_READ_SECTORS); + else + cmd.command = (iswrite ? ATA_CMD_WRITE_DMA + : ATA_CMD_READ_DMA); + } + + cmd.sector_count = op->count; + cmd.lba_low = lba; + cmd.lba_mid = lba >> 8; + cmd.lba_high = lba >> 16; + cmd.device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA; + + int ret; + if (usepio) + ret = ata_pio_cmd_data(op, iswrite, &cmd); + else + ret = ata_dma_cmd_data(op, &cmd); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// 16bit command demuxer for ATA harddrives. +int +process_ata_op(struct disk_op_s *op) +{ + if (!CONFIG_ATA) + return 0; + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + switch (op->command) { + case CMD_READ: + return ata_readwrite(op, 0); + case CMD_WRITE: + return ata_readwrite(op, 1); + case CMD_RESET: + ata_reset(adrive_gf); + return DISK_RET_SUCCESS; + case CMD_ISREADY: + return isready(adrive_gf); + case CMD_FORMAT: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * ATAPI functions + ****************************************************************/ + +#define CDROM_CDB_SIZE 12 + +// Low-level atapi command transmit function. +int +atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (! CONFIG_ATA) + return 0; + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.lba_mid = blocksize; + cmd.lba_high = blocksize >> 8; + cmd.command = ATA_CMD_PACKET; + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, &cmd); + if (ret) + goto fail; + ret = ata_wait_data(iobase1); + if (ret) + goto fail; + + // Send command to device + outsw_fl(iobase1, MAKE_FLATPTR(GET_SEG(SS), cdbcmd), CDROM_CDB_SIZE / 2); + + int status = pause_await_not_bsy(iobase1, iobase2); + if (status < 0) { + ret = status; + goto fail; + } + + if (status & ATA_CB_STAT_ERR) { + u8 err = inb(iobase1 + ATA_CB_ERR); + // skip "Not Ready" + if (err != 0x20) + dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n" + , status, err); + ret = -2; + goto fail; + } + if (blocksize) { + if (!(status & ATA_CB_STAT_DRQ)) { + dprintf(6, "send_atapi_cmd : DRQ not set (status %02x)\n", status); + ret = -3; + goto fail; + } + + ret = ata_pio_transfer(op, 0, blocksize); + } + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * ATA detect and init + ****************************************************************/ + +// Send an identify device or identify device packet command. +static int +send_ata_identity(struct atadrive_s *adrive, u16 *buffer, int command) +{ + memset(buffer, 0, DISK_SECTOR_SIZE); + + struct disk_op_s dop; + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = &adrive->drive; + dop.count = 1; + dop.lba = 1; + dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = command; + + return ata_pio_cmd_data(&dop, 0, &cmd); +} + +// Extract the ATA/ATAPI version info. +int +ata_extract_version(u16 *buffer) +{ + // Extract ATA/ATAPI version. + u16 ataversion = buffer[80]; + u8 version; + for (version=15; version>0; version--) + if (ataversion & (1<<version)) + break; + return version; +} + +#define MAXMODEL 40 + +// Extract the ATA/ATAPI model info. +char * +ata_extract_model(char *model, u32 size, u16 *buffer) +{ + // Read model name + int i; + for (i=0; i<size/2; i++) + *(u16*)&model[i*2] = be16_to_cpu(buffer[27+i]); + model[size] = 0x00; + nullTrailingSpace(model); + return model; +} + +// Common init code between ata and atapi +static struct atadrive_s * +init_atadrive(struct atadrive_s *dummy, u16 *buffer) +{ + struct atadrive_s *adrive = malloc_fseg(sizeof(*adrive)); + if (!adrive) { + warn_noalloc(); + return NULL; + } + memset(adrive, 0, sizeof(*adrive)); + adrive->chan_gf = dummy->chan_gf; + adrive->slave = dummy->slave; + adrive->drive.cntl_id = adrive->chan_gf->chanid * 2 + dummy->slave; + adrive->drive.removable = (buffer[0] & 0x80) ? 1 : 0; + return adrive; +} + +// Detect if the given drive is an atapi - initialize it if so. +static struct atadrive_s * +init_drive_atapi(struct atadrive_s *dummy, u16 *buffer) +{ + // Send an IDENTIFY_DEVICE_PACKET command to device + int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_PACKET_DEVICE); + if (ret) + return NULL; + + // Success - setup as ATAPI. + struct atadrive_s *adrive = init_atadrive(dummy, buffer); + if (!adrive) + return NULL; + adrive->drive.type = DTYPE_ATA_ATAPI; + adrive->drive.blksize = CDROM_SECTOR_SIZE; + adrive->drive.sectors = (u64)-1; + u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05; + char model[MAXMODEL+1]; + char *desc = znprintf(MAXDESCSIZE + , "DVD/CD [ata%d-%d: %s ATAPI-%d %s]" + , adrive->chan_gf->chanid, adrive->slave + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (iscd ? "DVD/CD" : "Device")); + dprintf(1, "%s\n", desc); + + // fill cdidmap + if (iscd) { + int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp, + adrive->chan_gf->chanid, + adrive->slave); + boot_add_cd(&adrive->drive, desc, prio); + } + + return adrive; +} + +// Detect if the given drive is a regular ata drive - initialize it if so. +static struct atadrive_s * +init_drive_ata(struct atadrive_s *dummy, u16 *buffer) +{ + // Send an IDENTIFY_DEVICE command to device + int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_DEVICE); + if (ret) + return NULL; + + // Success - setup as ATA. + struct atadrive_s *adrive = init_atadrive(dummy, buffer); + if (!adrive) + return NULL; + adrive->drive.type = DTYPE_ATA; + adrive->drive.blksize = DISK_SECTOR_SIZE; + + adrive->drive.pchs.cylinder = buffer[1]; + adrive->drive.pchs.head = buffer[3]; + adrive->drive.pchs.sector = buffer[6]; + + u64 sectors; + if (buffer[83] & (1 << 10)) // word 83 - lba48 support + sectors = *(u64*)&buffer[100]; // word 100-103 + else + sectors = *(u32*)&buffer[60]; // word 60 and word 61 + adrive->drive.sectors = sectors; + u64 adjsize = sectors >> 11; + char adjprefix = 'M'; + if (adjsize >= (1 << 16)) { + adjsize >>= 10; + adjprefix = 'G'; + } + char model[MAXMODEL+1]; + char *desc = znprintf(MAXDESCSIZE + , "ata%d-%d: %s ATA-%d Hard-Disk (%u %ciBytes)" + , adrive->chan_gf->chanid, adrive->slave + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (u32)adjsize, adjprefix); + dprintf(1, "%s\n", desc); + + int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp, + adrive->chan_gf->chanid, + adrive->slave); + // Register with bcv system. + boot_add_hd(&adrive->drive, desc, prio); + + return adrive; +} + +static u32 SpinupEnd; + +// Wait for non-busy status and check for "floating bus" condition. +static int +powerup_await_non_bsy(u16 base) +{ + u8 orstatus = 0; + u8 status; + for (;;) { + status = inb(base+ATA_CB_STAT); + if (!(status & ATA_CB_STAT_BSY)) + break; + orstatus |= status; + if (orstatus == 0xff) { + dprintf(4, "powerup IDE floating\n"); + return orstatus; + } + if (timer_check(SpinupEnd)) { + warn_timeout(); + return -1; + } + yield(); + } + dprintf(6, "powerup iobase=%x st=%x\n", base, status); + return status; +} + +// Detect any drives attached to a given controller. +static void +ata_detect(void *data) +{ + struct ata_channel_s *chan_gf = data; + struct atadrive_s dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.chan_gf = chan_gf; + // Device detection + int didreset = 0; + u8 slave; + for (slave=0; slave<=1; slave++) { + // Wait for not-bsy. + u16 iobase1 = chan_gf->iobase1; + int status = powerup_await_non_bsy(iobase1); + if (status < 0) + continue; + u8 newdh = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0; + outb(newdh, iobase1+ATA_CB_DH); + ndelay(400); + status = powerup_await_non_bsy(iobase1); + if (status < 0) + continue; + + // Check if ioport registers look valid. + outb(newdh, iobase1+ATA_CB_DH); + u8 dh = inb(iobase1+ATA_CB_DH); + outb(0x55, iobase1+ATA_CB_SC); + outb(0xaa, iobase1+ATA_CB_SN); + u8 sc = inb(iobase1+ATA_CB_SC); + u8 sn = inb(iobase1+ATA_CB_SN); + dprintf(6, "ata_detect ata%d-%d: sc=%x sn=%x dh=%x\n" + , chan_gf->chanid, slave, sc, sn, dh); + if (sc != 0x55 || sn != 0xaa || dh != newdh) + continue; + + // Prepare new drive. + dummy.slave = slave; + + // reset the channel + if (!didreset) { + ata_reset(&dummy); + didreset = 1; + } + + // check for ATAPI + u16 buffer[256]; + struct atadrive_s *adrive = init_drive_atapi(&dummy, buffer); + if (!adrive) { + // Didn't find an ATAPI drive - look for ATA drive. + u8 st = inb(iobase1+ATA_CB_STAT); + if (!st) + // Status not set - can't be a valid drive. + continue; + + // Wait for RDY. + int ret = await_rdy(iobase1); + if (ret < 0) + continue; + + // check for ATA. + adrive = init_drive_ata(&dummy, buffer); + if (!adrive) + // No ATA drive found + continue; + } + + u16 resetresult = buffer[93]; + dprintf(6, "ata_detect resetresult=%04x\n", resetresult); + if (!slave && (resetresult & 0xdf61) == 0x4041) + // resetresult looks valid and device 0 is responding to + // device 1 requests - device 1 must not be present - skip + // detection. + break; + } +} + +// Initialize an ata controller and detect its drives. +static void +init_controller(struct pci_device *pci, int irq + , u32 port1, u32 port2, u32 master) +{ + static int chanid = 0; + struct ata_channel_s *chan_gf = malloc_fseg(sizeof(*chan_gf)); + if (!chan_gf) { + warn_noalloc(); + return; + } + chan_gf->chanid = chanid++; + chan_gf->irq = irq; + chan_gf->pci_bdf = pci ? pci->bdf : -1; + chan_gf->pci_tmp = pci; + chan_gf->iobase1 = port1; + chan_gf->iobase2 = port2; + chan_gf->iomaster = master; + dprintf(1, "ATA controller %d at %x/%x/%x (irq %d dev %x)\n" + , chanid, port1, port2, master, irq, chan_gf->pci_bdf); + run_thread(ata_detect, chan_gf); +} + +#define IRQ_ATA1 14 +#define IRQ_ATA2 15 + +// Handle controllers on an ATA PCI device. +static void +init_pciata(struct pci_device *pci, u8 prog_if) +{ + pci->have_driver = 1; + u16 bdf = pci->bdf; + u8 pciirq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + int master = 0; + if (CONFIG_ATA_DMA && prog_if & 0x80) { + // Check for bus-mastering. + u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_4); + if (bar & PCI_BASE_ADDRESS_SPACE_IO) { + master = bar & PCI_BASE_ADDRESS_IO_MASK; + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + } + } + + u32 port1, port2, irq; + if (prog_if & 1) { + port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK); + port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_1) + & PCI_BASE_ADDRESS_IO_MASK); + irq = pciirq; + } else { + port1 = PORT_ATA1_CMD_BASE; + port2 = PORT_ATA1_CTRL_BASE; + irq = IRQ_ATA1; + } + init_controller(pci, irq, port1, port2, master); + + if (prog_if & 4) { + port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_2) + & PCI_BASE_ADDRESS_IO_MASK); + port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_3) + & PCI_BASE_ADDRESS_IO_MASK); + irq = pciirq; + } else { + port1 = PORT_ATA2_CMD_BASE; + port2 = PORT_ATA2_CTRL_BASE; + irq = IRQ_ATA2; + } + init_controller(pci, irq, port1, port2, master ? master + 8 : 0); +} + +static void +found_genericata(struct pci_device *pci, void *arg) +{ + init_pciata(pci, pci->prog_if); +} + +static void +found_compatibleahci(struct pci_device *pci, void *arg) +{ + if (CONFIG_AHCI) + // Already handled directly via native ahci interface. + return; + init_pciata(pci, 0x8f); +} + +static const struct pci_device_id pci_ata_tbl[] = { + PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE + , found_genericata), + PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4391, found_compatibleahci), + PCI_DEVICE_END, +}; + +// Locate and init ata controllers. +static void +ata_scan(void) +{ + if (CONFIG_QEMU && hlist_empty(&PCIDevices)) { + // No PCI devices found - probably a QEMU "-M isapc" machine. + // Try using ISA ports for ATA controllers. + init_controller(NULL, IRQ_ATA1 + , PORT_ATA1_CMD_BASE, PORT_ATA1_CTRL_BASE, 0); + init_controller(NULL, IRQ_ATA2 + , PORT_ATA2_CMD_BASE, PORT_ATA2_CTRL_BASE, 0); + return; + } + + // Scan PCI bus for ATA adapters + struct pci_device *pci; + foreachpci(pci) { + pci_init_device(pci_ata_tbl, pci, NULL); + } +} + +void +ata_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_ATA) + return; + + dprintf(3, "init hard drives\n"); + + SpinupEnd = timer_calc(IDE_TIMEOUT); + ata_scan(); + + SET_BDA(disk_control_byte, 0xc0); + + enable_hwirq(14, FUNC16(entry_76)); +} diff --git a/qemu/roms/seabios/src/hw/ata.h b/qemu/roms/seabios/src/hw/ata.h new file mode 100644 index 000000000..c73892bbe --- /dev/null +++ b/qemu/roms/seabios/src/hw/ata.h @@ -0,0 +1,158 @@ +#ifndef __ATA_H +#define __ATA_H + +#include "block.h" // struct drive_s +#include "config.h" // CONFIG_MAX_ATA_INTERFACES +#include "types.h" // u8 + +struct ata_channel_s { + u16 iobase1; + u16 iobase2; + u16 iomaster; + u8 irq; + u8 chanid; + int pci_bdf; + struct pci_device *pci_tmp; +}; + +struct atadrive_s { + struct drive_s drive; + struct ata_channel_s *chan_gf; + u8 slave; +}; + +// ata.c +char *ata_extract_model(char *model, u32 size, u16 *buffer); +int ata_extract_version(u16 *buffer); +int cdrom_read(struct disk_op_s *op); +int atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void ata_setup(void); +int process_ata_op(struct disk_op_s *op); + +#define PORT_ATA2_CMD_BASE 0x0170 +#define PORT_ATA1_CMD_BASE 0x01f0 +#define PORT_ATA2_CTRL_BASE 0x0374 +#define PORT_ATA1_CTRL_BASE 0x03f4 + +// Global defines -- ATA register and register bits. +// command block & control block regs +#define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0 +#define ATA_CB_ERR 1 // error in pio_base_addr1+1 +#define ATA_CB_FR 1 // feature reg out pio_base_addr1+1 +#define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2 +#define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3 +#define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4 +#define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5 +#define ATA_CB_DH 6 // device head in/out pio_base_addr1+6 +#define ATA_CB_STAT 7 // primary status in pio_base_addr1+7 +#define ATA_CB_CMD 7 // command out pio_base_addr1+7 + +#define ATA_CB_ASTAT 2 // alternate status in pio_base_addr2+2 +#define ATA_CB_DC 2 // device control out pio_base_addr2+2 +#define ATA_CB_DA 3 // device address in pio_base_addr2+3 + +#define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC +#define ATA_CB_ER_BBK 0x80 // ATA bad block +#define ATA_CB_ER_UNC 0x40 // ATA uncorrected error +#define ATA_CB_ER_MC 0x20 // ATA media change +#define ATA_CB_ER_IDNF 0x10 // ATA id not found +#define ATA_CB_ER_MCR 0x08 // ATA media change request +#define ATA_CB_ER_ABRT 0x04 // ATA command aborted +#define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found +#define ATA_CB_ER_NDAM 0x01 // ATA address mark not found + +#define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask) +#define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request +#define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort +#define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media +#define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication + +// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC) +#define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask) +#define ATA_CB_SC_P_REL 0x04 // ATAPI release +#define ATA_CB_SC_P_IO 0x02 // ATAPI I/O +#define ATA_CB_SC_P_CD 0x01 // ATAPI C/D + +// bits 7-4 of the device/head (CB_DH) reg +#define ATA_CB_DH_DEV0 0xa0 // select device 0 +#define ATA_CB_DH_DEV1 0xb0 // select device 1 +#define ATA_CB_DH_LBA 0x40 // use LBA + +// status reg (CB_STAT and CB_ASTAT) bits +#define ATA_CB_STAT_BSY 0x80 // busy +#define ATA_CB_STAT_RDY 0x40 // ready +#define ATA_CB_STAT_DF 0x20 // device fault +#define ATA_CB_STAT_WFT 0x20 // write fault (old name) +#define ATA_CB_STAT_SKC 0x10 // seek complete +#define ATA_CB_STAT_SERV 0x10 // service +#define ATA_CB_STAT_DRQ 0x08 // data request +#define ATA_CB_STAT_CORR 0x04 // corrected +#define ATA_CB_STAT_IDX 0x02 // index +#define ATA_CB_STAT_ERR 0x01 // error (ATA) +#define ATA_CB_STAT_CHK 0x01 // check (ATAPI) + +// device control reg (CB_DC) bits +#define ATA_CB_DC_HD15 0x08 // bit should always be set to one +#define ATA_CB_DC_SRST 0x04 // soft reset +#define ATA_CB_DC_NIEN 0x02 // disable interrupts + +// Most mandtory and optional ATA commands (from ATA-3), +#define ATA_CMD_NOP 0x00 +#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define ATA_CMD_DEVICE_RESET 0x08 +#define ATA_CMD_RECALIBRATE 0x10 +#define ATA_CMD_READ_SECTORS 0x20 +#define ATA_CMD_READ_SECTORS_EXT 0x24 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_READ_DMA_QUEUED_EXT 0x26 +#define ATA_CMD_READ_NATIVE_MAX_ADDRESS_EXT 0x27 +#define ATA_CMD_READ_MULTIPLE_EXT 0x29 +#define ATA_CMD_READ_LOG_EXT 0x2F +#define ATA_CMD_WRITE_SECTORS 0x30 +#define ATA_CMD_WRITE_SECTORS_EXT 0x34 +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_WRITE_DMA_QUEUED_EXT 0x36 +#define ATA_CMD_SET_MAX_ADDRESS_EXT 0x37 +#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define ATA_CMD_WRITE_MULTIPLE_EXT 0x39 +#define ATA_CMD_WRITE_VERIFY 0x3C +#define ATA_CMD_WRITE_LOG_EXT 0x3F +#define ATA_CMD_READ_VERIFY_SECTORS 0x40 +#define ATA_CMD_READ_VERIFY_SECTORS_EXT 0x42 +#define ATA_CMD_FORMAT_TRACK 0x50 +#define ATA_CMD_SEEK 0x70 +#define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define ATA_CMD_STANDBY_IMMEDIATE2 0x94 +#define ATA_CMD_IDLE_IMMEDIATE2 0x95 +#define ATA_CMD_STANDBY2 0x96 +#define ATA_CMD_IDLE2 0x97 +#define ATA_CMD_CHECK_POWER_MODE2 0x98 +#define ATA_CMD_SLEEP2 0x99 +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define ATA_CMD_CFA_ERASE_SECTORS 0xC0 +#define ATA_CMD_READ_MULTIPLE 0xC4 +#define ATA_CMD_WRITE_MULTIPLE 0xC5 +#define ATA_CMD_SET_MULTIPLE_MODE 0xC6 +#define ATA_CMD_READ_DMA_QUEUED 0xC7 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_QUEUED 0xCC +#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define ATA_CMD_STANDBY_IMMEDIATE 0xE0 +#define ATA_CMD_IDLE_IMMEDIATE 0xE1 +#define ATA_CMD_STANDBY 0xE2 +#define ATA_CMD_IDLE 0xE3 +#define ATA_CMD_READ_BUFFER 0xE4 +#define ATA_CMD_CHECK_POWER_MODE 0xE5 +#define ATA_CMD_SLEEP 0xE6 +#define ATA_CMD_FLUSH_CACHE 0xE7 +#define ATA_CMD_WRITE_BUFFER 0xE8 +#define ATA_CMD_IDENTIFY_DEVICE 0xEC +#define ATA_CMD_SET_FEATURES 0xEF +#define ATA_CMD_READ_NATIVE_MAX_ADDRESS 0xF8 +#define ATA_CMD_SET_MAX 0xF9 + +#endif // ata.h diff --git a/qemu/roms/seabios/src/hw/blockcmd.c b/qemu/roms/seabios/src/hw/blockcmd.c new file mode 100644 index 000000000..78c0e65f4 --- /dev/null +++ b/qemu/roms/seabios/src/hw/blockcmd.c @@ -0,0 +1,319 @@ +// Support for several common scsi like command data block requests +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ahci.h" // atapi_cmd_data +#include "ata.h" // atapi_cmd_data +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct disk_op_s +#include "blockcmd.h" // struct cdb_request_sense +#include "byteorder.h" // be32_to_cpu +#include "esp-scsi.h" // esp_scsi_cmd_data +#include "lsi-scsi.h" // lsi_scsi_cmd_data +#include "megasas.h" // megasas_cmd_data +#include "pvscsi.h" // pvscsi_cmd_data +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_EPARAM +#include "string.h" // memset +#include "usb-msc.h" // usb_cmd_data +#include "usb-uas.h" // usb_cmd_data +#include "util.h" // timer_calc +#include "virtio-scsi.h" // virtio_scsi_cmd_data + +// Route command to low-level handler. +static int +cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + u8 type = GET_GLOBALFLAT(op->drive_gf->type); + switch (type) { + case DTYPE_ATA_ATAPI: + return atapi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_USB: + return usb_cmd_data(op, cdbcmd, blocksize); + case DTYPE_UAS: + return uas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_VIRTIO_SCSI: + return virtio_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_LSI_SCSI: + return lsi_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_ESP_SCSI: + return esp_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_MEGASAS: + return megasas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_USB_32: + if (!MODESEGMENT) + return usb_cmd_data(op, cdbcmd, blocksize); + case DTYPE_UAS_32: + if (!MODESEGMENT) + return uas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_PVSCSI: + if (!MODESEGMENT) + return pvscsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_AHCI_ATAPI: + if (!MODESEGMENT) + return ahci_cmd_data(op, cdbcmd, blocksize); + default: + return DISK_RET_EPARAM; + } +} + +// Determine if the command is a request to pull data from the device +int +cdb_is_read(u8 *cdbcmd, u16 blocksize) +{ + return blocksize && cdbcmd[0] != CDB_CMD_WRITE_10; +} + + +/**************************************************************** + * Low level command requests + ****************************************************************/ + +static int +cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_INQUIRY; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Request SENSE +static int +cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_REQUEST_SENSE; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Test unit ready +static int +cdb_test_unit_ready(struct disk_op_s *op) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_TEST_UNIT_READY; + op->count = 0; + op->buf_fl = NULL; + return cdb_cmd_data(op, &cmd, 0); +} + +// Request capacity +static int +cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data) +{ + struct cdb_read_capacity cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_CAPACITY; + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Mode sense, geometry page. +static int +cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data) +{ + struct cdb_mode_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_MODE_SENSE; + cmd.flags = 8; /* DBD */ + cmd.page = MODE_PAGE_HD_GEOMETRY; + cmd.count = cpu_to_be16(sizeof(*data)); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Read sectors. +static int +cdb_read(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} + +// Write sectors. +static int +cdb_write(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_WRITE_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} + + +/**************************************************************** + * Main SCSI commands + ****************************************************************/ + +int VISIBLE32FLAT +scsi_process_op(struct disk_op_s *op) +{ + switch (op->command) { + case CMD_READ: + return cdb_read(op); + case CMD_WRITE: + return cdb_write(op); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + +int +scsi_is_ready(struct disk_op_s *op) +{ + dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_gf); + + /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is + * reported by the device. If the device reports "IN PROGRESS", + * 30 seconds is added. */ + int in_progress = 0; + u32 end = timer_calc(5000); + for (;;) { + if (timer_check(end)) { + dprintf(1, "test unit ready failed\n"); + return -1; + } + + int ret = cdb_test_unit_ready(op); + if (!ret) + // Success + break; + + struct cdbres_request_sense sense; + ret = cdb_get_sense(op, &sense); + if (ret) + // Error - retry. + continue; + + // Sense succeeded. + if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */ + dprintf(1, "Device reports MEDIUM NOT PRESENT\n"); + return -1; + } + + if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) { + /* IN PROGRESS OF BECOMING READY */ + printf("Waiting for device to detect medium... "); + /* Allow 30 seconds more */ + end = timer_calc(30000); + in_progress = 1; + } + } + return 0; +} + +// Validate drive, find block size / sector count, and register drive. +int +scsi_drive_setup(struct drive_s *drive, const char *s, int prio) +{ + struct disk_op_s dop; + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = drive; + struct cdbres_inquiry data; + int ret = cdb_get_inquiry(&dop, &data); + if (ret) + return ret; + char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1]; + char rev[sizeof(data.rev)+1]; + strtcpy(vendor, data.vendor, sizeof(vendor)); + nullTrailingSpace(vendor); + strtcpy(product, data.product, sizeof(product)); + nullTrailingSpace(product); + strtcpy(rev, data.rev, sizeof(rev)); + nullTrailingSpace(rev); + int pdt = data.pdt & 0x1f; + int removable = !!(data.removable & 0x80); + dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n" + , s, vendor, product, rev, pdt, removable); + drive->removable = removable; + + if (pdt == SCSI_TYPE_CDROM) { + drive->blksize = CDROM_SECTOR_SIZE; + drive->sectors = (u64)-1; + + char *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]" + , s, vendor, product, rev); + boot_add_cd(drive, desc, prio); + return 0; + } + + ret = scsi_is_ready(&dop); + if (ret) { + dprintf(1, "scsi_is_ready returned %d\n", ret); + return ret; + } + + struct cdbres_read_capacity capdata; + ret = cdb_read_capacity(&dop, &capdata); + if (ret) + return ret; + + // READ CAPACITY returns the address of the last block. + // We do not bother with READ CAPACITY(16) because BIOS does not support + // 64-bit LBA anyway. + drive->blksize = be32_to_cpu(capdata.blksize); + if (drive->blksize != DISK_SECTOR_SIZE) { + dprintf(1, "%s: unsupported block size %d\n", s, drive->blksize); + return -1; + } + drive->sectors = (u64)be32_to_cpu(capdata.sectors) + 1; + dprintf(1, "%s blksize=%d sectors=%d\n" + , s, drive->blksize, (unsigned)drive->sectors); + + // We do not recover from USB stalls, so try to be safe and avoid + // sending the command if the (obsolete, but still provided by QEMU) + // fixed disk geometry page may not be supported. + // + // We could also send the command only to small disks (e.g. <504MiB) + // but some old USB keys only support a very small subset of SCSI which + // does not even include the MODE SENSE command! + // + if (CONFIG_QEMU_HARDWARE && memcmp(vendor, "QEMU", 5) == 0) { + struct cdbres_mode_sense_geom geomdata; + ret = cdb_mode_sense_geom(&dop, &geomdata); + if (ret == 0) { + u32 cylinders; + cylinders = geomdata.cyl[0] << 16; + cylinders |= geomdata.cyl[1] << 8; + cylinders |= geomdata.cyl[2]; + if (cylinders && geomdata.heads && + drive->sectors <= 0xFFFFFFFFULL && + ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) { + drive->pchs.cylinder = cylinders; + drive->pchs.head = geomdata.heads; + drive->pchs.sector = (u32)drive->sectors / (geomdata.heads * cylinders); + } + } + } + + char *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s" + , s, vendor, product, rev); + boot_add_hd(drive, desc, prio); + return 0; +} diff --git a/qemu/roms/seabios/src/hw/blockcmd.h b/qemu/roms/seabios/src/hw/blockcmd.h new file mode 100644 index 000000000..df12a6d42 --- /dev/null +++ b/qemu/roms/seabios/src/hw/blockcmd.h @@ -0,0 +1,110 @@ +// Definitions for SCSI style command data blocks. +#ifndef __BLOCKCMD_H +#define __BLOCKCMD_H + +#include "types.h" // u8 + +#define CDB_CMD_READ_10 0x28 +#define CDB_CMD_VERIFY_10 0x2f +#define CDB_CMD_WRITE_10 0x2a + +struct cdb_rwdata_10 { + u8 command; + u8 flags; + u32 lba; + u8 resreved_06; + u16 count; + u8 reserved_09; + u8 pad[6]; +} PACKED; + +#define CDB_CMD_READ_CAPACITY 0x25 + +struct cdb_read_capacity { + u8 command; + u8 flags; + u8 resreved_02[8]; + u8 pad[6]; +} PACKED; + +struct cdbres_read_capacity { + u32 sectors; + u32 blksize; +} PACKED; + +#define CDB_CMD_TEST_UNIT_READY 0x00 +#define CDB_CMD_INQUIRY 0x12 +#define CDB_CMD_REQUEST_SENSE 0x03 + +struct cdb_request_sense { + u8 command; + u8 flags; + u16 reserved_02; + u8 length; + u8 reserved_05; + u8 pad[10]; +} PACKED; + +struct cdbres_request_sense { + u8 errcode; + u8 segment; + u8 flags; + u32 info; + u8 additional; + u32 specific; + u8 asc; + u8 ascq; + u32 reserved_0e; +} PACKED; + +#define SCSI_TYPE_DISK 0x00 +#define SCSI_TYPE_CDROM 0x05 + +struct cdbres_inquiry { + u8 pdt; + u8 removable; + u8 reserved_02[2]; + u8 additional; + u8 reserved_05[3]; + char vendor[8]; + char product[16]; + char rev[4]; +} PACKED; + +#define CDB_CMD_MODE_SENSE 0x5A +#define MODE_PAGE_HD_GEOMETRY 0x04 + +struct cdb_mode_sense { + u8 command; + u8 flags; + u8 page; + u32 reserved_03; + u16 count; + u8 reserved_09; + u8 pad[6]; +} PACKED; + +struct cdbres_mode_sense_geom { + u8 unused_00[3]; + u8 read_only; + u32 unused_04; + u8 page; + u8 length; + u8 cyl[3]; + u8 heads; + u8 precomp[3]; + u8 reduced[3]; + u16 step_rate; + u8 landing[3]; + u16 rpm; +} PACKED; + +// blockcmd.c +int cdb_is_read(u8 *cdbcmd, u16 blocksize); +struct disk_op_s; +int scsi_process_op(struct disk_op_s *op); +int scsi_is_ready(struct disk_op_s *op); +struct drive_s; +int scsi_drive_setup(struct drive_s *drive, const char *s, int prio); + +#endif // blockcmd.h diff --git a/qemu/roms/seabios/src/hw/dma.c b/qemu/roms/seabios/src/hw/dma.c new file mode 100644 index 000000000..20c9fbb76 --- /dev/null +++ b/qemu/roms/seabios/src/hw/dma.c @@ -0,0 +1,67 @@ +// Code to support legacy Intel 8237 DMA chip. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "util.h" // dma_setup +#include "x86.h" // outb + +#define PORT_DMA_ADDR_2 0x0004 +#define PORT_DMA_CNT_2 0x0005 +#define PORT_DMA1_MASK_REG 0x000a +#define PORT_DMA1_MODE_REG 0x000b +#define PORT_DMA1_CLEAR_FF_REG 0x000c +#define PORT_DMA1_MASTER_CLEAR 0x000d +#define PORT_DMA_PAGE_2 0x0081 +#define PORT_DMA2_MASK_REG 0x00d4 +#define PORT_DMA2_MODE_REG 0x00d6 +#define PORT_DMA2_MASTER_CLEAR 0x00da + +// Setup the DMA controller for a floppy transfer. +int +dma_floppy(u32 addr, int count, int isWrite) +{ + // check for 64K boundary overrun + u16 end = count - 1; + u32 last_addr = addr + end; + if ((addr >> 16) != (last_addr >> 16)) + return -1; + + u8 mode_register = 0x46; // single mode, increment, autoinit disable, + if (isWrite) + mode_register = 0x4a; + + outb(0x06, PORT_DMA1_MASK_REG); + outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop + outb(addr, PORT_DMA_ADDR_2); + outb(addr>>8, PORT_DMA_ADDR_2); + outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop + outb(end, PORT_DMA_CNT_2); + outb(end>>8, PORT_DMA_CNT_2); + + // port 0b: DMA-1 Mode Register + // transfer type=write, channel 2 + outb(mode_register, PORT_DMA1_MODE_REG); + + // port 81: DMA-1 Page Register, channel 2 + outb(addr>>16, PORT_DMA_PAGE_2); + + outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2 + + return 0; +} + +// Reset DMA controller +void +dma_setup(void) +{ + // first reset the DMA controllers + outb(0, PORT_DMA1_MASTER_CLEAR); + outb(0, PORT_DMA2_MASTER_CLEAR); + + // then initialize the DMA controllers + outb(0xc0, PORT_DMA2_MODE_REG); + outb(0x00, PORT_DMA2_MASK_REG); +} diff --git a/qemu/roms/seabios/src/hw/esp-scsi.c b/qemu/roms/seabios/src/hw/esp-scsi.c new file mode 100644 index 000000000..33cc44986 --- /dev/null +++ b/qemu/roms/seabios/src/hw/esp-scsi.c @@ -0,0 +1,238 @@ +// AMD PCscsi boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Paolo Bonzini <pbonzini@redhat.com> +// +// based on lsi-scsi.c which is written by: +// Gerd Hoffman <kraxel@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep + +#define ESP_TCLO 0x00 +#define ESP_TCMID 0x04 +#define ESP_FIFO 0x08 +#define ESP_CMD 0x0c +#define ESP_WBUSID 0x10 +#define ESP_TCHI 0x38 + +#define ESP_RSTAT 0x10 +#define ESP_RINTR 0x14 +#define ESP_RFLAGS 0x1c + +#define ESP_DMA_CMD 0x40 +#define ESP_DMA_STC 0x44 +#define ESP_DMA_SPA 0x48 +#define ESP_DMA_WBC 0x4c +#define ESP_DMA_WAC 0x50 +#define ESP_DMA_STAT 0x54 +#define ESP_DMA_SMDLA 0x58 +#define ESP_DMA_WMAC 0x58c + +#define ESP_CMD_DMA 0x80 +#define ESP_CMD_RESET 0x02 +#define ESP_CMD_TI 0x10 +#define ESP_CMD_ICCS 0x11 +#define ESP_CMD_SELATN 0x42 + +#define ESP_STAT_DI 0x01 +#define ESP_STAT_CD 0x02 +#define ESP_STAT_MSG 0x04 +#define ESP_STAT_TC 0x10 + +#define ESP_INTR_DC 0x20 + +struct esp_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; +}; + +static void +esp_scsi_dma(u32 iobase, u32 buf, u32 len, int read) +{ + outb(len & 0xff, iobase + ESP_TCLO); + outb((len >> 8) & 0xff, iobase + ESP_TCMID); + outb((len >> 16) & 0xff, iobase + ESP_TCHI); + outl(buf, iobase + ESP_DMA_SPA); + outl(len, iobase + ESP_DMA_STC); + outb(read ? 0x83 : 0x03, iobase + ESP_DMA_CMD); +} + +static int +esp_scsi_cmd(struct esp_lun_s *llun_gf, struct disk_op_s *op, + u8 *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); + int i, state; + u8 status; + + outb(target, iobase + ESP_WBUSID); + + /* + * We need to pass the LUN at the beginning of the command, and the FIFO + * is only 16 bytes, so we cannot support 16-byte CDBs. The alternative + * would be to use DMA for the 17-byte command too, which is quite + * overkill. + */ + outb(lun, iobase + ESP_FIFO); + cdbcmd[1] &= 0x1f; + cdbcmd[1] |= lun << 5; + for (i = 0; i < 12; i++) + outb(cdbcmd[i], iobase + ESP_FIFO); + outb(ESP_CMD_SELATN, iobase + ESP_CMD); + + for (state = 0;;) { + u8 stat = inb(iobase + ESP_RSTAT); + + /* Detect disconnected device. */ + if (state == 0 && (inb(iobase + ESP_RINTR) & ESP_INTR_DC)) { + return DISK_RET_ENOTREADY; + } + + /* HBA reads command, clears CD, sets TC -> do DMA if needed. */ + if (state == 0 && (stat & ESP_STAT_TC)) { + state++; + if (op->count && blocksize) { + /* Data phase. */ + u32 count = (u32)op->count * blocksize; + esp_scsi_dma(iobase, (u32)op->buf_fl, count, + cdb_is_read(cdbcmd, blocksize)); + outb(ESP_CMD_TI | ESP_CMD_DMA, iobase + ESP_CMD); + continue; + } + } + + /* At end of DMA TC is set again -> complete command. */ + if (state == 1 && (stat & ESP_STAT_TC)) { + state++; + outb(ESP_CMD_ICCS, iobase + ESP_CMD); + continue; + } + + /* Finally read data from the message in phase. */ + if (state == 2 && (stat & ESP_STAT_MSG)) { + state++; + status = inb(iobase + ESP_FIFO); + inb(iobase + ESP_FIFO); + break; + } + usleep(5); + } + + if (status == 0) { + return DISK_RET_SUCCESS; + } + + return DISK_RET_EBADTRACK; +} + +int +esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_ESP_SCSI) + return DISK_RET_EBADTRACK; + + struct esp_lun_s *llun_gf = + container_of(op->drive_gf, struct esp_lun_s, drive); + + return esp_scsi_cmd(llun_gf, op, cdbcmd, + GET_GLOBALFLAT(llun_gf->target), + GET_GLOBALFLAT(llun_gf->lun), + blocksize); +} + +static int +esp_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct esp_lun_s *llun = malloc_fseg(sizeof(*llun)); + if (!llun) { + warn_noalloc(); + return -1; + } + memset(llun, 0, sizeof(*llun)); + llun->drive.type = DTYPE_ESP_SCSI; + llun->drive.cntl_id = pci->bdf; + llun->pci = pci; + llun->target = target; + llun->lun = lun; + llun->iobase = iobase; + + char *name = znprintf(16, "esp %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&llun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(llun); + return -1; +} + +static void +esp_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target) +{ + esp_scsi_add_lun(pci, iobase, target, 0); +} + +static void +init_esp_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + dprintf(1, "found esp at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + // reset + outb(ESP_CMD_RESET, iobase + ESP_CMD); + + int i; + for (i = 0; i <= 7; i++) + esp_scsi_scan_target(pci, iobase, i); + + return; +} + +void +esp_scsi_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_ESP_SCSI || !runningOnQEMU()) + return; + + dprintf(3, "init esp\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_AMD + || pci->device != PCI_DEVICE_ID_AMD_SCSI) + continue; + init_esp_scsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/esp-scsi.h b/qemu/roms/seabios/src/hw/esp-scsi.h new file mode 100644 index 000000000..dc555f395 --- /dev/null +++ b/qemu/roms/seabios/src/hw/esp-scsi.h @@ -0,0 +1,8 @@ +#ifndef __ESP_SCSI_H +#define __ESP_SCSI_H + +struct disk_op_s; +int esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void esp_scsi_setup(void); + +#endif /* __ESP_SCSI_H */ diff --git a/qemu/roms/seabios/src/hw/floppy.c b/qemu/roms/seabios/src/hw/floppy.c new file mode 100644 index 000000000..d60362a34 --- /dev/null +++ b/qemu/roms/seabios/src/hw/floppy.c @@ -0,0 +1,673 @@ +// 16bit code to access floppy drives. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_FLOPPY +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "pci.h" // pci_to_bdf +#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA +#include "pic.h" // pic_eoi1 +#include "romfile.h" // romfile_loadint +#include "rtc.h" // rtc_read +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc + +#define PORT_FD_BASE 0x03f0 +#define PORT_FD_DOR 0x03f2 +#define PORT_FD_STATUS 0x03f4 +#define PORT_FD_DATA 0x03f5 +#define PORT_FD_DIR 0x03f7 + +#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors +#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02 +#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds +#define FLOPPY_FILLBYTE 0xf6 +#define FLOPPY_GAPLEN 0x1B +#define FLOPPY_FORMAT_GAPLEN 0x6c +#define FLOPPY_PIO_TIMEOUT 1000 + +// New diskette parameter table adding 3 parameters from IBM +// Since no provisions are made for multiple drive types, most +// values in this table are ignored. I set parameters for 1.44M +// floppy here +struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = { + .dbt = { + .specify1 = 0xAF, // step rate 12ms, head unload 240ms + .specify2 = 0x02, // head load time 4ms, DMA used + .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds + .bps_code = FLOPPY_SIZE_CODE, + .sectors = 18, + .interblock_len = FLOPPY_GAPLEN, + .data_len = FLOPPY_DATALEN, + .gap_len = FLOPPY_FORMAT_GAPLEN, + .fill_byte = FLOPPY_FILLBYTE, + .settle_time = 0x0F, // 15ms + .startup_time = 0x08, // 1 second + }, + .max_track = 79, // maximum track + .data_rate = 0, // data transfer rate + .drive_type = 4, // drive type in cmos +}; + +struct floppyinfo_s { + struct chs_s chs; + u8 floppy_size; + u8 data_rate; +}; + +#define FLOPPY_SIZE_525 0x01 +#define FLOPPY_SIZE_350 0x02 + +#define FLOPPY_RATE_500K 0x00 +#define FLOPPY_RATE_300K 0x01 +#define FLOPPY_RATE_250K 0x02 +#define FLOPPY_RATE_1M 0x03 + +struct floppyinfo_s FloppyInfo[] VARFSEG = { + // Unknown + { {0, 0, 0}, 0x00, 0x00}, + // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors + { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K}, + // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors + { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K}, + // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors + { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K}, + // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors + { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K}, + // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors + { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M}, + // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors + { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K}, + // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors + { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K}, + // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors + { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K}, +}; + +struct drive_s * +init_floppy(int floppyid, int ftype) +{ + if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) { + dprintf(1, "Bad floppy type %d\n", ftype); + return NULL; + } + + struct drive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return NULL; + } + memset(drive, 0, sizeof(*drive)); + drive->cntl_id = floppyid; + drive->type = DTYPE_FLOPPY; + drive->blksize = DISK_SECTOR_SIZE; + drive->floppy_type = ftype; + drive->sectors = (u64)-1; + + memcpy(&drive->lchs, &FloppyInfo[ftype].chs + , sizeof(FloppyInfo[ftype].chs)); + return drive; +} + +static void +addFloppy(int floppyid, int ftype) +{ + struct drive_s *drive = init_floppy(floppyid, ftype); + if (!drive) + return; + char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid); + struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */ + int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid); + boot_add_floppy(drive, desc, prio); +} + +void +floppy_setup(void) +{ + memcpy(&diskette_param_table, &diskette_param_table2 + , sizeof(diskette_param_table)); + SET_IVT(0x1E, SEGOFF(SEG_BIOS + , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR)); + + if (! CONFIG_FLOPPY) + return; + dprintf(3, "init floppy drives\n"); + + if (CONFIG_QEMU) { + u8 type = rtc_read(CMOS_FLOPPY_DRIVE_TYPE); + if (type & 0xf0) + addFloppy(0, type >> 4); + if (type & 0x0f) + addFloppy(1, type & 0x0f); + } else { + u8 type = romfile_loadint("etc/floppy0", 0); + if (type) + addFloppy(0, type); + type = romfile_loadint("etc/floppy1", 0); + if (type) + addFloppy(1, type); + } + + enable_hwirq(6, FUNC16(entry_0e)); +} + +// Find a floppy type that matches a given image size. +int +find_floppy_type(u32 size) +{ + int i; + for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) { + struct chs_s *c = &FloppyInfo[i].chs; + if (c->cylinder * c->head * c->sector * DISK_SECTOR_SIZE == size) + return i; + } + return -1; +} + + +/**************************************************************** + * Low-level floppy IO + ****************************************************************/ + +u8 FloppyDOR VARLOW; + +static inline void +floppy_dor_write(u8 val) +{ + outb(val, PORT_FD_DOR); + SET_LOW(FloppyDOR, val); +} + +static void +floppy_disable_controller(void) +{ + dprintf(2, "Floppy_disable_controller\n"); + floppy_dor_write(0x00); +} + +static int +floppy_wait_irq(void) +{ + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ); + for (;;) { + if (!GET_BDA(floppy_motor_counter)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + frs = GET_BDA(floppy_recalibration_status); + if (frs & FRS_IRQ) + break; + // Could use yield_toirq() here, but that causes issues on + // bochs, so use yield() instead. + yield(); + } + + SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ); + return DISK_RET_SUCCESS; +} + +// Floppy commands +#define FCF_WAITIRQ 0x10000 +#define FC_CHECKIRQ (0x08 | (0<<8) | (2<<12)) +#define FC_SEEK (0x0f | (2<<8) | (0<<12) | FCF_WAITIRQ) +#define FC_RECALIBRATE (0x07 | (1<<8) | (0<<12) | FCF_WAITIRQ) +#define FC_READID (0x4a | (1<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_READ (0xe6 | (8<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_WRITE (0xc5 | (8<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_FORMAT (0x4d | (5<<8) | (7<<12) | FCF_WAITIRQ) + +// Send the specified command and it's parameters to the floppy controller. +static int +floppy_pio(int command, u8 *param) +{ + dprintf(9, "Floppy pio command %x\n", command); + // Send command and parameters to controller. + u32 end = timer_calc(FLOPPY_PIO_TIMEOUT); + int send = (command >> 8) & 0xf; + int i = 0; + for (;;) { + u8 sts = inb(PORT_FD_STATUS); + if (!(sts & 0x80)) { + if (timer_check(end)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + yield(); + continue; + } + if (sts & 0x40) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + if (i == 0) + outb(command & 0xff, PORT_FD_DATA); + else + outb(param[i-1], PORT_FD_DATA); + if (i++ >= send) + break; + } + + // Wait for command to complete. + if (command & FCF_WAITIRQ) { + int ret = floppy_wait_irq(); + if (ret) + return ret; + } + + // Read response from controller. + end = timer_calc(FLOPPY_PIO_TIMEOUT); + int receive = (command >> 12) & 0xf; + i = 0; + for (;;) { + u8 sts = inb(PORT_FD_STATUS); + if (!(sts & 0x80)) { + if (timer_check(end)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + yield(); + continue; + } + if (i >= receive) { + if (sts & 0x40) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + break; + } + if (!(sts & 0x40)) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + param[i++] = inb(PORT_FD_DATA); + } + + return DISK_RET_SUCCESS; +} + +static int +floppy_enable_controller(void) +{ + dprintf(2, "Floppy_enable_controller\n"); + SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS); + floppy_dor_write(0x00); + floppy_dor_write(0x0c); + int ret = floppy_wait_irq(); + if (ret) + return ret; + + u8 param[2]; + return floppy_pio(FC_CHECKIRQ, param); +} + +// Activate a drive and send a command to it. +static int +floppy_drive_pio(u8 floppyid, int command, u8 *param) +{ + // Enable controller if it isn't running. + if (!(GET_LOW(FloppyDOR) & 0x04)) { + int ret = floppy_enable_controller(); + if (ret) + return ret; + } + + // reset the disk motor timeout value of INT 08 + SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS); + + // Turn on motor of selected drive, DMA & int enabled, normal operation + floppy_dor_write((floppyid ? 0x20 : 0x10) | 0x0c | floppyid); + + // Send command. + int ret = floppy_pio(command, param); + if (ret) + return ret; + + // Check IRQ command is needed after irq commands with no results + if ((command & FCF_WAITIRQ) && ((command >> 12) & 0xf) == 0) + return floppy_pio(FC_CHECKIRQ, param); + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy media sense and seeking + ****************************************************************/ + +static int +floppy_drive_recal(u8 floppyid) +{ + dprintf(2, "Floppy_drive_recal %d\n", floppyid); + // send Recalibrate command to controller + u8 param[2]; + param[0] = floppyid; + int ret = floppy_drive_pio(floppyid, FC_RECALIBRATE, param); + if (ret) + return ret; + + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs | (1<<floppyid)); + SET_BDA(floppy_track[floppyid], 0); + return DISK_RET_SUCCESS; +} + +static int +floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head) +{ + // Set data rate. + outb(data_rate, PORT_FD_DIR); + + // send Read Sector Id command + u8 param[7]; + param[0] = (head << 2) | floppyid; // HD DR1 DR2 + int ret = floppy_drive_pio(floppyid, FC_READID, param); + if (ret) + return ret; + if (param[0] & 0xc0) + return -1; + return 0; +} + +static int +floppy_media_sense(struct drive_s *drive_gf) +{ + u8 ftype = GET_GLOBALFLAT(drive_gf->floppy_type), stype = ftype; + u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id); + + u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate); + int ret = floppy_drive_readid(floppyid, data_rate, 0); + if (ret) { + // Attempt media sense. + for (stype=1; ; stype++) { + if (stype >= ARRAY_SIZE(FloppyInfo)) + return DISK_RET_EMEDIA; + if (stype==ftype + || (GET_GLOBAL(FloppyInfo[stype].floppy_size) + != GET_GLOBAL(FloppyInfo[ftype].floppy_size)) + || (GET_GLOBAL(FloppyInfo[stype].chs.head) + > GET_GLOBAL(FloppyInfo[ftype].chs.head)) + || (GET_GLOBAL(FloppyInfo[stype].chs.cylinder) + > GET_GLOBAL(FloppyInfo[ftype].chs.cylinder)) + || (GET_GLOBAL(FloppyInfo[stype].chs.sector) + > GET_GLOBAL(FloppyInfo[ftype].chs.sector))) + continue; + data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate); + ret = floppy_drive_readid(floppyid, data_rate, 0); + if (!ret) + break; + } + } + dprintf(2, "Floppy_media_sense on drive %d found rate %d\n" + , floppyid, data_rate); + + u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6; + SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6)); + u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07)); + u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media; + if (GET_GLOBAL(FloppyInfo[stype].chs.cylinder) + < GET_GLOBAL(FloppyInfo[ftype].chs.cylinder)) + fms |= FMS_DOUBLE_STEPPING; + SET_BDA(floppy_media_state[floppyid], fms); + + return DISK_RET_SUCCESS; +} + +// Prepare a floppy for a data transfer. +static int +floppy_prep(struct drive_s *drive_gf, u8 cylinder) +{ + u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id); + if (!(GET_BDA(floppy_recalibration_status) & (1<<floppyid)) || + !(GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED)) { + // Recalibrate drive. + int ret = floppy_drive_recal(floppyid); + if (ret) + return ret; + + // Sense media. + ret = floppy_media_sense(drive_gf); + if (ret) + return ret; + } + + // Seek to cylinder if needed. + u8 lastcyl = GET_BDA(floppy_track[floppyid]); + if (cylinder != lastcyl) { + u8 param[2]; + param[0] = floppyid; + param[1] = cylinder; + int ret = floppy_drive_pio(floppyid, FC_SEEK, param); + if (ret) + return ret; + SET_BDA(floppy_track[floppyid], cylinder); + } + + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy DMA transfer + ****************************************************************/ + +// Perform a floppy transfer command (setup DMA and issue PIO). +static int +floppy_dma_cmd(struct disk_op_s *op, int count, int command, u8 *param) +{ + // Setup DMA controller + int isWrite = command != FC_READ; + int ret = dma_floppy((u32)op->buf_fl, count, isWrite); + if (ret) + return DISK_RET_EBOUNDARY; + + // Invoke floppy controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + ret = floppy_drive_pio(floppyid, command, param); + if (ret) + return ret; + + // Populate floppy_return_status in BDA + int i; + for (i=0; i<7; i++) + SET_BDA(floppy_return_status[i], param[i]); + + if (param[0] & 0xc0) { + if (param[1] & 0x02) + return DISK_RET_EWRITEPROTECT; + dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n" + , param[0], param[1], param[2], param[3] + , param[4], param[5], param[6]); + return DISK_RET_ECONTROLLER; + } + + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy handlers + ****************************************************************/ + +static struct chs_s +lba2chs(struct disk_op_s *op) +{ + struct chs_s res = { }; + + u32 tmp = op->lba; + u16 nls = GET_GLOBALFLAT(op->drive_gf->lchs.sector); + res.sector = (tmp % nls) + 1; + + tmp /= nls; + u16 nlh = GET_GLOBALFLAT(op->drive_gf->lchs.head); + res.head = tmp % nlh; + + tmp /= nlh; + res.cylinder = tmp; + + return res; +} + +// diskette controller reset +static int +floppy_reset(struct disk_op_s *op) +{ + SET_BDA(floppy_recalibration_status, 0); + SET_BDA(floppy_media_state[0], 0); + SET_BDA(floppy_media_state[1], 0); + SET_BDA(floppy_track[0], 0); + SET_BDA(floppy_track[1], 0); + SET_BDA(floppy_last_data_rate, 0); + floppy_disable_controller(); + return floppy_enable_controller(); +} + +// Read Diskette Sectors +static int +floppy_read(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // send read-normal-data command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[8]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = chs.cylinder; + param[2] = chs.head; + param[3] = chs.sector; + param[4] = FLOPPY_SIZE_CODE; + param[5] = chs.sector + op->count - 1; // last sector to read on track + param[6] = FLOPPY_GAPLEN; + param[7] = FLOPPY_DATALEN; + return floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_READ, param); +} + +// Write Diskette Sectors +static int +floppy_write(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // send write-normal-data command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[8]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = chs.cylinder; + param[2] = chs.head; + param[3] = chs.sector; + param[4] = FLOPPY_SIZE_CODE; + param[5] = chs.sector + op->count - 1; // last sector to write on track + param[6] = FLOPPY_GAPLEN; + param[7] = FLOPPY_DATALEN; + return floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_WRITE, param); +} + +// Verify Diskette Sectors +static int +floppy_verify(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // This command isn't implemented - just return success. + return DISK_RET_SUCCESS; +} + +// format diskette track +static int +floppy_format(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // send format-track command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[7]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = FLOPPY_SIZE_CODE; + param[2] = op->count; // number of sectors per track + param[3] = FLOPPY_FORMAT_GAPLEN; + param[4] = FLOPPY_FILLBYTE; + return floppy_dma_cmd(op, op->count * 4, FC_FORMAT, param); +} + +int +process_floppy_op(struct disk_op_s *op) +{ + if (!CONFIG_FLOPPY) + return 0; + + switch (op->command) { + case CMD_RESET: + return floppy_reset(op); + case CMD_READ: + return floppy_read(op); + case CMD_WRITE: + return floppy_write(op); + case CMD_VERIFY: + return floppy_verify(op); + case CMD_FORMAT: + return floppy_format(op); + default: + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * HW irqs + ****************************************************************/ + +// INT 0Eh Diskette Hardware ISR Entry Point +void VISIBLE16 +handle_0e(void) +{ + if (! CONFIG_FLOPPY) + return; + debug_isr(DEBUG_ISR_0e); + + // diskette interrupt has occurred + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs | FRS_IRQ); + + pic_eoi1(); +} + +// Called from int08 handler. +void +floppy_tick(void) +{ + if (! CONFIG_FLOPPY) + return; + + // time to turn off drive(s)? + u8 fcount = GET_BDA(floppy_motor_counter); + if (fcount) { + fcount--; + SET_BDA(floppy_motor_counter, fcount); + if (fcount == 0) + // turn motor(s) off + floppy_dor_write(GET_LOW(FloppyDOR) & ~0xf0); + } +} diff --git a/qemu/roms/seabios/src/hw/lsi-scsi.c b/qemu/roms/seabios/src/hw/lsi-scsi.c new file mode 100644 index 000000000..b1d6bbf4b --- /dev/null +++ b/qemu/roms/seabios/src/hw/lsi-scsi.c @@ -0,0 +1,217 @@ +// (qemu-emulated) lsi53c895a boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Gerd Hoffmann <kraxel@redhat.com> +// +// based on virtio-scsi.c which is written by: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep + +#define LSI_REG_DSTAT 0x0c +#define LSI_REG_ISTAT0 0x14 +#define LSI_REG_DSP0 0x2c +#define LSI_REG_DSP1 0x2d +#define LSI_REG_DSP2 0x2e +#define LSI_REG_DSP3 0x2f +#define LSI_REG_SIST0 0x42 +#define LSI_REG_SIST1 0x43 + +#define LSI_ISTAT0_DIP 0x01 +#define LSI_ISTAT0_SIP 0x02 +#define LSI_ISTAT0_INTF 0x04 +#define LSI_ISTAT0_CON 0x08 +#define LSI_ISTAT0_SEM 0x10 +#define LSI_ISTAT0_SIGP 0x20 +#define LSI_ISTAT0_SRST 0x40 +#define LSI_ISTAT0_ABRT 0x80 + +struct lsi_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; +}; + +static int +lsi_scsi_cmd(struct lsi_lun_s *llun_gf, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); + u32 dma = ((cdb_is_read(cdbcmd, blocksize) ? 0x01000000 : 0x00000000) | + (op->count * blocksize)); + u8 msgout[] = { + 0x80 | lun, // select lun + 0x08, + }; + u8 status = 0xff; + u8 msgin_tmp[2]; + u8 msgin = 0xff; + + u32 script[] = { + /* select target, send scsi command */ + 0x40000000 | target << 16, // select target + 0x00000000, + 0x06000001, // msgout + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgout), + 0x02000010, // scsi command + (u32)MAKE_FLATPTR(GET_SEG(SS), cdbcmd), + + /* handle disconnect */ + 0x87820000, // phase == msgin ? + 0x00000018, + 0x07000002, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin_tmp), + 0x50000000, // re-select + 0x00000000, + 0x07000002, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin_tmp), + + /* dma data, get status, raise irq */ + dma, // dma data + (u32)op->buf_fl, + 0x03000001, // status + (u32)MAKE_FLATPTR(GET_SEG(SS), &status), + 0x07000001, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin), + 0x98080000, // dma irq + 0x00000000, + }; + u32 dsp = (u32)MAKE_FLATPTR(GET_SEG(SS), &script); + + outb(dsp & 0xff, iobase + LSI_REG_DSP0); + outb((dsp >> 8) & 0xff, iobase + LSI_REG_DSP1); + outb((dsp >> 16) & 0xff, iobase + LSI_REG_DSP2); + outb((dsp >> 24) & 0xff, iobase + LSI_REG_DSP3); + + for (;;) { + u8 dstat = inb(iobase + LSI_REG_DSTAT); + u8 sist0 = inb(iobase + LSI_REG_SIST0); + u8 sist1 = inb(iobase + LSI_REG_SIST1); + if (sist0 || sist1) { + goto fail; + } + if (dstat & 0x04) { + break; + } + usleep(5); + } + + if (msgin == 0 && status == 0) { + return DISK_RET_SUCCESS; + } + +fail: + return DISK_RET_EBADTRACK; +} + +int +lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_LSI_SCSI) + return DISK_RET_EBADTRACK; + + struct lsi_lun_s *llun_gf = + container_of(op->drive_gf, struct lsi_lun_s, drive); + + return lsi_scsi_cmd(llun_gf, op, cdbcmd, + GET_GLOBALFLAT(llun_gf->target), + GET_GLOBALFLAT(llun_gf->lun), + blocksize); +} + +static int +lsi_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct lsi_lun_s *llun = malloc_fseg(sizeof(*llun)); + if (!llun) { + warn_noalloc(); + return -1; + } + memset(llun, 0, sizeof(*llun)); + llun->drive.type = DTYPE_LSI_SCSI; + llun->drive.cntl_id = pci->bdf; + llun->pci = pci; + llun->target = target; + llun->lun = lun; + llun->iobase = iobase; + + char *name = znprintf(16, "lsi %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&llun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(llun); + return -1; +} + +static void +lsi_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + lsi_scsi_add_lun(pci, iobase, target, 0); +} + +static void +init_lsi_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + dprintf(1, "found lsi53c895a at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + // reset + outb(LSI_ISTAT0_SRST, iobase + LSI_REG_ISTAT0); + + int i; + for (i = 0; i < 7; i++) + lsi_scsi_scan_target(pci, iobase, i); + + return; +} + +void +lsi_scsi_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_LSI_SCSI || !runningOnQEMU()) + return; + + dprintf(3, "init lsi53c895a\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC + || pci->device != PCI_DEVICE_ID_LSI_53C895A) + continue; + init_lsi_scsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/lsi-scsi.h b/qemu/roms/seabios/src/hw/lsi-scsi.h new file mode 100644 index 000000000..9c5a9b212 --- /dev/null +++ b/qemu/roms/seabios/src/hw/lsi-scsi.h @@ -0,0 +1,8 @@ +#ifndef __LSI_SCSI_H +#define __LSI_SCSI_H + +struct disk_op_s; +int lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void lsi_scsi_setup(void); + +#endif /* __LSI_SCSI_H */ diff --git a/qemu/roms/seabios/src/hw/megasas.c b/qemu/roms/seabios/src/hw/megasas.c new file mode 100644 index 000000000..b2a65e48b --- /dev/null +++ b/qemu/roms/seabios/src/hw/megasas.c @@ -0,0 +1,404 @@ +// MegaRAID SAS boot support. +// +// Copyright (C) 2012 Hannes Reinecke, SUSE Linux Products GmbH +// +// Authors: +// Hannes Reinecke <hare@suse.de> +// +// based on virtio-scsi.c which is written by: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_XXX +#include "pci_regs.h" // PCI_VENDOR_ID +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc + +#define MFI_DB 0x0 // Doorbell +#define MFI_OMSG0 0x18 // Outbound message 0 +#define MFI_IDB 0x20 // Inbound doorbell +#define MFI_ODB 0x2c // Outbound doorbell +#define MFI_IQP 0x40 // Inbound queue port +#define MFI_OSP0 0xb0 // Outbound scratch pad0 +#define MFI_IQPL 0xc0 // Inbound queue port (low bytes) +#define MFI_IQPH 0xc4 // Inbound queue port (high bytes) + +#define MFI_STATE_MASK 0xf0000000 +#define MFI_STATE_WAIT_HANDSHAKE 0x60000000 +#define MFI_STATE_BOOT_MESSAGE_PENDING 0x90000000 +#define MFI_STATE_READY 0xb0000000 +#define MFI_STATE_OPERATIONAL 0xc0000000 +#define MFI_STATE_FAULT 0xf0000000 + +/* MFI Commands */ +typedef enum { + MFI_CMD_INIT = 0x00, + MFI_CMD_LD_READ, + MFI_CMD_LD_WRITE, + MFI_CMD_LD_SCSI_IO, + MFI_CMD_PD_SCSI_IO, + MFI_CMD_DCMD, + MFI_CMD_ABORT, + MFI_CMD_SMP, + MFI_CMD_STP +} mfi_cmd_t; + +struct megasas_cmd_frame { + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 lun; /*05h */ + u8 cdb_len; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 context_64; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 data_xfer_len; /*14h */ + + union { + struct { + u32 opcode; /*18h */ + u8 mbox[12]; /*1Ch */ + u32 sgl_addr; /*28h */ + u32 sgl_len; /*32h */ + u32 pad; /*34h */ + } dcmd; + struct { + u32 sense_buf_lo; /*18h */ + u32 sense_buf_hi; /*1Ch */ + u8 cdb[16]; /*20h */ + u32 sgl_addr; /*30h */ + u32 sgl_len; /*34h */ + } pthru; + struct { + u8 pad[22]; /*18h */ + } gen; + }; +} __attribute__ ((packed)); + +struct mfi_ld_list_s { + u32 count; + u32 reserved_0; + struct { + u8 target; + u8 lun; + u16 seq; + u8 state; + u8 reserved_1[3]; + u64 size; + } lds[64]; +} __attribute__ ((packed)); + +#define MEGASAS_POLL_TIMEOUT 60000 // 60 seconds polling timeout + +struct megasas_lun_s { + struct drive_s drive; + struct megasas_cmd_frame *frame; + u32 iobase; + u16 pci_id; + u8 target; + u8 lun; +}; + +static int megasas_fire_cmd(u16 pci_id, u32 ioaddr, + struct megasas_cmd_frame *frame) +{ + u32 frame_addr = (u32)frame; + int frame_count = 1; + u8 cmd_state; + + dprintf(2, "Frame 0x%x\n", frame_addr); + if (pci_id == PCI_DEVICE_ID_LSI_SAS2004 || + pci_id == PCI_DEVICE_ID_LSI_SAS2008) { + outl(0, ioaddr + MFI_IQPH); + outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQPL); + } else if (pci_id == PCI_DEVICE_ID_DELL_PERC5 || + pci_id == PCI_DEVICE_ID_LSI_SAS1064R || + pci_id == PCI_DEVICE_ID_LSI_VERDE_ZCR) { + outl(frame_addr >> 3 | frame_count, ioaddr + MFI_IQP); + } else { + outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQP); + } + + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); + do { + for (;;) { + cmd_state = GET_LOWFLAT(frame->cmd_status); + if (cmd_state != 0xff) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + } while (cmd_state == 0xff); + + if (cmd_state == 0 || cmd_state == 0x2d) + return 0; + dprintf(1, "ERROR: Frame 0x%x, status 0x%x\n", frame_addr, cmd_state); + return -1; +} + +int +megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + struct megasas_lun_s *mlun_gf = + container_of(op->drive_gf, struct megasas_lun_s, drive); + u8 *cdb = cdbcmd; + struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame); + u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id); + int i; + + if (!CONFIG_MEGASAS) + return DISK_RET_EBADTRACK; + + memset_fl(frame, 0, sizeof(*frame)); + SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO); + SET_LOWFLAT(frame->cmd_status, 0xFF); + SET_LOWFLAT(frame->target_id, GET_GLOBALFLAT(mlun_gf->target)); + SET_LOWFLAT(frame->lun, GET_GLOBALFLAT(mlun_gf->lun)); + SET_LOWFLAT(frame->flags, 0x0001); + SET_LOWFLAT(frame->data_xfer_len, op->count * blocksize); + SET_LOWFLAT(frame->cdb_len, 16); + + for (i = 0; i < 16; i++) { + SET_LOWFLAT(frame->pthru.cdb[i], cdb[i]); + } + dprintf(2, "pthru cmd 0x%x count %d bs %d\n", + cdb[0], op->count, blocksize); + + if (op->count) { + SET_LOWFLAT(frame->pthru.sgl_addr, (u32)op->buf_fl); + SET_LOWFLAT(frame->pthru.sgl_len, op->count * blocksize); + SET_LOWFLAT(frame->sge_count, 1); + } + SET_LOWFLAT(frame->context, (u32)frame); + + if (megasas_fire_cmd(pci_id, GET_GLOBALFLAT(mlun_gf->iobase), frame) == 0) + return DISK_RET_SUCCESS; + + dprintf(2, "pthru cmd 0x%x failed\n", cdb[0]); + return DISK_RET_EBADTRACK; +} + +static int +megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct megasas_lun_s *mlun = malloc_fseg(sizeof(*mlun)); + char *name; + int prio, ret = 0; + + if (!mlun) { + warn_noalloc(); + return -1; + } + memset(mlun, 0, sizeof(*mlun)); + mlun->drive.type = DTYPE_MEGASAS; + mlun->drive.cntl_id = pci->bdf; + mlun->pci_id = pci->device; + mlun->target = target; + mlun->lun = lun; + mlun->iobase = iobase; + mlun->frame = memalign_low(256, sizeof(struct megasas_cmd_frame)); + if (!mlun->frame) { + warn_noalloc(); + free(mlun); + return -1; + } + name = znprintf(36, "MegaRAID SAS (PCI %02x:%02x.%x) LD %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + prio = bootprio_find_scsi_device(pci, target, lun); + ret = scsi_drive_setup(&mlun->drive, name, prio); + free(name); + if (ret) { + free(mlun->frame); + free(mlun); + ret = -1; + } + + return ret; +} + +static void megasas_scan_target(struct pci_device *pci, u32 iobase) +{ + struct mfi_ld_list_s ld_list; + struct megasas_cmd_frame *frame = memalign_tmp(256, sizeof(*frame)); + int i; + + memset(&ld_list, 0, sizeof(ld_list)); + memset_fl(frame, 0, sizeof(*frame)); + + frame->cmd = MFI_CMD_DCMD; + frame->cmd_status = 0xFF; + frame->sge_count = 1; + frame->flags = 0x0011; + frame->data_xfer_len = sizeof(ld_list); + frame->dcmd.opcode = 0x03010000; + frame->dcmd.sgl_addr = (u32)MAKE_FLATPTR(GET_SEG(SS), &ld_list); + frame->dcmd.sgl_len = sizeof(ld_list); + frame->context = (u32)frame; + + if (megasas_fire_cmd(pci->device, iobase, frame) == 0) { + dprintf(2, "%d LD found\n", ld_list.count); + for (i = 0; i < ld_list.count; i++) { + dprintf(2, "LD %d:%d state 0x%x\n", + ld_list.lds[i].target, ld_list.lds[i].lun, + ld_list.lds[i].state); + if (ld_list.lds[i].state != 0) { + megasas_add_lun(pci, iobase, + ld_list.lds[i].target, ld_list.lds[i].lun); + } + } + } +} + +static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) +{ + u32 fw_state = 0, new_state, mfi_flags = 0; + + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_DELL_PERC5) + new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK; + else + new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK; + + while (fw_state != new_state) { + switch (new_state) { + case MFI_STATE_FAULT: + dprintf(1, "ERROR: fw in fault state\n"); + return -1; + break; + case MFI_STATE_WAIT_HANDSHAKE: + mfi_flags = 0x08; + /* fallthrough */ + case MFI_STATE_BOOT_MESSAGE_PENDING: + mfi_flags |= 0x10; + if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + outl(ioaddr + MFI_DB, mfi_flags); + } else { + outl(ioaddr + MFI_IDB, mfi_flags); + } + break; + case MFI_STATE_OPERATIONAL: + mfi_flags = 0x07; + if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + outl(ioaddr + MFI_DB, mfi_flags); + if (pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + int j = 0; + u32 doorbell; + + while (j < MEGASAS_POLL_TIMEOUT) { + doorbell = inl(ioaddr + MFI_DB) & 1; + if (!doorbell) + break; + msleep(20); + j++; + } + } + } else { + outw(ioaddr + MFI_IDB, mfi_flags); + } + break; + case MFI_STATE_READY: + dprintf(2, "MegaRAID SAS fw ready\n"); + return 0; + } + // The current state should not last longer than poll timeout + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); + for (;;) { + if (timer_check(end)) { + break; + } + yield(); + fw_state = new_state; + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_DELL_PERC5) + new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK; + else + new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK; + if (new_state != fw_state) { + break; + } + } + } + dprintf(1, "ERROR: fw in state %x\n", new_state & MFI_STATE_MASK); + return -1; +} + +static void +init_megasas(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2) + & PCI_BASE_ADDRESS_IO_MASK; + + if (!iobase) + iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + // reset + if (megasas_transition_to_ready(pci, iobase) == 0) + megasas_scan_target(pci, iobase); + + return; +} + +void +megasas_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_MEGASAS) + return; + + dprintf(3, "init megasas\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC && + pci->vendor != PCI_VENDOR_ID_DELL) + continue; + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_LSI_SAS1078 || + pci->device == PCI_DEVICE_ID_LSI_SAS1078DE || + pci->device == PCI_DEVICE_ID_LSI_SAS2108 || + pci->device == PCI_DEVICE_ID_LSI_SAS2108E || + pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_VERDE_ZCR || + pci->device == PCI_DEVICE_ID_DELL_PERC5 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) + init_megasas(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/megasas.h b/qemu/roms/seabios/src/hw/megasas.h new file mode 100644 index 000000000..124042e1c --- /dev/null +++ b/qemu/roms/seabios/src/hw/megasas.h @@ -0,0 +1,8 @@ +#ifndef __MEGASAS_H +#define __MEGASAS_H + +struct disk_op_s; +int megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void megasas_setup(void); + +#endif /* __MEGASAS_H */ diff --git a/qemu/roms/seabios/src/hw/pci.c b/qemu/roms/seabios/src/hw/pci.c new file mode 100644 index 000000000..0379b558e --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci.c @@ -0,0 +1,277 @@ +// PCI config space access functions. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "malloc.h" // malloc_tmp +#include "output.h" // dprintf +#include "pci.h" // pci_config_writel +#include "pci_regs.h" // PCI_VENDOR_ID +#include "romfile.h" // romfile_loadint +#include "string.h" // memset +#include "util.h" // udelay +#include "x86.h" // outl + +void pci_config_writel(u16 bdf, u32 addr, u32 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outl(val, PORT_PCI_DATA); +} + +void pci_config_writew(u16 bdf, u32 addr, u16 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outw(val, PORT_PCI_DATA + (addr & 2)); +} + +void pci_config_writeb(u16 bdf, u32 addr, u8 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outb(val, PORT_PCI_DATA + (addr & 3)); +} + +u32 pci_config_readl(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inl(PORT_PCI_DATA); +} + +u16 pci_config_readw(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inw(PORT_PCI_DATA + (addr & 2)); +} + +u8 pci_config_readb(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inb(PORT_PCI_DATA + (addr & 3)); +} + +void +pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on) +{ + u16 val = pci_config_readw(bdf, addr); + val = (val & ~off) | on; + pci_config_writew(bdf, addr, val); +} + +// Helper function for foreachbdf() macro - return next device +int +pci_next(int bdf, int bus) +{ + if (pci_bdf_to_fn(bdf) == 0 + && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0) + // Last found device wasn't a multi-function device - skip to + // the next device. + bdf += 8; + else + bdf += 1; + + for (;;) { + if (pci_bdf_to_bus(bdf) != bus) + return -1; + + u16 v = pci_config_readw(bdf, PCI_VENDOR_ID); + if (v != 0x0000 && v != 0xffff) + // Device is present. + return bdf; + + if (pci_bdf_to_fn(bdf) == 0) + bdf += 8; + else + bdf += 1; + } +} + +struct hlist_head PCIDevices VARVERIFY32INIT; +int MaxPCIBus VARFSEG; + +// Check if PCI is available at all +int +pci_probe_host(void) +{ + outl(0x80000000, PORT_PCI_CMD); + if (inl(PORT_PCI_CMD) != 0x80000000) { + dprintf(1, "Detected non-PCI system\n"); + return -1; + } + return 0; +} + +// Find all PCI devices and populate PCIDevices linked list. +void +pci_probe_devices(void) +{ + dprintf(3, "PCI probe\n"); + struct pci_device *busdevs[256]; + memset(busdevs, 0, sizeof(busdevs)); + struct hlist_node **pprev = &PCIDevices.first; + int extraroots = romfile_loadint("etc/extra-pci-roots", 0); + int bus = -1, lastbus = 0, rootbuses = 0, count=0; + while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) { + bus++; + int bdf; + foreachbdf(bdf, bus) { + // Create new pci_device struct and add to list. + struct pci_device *dev = malloc_tmp(sizeof(*dev)); + if (!dev) { + warn_noalloc(); + return; + } + memset(dev, 0, sizeof(*dev)); + hlist_add(&dev->node, pprev); + pprev = &dev->node.next; + count++; + + // Find parent device. + int rootbus; + struct pci_device *parent = busdevs[bus]; + if (!parent) { + if (bus != lastbus) + rootbuses++; + lastbus = bus; + rootbus = rootbuses; + if (bus > MaxPCIBus) + MaxPCIBus = bus; + } else { + rootbus = parent->rootbus; + } + + // Populate pci_device info. + dev->bdf = bdf; + dev->parent = parent; + dev->rootbus = rootbus; + u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); + dev->vendor = vendev & 0xffff; + dev->device = vendev >> 16; + u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION); + dev->class = classrev >> 16; + dev->prog_if = classrev >> 8; + dev->revision = classrev & 0xff; + dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE); + u8 v = dev->header_type & 0x7f; + if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) { + u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); + dev->secondary_bus = secbus; + if (secbus > bus && !busdevs[secbus]) + busdevs[secbus] = dev; + if (secbus > MaxPCIBus) + MaxPCIBus = secbus; + } + dprintf(4, "PCI device %02x:%02x.%x (vd=%04x:%04x c=%04x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf) + , dev->vendor, dev->device, dev->class); + } + } + dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus); +} + +// Search for a device with the specified vendor and device ids. +struct pci_device * +pci_find_device(u16 vendid, u16 devid) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor == vendid && pci->device == devid) + return pci; + } + return NULL; +} + +// Search for a device with the specified class id. +struct pci_device * +pci_find_class(u16 classid) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci->class == classid) + return pci; + } + return NULL; +} + +int pci_init_device(const struct pci_device_id *ids + , struct pci_device *pci, void *arg) +{ + while (ids->vendid || ids->class_mask) { + if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) && + (ids->devid == PCI_ANY_ID || ids->devid == pci->device) && + !((ids->class ^ pci->class) & ids->class_mask)) { + if (ids->func) + ids->func(pci, arg); + return 0; + } + ids++; + } + return -1; +} + +struct pci_device * +pci_find_init_device(const struct pci_device_id *ids, void *arg) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci_init_device(ids, pci, arg) == 0) + return pci; + } + return NULL; +} + +u8 pci_find_capability(struct pci_device *pci, u8 cap_id) +{ + int i; + u8 cap; + u16 status = pci_config_readw(pci->bdf, PCI_STATUS); + + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + cap = pci_config_readb(pci->bdf, PCI_CAPABILITY_LIST); + for (i = 0; cap && i <= 0xff; i++) { + if (pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_ID) == cap_id) + return cap; + cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT); + } + + return 0; +} + +/* Test whether bridge support forwarding of transactions + * of a specific type. + * Note: disables bridge's window registers as a side effect. + */ +int pci_bridge_has_region(struct pci_device *pci, + enum pci_region_type region_type) +{ + u8 base; + + switch (region_type) { + case PCI_REGION_TYPE_IO: + base = PCI_IO_BASE; + break; + case PCI_REGION_TYPE_PREFMEM: + base = PCI_PREF_MEMORY_BASE; + break; + default: + /* Regular memory support is mandatory */ + return 1; + } + + pci_config_writeb(pci->bdf, base, 0xFF); + + return pci_config_readb(pci->bdf, base) != 0; +} + +void +pci_reboot(void) +{ + u8 v = inb(PORT_PCI_REBOOT) & ~6; + outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */ + udelay(50); + outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */ + udelay(50); +} diff --git a/qemu/roms/seabios/src/hw/pci.h b/qemu/roms/seabios/src/hw/pci.h new file mode 100644 index 000000000..0aaa84c1a --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci.h @@ -0,0 +1,131 @@ +#ifndef __PCI_H +#define __PCI_H + +#include "types.h" // u32 +#include "list.h" // hlist_node + +#define PORT_PCI_CMD 0x0cf8 +#define PORT_PCI_REBOOT 0x0cf9 +#define PORT_PCI_DATA 0x0cfc + +#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 +#define PCI_BRIDGE_NUM_REGIONS 2 + +enum pci_region_type { + PCI_REGION_TYPE_IO, + PCI_REGION_TYPE_MEM, + PCI_REGION_TYPE_PREFMEM, + PCI_REGION_TYPE_COUNT, +}; + +static inline u8 pci_bdf_to_bus(u16 bdf) { + return bdf >> 8; +} +static inline u8 pci_bdf_to_devfn(u16 bdf) { + return bdf & 0xff; +} +static inline u16 pci_bdf_to_busdev(u16 bdf) { + return bdf & ~0x07; +} +static inline u8 pci_bdf_to_dev(u16 bdf) { + return (bdf >> 3) & 0x1f; +} +static inline u8 pci_bdf_to_fn(u16 bdf) { + return bdf & 0x07; +} +static inline u16 pci_to_bdf(int bus, int dev, int fn) { + return (bus<<8) | (dev<<3) | fn; +} +static inline u16 pci_bus_devfn_to_bdf(int bus, u16 devfn) { + return (bus << 8) | devfn; +} + +void pci_config_writel(u16 bdf, u32 addr, u32 val); +void pci_config_writew(u16 bdf, u32 addr, u16 val); +void pci_config_writeb(u16 bdf, u32 addr, u8 val); +u32 pci_config_readl(u16 bdf, u32 addr); +u16 pci_config_readw(u16 bdf, u32 addr); +u8 pci_config_readb(u16 bdf, u32 addr); +void pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on); + +struct pci_device *pci_find_device(u16 vendid, u16 devid); +struct pci_device *pci_find_class(u16 classid); + +struct pci_device { + u16 bdf; + u8 rootbus; + struct hlist_node node; + struct pci_device *parent; + + // Configuration space device information + u16 vendor, device; + u16 class; + u8 prog_if, revision; + u8 header_type; + u8 secondary_bus; + + // Local information on device. + int have_driver; +}; +extern u64 pcimem_start, pcimem_end; +extern u64 pcimem64_start, pcimem64_end; +extern struct hlist_head PCIDevices; +extern int MaxPCIBus; +int pci_probe_host(void); +void pci_probe_devices(void); +static inline u32 pci_classprog(struct pci_device *pci) { + return (pci->class << 8) | pci->prog_if; +} + +#define foreachpci(PCI) \ + hlist_for_each_entry(PCI, &PCIDevices, node) + +int pci_next(int bdf, int bus); +#define foreachbdf(BDF, BUS) \ + for (BDF=pci_next(pci_bus_devfn_to_bdf((BUS), 0)-1, (BUS)) \ + ; BDF >= 0 \ + ; BDF=pci_next(BDF, (BUS))) + +#define PCI_ANY_ID (~0) +struct pci_device_id { + u32 vendid; + u32 devid; + u32 class; + u32 class_mask; + void (*func)(struct pci_device *pci, void *arg); +}; + +#define PCI_DEVICE(vendor_id, device_id, init_func) \ + { \ + .vendid = (vendor_id), \ + .devid = (device_id), \ + .class = PCI_ANY_ID, \ + .class_mask = 0, \ + .func = (init_func) \ + } + +#define PCI_DEVICE_CLASS(vendor_id, device_id, class_code, init_func) \ + { \ + .vendid = (vendor_id), \ + .devid = (device_id), \ + .class = (class_code), \ + .class_mask = ~0, \ + .func = (init_func) \ + } + +#define PCI_DEVICE_END \ + { \ + .vendid = 0, \ + } + +int pci_init_device(const struct pci_device_id *ids + , struct pci_device *pci, void *arg); +struct pci_device *pci_find_init_device(const struct pci_device_id *ids + , void *arg); +u8 pci_find_capability(struct pci_device *pci, u8 cap_id); +int pci_bridge_has_region(struct pci_device *pci, + enum pci_region_type region_type); +void pci_reboot(void); + +#endif diff --git a/qemu/roms/seabios/src/hw/pci_ids.h b/qemu/roms/seabios/src/hw/pci_ids.h new file mode 100644 index 000000000..1cd4f7269 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci_ids.h @@ -0,0 +1,2623 @@ +/* + * PCI Class, Vendor and Device IDs + * + * Please keep sorted. + */ + +/* Device classes and subclasses */ + +#define PCI_CLASS_NOT_DEFINED 0x0000 +#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_FLOPPY 0x0102 +#define PCI_CLASS_STORAGE_IPI 0x0103 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_SATA 0x0106 +#define PCI_CLASS_STORAGE_SATA_AHCI 0x010601 +#define PCI_CLASS_STORAGE_SAS 0x0107 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_BASE_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 +#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define PCI_CLASS_NETWORK_FDDI 0x0202 +#define PCI_CLASS_NETWORK_ATM 0x0203 +#define PCI_CLASS_NETWORK_OTHER 0x0280 + +#define PCI_BASE_CLASS_DISPLAY 0x03 +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_XGA 0x0301 +#define PCI_CLASS_DISPLAY_3D 0x0302 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_BASE_CLASS_MULTIMEDIA 0x04 +#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define PCI_BASE_CLASS_MEMORY 0x05 +#define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_MEMORY_FLASH 0x0501 +#define PCI_CLASS_MEMORY_OTHER 0x0580 + +#define PCI_BASE_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_EISA 0x0602 +#define PCI_CLASS_BRIDGE_MC 0x0603 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define PCI_CLASS_BRIDGE_NUBUS 0x0606 +#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define PCI_CLASS_BRIDGE_RACEWAY 0x0608 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_BASE_CLASS_COMMUNICATION 0x07 +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 +#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 +#define PCI_CLASS_COMMUNICATION_MODEM 0x0703 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_BASE_CLASS_SYSTEM 0x08 +#define PCI_CLASS_SYSTEM_PIC 0x0800 +#define PCI_CLASS_SYSTEM_PIC_IOAPIC 0x080010 +#define PCI_CLASS_SYSTEM_PIC_IOXAPIC 0x080020 +#define PCI_CLASS_SYSTEM_DMA 0x0801 +#define PCI_CLASS_SYSTEM_TIMER 0x0802 +#define PCI_CLASS_SYSTEM_RTC 0x0803 +#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 +#define PCI_CLASS_SYSTEM_SDHCI 0x0805 +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_BASE_CLASS_INPUT 0x09 +#define PCI_CLASS_INPUT_KEYBOARD 0x0900 +#define PCI_CLASS_INPUT_PEN 0x0901 +#define PCI_CLASS_INPUT_MOUSE 0x0902 +#define PCI_CLASS_INPUT_SCANNER 0x0903 +#define PCI_CLASS_INPUT_GAMEPORT 0x0904 +#define PCI_CLASS_INPUT_OTHER 0x0980 + +#define PCI_BASE_CLASS_DOCKING 0x0a +#define PCI_CLASS_DOCKING_GENERIC 0x0a00 +#define PCI_CLASS_DOCKING_OTHER 0x0a80 + +#define PCI_BASE_CLASS_PROCESSOR 0x0b +#define PCI_CLASS_PROCESSOR_386 0x0b00 +#define PCI_CLASS_PROCESSOR_486 0x0b01 +#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 +#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define PCI_CLASS_PROCESSOR_MIPS 0x0b30 +#define PCI_CLASS_PROCESSOR_CO 0x0b40 + +#define PCI_BASE_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 +#define PCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010 +#define PCI_CLASS_SERIAL_ACCESS 0x0c01 +#define PCI_CLASS_SERIAL_SSA 0x0c02 +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_USB_UHCI 0x0c0300 +#define PCI_CLASS_SERIAL_USB_OHCI 0x0c0310 +#define PCI_CLASS_SERIAL_USB_EHCI 0x0c0320 +#define PCI_CLASS_SERIAL_USB_XHCI 0x0c0330 +#define PCI_CLASS_SERIAL_FIBER 0x0c04 +#define PCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define PCI_BASE_CLASS_WIRELESS 0x0d +#define PCI_CLASS_WIRELESS_RF_CONTROLLER 0x0d10 +#define PCI_CLASS_WIRELESS_WHCI 0x0d1010 + +#define PCI_BASE_CLASS_INTELLIGENT 0x0e +#define PCI_CLASS_INTELLIGENT_I2O 0x0e00 + +#define PCI_BASE_CLASS_SATELLITE 0x0f +#define PCI_CLASS_SATELLITE_TV 0x0f00 +#define PCI_CLASS_SATELLITE_AUDIO 0x0f01 +#define PCI_CLASS_SATELLITE_VOICE 0x0f03 +#define PCI_CLASS_SATELLITE_DATA 0x0f04 + +#define PCI_BASE_CLASS_CRYPT 0x10 +#define PCI_CLASS_CRYPT_NETWORK 0x1000 +#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 +#define PCI_CLASS_CRYPT_OTHER 0x1080 + +#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 +#define PCI_CLASS_SP_DPIO 0x1100 +#define PCI_CLASS_SP_OTHER 0x1180 + +#define PCI_CLASS_OTHERS 0xff + +/* Vendors and devices. Sort key: vendor first, device next. */ + +#define PCI_VENDOR_ID_TTTECH 0x0357 +#define PCI_DEVICE_ID_TTTECH_MC322 0x000a + +#define PCI_VENDOR_ID_DYNALINK 0x0675 +#define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702 + +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_DEVICE_ID_BERKOM_A1T 0xffa1 +#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xffa2 +#define PCI_DEVICE_ID_BERKOM_A4T 0xffa4 +#define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO 0xffa8 + +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508 +#define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc +#define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10 +#define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32 +#define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34 +#define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3I 0xae35 +#define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40 +#define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43 +#define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011 +#define PCI_DEVICE_ID_COMPAQ_CISS 0xb060 +#define PCI_DEVICE_ID_COMPAQ_CISSB 0xb178 +#define PCI_DEVICE_ID_COMPAQ_CISSC 0x46 +#define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150 + +#define PCI_VENDOR_ID_NCR 0x1000 +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_NCR_53C810 0x0001 +#define PCI_DEVICE_ID_NCR_53C820 0x0002 +#define PCI_DEVICE_ID_NCR_53C825 0x0003 +#define PCI_DEVICE_ID_NCR_53C815 0x0004 +#define PCI_DEVICE_ID_LSI_53C810AP 0x0005 +#define PCI_DEVICE_ID_NCR_53C860 0x0006 +#define PCI_DEVICE_ID_LSI_53C1510 0x000a +#define PCI_DEVICE_ID_NCR_53C896 0x000b +#define PCI_DEVICE_ID_NCR_53C895 0x000c +#define PCI_DEVICE_ID_NCR_53C885 0x000d +#define PCI_DEVICE_ID_NCR_53C875 0x000f +#define PCI_DEVICE_ID_NCR_53C1510 0x0010 +#define PCI_DEVICE_ID_LSI_53C895A 0x0012 +#define PCI_DEVICE_ID_LSI_53C875A 0x0013 +#define PCI_DEVICE_ID_LSI_53C1010_33 0x0020 +#define PCI_DEVICE_ID_LSI_53C1010_66 0x0021 +#define PCI_DEVICE_ID_LSI_53C1030 0x0030 +#define PCI_DEVICE_ID_LSI_1030_53C1035 0x0032 +#define PCI_DEVICE_ID_LSI_53C1035 0x0040 +#define PCI_DEVICE_ID_NCR_53C875J 0x008f +#define PCI_DEVICE_ID_LSI_FC909 0x0621 +#define PCI_DEVICE_ID_LSI_FC929 0x0622 +#define PCI_DEVICE_ID_LSI_FC929_LAN 0x0623 +#define PCI_DEVICE_ID_LSI_FC919 0x0624 +#define PCI_DEVICE_ID_LSI_FC919_LAN 0x0625 +#define PCI_DEVICE_ID_LSI_FC929X 0x0626 +#define PCI_DEVICE_ID_LSI_FC939X 0x0642 +#define PCI_DEVICE_ID_LSI_FC949X 0x0640 +#define PCI_DEVICE_ID_LSI_FC949ES 0x0646 +#define PCI_DEVICE_ID_LSI_FC919X 0x0628 +#define PCI_DEVICE_ID_NCR_YELLOWFIN 0x0701 +#define PCI_DEVICE_ID_LSI_61C102 0x0901 +#define PCI_DEVICE_ID_LSI_63C815 0x1000 +#define PCI_DEVICE_ID_LSI_SAS1064 0x0050 +#define PCI_DEVICE_ID_LSI_SAS1064R 0x0411 +#define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413 +#define PCI_DEVICE_ID_LSI_SAS1066 0x005E +#define PCI_DEVICE_ID_LSI_SAS1068 0x0054 +#define PCI_DEVICE_ID_LSI_SAS1064A 0x005C +#define PCI_DEVICE_ID_LSI_SAS1064E 0x0056 +#define PCI_DEVICE_ID_LSI_SAS1066E 0x005A +#define PCI_DEVICE_ID_LSI_SAS1068E 0x0058 +#define PCI_DEVICE_ID_LSI_SAS1078 0x0060 +#define PCI_DEVICE_ID_LSI_SAS1078DE 0x007C +#define PCI_DEVICE_ID_LSI_SAS2108E 0x0078 +#define PCI_DEVICE_ID_LSI_SAS2108 0x0079 +#define PCI_DEVICE_ID_LSI_SAS2208 0x005B +#define PCI_DEVICE_ID_LSI_SAS3108 0x005D +#define PCI_DEVICE_ID_LSI_SAS2004 0x0071 +#define PCI_DEVICE_ID_LSI_SAS2008 0x0073 + +#define PCI_VENDOR_ID_ATI 0x1002 +/* Mach64 */ +#define PCI_DEVICE_ID_ATI_68800 0x4158 +#define PCI_DEVICE_ID_ATI_215CT222 0x4354 +#define PCI_DEVICE_ID_ATI_210888CX 0x4358 +#define PCI_DEVICE_ID_ATI_215ET222 0x4554 +/* Mach64 / Rage */ +#define PCI_DEVICE_ID_ATI_215GB 0x4742 +#define PCI_DEVICE_ID_ATI_215GD 0x4744 +#define PCI_DEVICE_ID_ATI_215GI 0x4749 +#define PCI_DEVICE_ID_ATI_215GP 0x4750 +#define PCI_DEVICE_ID_ATI_215GQ 0x4751 +#define PCI_DEVICE_ID_ATI_215XL 0x4752 +#define PCI_DEVICE_ID_ATI_215GT 0x4754 +#define PCI_DEVICE_ID_ATI_215GTB 0x4755 +#define PCI_DEVICE_ID_ATI_215_IV 0x4756 +#define PCI_DEVICE_ID_ATI_215_IW 0x4757 +#define PCI_DEVICE_ID_ATI_215_IZ 0x475A +#define PCI_DEVICE_ID_ATI_210888GX 0x4758 +#define PCI_DEVICE_ID_ATI_215_LB 0x4c42 +#define PCI_DEVICE_ID_ATI_215_LD 0x4c44 +#define PCI_DEVICE_ID_ATI_215_LG 0x4c47 +#define PCI_DEVICE_ID_ATI_215_LI 0x4c49 +#define PCI_DEVICE_ID_ATI_215_LM 0x4c4D +#define PCI_DEVICE_ID_ATI_215_LN 0x4c4E +#define PCI_DEVICE_ID_ATI_215_LR 0x4c52 +#define PCI_DEVICE_ID_ATI_215_LS 0x4c53 +#define PCI_DEVICE_ID_ATI_264_LT 0x4c54 +/* Mach64 VT */ +#define PCI_DEVICE_ID_ATI_264VT 0x5654 +#define PCI_DEVICE_ID_ATI_264VU 0x5655 +#define PCI_DEVICE_ID_ATI_264VV 0x5656 +/* Rage128 GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_RE 0x5245 +#define PCI_DEVICE_ID_ATI_RAGE128_RF 0x5246 +#define PCI_DEVICE_ID_ATI_RAGE128_RG 0x5247 +/* Rage128 VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_RK 0x524b +#define PCI_DEVICE_ID_ATI_RAGE128_RL 0x524c +#define PCI_DEVICE_ID_ATI_RAGE128_SE 0x5345 +#define PCI_DEVICE_ID_ATI_RAGE128_SF 0x5346 +#define PCI_DEVICE_ID_ATI_RAGE128_SG 0x5347 +#define PCI_DEVICE_ID_ATI_RAGE128_SH 0x5348 +#define PCI_DEVICE_ID_ATI_RAGE128_SK 0x534b +#define PCI_DEVICE_ID_ATI_RAGE128_SL 0x534c +#define PCI_DEVICE_ID_ATI_RAGE128_SM 0x534d +#define PCI_DEVICE_ID_ATI_RAGE128_SN 0x534e +/* Rage128 Ultra */ +#define PCI_DEVICE_ID_ATI_RAGE128_TF 0x5446 +#define PCI_DEVICE_ID_ATI_RAGE128_TL 0x544c +#define PCI_DEVICE_ID_ATI_RAGE128_TR 0x5452 +#define PCI_DEVICE_ID_ATI_RAGE128_TS 0x5453 +#define PCI_DEVICE_ID_ATI_RAGE128_TT 0x5454 +#define PCI_DEVICE_ID_ATI_RAGE128_TU 0x5455 +/* Rage128 M3 */ +#define PCI_DEVICE_ID_ATI_RAGE128_LE 0x4c45 +#define PCI_DEVICE_ID_ATI_RAGE128_LF 0x4c46 +/* Rage128 M4 */ +#define PCI_DEVICE_ID_ATI_RAGE128_MF 0x4d46 +#define PCI_DEVICE_ID_ATI_RAGE128_ML 0x4d4c +/* Rage128 Pro GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_PA 0x5041 +#define PCI_DEVICE_ID_ATI_RAGE128_PB 0x5042 +#define PCI_DEVICE_ID_ATI_RAGE128_PC 0x5043 +#define PCI_DEVICE_ID_ATI_RAGE128_PD 0x5044 +#define PCI_DEVICE_ID_ATI_RAGE128_PE 0x5045 +#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 +/* Rage128 Pro VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_PG 0x5047 +#define PCI_DEVICE_ID_ATI_RAGE128_PH 0x5048 +#define PCI_DEVICE_ID_ATI_RAGE128_PI 0x5049 +#define PCI_DEVICE_ID_ATI_RAGE128_PJ 0x504A +#define PCI_DEVICE_ID_ATI_RAGE128_PK 0x504B +#define PCI_DEVICE_ID_ATI_RAGE128_PL 0x504C +#define PCI_DEVICE_ID_ATI_RAGE128_PM 0x504D +#define PCI_DEVICE_ID_ATI_RAGE128_PN 0x504E +#define PCI_DEVICE_ID_ATI_RAGE128_PO 0x504F +#define PCI_DEVICE_ID_ATI_RAGE128_PP 0x5050 +#define PCI_DEVICE_ID_ATI_RAGE128_PQ 0x5051 +#define PCI_DEVICE_ID_ATI_RAGE128_PR 0x5052 +#define PCI_DEVICE_ID_ATI_RAGE128_PS 0x5053 +#define PCI_DEVICE_ID_ATI_RAGE128_PT 0x5054 +#define PCI_DEVICE_ID_ATI_RAGE128_PU 0x5055 +#define PCI_DEVICE_ID_ATI_RAGE128_PV 0x5056 +#define PCI_DEVICE_ID_ATI_RAGE128_PW 0x5057 +#define PCI_DEVICE_ID_ATI_RAGE128_PX 0x5058 +/* Rage128 M4 */ +/* Radeon R100 */ +#define PCI_DEVICE_ID_ATI_RADEON_QD 0x5144 +#define PCI_DEVICE_ID_ATI_RADEON_QE 0x5145 +#define PCI_DEVICE_ID_ATI_RADEON_QF 0x5146 +#define PCI_DEVICE_ID_ATI_RADEON_QG 0x5147 +/* Radeon RV100 (VE) */ +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 +#define PCI_DEVICE_ID_ATI_RADEON_QZ 0x515a +/* Radeon R200 (8500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QL 0x514c +#define PCI_DEVICE_ID_ATI_RADEON_QN 0x514e +#define PCI_DEVICE_ID_ATI_RADEON_QO 0x514f +#define PCI_DEVICE_ID_ATI_RADEON_Ql 0x516c +#define PCI_DEVICE_ID_ATI_RADEON_BB 0x4242 +/* Radeon R200 (9100) */ +#define PCI_DEVICE_ID_ATI_RADEON_QM 0x514d +/* Radeon RV200 (7500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QW 0x5157 +#define PCI_DEVICE_ID_ATI_RADEON_QX 0x5158 +/* Radeon NV-100 */ +/* Radeon RV250 (9000) */ +#define PCI_DEVICE_ID_ATI_RADEON_Id 0x4964 +#define PCI_DEVICE_ID_ATI_RADEON_Ie 0x4965 +#define PCI_DEVICE_ID_ATI_RADEON_If 0x4966 +#define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967 +/* Radeon RV280 (9200) */ +#define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961 +#define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964 +/* Radeon R300 (9500) */ +/* Radeon R300 (9700) */ +#define PCI_DEVICE_ID_ATI_RADEON_ND 0x4e44 +#define PCI_DEVICE_ID_ATI_RADEON_NE 0x4e45 +#define PCI_DEVICE_ID_ATI_RADEON_NF 0x4e46 +#define PCI_DEVICE_ID_ATI_RADEON_NG 0x4e47 +/* Radeon R350 (9800) */ +/* Radeon RV350 (9600) */ +/* Radeon M6 */ +#define PCI_DEVICE_ID_ATI_RADEON_LY 0x4c59 +#define PCI_DEVICE_ID_ATI_RADEON_LZ 0x4c5a +/* Radeon M7 */ +#define PCI_DEVICE_ID_ATI_RADEON_LW 0x4c57 +#define PCI_DEVICE_ID_ATI_RADEON_LX 0x4c58 +/* Radeon M9 */ +#define PCI_DEVICE_ID_ATI_RADEON_Ld 0x4c64 +#define PCI_DEVICE_ID_ATI_RADEON_Le 0x4c65 +#define PCI_DEVICE_ID_ATI_RADEON_Lf 0x4c66 +#define PCI_DEVICE_ID_ATI_RADEON_Lg 0x4c67 +/* Radeon */ +/* RadeonIGP */ +#define PCI_DEVICE_ID_ATI_RS100 0xcab0 +#define PCI_DEVICE_ID_ATI_RS200 0xcab2 +#define PCI_DEVICE_ID_ATI_RS200_B 0xcbb2 +#define PCI_DEVICE_ID_ATI_RS250 0xcab3 +#define PCI_DEVICE_ID_ATI_RS300_100 0x5830 +#define PCI_DEVICE_ID_ATI_RS300_133 0x5831 +#define PCI_DEVICE_ID_ATI_RS300_166 0x5832 +#define PCI_DEVICE_ID_ATI_RS300_200 0x5833 +#define PCI_DEVICE_ID_ATI_RS350_100 0x7830 +#define PCI_DEVICE_ID_ATI_RS350_133 0x7831 +#define PCI_DEVICE_ID_ATI_RS350_166 0x7832 +#define PCI_DEVICE_ID_ATI_RS350_200 0x7833 +#define PCI_DEVICE_ID_ATI_RS400_100 0x5a30 +#define PCI_DEVICE_ID_ATI_RS400_133 0x5a31 +#define PCI_DEVICE_ID_ATI_RS400_166 0x5a32 +#define PCI_DEVICE_ID_ATI_RS400_200 0x5a33 +#define PCI_DEVICE_ID_ATI_RS480 0x5950 +/* ATI IXP Chipset */ +#define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 +#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353 +#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363 +#define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369 +#define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e +#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372 +#define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 +#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 +#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a +#define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380 +#define PCI_DEVICE_ID_ATI_SBX00_SMBUS 0x4385 +#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c +#define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390 +#define PCI_DEVICE_ID_ATI_IXP700_IDE 0x439c + +#define PCI_VENDOR_ID_VLSI 0x1004 +#define PCI_DEVICE_ID_VLSI_82C592 0x0005 +#define PCI_DEVICE_ID_VLSI_82C593 0x0006 +#define PCI_DEVICE_ID_VLSI_82C594 0x0007 +#define PCI_DEVICE_ID_VLSI_82C597 0x0009 +#define PCI_DEVICE_ID_VLSI_82C541 0x000c +#define PCI_DEVICE_ID_VLSI_82C543 0x000d +#define PCI_DEVICE_ID_VLSI_82C532 0x0101 +#define PCI_DEVICE_ID_VLSI_82C534 0x0102 +#define PCI_DEVICE_ID_VLSI_82C535 0x0104 +#define PCI_DEVICE_ID_VLSI_82C147 0x0105 +#define PCI_DEVICE_ID_VLSI_VAS96011 0x0702 + +#define PCI_VENDOR_ID_ADL 0x1005 +#define PCI_DEVICE_ID_ADL_2301 0x2301 + +#define PCI_VENDOR_ID_NS 0x100b +#define PCI_DEVICE_ID_NS_87415 0x0002 +#define PCI_DEVICE_ID_NS_87560_LIO 0x000e +#define PCI_DEVICE_ID_NS_87560_USB 0x0012 +#define PCI_DEVICE_ID_NS_83815 0x0020 +#define PCI_DEVICE_ID_NS_83820 0x0022 +#define PCI_DEVICE_ID_NS_CS5535_ISA 0x002b +#define PCI_DEVICE_ID_NS_CS5535_IDE 0x002d +#define PCI_DEVICE_ID_NS_CS5535_AUDIO 0x002e +#define PCI_DEVICE_ID_NS_CS5535_USB 0x002f +#define PCI_DEVICE_ID_NS_GX_VIDEO 0x0030 +#define PCI_DEVICE_ID_NS_SATURN 0x0035 +#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500 +#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501 +#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502 +#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503 +#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504 +#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505 +#define PCI_DEVICE_ID_NS_SC1100_BRIDGE 0x0510 +#define PCI_DEVICE_ID_NS_SC1100_SMI 0x0511 +#define PCI_DEVICE_ID_NS_SC1100_XBUS 0x0515 +#define PCI_DEVICE_ID_NS_87410 0xd001 + +#define PCI_DEVICE_ID_NS_GX_HOST_BRIDGE 0x0028 + +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_DEVICE_ID_TSENG_W32P_2 0x3202 +#define PCI_DEVICE_ID_TSENG_W32P_b 0x3205 +#define PCI_DEVICE_ID_TSENG_W32P_c 0x3206 +#define PCI_DEVICE_ID_TSENG_W32P_d 0x3207 +#define PCI_DEVICE_ID_TSENG_ET6000 0x3208 + +#define PCI_VENDOR_ID_WEITEK 0x100e +#define PCI_DEVICE_ID_WEITEK_P9000 0x9001 +#define PCI_DEVICE_ID_WEITEK_P9100 0x9100 + +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_BRD 0x0001 +#define PCI_DEVICE_ID_DEC_TULIP 0x0002 +#define PCI_DEVICE_ID_DEC_TGA 0x0004 +#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 +#define PCI_DEVICE_ID_DEC_TGA2 0x000D +#define PCI_DEVICE_ID_DEC_FDDI 0x000F +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 +#define PCI_DEVICE_ID_DEC_21142 0x0019 +#define PCI_DEVICE_ID_DEC_21052 0x0021 +#define PCI_DEVICE_ID_DEC_21150 0x0022 +#define PCI_DEVICE_ID_DEC_21152 0x0024 +#define PCI_DEVICE_ID_DEC_21153 0x0025 +#define PCI_DEVICE_ID_DEC_21154 0x0026 +#define PCI_DEVICE_ID_DEC_21285 0x1065 +#define PCI_DEVICE_ID_COMPAQ_42XX 0x0046 + +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_DEVICE_ID_CIRRUS_7548 0x0038 +#define PCI_DEVICE_ID_CIRRUS_5430 0x00a0 +#define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4 +#define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8 +#define PCI_DEVICE_ID_CIRRUS_5436 0x00ac +#define PCI_DEVICE_ID_CIRRUS_5446 0x00b8 +#define PCI_DEVICE_ID_CIRRUS_5480 0x00bc +#define PCI_DEVICE_ID_CIRRUS_5462 0x00d0 +#define PCI_DEVICE_ID_CIRRUS_5464 0x00d4 +#define PCI_DEVICE_ID_CIRRUS_5465 0x00d6 +#define PCI_DEVICE_ID_CIRRUS_6729 0x1100 +#define PCI_DEVICE_ID_CIRRUS_6832 0x1110 +#define PCI_DEVICE_ID_CIRRUS_7543 0x1202 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 + +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_DEVICE_ID_IBM_TR 0x0018 +#define PCI_DEVICE_ID_IBM_TR_WAKE 0x003e +#define PCI_DEVICE_ID_IBM_CPC710_PCI64 0x00fc +#define PCI_DEVICE_ID_IBM_SNIPE 0x0180 +#define PCI_DEVICE_ID_IBM_CITRINE 0x028C +#define PCI_DEVICE_ID_IBM_GEMSTONE 0xB166 +#define PCI_DEVICE_ID_IBM_OBSIDIAN 0x02BD +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1 0x0031 +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2 0x0219 +#define PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX 0x021A +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM 0x0251 +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361 +#define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252 + +#define PCI_VENDOR_ID_UNISYS 0x1018 +#define PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR 0x001C + +#define PCI_VENDOR_ID_COMPEX2 0x101a /* pci.ids says "AT&T GIS (NCR)" */ +#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 + +#define PCI_VENDOR_ID_WD 0x101c +#define PCI_DEVICE_ID_WD_90C 0xc24a + +#define PCI_VENDOR_ID_AMI 0x101e +#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 +#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 +#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060 + +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_K8_NB 0x1100 +#define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 +#define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 +#define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 +#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 +#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 +#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 +#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 +#define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300 +#define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301 +#define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 +#define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 +#define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#define PCI_DEVICE_ID_AMD_SERENADE 0x36c0 +#define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006 +#define PCI_DEVICE_ID_AMD_FE_GATE_7007 0x7007 +#define PCI_DEVICE_ID_AMD_FE_GATE_700C 0x700C +#define PCI_DEVICE_ID_AMD_FE_GATE_700E 0x700E +#define PCI_DEVICE_ID_AMD_COBRA_7401 0x7401 +#define PCI_DEVICE_ID_AMD_VIPER_7409 0x7409 +#define PCI_DEVICE_ID_AMD_VIPER_740B 0x740B +#define PCI_DEVICE_ID_AMD_VIPER_7410 0x7410 +#define PCI_DEVICE_ID_AMD_VIPER_7411 0x7411 +#define PCI_DEVICE_ID_AMD_VIPER_7413 0x7413 +#define PCI_DEVICE_ID_AMD_VIPER_7440 0x7440 +#define PCI_DEVICE_ID_AMD_OPUS_7441 0x7441 +#define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443 +#define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443 +#define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445 +#define PCI_DEVICE_ID_AMD_8111_LPC 0x7468 +#define PCI_DEVICE_ID_AMD_8111_IDE 0x7469 +#define PCI_DEVICE_ID_AMD_8111_SMBUS2 0x746a +#define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746b +#define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d +#define PCI_DEVICE_ID_AMD_8151_0 0x7454 +#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 +#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 +#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458 +#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 +#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 +#define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093 +#define PCI_DEVICE_ID_AMD_CS5536_OHC 0x2094 +#define PCI_DEVICE_ID_AMD_CS5536_EHC 0x2095 +#define PCI_DEVICE_ID_AMD_CS5536_UDC 0x2096 +#define PCI_DEVICE_ID_AMD_CS5536_UOC 0x2097 +#define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A + +#define PCI_DEVICE_ID_AMD_LX_VIDEO 0x2081 +#define PCI_DEVICE_ID_AMD_LX_AES 0x2082 + +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#define PCI_DEVICE_ID_TRIDENT_9320 0x9320 +#define PCI_DEVICE_ID_TRIDENT_9388 0x9388 +#define PCI_DEVICE_ID_TRIDENT_9397 0x9397 +#define PCI_DEVICE_ID_TRIDENT_939A 0x939A +#define PCI_DEVICE_ID_TRIDENT_9520 0x9520 +#define PCI_DEVICE_ID_TRIDENT_9525 0x9525 +#define PCI_DEVICE_ID_TRIDENT_9420 0x9420 +#define PCI_DEVICE_ID_TRIDENT_9440 0x9440 +#define PCI_DEVICE_ID_TRIDENT_9660 0x9660 +#define PCI_DEVICE_ID_TRIDENT_9750 0x9750 +#define PCI_DEVICE_ID_TRIDENT_9850 0x9850 +#define PCI_DEVICE_ID_TRIDENT_9880 0x9880 +#define PCI_DEVICE_ID_TRIDENT_8400 0x8400 +#define PCI_DEVICE_ID_TRIDENT_8420 0x8420 +#define PCI_DEVICE_ID_TRIDENT_8500 0x8500 + +#define PCI_VENDOR_ID_AI 0x1025 +#define PCI_DEVICE_ID_AI_M1435 0x1435 + +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_DEVICE_ID_DELL_RACIII 0x0008 +#define PCI_DEVICE_ID_DELL_RAC4 0x0012 +#define PCI_DEVICE_ID_DELL_PERC5 0x0015 + +#define PCI_VENDOR_ID_MATROX 0x102B +#define PCI_DEVICE_ID_MATROX_MGA_2 0x0518 +#define PCI_DEVICE_ID_MATROX_MIL 0x0519 +#define PCI_DEVICE_ID_MATROX_MYS 0x051A +#define PCI_DEVICE_ID_MATROX_MIL_2 0x051b +#define PCI_DEVICE_ID_MATROX_MYS_AGP 0x051e +#define PCI_DEVICE_ID_MATROX_MIL_2_AGP 0x051f +#define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10 +#define PCI_DEVICE_ID_MATROX_G100_MM 0x1000 +#define PCI_DEVICE_ID_MATROX_G100_AGP 0x1001 +#define PCI_DEVICE_ID_MATROX_G200_PCI 0x0520 +#define PCI_DEVICE_ID_MATROX_G200_AGP 0x0521 +#define PCI_DEVICE_ID_MATROX_G400 0x0525 +#define PCI_DEVICE_ID_MATROX_G200EV_PCI 0x0530 +#define PCI_DEVICE_ID_MATROX_G550 0x2527 +#define PCI_DEVICE_ID_MATROX_VIA 0x4536 + +#define PCI_VENDOR_ID_CT 0x102c +#define PCI_DEVICE_ID_CT_69000 0x00c0 +#define PCI_DEVICE_ID_CT_65545 0x00d8 +#define PCI_DEVICE_ID_CT_65548 0x00dc +#define PCI_DEVICE_ID_CT_65550 0x00e0 +#define PCI_DEVICE_ID_CT_65554 0x00e4 +#define PCI_DEVICE_ID_CT_65555 0x00e5 + +#define PCI_VENDOR_ID_MIRO 0x1031 +#define PCI_DEVICE_ID_MIRO_36050 0x5601 +#define PCI_DEVICE_ID_MIRO_DC10PLUS 0x7efe +#define PCI_DEVICE_ID_MIRO_DC30PLUS 0xd801 + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_LOCAL 0x0002 /* Local Bridge */ +#define PCI_DEVICE_ID_NEC_ATM 0x0003 /* ATM LAN Controller */ +#define PCI_DEVICE_ID_NEC_R4000 0x0004 /* R4000 Bridge */ +#define PCI_DEVICE_ID_NEC_486 0x0005 /* 486 Like Peripheral Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_1 0x0006 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_UXBUS 0x0007 /* UX-Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_2 0x0008 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_GRAPH 0x0009 /* PCI-CoreGraph Bridge */ +#define PCI_DEVICE_ID_NEC_VL 0x0016 /* PCI-VL Bridge */ +#define PCI_DEVICE_ID_NEC_STARALPHA2 0x002c /* STAR ALPHA2 */ +#define PCI_DEVICE_ID_NEC_CBUS_2 0x002d /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_USB 0x0035 /* PCI-USB Host */ +#define PCI_DEVICE_ID_NEC_CBUS_3 0x003b +#define PCI_DEVICE_ID_NEC_NAPCCARD 0x003e +#define PCI_DEVICE_ID_NEC_PCX2 0x0046 /* PowerVR */ +#define PCI_DEVICE_ID_NEC_VRC5476 0x009b +#define PCI_DEVICE_ID_NEC_VRC4173 0x00a5 +#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00a6 +#define PCI_DEVICE_ID_NEC_PC9821CS01 0x800c /* PC-9821-CS01 */ +#define PCI_DEVICE_ID_NEC_PC9821NRB06 0x800d /* PC-9821NR-B06 */ + +#define PCI_VENDOR_ID_FD 0x1036 +#define PCI_DEVICE_ID_FD_36C70 0x0000 + +#define PCI_VENDOR_ID_SI 0x1039 +#define PCI_DEVICE_ID_SI_5591_AGP 0x0001 +#define PCI_DEVICE_ID_SI_6202 0x0002 +#define PCI_DEVICE_ID_SI_503 0x0008 +#define PCI_DEVICE_ID_SI_ACPI 0x0009 +#define PCI_DEVICE_ID_SI_SMBUS 0x0016 +#define PCI_DEVICE_ID_SI_LPC 0x0018 +#define PCI_DEVICE_ID_SI_5597_VGA 0x0200 +#define PCI_DEVICE_ID_SI_6205 0x0205 +#define PCI_DEVICE_ID_SI_501 0x0406 +#define PCI_DEVICE_ID_SI_496 0x0496 +#define PCI_DEVICE_ID_SI_300 0x0300 +#define PCI_DEVICE_ID_SI_315H 0x0310 +#define PCI_DEVICE_ID_SI_315 0x0315 +#define PCI_DEVICE_ID_SI_315PRO 0x0325 +#define PCI_DEVICE_ID_SI_530 0x0530 +#define PCI_DEVICE_ID_SI_540 0x0540 +#define PCI_DEVICE_ID_SI_550 0x0550 +#define PCI_DEVICE_ID_SI_540_VGA 0x5300 +#define PCI_DEVICE_ID_SI_550_VGA 0x5315 +#define PCI_DEVICE_ID_SI_620 0x0620 +#define PCI_DEVICE_ID_SI_630 0x0630 +#define PCI_DEVICE_ID_SI_633 0x0633 +#define PCI_DEVICE_ID_SI_635 0x0635 +#define PCI_DEVICE_ID_SI_640 0x0640 +#define PCI_DEVICE_ID_SI_645 0x0645 +#define PCI_DEVICE_ID_SI_646 0x0646 +#define PCI_DEVICE_ID_SI_648 0x0648 +#define PCI_DEVICE_ID_SI_650 0x0650 +#define PCI_DEVICE_ID_SI_651 0x0651 +#define PCI_DEVICE_ID_SI_655 0x0655 +#define PCI_DEVICE_ID_SI_661 0x0661 +#define PCI_DEVICE_ID_SI_730 0x0730 +#define PCI_DEVICE_ID_SI_733 0x0733 +#define PCI_DEVICE_ID_SI_630_VGA 0x6300 +#define PCI_DEVICE_ID_SI_735 0x0735 +#define PCI_DEVICE_ID_SI_740 0x0740 +#define PCI_DEVICE_ID_SI_741 0x0741 +#define PCI_DEVICE_ID_SI_745 0x0745 +#define PCI_DEVICE_ID_SI_746 0x0746 +#define PCI_DEVICE_ID_SI_755 0x0755 +#define PCI_DEVICE_ID_SI_760 0x0760 +#define PCI_DEVICE_ID_SI_900 0x0900 +#define PCI_DEVICE_ID_SI_961 0x0961 +#define PCI_DEVICE_ID_SI_962 0x0962 +#define PCI_DEVICE_ID_SI_963 0x0963 +#define PCI_DEVICE_ID_SI_965 0x0965 +#define PCI_DEVICE_ID_SI_966 0x0966 +#define PCI_DEVICE_ID_SI_968 0x0968 +#define PCI_DEVICE_ID_SI_1180 0x1180 +#define PCI_DEVICE_ID_SI_5511 0x5511 +#define PCI_DEVICE_ID_SI_5513 0x5513 +#define PCI_DEVICE_ID_SI_5517 0x5517 +#define PCI_DEVICE_ID_SI_5518 0x5518 +#define PCI_DEVICE_ID_SI_5571 0x5571 +#define PCI_DEVICE_ID_SI_5581 0x5581 +#define PCI_DEVICE_ID_SI_5582 0x5582 +#define PCI_DEVICE_ID_SI_5591 0x5591 +#define PCI_DEVICE_ID_SI_5596 0x5596 +#define PCI_DEVICE_ID_SI_5597 0x5597 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#define PCI_DEVICE_ID_SI_5600 0x5600 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#define PCI_DEVICE_ID_SI_7013 0x7013 +#define PCI_DEVICE_ID_SI_7016 0x7016 +#define PCI_DEVICE_ID_SI_7018 0x7018 + +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_DEVICE_ID_HP_VISUALIZE_EG 0x1005 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX6 0x1006 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX4 0x1008 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX2 0x100a +#define PCI_DEVICE_ID_HP_TACHYON 0x1028 +#define PCI_DEVICE_ID_HP_TACHLITE 0x1029 +#define PCI_DEVICE_ID_HP_J2585A 0x1030 +#define PCI_DEVICE_ID_HP_J2585B 0x1031 +#define PCI_DEVICE_ID_HP_J2973A 0x1040 +#define PCI_DEVICE_ID_HP_J2970A 0x1042 +#define PCI_DEVICE_ID_HP_DIVA 0x1048 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA1 0x1049 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA2 0x104A +#define PCI_DEVICE_ID_HP_DIVA_MAESTRO 0x104B +#define PCI_DEVICE_ID_HP_REO_IOC 0x10f1 +#define PCI_DEVICE_ID_HP_VISUALIZE_FXE 0x108b +#define PCI_DEVICE_ID_HP_DIVA_HALFDOME 0x1223 +#define PCI_DEVICE_ID_HP_DIVA_KEYSTONE 0x1226 +#define PCI_DEVICE_ID_HP_DIVA_POWERBAR 0x1227 +#define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a +#define PCI_DEVICE_ID_HP_PCIX_LBA 0x122e +#define PCI_DEVICE_ID_HP_SX1000_IOC 0x127c +#define PCI_DEVICE_ID_HP_DIVA_EVEREST 0x1282 +#define PCI_DEVICE_ID_HP_DIVA_AUX 0x1290 +#define PCI_DEVICE_ID_HP_DIVA_RMP3 0x1301 +#define PCI_DEVICE_ID_HP_DIVA_HURRICANE 0x132a +#define PCI_DEVICE_ID_HP_CISSA 0x3220 +#define PCI_DEVICE_ID_HP_CISSC 0x3230 +#define PCI_DEVICE_ID_HP_CISSD 0x3238 +#define PCI_DEVICE_ID_HP_CISSE 0x323a +#define PCI_DEVICE_ID_HP_ZX2_IOC 0x4031 + +#define PCI_VENDOR_ID_PCTECH 0x1042 +#define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000 +#define PCI_DEVICE_ID_PCTECH_RZ1001 0x1001 +#define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020 + +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_DEVICE_ID_ASUSTEK_0675 0x0675 + +#define PCI_VENDOR_ID_DPT 0x1044 +#define PCI_DEVICE_ID_DPT 0xa400 + +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_DEVICE_ID_OPTI_82C558 0xc558 +#define PCI_DEVICE_ID_OPTI_82C621 0xc621 +#define PCI_DEVICE_ID_OPTI_82C700 0xc700 +#define PCI_DEVICE_ID_OPTI_82C825 0xd568 + +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_DEVICE_ID_ELSA_MICROLINK 0x1000 +#define PCI_DEVICE_ID_ELSA_QS3000 0x3000 + +#define PCI_VENDOR_ID_BUSLOGIC 0x104B +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140 +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040 +#define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130 + +#define PCI_VENDOR_ID_TI 0x104c +#define PCI_DEVICE_ID_TI_TVP4020 0x3d07 +#define PCI_DEVICE_ID_TI_4450 0x8011 +#define PCI_DEVICE_ID_TI_TSB43AB22 0x8023 +#define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 +#define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 +#define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 +#define PCI_DEVICE_ID_TI_X515 0x8036 +#define PCI_DEVICE_ID_TI_XX12 0x8039 +#define PCI_DEVICE_ID_TI_XX12_FM 0x803b +#define PCI_DEVICE_ID_TI_1130 0xac12 +#define PCI_DEVICE_ID_TI_1031 0xac13 +#define PCI_DEVICE_ID_TI_1131 0xac15 +#define PCI_DEVICE_ID_TI_1250 0xac16 +#define PCI_DEVICE_ID_TI_1220 0xac17 +#define PCI_DEVICE_ID_TI_1221 0xac19 +#define PCI_DEVICE_ID_TI_1210 0xac1a +#define PCI_DEVICE_ID_TI_1450 0xac1b +#define PCI_DEVICE_ID_TI_1225 0xac1c +#define PCI_DEVICE_ID_TI_1251A 0xac1d +#define PCI_DEVICE_ID_TI_1211 0xac1e +#define PCI_DEVICE_ID_TI_1251B 0xac1f +#define PCI_DEVICE_ID_TI_4410 0xac41 +#define PCI_DEVICE_ID_TI_4451 0xac42 +#define PCI_DEVICE_ID_TI_4510 0xac44 +#define PCI_DEVICE_ID_TI_4520 0xac46 +#define PCI_DEVICE_ID_TI_7510 0xac47 +#define PCI_DEVICE_ID_TI_7610 0xac48 +#define PCI_DEVICE_ID_TI_7410 0xac49 +#define PCI_DEVICE_ID_TI_1410 0xac50 +#define PCI_DEVICE_ID_TI_1420 0xac51 +#define PCI_DEVICE_ID_TI_1451A 0xac52 +#define PCI_DEVICE_ID_TI_1620 0xac54 +#define PCI_DEVICE_ID_TI_1520 0xac55 +#define PCI_DEVICE_ID_TI_1510 0xac56 +#define PCI_DEVICE_ID_TI_X620 0xac8d +#define PCI_DEVICE_ID_TI_X420 0xac8e +#define PCI_DEVICE_ID_TI_XX20_FM 0xac8f + +#define PCI_VENDOR_ID_SONY 0x104d + +/* Winbond have two vendor IDs! See 0x10ad as well */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 +#define PCI_DEVICE_ID_WINBOND2_89C940F 0x5a5a +#define PCI_DEVICE_ID_WINBOND2_6692 0x6692 + +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100 + +#define PCI_VENDOR_ID_EFAR 0x1055 +#define PCI_DEVICE_ID_EFAR_SLC90E66_1 0x9130 +#define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463 + +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001 +#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 +#define PCI_DEVICE_ID_MOTOROLA_MPC107 0x0004 +#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 +#define PCI_DEVICE_ID_MOTOROLA_FALCON 0x4802 +#define PCI_DEVICE_ID_MOTOROLA_HAWK 0x4803 +#define PCI_DEVICE_ID_MOTOROLA_HARRIER 0x480b +#define PCI_DEVICE_ID_MOTOROLA_MPC5200 0x5803 +#define PCI_DEVICE_ID_MOTOROLA_MPC5200B 0x5809 + +#define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_DEVICE_ID_PROMISE_20265 0x0d30 +#define PCI_DEVICE_ID_PROMISE_20267 0x4d30 +#define PCI_DEVICE_ID_PROMISE_20246 0x4d33 +#define PCI_DEVICE_ID_PROMISE_20262 0x4d38 +#define PCI_DEVICE_ID_PROMISE_20263 0x0D38 +#define PCI_DEVICE_ID_PROMISE_20268 0x4d68 +#define PCI_DEVICE_ID_PROMISE_20269 0x4d69 +#define PCI_DEVICE_ID_PROMISE_20270 0x6268 +#define PCI_DEVICE_ID_PROMISE_20271 0x6269 +#define PCI_DEVICE_ID_PROMISE_20275 0x1275 +#define PCI_DEVICE_ID_PROMISE_20276 0x5275 +#define PCI_DEVICE_ID_PROMISE_20277 0x7275 + +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_DEVICE_ID_UMC_UM8673F 0x0101 +#define PCI_DEVICE_ID_UMC_UM8886BF 0x673a +#define PCI_DEVICE_ID_UMC_UM8886A 0x886a + +#define PCI_VENDOR_ID_PICOPOWER 0x1066 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523 0x0002 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523BBP 0x8002 + +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_DEVICE_ID_MYLEX_DAC960_P 0x0001 +#define PCI_DEVICE_ID_MYLEX_DAC960_PD 0x0002 +#define PCI_DEVICE_ID_MYLEX_DAC960_PG 0x0010 +#define PCI_DEVICE_ID_MYLEX_DAC960_LA 0x0020 +#define PCI_DEVICE_ID_MYLEX_DAC960_LP 0x0050 +#define PCI_DEVICE_ID_MYLEX_DAC960_BA 0xBA56 +#define PCI_DEVICE_ID_MYLEX_DAC960_GEM 0xB166 + +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_DEVICE_ID_APPLE_BANDIT 0x0001 +#define PCI_DEVICE_ID_APPLE_HYDRA 0x000e +#define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP 0x0024 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P 0x0027 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15 0x002d +#define PCI_DEVICE_ID_APPLE_UNI_N_PCI15 0x002e +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2 0x0032 +#define PCI_DEVICE_ID_APPLE_UNI_N_ATA 0x0033 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP2 0x0034 +#define PCI_DEVICE_ID_APPLE_IPID_ATA100 0x003b +#define PCI_DEVICE_ID_APPLE_K2_ATA100 0x0043 +#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b +#define PCI_DEVICE_ID_APPLE_K2_GMAC 0x004c +#define PCI_DEVICE_ID_APPLE_SH_ATA 0x0050 +#define PCI_DEVICE_ID_APPLE_SH_SUNGEM 0x0051 +#define PCI_DEVICE_ID_APPLE_U3L_AGP 0x0058 +#define PCI_DEVICE_ID_APPLE_U3H_AGP 0x0059 +#define PCI_DEVICE_ID_APPLE_IPID2_AGP 0x0066 +#define PCI_DEVICE_ID_APPLE_IPID2_ATA 0x0069 +#define PCI_DEVICE_ID_APPLE_IPID2_FW 0x006a +#define PCI_DEVICE_ID_APPLE_IPID2_GMAC 0x006b +#define PCI_DEVICE_ID_APPLE_TIGON3 0x1645 + +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#define PCI_DEVICE_ID_YAMAHA_724 0x0004 +#define PCI_DEVICE_ID_YAMAHA_724F 0x000d +#define PCI_DEVICE_ID_YAMAHA_740 0x000a +#define PCI_DEVICE_ID_YAMAHA_740C 0x000c +#define PCI_DEVICE_ID_YAMAHA_744 0x0010 +#define PCI_DEVICE_ID_YAMAHA_754 0x0012 + +#define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016 +#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020 +#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080 +#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216 +#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240 +#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280 +#define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100 +#define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200 +#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300 +#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312 +#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322 +#define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312 +#define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322 +#define PCI_DEVICE_ID_QLOGIC_ISP2422 0x2422 +#define PCI_DEVICE_ID_QLOGIC_ISP2432 0x2432 +#define PCI_DEVICE_ID_QLOGIC_ISP2512 0x2512 +#define PCI_DEVICE_ID_QLOGIC_ISP2522 0x2522 +#define PCI_DEVICE_ID_QLOGIC_ISP5422 0x5422 +#define PCI_DEVICE_ID_QLOGIC_ISP5432 0x5432 + +#define PCI_VENDOR_ID_CYRIX 0x1078 +#define PCI_DEVICE_ID_CYRIX_5510 0x0000 +#define PCI_DEVICE_ID_CYRIX_PCI_MASTER 0x0001 +#define PCI_DEVICE_ID_CYRIX_5520 0x0002 +#define PCI_DEVICE_ID_CYRIX_5530_LEGACY 0x0100 +#define PCI_DEVICE_ID_CYRIX_5530_IDE 0x0102 +#define PCI_DEVICE_ID_CYRIX_5530_AUDIO 0x0103 +#define PCI_DEVICE_ID_CYRIX_5530_VIDEO 0x0104 + +#define PCI_VENDOR_ID_CONTAQ 0x1080 +#define PCI_DEVICE_ID_CONTAQ_82C693 0xc693 + +#define PCI_VENDOR_ID_OLICOM 0x108d +#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012 +#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013 +#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014 + +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_DEVICE_ID_SUN_EBUS 0x1000 +#define PCI_DEVICE_ID_SUN_HAPPYMEAL 0x1001 +#define PCI_DEVICE_ID_SUN_RIO_EBUS 0x1100 +#define PCI_DEVICE_ID_SUN_RIO_GEM 0x1101 +#define PCI_DEVICE_ID_SUN_RIO_1394 0x1102 +#define PCI_DEVICE_ID_SUN_RIO_USB 0x1103 +#define PCI_DEVICE_ID_SUN_GEM 0x2bad +#define PCI_DEVICE_ID_SUN_SIMBA 0x5000 +#define PCI_DEVICE_ID_SUN_PBM 0x8000 +#define PCI_DEVICE_ID_SUN_SCHIZO 0x8001 +#define PCI_DEVICE_ID_SUN_SABRE 0xa000 +#define PCI_DEVICE_ID_SUN_HUMMINGBIRD 0xa001 +#define PCI_DEVICE_ID_SUN_TOMATILLO 0xa801 +#define PCI_DEVICE_ID_SUN_CASSINI 0xabba + +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_DEVICE_ID_CMD_643 0x0643 +#define PCI_DEVICE_ID_CMD_646 0x0646 +#define PCI_DEVICE_ID_CMD_648 0x0648 +#define PCI_DEVICE_ID_CMD_649 0x0649 + +#define PCI_DEVICE_ID_SII_680 0x0680 +#define PCI_DEVICE_ID_SII_3112 0x3112 +#define PCI_DEVICE_ID_SII_1210SA 0x0240 + +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_DEVICE_ID_BROOKTREE_878 0x0878 +#define PCI_DEVICE_ID_BROOKTREE_879 0x0879 + +#define PCI_VENDOR_ID_SGI 0x10a9 +#define PCI_DEVICE_ID_SGI_IOC3 0x0003 +#define PCI_DEVICE_ID_SGI_LITHIUM 0x1002 +#define PCI_DEVICE_ID_SGI_IOC4 0x100a + +#define PCI_VENDOR_ID_WINBOND 0x10ad +#define PCI_DEVICE_ID_WINBOND_82C105 0x0105 +#define PCI_DEVICE_ID_WINBOND_83C553 0x0565 + +#define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_DEVICE_ID_PLX_R685 0x1030 +#define PCI_DEVICE_ID_PLX_ROMULUS 0x106a +#define PCI_DEVICE_ID_PLX_SPCOM800 0x1076 +#define PCI_DEVICE_ID_PLX_1077 0x1077 +#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 +#define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 +#define PCI_DEVICE_ID_PLX_R753 0x1152 +#define PCI_DEVICE_ID_PLX_OLITEC 0x1187 +#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196 +#define PCI_DEVICE_ID_PLX_9030 0x9030 +#define PCI_DEVICE_ID_PLX_9050 0x9050 +#define PCI_DEVICE_ID_PLX_9080 0x9080 +#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001 + +#define PCI_VENDOR_ID_MADGE 0x10b6 +#define PCI_DEVICE_ID_MADGE_MK2 0x0002 + +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_DEVICE_ID_3COM_3C985 0x0001 +#define PCI_DEVICE_ID_3COM_3C940 0x1700 +#define PCI_DEVICE_ID_3COM_3C339 0x3390 +#define PCI_DEVICE_ID_3COM_3C359 0x3590 +#define PCI_DEVICE_ID_3COM_3C940B 0x80eb +#define PCI_DEVICE_ID_3COM_3CR990 0x9900 +#define PCI_DEVICE_ID_3COM_3CR990_TX_95 0x9902 +#define PCI_DEVICE_ID_3COM_3CR990_TX_97 0x9903 +#define PCI_DEVICE_ID_3COM_3CR990B 0x9904 +#define PCI_DEVICE_ID_3COM_3CR990_FX 0x9905 +#define PCI_DEVICE_ID_3COM_3CR990SVR95 0x9908 +#define PCI_DEVICE_ID_3COM_3CR990SVR97 0x9909 +#define PCI_DEVICE_ID_3COM_3CR990SVR 0x990a + +#define PCI_VENDOR_ID_AL 0x10b9 +#define PCI_DEVICE_ID_AL_M1533 0x1533 +#define PCI_DEVICE_ID_AL_M1535 0x1535 +#define PCI_DEVICE_ID_AL_M1541 0x1541 +#define PCI_DEVICE_ID_AL_M1563 0x1563 +#define PCI_DEVICE_ID_AL_M1621 0x1621 +#define PCI_DEVICE_ID_AL_M1631 0x1631 +#define PCI_DEVICE_ID_AL_M1632 0x1632 +#define PCI_DEVICE_ID_AL_M1641 0x1641 +#define PCI_DEVICE_ID_AL_M1644 0x1644 +#define PCI_DEVICE_ID_AL_M1647 0x1647 +#define PCI_DEVICE_ID_AL_M1651 0x1651 +#define PCI_DEVICE_ID_AL_M1671 0x1671 +#define PCI_DEVICE_ID_AL_M1681 0x1681 +#define PCI_DEVICE_ID_AL_M1683 0x1683 +#define PCI_DEVICE_ID_AL_M1689 0x1689 +#define PCI_DEVICE_ID_AL_M5219 0x5219 +#define PCI_DEVICE_ID_AL_M5228 0x5228 +#define PCI_DEVICE_ID_AL_M5229 0x5229 +#define PCI_DEVICE_ID_AL_M5451 0x5451 +#define PCI_DEVICE_ID_AL_M7101 0x7101 + +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016 + +#define PCI_VENDOR_ID_TCONRAD 0x10da +#define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508 + +#define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_DEVICE_ID_NVIDIA_TNT 0x0020 +#define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028 +#define PCI_DEVICE_ID_NVIDIA_UTNT2 0x0029 +#define PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN 0x002a +#define PCI_DEVICE_ID_NVIDIA_VTNT2 0x002C +#define PCI_DEVICE_ID_NVIDIA_UVTNT2 0x002D +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS 0x0034 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE 0x0035 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA 0x0036 +#define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037 +#define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2 0x003e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA 0x0040 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800 0x0041 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE 0x0042 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x0045 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000 0x004E +#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS 0x0052 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE 0x0053 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA 0x0054 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2 0x0055 +#define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056 +#define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057 +#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 +#define PCI_DEVICE_ID_NVIDIA_CK804_PCIE 0x005d +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065 +#define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066 +#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069 +#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS 0x0084 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE 0x0085 +#define PCI_DEVICE_ID_NVIDIA_NVENET_4 0x0086 +#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089 +#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a +#define PCI_DEVICE_ID_NVIDIA_NVENET_5 0x008c +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA 0x008e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT 0x0090 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX 0x0091 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800 0x0098 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX 0x0099 +#define PCI_DEVICE_ID_NVIDIA_ITNT2 0x00A0 +#define PCI_DEVICE_ID_GEFORCE_6800A 0x00c1 +#define PCI_DEVICE_ID_GEFORCE_6800A_LE 0x00c2 +#define PCI_DEVICE_ID_GEFORCE_GO_6800 0x00c8 +#define PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA 0x00c9 +#define PCI_DEVICE_ID_QUADRO_FX_GO1400 0x00cc +#define PCI_DEVICE_ID_QUADRO_FX_1400 0x00ce +#define PCI_DEVICE_ID_NVIDIA_NFORCE3 0x00d1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS 0x00d4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE 0x00d5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_3 0x00d6 +#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9 +#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da +#define PCI_DEVICE_ID_NVIDIA_NVENET_7 0x00df +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S 0x00e1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA 0x00e3 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS 0x00e4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE 0x00e5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_6 0x00e6 +#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2 0x00ee +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_ALT1 0x00f0 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT1 0x00f1 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9 +#define PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280 0x00fd +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101 +#define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX 0x0110 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2 0x0111 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO 0x0112 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR 0x0113 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT 0x0140 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600 0x0141 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL 0x0145 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540 0x014E +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200 0x014F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS 0x0150 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2 0x0151 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA 0x0152 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO 0x0153 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE 0x0161 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200 0x0164 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250 0x0166 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1 0x0167 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1 0x0168 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460 0x0170 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440 0x0171 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420 0x0172 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE 0x0173 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO 0x0174 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO 0x0175 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32 0x0176 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO 0x0177 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL 0x0178 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64 0x0179 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_200 0x017A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL 0x017B +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL 0x017C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16 0x017D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X 0x0181 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X 0x0182 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X 0x0183 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_4000 0x0185 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO 0x0186 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO 0x0187 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL 0x0188 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC 0x0189 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS 0x018A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL 0x018B +#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2 0x01a0 +#define PCI_DEVICE_ID_NVIDIA_NFORCE 0x01a4 +#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01b4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_IDE 0x01bc +#define PCI_DEVICE_ID_NVIDIA_MCP1_MODEM 0x01c1 +#define PCI_DEVICE_ID_NVIDIA_NVENET_1 0x01c3 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2 0x01e0 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3 0x0200 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1 0x0201 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2 0x0202 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC 0x0203 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B 0x0211 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE 0x0212 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT 0x0215 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600 0x0250 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400 0x0251 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200 0x0253 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F +#define PCI_DEVICE_ID_NVIDIA_NVENET_12 0x0268 +#define PCI_DEVICE_ID_NVIDIA_NVENET_13 0x0269 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800 0x0280 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X 0x0281 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE 0x0282 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO 0x0286 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL 0x0288 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL 0x0289 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL 0x028C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA 0x0301 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800 0x0302 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000 0x0308 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000 0x0309 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA 0x0311 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600 0x0312 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE 0x0314 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600 0x031A +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650 0x031B +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700 0x031C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200 0x0320 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA 0x0321 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1 0x0322 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE 0x0323 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200 0x0324 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250 0x0325 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500 0x0326 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100 0x0327 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32 0x0328 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200 0x0329 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI 0x032A +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500 0x032B +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300 0x032C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100 0x032D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA 0x0330 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900 0x0331 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT 0x0332 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA 0x0333 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT 0x0334 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000 0x0338 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700 0x033F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA 0x0341 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700 0x0342 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE 0x0343 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE 0x0344 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1 0x0347 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E +#define PCI_DEVICE_ID_NVIDIA_NVENET_14 0x0372 +#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 +#define PCI_DEVICE_ID_NVIDIA_NVENET_16 0x03E5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_17 0x03E6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC +#define PCI_DEVICE_ID_NVIDIA_NVENET_18 0x03EE +#define PCI_DEVICE_ID_NVIDIA_NVENET_19 0x03EF +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2 0x03F6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3 0x03F7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS 0x0446 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448 +#define PCI_DEVICE_ID_NVIDIA_NVENET_20 0x0450 +#define PCI_DEVICE_ID_NVIDIA_NVENET_21 0x0451 +#define PCI_DEVICE_ID_NVIDIA_NVENET_22 0x0452 +#define PCI_DEVICE_ID_NVIDIA_NVENET_23 0x0453 +#define PCI_DEVICE_ID_NVIDIA_NVENET_24 0x054C +#define PCI_DEVICE_ID_NVIDIA_NVENET_25 0x054D +#define PCI_DEVICE_ID_NVIDIA_NVENET_26 0x054E +#define PCI_DEVICE_ID_NVIDIA_NVENET_27 0x054F +#define PCI_DEVICE_ID_NVIDIA_NVENET_28 0x07DC +#define PCI_DEVICE_ID_NVIDIA_NVENET_29 0x07DD +#define PCI_DEVICE_ID_NVIDIA_NVENET_30 0x07DE +#define PCI_DEVICE_ID_NVIDIA_NVENET_31 0x07DF +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 +#define PCI_DEVICE_ID_NVIDIA_NVENET_32 0x0760 +#define PCI_DEVICE_ID_NVIDIA_NVENET_33 0x0761 +#define PCI_DEVICE_ID_NVIDIA_NVENET_34 0x0762 +#define PCI_DEVICE_ID_NVIDIA_NVENET_35 0x0763 +#define PCI_DEVICE_ID_NVIDIA_NVENET_36 0x0AB0 +#define PCI_DEVICE_ID_NVIDIA_NVENET_37 0x0AB1 +#define PCI_DEVICE_ID_NVIDIA_NVENET_38 0x0AB2 +#define PCI_DEVICE_ID_NVIDIA_NVENET_39 0x0AB3 + +#define PCI_VENDOR_ID_IMS 0x10e0 +#define PCI_DEVICE_ID_IMS_TT128 0x9128 +#define PCI_DEVICE_ID_IMS_TT3D 0x9135 + +#define PCI_VENDOR_ID_INTERG 0x10ea +#define PCI_DEVICE_ID_INTERG_1682 0x1682 +#define PCI_DEVICE_ID_INTERG_2000 0x2000 +#define PCI_DEVICE_ID_INTERG_2010 0x2010 +#define PCI_DEVICE_ID_INTERG_5000 0x5000 +#define PCI_DEVICE_ID_INTERG_5050 0x5050 + +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_DEVICE_ID_REALTEK_8139 0x8139 + +#define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_DEVICE_ID_RME_DIGI96 0x3fc0 +#define PCI_DEVICE_ID_RME_DIGI96_8 0x3fc1 +#define PCI_DEVICE_ID_RME_DIGI96_8_PRO 0x3fc2 +#define PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST 0x3fc3 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6 + +#define PCI_VENDOR_ID_INIT 0x1101 + +#define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */ +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 + +#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */ +#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 + +#define PCI_VENDOR_ID_TTI 0x1103 +#define PCI_DEVICE_ID_TTI_HPT343 0x0003 +#define PCI_DEVICE_ID_TTI_HPT366 0x0004 +#define PCI_DEVICE_ID_TTI_HPT372 0x0005 +#define PCI_DEVICE_ID_TTI_HPT302 0x0006 +#define PCI_DEVICE_ID_TTI_HPT371 0x0007 +#define PCI_DEVICE_ID_TTI_HPT374 0x0008 +#define PCI_DEVICE_ID_TTI_HPT372N 0x0009 /* apparently a 372N variant? */ + +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_8763_0 0x0198 +#define PCI_DEVICE_ID_VIA_8380_0 0x0204 +#define PCI_DEVICE_ID_VIA_3238_0 0x0238 +#define PCI_DEVICE_ID_VIA_PT880 0x0258 +#define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0308 +#define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259 +#define PCI_DEVICE_ID_VIA_3269_0 0x0269 +#define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282 +#define PCI_DEVICE_ID_VIA_3296_0 0x0296 +#define PCI_DEVICE_ID_VIA_8363_0 0x0305 +#define PCI_DEVICE_ID_VIA_P4M800CE 0x0314 +#define PCI_DEVICE_ID_VIA_P4M890 0x0327 +#define PCI_DEVICE_ID_VIA_VT3324 0x0324 +#define PCI_DEVICE_ID_VIA_VT3336 0x0336 +#define PCI_DEVICE_ID_VIA_VT3351 0x0351 +#define PCI_DEVICE_ID_VIA_VT3364 0x0364 +#define PCI_DEVICE_ID_VIA_8371_0 0x0391 +#define PCI_DEVICE_ID_VIA_8501_0 0x0501 +#define PCI_DEVICE_ID_VIA_82C561 0x0561 +#define PCI_DEVICE_ID_VIA_82C586_1 0x0571 +#define PCI_DEVICE_ID_VIA_82C576 0x0576 +#define PCI_DEVICE_ID_VIA_82C586_0 0x0586 +#define PCI_DEVICE_ID_VIA_82C596 0x0596 +#define PCI_DEVICE_ID_VIA_82C597_0 0x0597 +#define PCI_DEVICE_ID_VIA_82C598_0 0x0598 +#define PCI_DEVICE_ID_VIA_8601_0 0x0601 +#define PCI_DEVICE_ID_VIA_8605_0 0x0605 +#define PCI_DEVICE_ID_VIA_82C686 0x0686 +#define PCI_DEVICE_ID_VIA_82C691_0 0x0691 +#define PCI_DEVICE_ID_VIA_82C576_1 0x1571 +#define PCI_DEVICE_ID_VIA_82C586_2 0x3038 +#define PCI_DEVICE_ID_VIA_82C586_3 0x3040 +#define PCI_DEVICE_ID_VIA_82C596_3 0x3050 +#define PCI_DEVICE_ID_VIA_82C596B_3 0x3051 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#define PCI_DEVICE_ID_VIA_8233_0 0x3074 +#define PCI_DEVICE_ID_VIA_8633_0 0x3091 +#define PCI_DEVICE_ID_VIA_8367_0 0x3099 +#define PCI_DEVICE_ID_VIA_8653_0 0x3101 +#define PCI_DEVICE_ID_VIA_8622 0x3102 +#define PCI_DEVICE_ID_VIA_8235_USB_2 0x3104 +#define PCI_DEVICE_ID_VIA_8233C_0 0x3109 +#define PCI_DEVICE_ID_VIA_8361 0x3112 +#define PCI_DEVICE_ID_VIA_XM266 0x3116 +#define PCI_DEVICE_ID_VIA_612X 0x3119 +#define PCI_DEVICE_ID_VIA_862X_0 0x3123 +#define PCI_DEVICE_ID_VIA_8753_0 0x3128 +#define PCI_DEVICE_ID_VIA_8233A 0x3147 +#define PCI_DEVICE_ID_VIA_8703_51_0 0x3148 +#define PCI_DEVICE_ID_VIA_8237_SATA 0x3149 +#define PCI_DEVICE_ID_VIA_XN266 0x3156 +#define PCI_DEVICE_ID_VIA_6410 0x3164 +#define PCI_DEVICE_ID_VIA_8754C_0 0x3168 +#define PCI_DEVICE_ID_VIA_8235 0x3177 +#define PCI_DEVICE_ID_VIA_8385_0 0x3188 +#define PCI_DEVICE_ID_VIA_8377_0 0x3189 +#define PCI_DEVICE_ID_VIA_8378_0 0x3205 +#define PCI_DEVICE_ID_VIA_8783_0 0x3208 +#define PCI_DEVICE_ID_VIA_8237 0x3227 +#define PCI_DEVICE_ID_VIA_8251 0x3287 +#define PCI_DEVICE_ID_VIA_8237A 0x3337 +#define PCI_DEVICE_ID_VIA_8237S 0x3372 +#define PCI_DEVICE_ID_VIA_SATA_EIDE 0x5324 +#define PCI_DEVICE_ID_VIA_8231 0x8231 +#define PCI_DEVICE_ID_VIA_8231_4 0x8235 +#define PCI_DEVICE_ID_VIA_8365_1 0x8305 +#define PCI_DEVICE_ID_VIA_CX700 0x8324 +#define PCI_DEVICE_ID_VIA_CX700_IDE 0x0581 +#define PCI_DEVICE_ID_VIA_VX800 0x8353 +#define PCI_DEVICE_ID_VIA_8371_1 0x8391 +#define PCI_DEVICE_ID_VIA_82C598_1 0x8598 +#define PCI_DEVICE_ID_VIA_838X_1 0xB188 +#define PCI_DEVICE_ID_VIA_83_87XX_1 0xB198 + +#define PCI_VENDOR_ID_SIEMENS 0x110A +#define PCI_DEVICE_ID_SIEMENS_DSCC4 0x2102 + +#define PCI_VENDOR_ID_VORTEX 0x1119 +#define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000 +#define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001 +#define PCI_DEVICE_ID_VORTEX_GDT6x10 0x0002 +#define PCI_DEVICE_ID_VORTEX_GDT6x20 0x0003 +#define PCI_DEVICE_ID_VORTEX_GDT6530 0x0004 +#define PCI_DEVICE_ID_VORTEX_GDT6550 0x0005 +#define PCI_DEVICE_ID_VORTEX_GDT6x17 0x0006 +#define PCI_DEVICE_ID_VORTEX_GDT6x27 0x0007 +#define PCI_DEVICE_ID_VORTEX_GDT6537 0x0008 +#define PCI_DEVICE_ID_VORTEX_GDT6557 0x0009 +#define PCI_DEVICE_ID_VORTEX_GDT6x15 0x000a +#define PCI_DEVICE_ID_VORTEX_GDT6x25 0x000b +#define PCI_DEVICE_ID_VORTEX_GDT6535 0x000c +#define PCI_DEVICE_ID_VORTEX_GDT6555 0x000d +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x0100 +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x0101 +#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x0102 +#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x0103 +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x0104 +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x0105 + +#define PCI_VENDOR_ID_EF 0x111a +#define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000 +#define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002 +#define PCI_DEVICE_ID_EF_ATM_LANAI2 0x0003 +#define PCI_DEVICE_ID_EF_ATM_LANAIHB 0x0005 + +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_DEVICE_ID_IDT_IDT77201 0x0001 + +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_DEVICE_ID_FORE_PCA200E 0x0300 + +#define PCI_VENDOR_ID_PHILIPS 0x1131 +#define PCI_DEVICE_ID_PHILIPS_SAA7146 0x7146 +#define PCI_DEVICE_ID_PHILIPS_SAA9730 0x9730 + +#define PCI_VENDOR_ID_EICON 0x1133 +#define PCI_DEVICE_ID_EICON_DIVA20 0xe002 +#define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004 +#define PCI_DEVICE_ID_EICON_DIVA201 0xe005 +#define PCI_DEVICE_ID_EICON_DIVA202 0xe00b +#define PCI_DEVICE_ID_EICON_MAESTRA 0xe010 +#define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012 +#define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013 +#define PCI_DEVICE_ID_EICON_MAESTRAP 0xe014 + +#define PCI_VENDOR_ID_CISCO 0x1137 + +#define PCI_VENDOR_ID_ZIATECH 0x1138 +#define PCI_DEVICE_ID_ZIATECH_5550_HC 0x5550 + + +#define PCI_VENDOR_ID_SYSKONNECT 0x1148 +#define PCI_DEVICE_ID_SYSKONNECT_TR 0x4200 +#define PCI_DEVICE_ID_SYSKONNECT_GE 0x4300 +#define PCI_DEVICE_ID_SYSKONNECT_YU 0x4320 +#define PCI_DEVICE_ID_SYSKONNECT_9DXX 0x4400 +#define PCI_DEVICE_ID_SYSKONNECT_9MXX 0x4500 + +#define PCI_VENDOR_ID_DIGI 0x114f +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070 +#define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 +#define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 +#define PCI_DEVICE_ID_NEO_2DB9 0x00C8 +#define PCI_DEVICE_ID_NEO_2DB9PRI 0x00C9 +#define PCI_DEVICE_ID_NEO_2RJ45 0x00CA +#define PCI_DEVICE_ID_NEO_2RJ45PRI 0x00CB +#define PCIE_DEVICE_ID_NEO_4_IBM 0x00F4 + +#define PCI_VENDOR_ID_XIRCOM 0x115d +#define PCI_DEVICE_ID_XIRCOM_RBM56G 0x0101 +#define PCI_DEVICE_ID_XIRCOM_X3201_MDM 0x0103 + +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_DEVICE_ID_SERVERWORKS_HE 0x0008 +#define PCI_DEVICE_ID_SERVERWORKS_LE 0x0009 +#define PCI_DEVICE_ID_SERVERWORKS_GCNB_LE 0x0017 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB 0x0036 +#define PCI_DEVICE_ID_SERVERWORKS_EPB 0x0103 +#define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4IDE 0x0211 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5IDE 0x0212 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE 0x0213 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 + +#define PCI_VENDOR_ID_SBE 0x1176 +#define PCI_DEVICE_ID_SBE_WANXL100 0x0301 +#define PCI_DEVICE_ID_SBE_WANXL200 0x0302 +#define PCI_DEVICE_ID_SBE_WANXL400 0x0104 + +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO 0x0102 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_1 0x0103 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_2 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a +#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f +#define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x0617 + +#define PCI_VENDOR_ID_TOSHIBA_2 0x102f +#define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU 0x0031 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939 0x0032 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108 +#define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3 + +#define PCI_VENDOR_ID_ATTO 0x117c + +#define PCI_VENDOR_ID_RICOH 0x1180 +#define PCI_DEVICE_ID_RICOH_RL5C465 0x0465 +#define PCI_DEVICE_ID_RICOH_RL5C466 0x0466 +#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475 +#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 +#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 +#define PCI_DEVICE_ID_RICOH_R5C822 0x0822 +#define PCI_DEVICE_ID_RICOH_R5C832 0x0832 +#define PCI_DEVICE_ID_RICOH_R5C843 0x0843 + +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00 + +#define PCI_VENDOR_ID_ARTOP 0x1191 +#define PCI_DEVICE_ID_ARTOP_ATP850UF 0x0005 +#define PCI_DEVICE_ID_ARTOP_ATP860 0x0006 +#define PCI_DEVICE_ID_ARTOP_ATP860R 0x0007 +#define PCI_DEVICE_ID_ARTOP_ATP865 0x0008 +#define PCI_DEVICE_ID_ARTOP_ATP865R 0x0009 +#define PCI_DEVICE_ID_ARTOP_AEC7610 0x8002 +#define PCI_DEVICE_ID_ARTOP_AEC7612UW 0x8010 +#define PCI_DEVICE_ID_ARTOP_AEC7612U 0x8020 +#define PCI_DEVICE_ID_ARTOP_AEC7612S 0x8030 +#define PCI_DEVICE_ID_ARTOP_AEC7612D 0x8040 +#define PCI_DEVICE_ID_ARTOP_AEC7612SUW 0x8050 +#define PCI_DEVICE_ID_ARTOP_8060 0x8060 + +#define PCI_VENDOR_ID_ZEITNET 0x1193 +#define PCI_DEVICE_ID_ZEITNET_1221 0x0001 +#define PCI_DEVICE_ID_ZEITNET_1225 0x0002 + +#define PCI_VENDOR_ID_FUJITSU_ME 0x119e +#define PCI_DEVICE_ID_FUJITSU_FS155 0x0001 +#define PCI_DEVICE_ID_FUJITSU_FS50 0x0003 + +#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 +#define PCI_SUBDEVICE_ID_KEYSPAN_SX2 0x5334 + +#define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_DEVICE_ID_MARVELL_GT64111 0x4146 +#define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 +#define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 +#define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 +#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 +#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 + +#define PCI_VENDOR_ID_V3 0x11b0 +#define PCI_DEVICE_ID_V3_V960 0x0001 +#define PCI_DEVICE_ID_V3_V351 0x0002 + +#define PCI_VENDOR_ID_ATT 0x11c1 +#define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480 + +#define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000 +#define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 +#define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004 + +#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 +#define PCI_DEVICE_ID_AD1889JS 0x1889 + +#define PCI_DEVICE_ID_SEGA_BBA 0x1234 + +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_DEVICE_ID_ZORAN_36057 0x6057 +#define PCI_DEVICE_ID_ZORAN_36120 0x6120 + +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 + +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_DEVICE_ID_RP32INTF 0x0001 +#define PCI_DEVICE_ID_RP8INTF 0x0002 +#define PCI_DEVICE_ID_RP16INTF 0x0003 +#define PCI_DEVICE_ID_RP4QUAD 0x0004 +#define PCI_DEVICE_ID_RP8OCTA 0x0005 +#define PCI_DEVICE_ID_RP8J 0x0006 +#define PCI_DEVICE_ID_RP4J 0x0007 +#define PCI_DEVICE_ID_RP8SNI 0x0008 +#define PCI_DEVICE_ID_RP16SNI 0x0009 +#define PCI_DEVICE_ID_RPP4 0x000A +#define PCI_DEVICE_ID_RPP8 0x000B +#define PCI_DEVICE_ID_RP4M 0x000D +#define PCI_DEVICE_ID_RP2_232 0x000E +#define PCI_DEVICE_ID_RP2_422 0x000F +#define PCI_DEVICE_ID_URP32INTF 0x0801 +#define PCI_DEVICE_ID_URP8INTF 0x0802 +#define PCI_DEVICE_ID_URP16INTF 0x0803 +#define PCI_DEVICE_ID_URP8OCTA 0x0805 +#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C +#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D +#define PCI_DEVICE_ID_CRP16INTF 0x0903 + +#define PCI_VENDOR_ID_CYCLADES 0x120e +#define PCI_DEVICE_ID_CYCLOM_Y_Lo 0x0100 +#define PCI_DEVICE_ID_CYCLOM_Y_Hi 0x0101 +#define PCI_DEVICE_ID_CYCLOM_4Y_Lo 0x0102 +#define PCI_DEVICE_ID_CYCLOM_4Y_Hi 0x0103 +#define PCI_DEVICE_ID_CYCLOM_8Y_Lo 0x0104 +#define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105 +#define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200 +#define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201 +#define PCI_DEVICE_ID_PC300_RX_2 0x0300 +#define PCI_DEVICE_ID_PC300_RX_1 0x0301 +#define PCI_DEVICE_ID_PC300_TE_2 0x0310 +#define PCI_DEVICE_ID_PC300_TE_1 0x0311 +#define PCI_DEVICE_ID_PC300_TE_M_2 0x0320 +#define PCI_DEVICE_ID_PC300_TE_M_1 0x0321 + +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001 + +#define PCI_VENDOR_ID_O2 0x1217 +#define PCI_DEVICE_ID_O2_6729 0x6729 +#define PCI_DEVICE_ID_O2_6730 0x673a +#define PCI_DEVICE_ID_O2_6832 0x6832 +#define PCI_DEVICE_ID_O2_6836 0x6836 + +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_DEVICE_ID_3DFX_VOODOO 0x0001 +#define PCI_DEVICE_ID_3DFX_VOODOO2 0x0002 +#define PCI_DEVICE_ID_3DFX_BANSHEE 0x0003 +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005 +#define PCI_DEVICE_ID_3DFX_VOODOO5 0x0009 + +#define PCI_VENDOR_ID_AVM 0x1244 +#define PCI_DEVICE_ID_AVM_B1 0x0700 +#define PCI_DEVICE_ID_AVM_C4 0x0800 +#define PCI_DEVICE_ID_AVM_A1 0x0a00 +#define PCI_DEVICE_ID_AVM_A1_V2 0x0e00 +#define PCI_DEVICE_ID_AVM_C2 0x1100 +#define PCI_DEVICE_ID_AVM_T1 0x1200 + +#define PCI_VENDOR_ID_STALLION 0x124d + +/* Allied Telesyn */ +#define PCI_VENDOR_ID_AT 0x1259 +#define PCI_SUBDEVICE_ID_AT_2700FX 0x2701 +#define PCI_SUBDEVICE_ID_AT_2701FX 0x2703 + +#define PCI_VENDOR_ID_ESS 0x125d +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 +#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 +#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 +#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 +#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 +#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 +#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 +#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a +#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b + +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#define PCI_DEVICE_ID_SATSAGEM_NICCY 0x1016 + +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 + +#define PCI_VENDOR_ID_TRANSMETA 0x1279 +#define PCI_DEVICE_ID_EFFICEON 0x0060 + +#define PCI_VENDOR_ID_ROCKWELL 0x127A + +#define PCI_VENDOR_ID_ITE 0x1283 +#define PCI_DEVICE_ID_ITE_8211 0x8211 +#define PCI_DEVICE_ID_ITE_8212 0x8212 +#define PCI_DEVICE_ID_ITE_8213 0x8213 +#define PCI_DEVICE_ID_ITE_8152 0x8152 +#define PCI_DEVICE_ID_ITE_8872 0x8872 +#define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886 + +/* formerly Platform Tech */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 + +#define PCI_VENDOR_ID_ALTEON 0x12ae + +#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232 0x0001 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232 0x0002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232 0x0003 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485 0x0004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4 0x0005 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485 0x0006 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2 0x0007 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485 0x0008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6 0x0009 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1 0x000A +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1 0x000B +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ 0x000C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_PTM 0x000D +#define PCI_SUBDEVICE_ID_CONNECT_TECH_NT960PCI 0x0100 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2 0x0201 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4 0x0202 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232 0x0300 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232 0x0301 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232 0x0302 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1 0x0310 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2 0x0311 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4 0x0312 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2 0x0320 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4 0x0321 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8 0x0322 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485 0x0330 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485 0x0331 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485 0x0332 + +#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 +#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018 + +#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST4 0x0031 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST8 0x0021 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16 0x0011 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC 0x0041 +#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS4 0xF001 +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS8 0xF010 + +#define PCI_VENDOR_ID_AUREAL 0x12eb +#define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001 +#define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002 +#define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003 + +#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8 +#define PCI_DEVICE_ID_LML_33R10 0x8a02 + +#define PCI_VENDOR_ID_ESDGMBH 0x12fe +#define PCI_DEVICE_ID_ESDGMBH_CPCIASIO4 0x0111 + +#define PCI_VENDOR_ID_SIIG 0x131f +#define PCI_SUBVENDOR_ID_SIIG 0x131f +#define PCI_DEVICE_ID_SIIG_1S_10x_550 0x1000 +#define PCI_DEVICE_ID_SIIG_1S_10x_650 0x1001 +#define PCI_DEVICE_ID_SIIG_1S_10x_850 0x1002 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012 +#define PCI_DEVICE_ID_SIIG_1P_10x 0x1020 +#define PCI_DEVICE_ID_SIIG_2P_10x 0x1021 +#define PCI_DEVICE_ID_SIIG_2S_10x_550 0x1030 +#define PCI_DEVICE_ID_SIIG_2S_10x_650 0x1031 +#define PCI_DEVICE_ID_SIIG_2S_10x_850 0x1032 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036 +#define PCI_DEVICE_ID_SIIG_4S_10x_550 0x1050 +#define PCI_DEVICE_ID_SIIG_4S_10x_650 0x1051 +#define PCI_DEVICE_ID_SIIG_4S_10x_850 0x1052 +#define PCI_DEVICE_ID_SIIG_1S_20x_550 0x2000 +#define PCI_DEVICE_ID_SIIG_1S_20x_650 0x2001 +#define PCI_DEVICE_ID_SIIG_1S_20x_850 0x2002 +#define PCI_DEVICE_ID_SIIG_1P_20x 0x2020 +#define PCI_DEVICE_ID_SIIG_2P_20x 0x2021 +#define PCI_DEVICE_ID_SIIG_2S_20x_550 0x2030 +#define PCI_DEVICE_ID_SIIG_2S_20x_650 0x2031 +#define PCI_DEVICE_ID_SIIG_2S_20x_850 0x2032 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012 +#define PCI_DEVICE_ID_SIIG_4S_20x_550 0x2050 +#define PCI_DEVICE_ID_SIIG_4S_20x_650 0x2051 +#define PCI_DEVICE_ID_SIIG_4S_20x_850 0x2052 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062 +#define PCI_DEVICE_ID_SIIG_8S_20x_550 0x2080 +#define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081 +#define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082 +#define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050 + +#define PCI_VENDOR_ID_RADISYS 0x1331 + +#define PCI_VENDOR_ID_MICRO_MEMORY 0x1332 +#define PCI_DEVICE_ID_MICRO_MEMORY_5415CN 0x5415 +#define PCI_DEVICE_ID_MICRO_MEMORY_5425CN 0x5425 +#define PCI_DEVICE_ID_MICRO_MEMORY_6155 0x6155 + +#define PCI_VENDOR_ID_DOMEX 0x134a +#define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001 + +#define PCI_VENDOR_ID_INTASHIELD 0x135a +#define PCI_DEVICE_ID_INTASHIELD_IS200 0x0d80 +#define PCI_DEVICE_ID_INTASHIELD_IS400 0x0dc0 + +#define PCI_VENDOR_ID_QUATECH 0x135C +#define PCI_DEVICE_ID_QUATECH_QSC100 0x0010 +#define PCI_DEVICE_ID_QUATECH_DSC100 0x0020 +#define PCI_DEVICE_ID_QUATECH_ESC100D 0x0050 +#define PCI_DEVICE_ID_QUATECH_ESC100M 0x0060 +#define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278 + +#define PCI_VENDOR_ID_SEALEVEL 0x135e +#define PCI_DEVICE_ID_SEALEVEL_U530 0x7101 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM2 0x7201 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM422 0x7402 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM232 0x7202 +#define PCI_DEVICE_ID_SEALEVEL_COMM4 0x7401 +#define PCI_DEVICE_ID_SEALEVEL_COMM8 0x7801 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM8 0x7804 + +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_DEVICE_ID_HYPERCOPE_PLX 0x9050 +#define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO 0x0104 +#define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO 0x0106 +#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO 0x0107 +#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2 0x0108 + +#define PCI_VENDOR_ID_KAWASAKI 0x136b +#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01 + +#define PCI_VENDOR_ID_CNET 0x1371 +#define PCI_DEVICE_ID_CNET_GIGACARD 0x434e + +#define PCI_VENDOR_ID_LMC 0x1376 +#define PCI_DEVICE_ID_LMC_HSSI 0x0003 +#define PCI_DEVICE_ID_LMC_DS3 0x0004 +#define PCI_DEVICE_ID_LMC_SSI 0x0005 +#define PCI_DEVICE_ID_LMC_T1 0x0006 + +#define PCI_VENDOR_ID_NETGEAR 0x1385 +#define PCI_DEVICE_ID_NETGEAR_GA620 0x620a + +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 +#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 +#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 + +#define PCI_VENDOR_ID_MOXA 0x1393 +#define PCI_DEVICE_ID_MOXA_RC7000 0x0001 +#define PCI_DEVICE_ID_MOXA_CP102 0x1020 +#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021 +#define PCI_DEVICE_ID_MOXA_CP102U 0x1022 +#define PCI_DEVICE_ID_MOXA_C104 0x1040 +#define PCI_DEVICE_ID_MOXA_CP104U 0x1041 +#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042 +#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043 +#define PCI_DEVICE_ID_MOXA_CT114 0x1140 +#define PCI_DEVICE_ID_MOXA_CP114 0x1141 +#define PCI_DEVICE_ID_MOXA_CP118U 0x1180 +#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181 +#define PCI_DEVICE_ID_MOXA_CP132 0x1320 +#define PCI_DEVICE_ID_MOXA_CP132U 0x1321 +#define PCI_DEVICE_ID_MOXA_CP134U 0x1340 +#define PCI_DEVICE_ID_MOXA_C168 0x1680 +#define PCI_DEVICE_ID_MOXA_CP168U 0x1681 +#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682 +#define PCI_DEVICE_ID_MOXA_CP204J 0x2040 +#define PCI_DEVICE_ID_MOXA_C218 0x2180 +#define PCI_DEVICE_ID_MOXA_C320 0x3200 + +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4 +#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234 +#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8 +#define PCI_DEVICE_ID_CCD_2BD0 0x2bd0 +#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1 +#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136 +#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137 +#define PCI_DEVICE_ID_CCD_B000 0xb000 +#define PCI_DEVICE_ID_CCD_B006 0xb006 +#define PCI_DEVICE_ID_CCD_B007 0xb007 +#define PCI_DEVICE_ID_CCD_B008 0xb008 +#define PCI_DEVICE_ID_CCD_B009 0xb009 +#define PCI_DEVICE_ID_CCD_B00A 0xb00a +#define PCI_DEVICE_ID_CCD_B00B 0xb00b +#define PCI_DEVICE_ID_CCD_B00C 0xb00c +#define PCI_DEVICE_ID_CCD_B100 0xb100 +#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520 +#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522 +#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523 +#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540 +#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552 +#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560 +#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562 +#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563 +#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564 +#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565 +#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566 +#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567 +#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568 +#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569 +#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A +#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B +#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620 +#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622 +#define PCI_DEVICE_ID_CCD_B700 0xb700 +#define PCI_DEVICE_ID_CCD_B701 0xb701 +#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523 +#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884 +#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888 +#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998 + +#define PCI_VENDOR_ID_EXAR 0x13a8 +#define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 +#define PCI_DEVICE_ID_EXAR_XR17C154 0x0154 +#define PCI_DEVICE_ID_EXAR_XR17C158 0x0158 + +#define PCI_VENDOR_ID_MICROGATE 0x13c0 +#define PCI_DEVICE_ID_MICROGATE_USC 0x0010 +#define PCI_DEVICE_ID_MICROGATE_SCA 0x0030 + +#define PCI_VENDOR_ID_3WARE 0x13C1 +#define PCI_DEVICE_ID_3WARE_1000 0x1000 +#define PCI_DEVICE_ID_3WARE_7000 0x1001 +#define PCI_DEVICE_ID_3WARE_9000 0x1002 + +#define PCI_VENDOR_ID_IOMEGA 0x13ca +#define PCI_DEVICE_ID_IOMEGA_BUZ 0x4231 + +#define PCI_VENDOR_ID_ABOCOM 0x13D1 +#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 + +#define PCI_VENDOR_ID_SUNDANCE 0x13f0 + +#define PCI_VENDOR_ID_CMEDIA 0x13f6 +#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 +#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 + +#define PCI_VENDOR_ID_LAVA 0x1407 +#define PCI_DEVICE_ID_LAVA_DSERIAL 0x0100 /* 2x 16550 */ +#define PCI_DEVICE_ID_LAVA_QUATRO_A 0x0101 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUATRO_B 0x0102 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_A 0x0180 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_B 0x0181 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_PORT_PLUS 0x0200 /* 2x 16650 */ +#define PCI_DEVICE_ID_LAVA_QUAD_A 0x0201 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUAD_B 0x0202 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_SSERIAL 0x0500 /* 1x 16550 */ +#define PCI_DEVICE_ID_LAVA_PORT_650 0x0600 /* 1x 16650 */ +#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000 +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8002 /* The Lava Dual Parallel is */ +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8003 /* two PCI devices on a card */ +#define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR 0x8800 + +#define PCI_VENDOR_ID_TIMEDIA 0x1409 +#define PCI_DEVICE_ID_TIMEDIA_1889 0x7168 + +#define PCI_VENDOR_ID_ICE 0x1412 +#define PCI_DEVICE_ID_ICE_1712 0x1712 +#define PCI_DEVICE_ID_VT1724 0x1724 + +#define PCI_VENDOR_ID_OXSEMI 0x1415 +#define PCI_DEVICE_ID_OXSEMI_12PCI840 0x8403 +#define PCI_DEVICE_ID_OXSEMI_PCIe840 0xC000 +#define PCI_DEVICE_ID_OXSEMI_PCIe840_G 0xC004 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0 0xC100 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0_G 0xC104 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1 0xC110 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_G 0xC114 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_U 0xC118 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU 0xC11C +#define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501 +#define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511 +#define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513 +#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521 +#define PCI_DEVICE_ID_OXSEMI_16PCI952PP 0x9523 + +#define PCI_VENDOR_ID_CHELSIO 0x1425 + +#define PCI_VENDOR_ID_SAMSUNG 0x144d + +#define PCI_VENDOR_ID_MYRICOM 0x14c1 + +#define PCI_VENDOR_ID_TITAN 0x14D2 +#define PCI_DEVICE_ID_TITAN_010L 0x8001 +#define PCI_DEVICE_ID_TITAN_100L 0x8010 +#define PCI_DEVICE_ID_TITAN_110L 0x8011 +#define PCI_DEVICE_ID_TITAN_200L 0x8020 +#define PCI_DEVICE_ID_TITAN_210L 0x8021 +#define PCI_DEVICE_ID_TITAN_400L 0x8040 +#define PCI_DEVICE_ID_TITAN_800L 0x8080 +#define PCI_DEVICE_ID_TITAN_100 0xA001 +#define PCI_DEVICE_ID_TITAN_200 0xA005 +#define PCI_DEVICE_ID_TITAN_400 0xA003 +#define PCI_DEVICE_ID_TITAN_800B 0xA004 + +#define PCI_VENDOR_ID_PANACOM 0x14d4 +#define PCI_DEVICE_ID_PANACOM_QUADMODEM 0x0400 +#define PCI_DEVICE_ID_PANACOM_DUALMODEM 0x0402 + +#define PCI_VENDOR_ID_SIPACKETS 0x14d9 +#define PCI_DEVICE_ID_SP1011 0x0010 + +#define PCI_VENDOR_ID_AFAVLAB 0x14db +#define PCI_DEVICE_ID_AFAVLAB_P028 0x2180 +#define PCI_DEVICE_ID_AFAVLAB_P030 0x2182 +#define PCI_SUBDEVICE_ID_AFAVLAB_P061 0x2150 + +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_TIGON3_5752 0x1600 +#define PCI_DEVICE_ID_TIGON3_5752M 0x1601 +#define PCI_DEVICE_ID_NX2_5709 0x1639 +#define PCI_DEVICE_ID_NX2_5709S 0x163a +#define PCI_DEVICE_ID_TIGON3_5700 0x1644 +#define PCI_DEVICE_ID_TIGON3_5701 0x1645 +#define PCI_DEVICE_ID_TIGON3_5702 0x1646 +#define PCI_DEVICE_ID_TIGON3_5703 0x1647 +#define PCI_DEVICE_ID_TIGON3_5704 0x1648 +#define PCI_DEVICE_ID_TIGON3_5704S_2 0x1649 +#define PCI_DEVICE_ID_NX2_5706 0x164a +#define PCI_DEVICE_ID_NX2_5708 0x164c +#define PCI_DEVICE_ID_TIGON3_5702FE 0x164d +#define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 +#define PCI_DEVICE_ID_TIGON3_5705 0x1653 +#define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 +#define PCI_DEVICE_ID_TIGON3_5720 0x1658 +#define PCI_DEVICE_ID_TIGON3_5721 0x1659 +#define PCI_DEVICE_ID_TIGON3_5722 0x165a +#define PCI_DEVICE_ID_TIGON3_5723 0x165b +#define PCI_DEVICE_ID_TIGON3_5705M 0x165d +#define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e +#define PCI_DEVICE_ID_TIGON3_5714 0x1668 +#define PCI_DEVICE_ID_TIGON3_5714S 0x1669 +#define PCI_DEVICE_ID_TIGON3_5780 0x166a +#define PCI_DEVICE_ID_TIGON3_5780S 0x166b +#define PCI_DEVICE_ID_TIGON3_5705F 0x166e +#define PCI_DEVICE_ID_TIGON3_5754M 0x1672 +#define PCI_DEVICE_ID_TIGON3_5755M 0x1673 +#define PCI_DEVICE_ID_TIGON3_5756 0x1674 +#define PCI_DEVICE_ID_TIGON3_5750 0x1676 +#define PCI_DEVICE_ID_TIGON3_5751 0x1677 +#define PCI_DEVICE_ID_TIGON3_5715 0x1678 +#define PCI_DEVICE_ID_TIGON3_5715S 0x1679 +#define PCI_DEVICE_ID_TIGON3_5754 0x167a +#define PCI_DEVICE_ID_TIGON3_5755 0x167b +#define PCI_DEVICE_ID_TIGON3_5750M 0x167c +#define PCI_DEVICE_ID_TIGON3_5751M 0x167d +#define PCI_DEVICE_ID_TIGON3_5751F 0x167e +#define PCI_DEVICE_ID_TIGON3_5787F 0x167f +#define PCI_DEVICE_ID_TIGON3_5761E 0x1680 +#define PCI_DEVICE_ID_TIGON3_5761 0x1681 +#define PCI_DEVICE_ID_TIGON3_5764 0x1684 +#define PCI_DEVICE_ID_TIGON3_5787M 0x1693 +#define PCI_DEVICE_ID_TIGON3_5782 0x1696 +#define PCI_DEVICE_ID_TIGON3_5784 0x1698 +#define PCI_DEVICE_ID_TIGON3_5785 0x1699 +#define PCI_DEVICE_ID_TIGON3_5786 0x169a +#define PCI_DEVICE_ID_TIGON3_5787 0x169b +#define PCI_DEVICE_ID_TIGON3_5788 0x169c +#define PCI_DEVICE_ID_TIGON3_5789 0x169d +#define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 +#define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 +#define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 +#define PCI_DEVICE_ID_NX2_5706S 0x16aa +#define PCI_DEVICE_ID_NX2_5708S 0x16ac +#define PCI_DEVICE_ID_TIGON3_5702A3 0x16c6 +#define PCI_DEVICE_ID_TIGON3_5703A3 0x16c7 +#define PCI_DEVICE_ID_TIGON3_5781 0x16dd +#define PCI_DEVICE_ID_TIGON3_5753 0x16f7 +#define PCI_DEVICE_ID_TIGON3_5753M 0x16fd +#define PCI_DEVICE_ID_TIGON3_5753F 0x16fe +#define PCI_DEVICE_ID_TIGON3_5901 0x170d +#define PCI_DEVICE_ID_BCM4401B1 0x170c +#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e +#define PCI_DEVICE_ID_TIGON3_5906 0x1712 +#define PCI_DEVICE_ID_TIGON3_5906M 0x1713 +#define PCI_DEVICE_ID_BCM4401 0x4401 +#define PCI_DEVICE_ID_BCM4401B0 0x4402 + +#define PCI_VENDOR_ID_TOPIC 0x151f +#define PCI_DEVICE_ID_TOPIC_TP560 0x0000 + +#define PCI_VENDOR_ID_MAINPINE 0x1522 +#define PCI_DEVICE_ID_MAINPINE_PBRIDGE 0x0100 +#define PCI_VENDOR_ID_ENE 0x1524 +#define PCI_DEVICE_ID_ENE_CB712_SD 0x0550 +#define PCI_DEVICE_ID_ENE_CB712_SD_2 0x0551 +#define PCI_DEVICE_ID_ENE_CB714_SD 0x0750 +#define PCI_DEVICE_ID_ENE_CB714_SD_2 0x0751 +#define PCI_DEVICE_ID_ENE_1211 0x1211 +#define PCI_DEVICE_ID_ENE_1225 0x1225 +#define PCI_DEVICE_ID_ENE_1410 0x1410 +#define PCI_DEVICE_ID_ENE_710 0x1411 +#define PCI_DEVICE_ID_ENE_712 0x1412 +#define PCI_DEVICE_ID_ENE_1420 0x1420 +#define PCI_DEVICE_ID_ENE_720 0x1421 +#define PCI_DEVICE_ID_ENE_722 0x1422 + +#define PCI_SUBVENDOR_ID_PERLE 0x155f +#define PCI_SUBDEVICE_ID_PCI_RAS4 0xf001 +#define PCI_SUBDEVICE_ID_PCI_RAS8 0xf010 + +#define PCI_VENDOR_ID_SYBA 0x1592 +#define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782 +#define PCI_DEVICE_ID_SYBA_1P_ECP 0x0783 + +#define PCI_VENDOR_ID_MORETON 0x15aa +#define PCI_DEVICE_ID_RASTEL_2PORT 0x2000 + +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd0 + +#define PCI_VENDOR_ID_MELLANOX 0x15b3 +#define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44 +#define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46 +#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278 +#define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282 +#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c +#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 + +#define PCI_VENDOR_ID_QUICKNET 0x15e2 +#define PCI_DEVICE_ID_QUICKNET_XJ 0x0500 + +/* + * ADDI-DATA GmbH communication cards <info@addi-data.com> + */ +#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 +#define PCI_VENDOR_ID_ADDIDATA 0x15B8 +#define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001 +#define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002 +#define PCI_DEVICE_ID_ADDIDATA_APCI7800 0x818E +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_3 0x700C +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_3 0x700D +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_3 0x700E +#define PCI_DEVICE_ID_ADDIDATA_APCI7800_3 0x700F + +#define PCI_VENDOR_ID_PDC 0x15e9 + +#define PCI_VENDOR_ID_FARSITE 0x1619 +#define PCI_DEVICE_ID_FARSITE_T2P 0x0400 +#define PCI_DEVICE_ID_FARSITE_T4P 0x0440 +#define PCI_DEVICE_ID_FARSITE_T1U 0x0610 +#define PCI_DEVICE_ID_FARSITE_T2U 0x0620 +#define PCI_DEVICE_ID_FARSITE_T4U 0x0640 +#define PCI_DEVICE_ID_FARSITE_TE1 0x1610 +#define PCI_DEVICE_ID_FARSITE_TE1C 0x1612 + +#define PCI_VENDOR_ID_ARIMA 0x161f + +#define PCI_VENDOR_ID_BROCADE 0x1657 + +#define PCI_VENDOR_ID_SIBYTE 0x166d +#define PCI_DEVICE_ID_BCM1250_PCI 0x0001 +#define PCI_DEVICE_ID_BCM1250_HT 0x0002 + +#define PCI_VENDOR_ID_ATHEROS 0x168c + +#define PCI_VENDOR_ID_NETCELL 0x169c +#define PCI_DEVICE_ID_REVOLUTION 0x0044 + +#define PCI_VENDOR_ID_CENATEK 0x16CA +#define PCI_DEVICE_ID_CENATEK_IDE 0x0001 + +#define PCI_VENDOR_ID_VITESSE 0x1725 +#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 + +#define PCI_VENDOR_ID_LINKSYS 0x1737 +#define PCI_DEVICE_ID_LINKSYS_EG1064 0x1064 + +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8 +#define PCI_DEVICE_ID_ALTIMA_AC1001 0x03e9 +#define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea +#define PCI_DEVICE_ID_ALTIMA_AC1003 0x03eb + +#define PCI_VENDOR_ID_BELKIN 0x1799 +#define PCI_DEVICE_ID_BELKIN_F5D7010V7 0x701f + +#define PCI_VENDOR_ID_RDC 0x17f3 +#define PCI_DEVICE_ID_RDC_R6020 0x6020 +#define PCI_DEVICE_ID_RDC_R6030 0x6030 +#define PCI_DEVICE_ID_RDC_R6040 0x6040 +#define PCI_DEVICE_ID_RDC_R6060 0x6060 +#define PCI_DEVICE_ID_RDC_R6061 0x6061 + +#define PCI_VENDOR_ID_LENOVO 0x17aa + +#define PCI_VENDOR_ID_ARECA 0x17d3 +#define PCI_DEVICE_ID_ARECA_1110 0x1110 +#define PCI_DEVICE_ID_ARECA_1120 0x1120 +#define PCI_DEVICE_ID_ARECA_1130 0x1130 +#define PCI_DEVICE_ID_ARECA_1160 0x1160 +#define PCI_DEVICE_ID_ARECA_1170 0x1170 +#define PCI_DEVICE_ID_ARECA_1200 0x1200 +#define PCI_DEVICE_ID_ARECA_1201 0x1201 +#define PCI_DEVICE_ID_ARECA_1202 0x1202 +#define PCI_DEVICE_ID_ARECA_1210 0x1210 +#define PCI_DEVICE_ID_ARECA_1220 0x1220 +#define PCI_DEVICE_ID_ARECA_1230 0x1230 +#define PCI_DEVICE_ID_ARECA_1260 0x1260 +#define PCI_DEVICE_ID_ARECA_1270 0x1270 +#define PCI_DEVICE_ID_ARECA_1280 0x1280 +#define PCI_DEVICE_ID_ARECA_1380 0x1380 +#define PCI_DEVICE_ID_ARECA_1381 0x1381 +#define PCI_DEVICE_ID_ARECA_1680 0x1680 +#define PCI_DEVICE_ID_ARECA_1681 0x1681 + +#define PCI_VENDOR_ID_S2IO 0x17d5 +#define PCI_DEVICE_ID_S2IO_WIN 0x5731 +#define PCI_DEVICE_ID_S2IO_UNI 0x5831 +#define PCI_DEVICE_ID_HERC_WIN 0x5732 +#define PCI_DEVICE_ID_HERC_UNI 0x5832 + +#define PCI_VENDOR_ID_SITECOM 0x182d +#define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069 + +#define PCI_VENDOR_ID_TOPSPIN 0x1867 + +#define PCI_VENDOR_ID_TDI 0x192E +#define PCI_DEVICE_ID_TDI_EHCI 0x0101 + +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_DEVICE_ID_MPC8548E 0x0012 +#define PCI_DEVICE_ID_MPC8548 0x0013 +#define PCI_DEVICE_ID_MPC8543E 0x0014 +#define PCI_DEVICE_ID_MPC8543 0x0015 +#define PCI_DEVICE_ID_MPC8547E 0x0018 +#define PCI_DEVICE_ID_MPC8545E 0x0019 +#define PCI_DEVICE_ID_MPC8545 0x001a +#define PCI_DEVICE_ID_MPC8568E 0x0020 +#define PCI_DEVICE_ID_MPC8568 0x0021 +#define PCI_DEVICE_ID_MPC8567E 0x0022 +#define PCI_DEVICE_ID_MPC8567 0x0023 +#define PCI_DEVICE_ID_MPC8533E 0x0030 +#define PCI_DEVICE_ID_MPC8533 0x0031 +#define PCI_DEVICE_ID_MPC8544E 0x0032 +#define PCI_DEVICE_ID_MPC8544 0x0033 +#define PCI_DEVICE_ID_MPC8572E 0x0040 +#define PCI_DEVICE_ID_MPC8572 0x0041 +#define PCI_DEVICE_ID_MPC8536E 0x0050 +#define PCI_DEVICE_ID_MPC8536 0x0051 +#define PCI_DEVICE_ID_MPC8641 0x7010 +#define PCI_DEVICE_ID_MPC8641D 0x7011 +#define PCI_DEVICE_ID_MPC8610 0x7018 + +#define PCI_VENDOR_ID_PASEMI 0x1959 + +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_DEVICE_ID_ATTANSIC_L1 0x1048 +#define PCI_DEVICE_ID_ATTANSIC_L2 0x2048 + +#define PCI_VENDOR_ID_JMICRON 0x197B +#define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 +#define PCI_DEVICE_ID_JMICRON_JMB361 0x2361 +#define PCI_DEVICE_ID_JMICRON_JMB363 0x2363 +#define PCI_DEVICE_ID_JMICRON_JMB365 0x2365 +#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 +#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 +#define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383 + +#define PCI_VENDOR_ID_KORENIX 0x1982 +#define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600 +#define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff + +#define PCI_VENDOR_ID_TEKRAM 0x1de1 +#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 + +#define PCI_VENDOR_ID_TEHUTI 0x1fc9 +#define PCI_DEVICE_ID_TEHUTI_3009 0x3009 +#define PCI_DEVICE_ID_TEHUTI_3010 0x3010 +#define PCI_DEVICE_ID_TEHUTI_3014 0x3014 + +#define PCI_VENDOR_ID_HINT 0x3388 +#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 + +#define PCI_VENDOR_ID_3DLABS 0x3d3d +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007 +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009 + +#define PCI_VENDOR_ID_NETXEN 0x4040 +#define PCI_DEVICE_ID_NX2031_10GXSR 0x0001 +#define PCI_DEVICE_ID_NX2031_10GCX4 0x0002 +#define PCI_DEVICE_ID_NX2031_4GCU 0x0003 +#define PCI_DEVICE_ID_NX2031_IMEZ 0x0004 +#define PCI_DEVICE_ID_NX2031_HMEZ 0x0005 +#define PCI_DEVICE_ID_NX2031_XG_MGMT 0x0024 +#define PCI_DEVICE_ID_NX2031_XG_MGMT2 0x0025 +#define PCI_DEVICE_ID_NX3031 0x0100 + +#define PCI_VENDOR_ID_AKS 0x416c +#define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100 + +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_DEVICE_ID_S3_TRIO 0x8811 +#define PCI_DEVICE_ID_S3_868 0x8880 +#define PCI_DEVICE_ID_S3_968 0x88f0 +#define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25 +#define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04 +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 + +#define PCI_VENDOR_ID_DUNORD 0x5544 +#define PCI_DEVICE_ID_DUNORD_I3000 0x0001 + +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_DEVICE_ID_DCI_PCCOM4 0x0001 +#define PCI_DEVICE_ID_DCI_PCCOM8 0x0002 +#define PCI_DEVICE_ID_DCI_PCCOM2 0x0004 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_EESSC 0x0008 +#define PCI_DEVICE_ID_INTEL_PXHD_0 0x0320 +#define PCI_DEVICE_ID_INTEL_PXHD_1 0x0321 +#define PCI_DEVICE_ID_INTEL_PXH_0 0x0329 +#define PCI_DEVICE_ID_INTEL_PXH_1 0x032A +#define PCI_DEVICE_ID_INTEL_PXHV 0x032C +#define PCI_DEVICE_ID_INTEL_82375 0x0482 +#define PCI_DEVICE_ID_INTEL_82424 0x0483 +#define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_I960 0x0960 +#define PCI_DEVICE_ID_INTEL_I960RM 0x0962 +#define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 +#define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 +#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 +#define PCI_DEVICE_ID_INTEL_7505_0 0x2550 +#define PCI_DEVICE_ID_INTEL_7205_0 0x255d +#define PCI_DEVICE_ID_INTEL_82437 0x122d +#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e +#define PCI_DEVICE_ID_INTEL_82371FB_1 0x1230 +#define PCI_DEVICE_ID_INTEL_82371MX 0x1234 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82380FB 0x124b +#define PCI_DEVICE_ID_INTEL_82439 0x1250 +#define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 +#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 +#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 +#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 +#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 +#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 +#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 +#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801AA_6 0x2416 +#define PCI_DEVICE_ID_INTEL_82801AA_8 0x2418 +#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420 +#define PCI_DEVICE_ID_INTEL_82801AB_1 0x2421 +#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 +#define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425 +#define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426 +#define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428 +#define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440 +#define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443 +#define PCI_DEVICE_ID_INTEL_82801BA_4 0x2445 +#define PCI_DEVICE_ID_INTEL_82801BA_6 0x2448 +#define PCI_DEVICE_ID_INTEL_82801BA_8 0x244a +#define PCI_DEVICE_ID_INTEL_82801BA_9 0x244b +#define PCI_DEVICE_ID_INTEL_82801BA_10 0x244c +#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e +#define PCI_DEVICE_ID_INTEL_82801E_0 0x2450 +#define PCI_DEVICE_ID_INTEL_82801E_11 0x245b +#define PCI_DEVICE_ID_INTEL_82801CA_0 0x2480 +#define PCI_DEVICE_ID_INTEL_82801CA_3 0x2483 +#define PCI_DEVICE_ID_INTEL_82801CA_5 0x2485 +#define PCI_DEVICE_ID_INTEL_82801CA_6 0x2486 +#define PCI_DEVICE_ID_INTEL_82801CA_10 0x248a +#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b +#define PCI_DEVICE_ID_INTEL_82801CA_12 0x248c +#define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0 +#define PCI_DEVICE_ID_INTEL_82801DB_1 0x24c1 +#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3 +#define PCI_DEVICE_ID_INTEL_82801DB_5 0x24c5 +#define PCI_DEVICE_ID_INTEL_82801DB_6 0x24c6 +#define PCI_DEVICE_ID_INTEL_82801DB_9 0x24c9 +#define PCI_DEVICE_ID_INTEL_82801DB_10 0x24ca +#define PCI_DEVICE_ID_INTEL_82801DB_11 0x24cb +#define PCI_DEVICE_ID_INTEL_82801DB_12 0x24cc +#define PCI_DEVICE_ID_INTEL_82801EB_0 0x24d0 +#define PCI_DEVICE_ID_INTEL_82801EB_1 0x24d1 +#define PCI_DEVICE_ID_INTEL_82801EB_3 0x24d3 +#define PCI_DEVICE_ID_INTEL_82801EB_5 0x24d5 +#define PCI_DEVICE_ID_INTEL_82801EB_6 0x24d6 +#define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db +#define PCI_DEVICE_ID_INTEL_82801EB_12 0x24dc +#define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd +#define PCI_DEVICE_ID_INTEL_ESB_1 0x25a1 +#define PCI_DEVICE_ID_INTEL_ESB_2 0x25a2 +#define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4 +#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6 +#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500 +#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501 +#define PCI_DEVICE_ID_INTEL_82850_HB 0x2530 +#define PCI_DEVICE_ID_INTEL_82860_HB 0x2531 +#define PCI_DEVICE_ID_INTEL_E7501_MCH 0x254c +#define PCI_DEVICE_ID_INTEL_82845G_HB 0x2560 +#define PCI_DEVICE_ID_INTEL_82845G_IG 0x2562 +#define PCI_DEVICE_ID_INTEL_82865_HB 0x2570 +#define PCI_DEVICE_ID_INTEL_82865_IG 0x2572 +#define PCI_DEVICE_ID_INTEL_82875_HB 0x2578 +#define PCI_DEVICE_ID_INTEL_82915G_HB 0x2580 +#define PCI_DEVICE_ID_INTEL_82915G_IG 0x2582 +#define PCI_DEVICE_ID_INTEL_82915GM_HB 0x2590 +#define PCI_DEVICE_ID_INTEL_82915GM_IG 0x2592 +#define PCI_DEVICE_ID_INTEL_5000_ERR 0x25F0 +#define PCI_DEVICE_ID_INTEL_5000_FBD0 0x25F5 +#define PCI_DEVICE_ID_INTEL_5000_FBD1 0x25F6 +#define PCI_DEVICE_ID_INTEL_82945G_HB 0x2770 +#define PCI_DEVICE_ID_INTEL_82945G_IG 0x2772 +#define PCI_DEVICE_ID_INTEL_3000_HB 0x2778 +#define PCI_DEVICE_ID_INTEL_82945GM_HB 0x27A0 +#define PCI_DEVICE_ID_INTEL_82945GM_IG 0x27A2 +#define PCI_DEVICE_ID_INTEL_ICH6_0 0x2640 +#define PCI_DEVICE_ID_INTEL_ICH6_1 0x2641 +#define PCI_DEVICE_ID_INTEL_ICH6_2 0x2642 +#define PCI_DEVICE_ID_INTEL_ICH6_16 0x266a +#define PCI_DEVICE_ID_INTEL_ICH6_17 0x266d +#define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e +#define PCI_DEVICE_ID_INTEL_ICH6_19 0x266f +#define PCI_DEVICE_ID_INTEL_ESB2_0 0x2670 +#define PCI_DEVICE_ID_INTEL_ESB2_14 0x2698 +#define PCI_DEVICE_ID_INTEL_ESB2_17 0x269b +#define PCI_DEVICE_ID_INTEL_ESB2_18 0x269e +#define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b8 +#define PCI_DEVICE_ID_INTEL_ICH7_1 0x27b9 +#define PCI_DEVICE_ID_INTEL_ICH7_30 0x27b0 +#define PCI_DEVICE_ID_INTEL_ICH7_31 0x27bd +#define PCI_DEVICE_ID_INTEL_ICH7_17 0x27da +#define PCI_DEVICE_ID_INTEL_ICH7_19 0x27dd +#define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de +#define PCI_DEVICE_ID_INTEL_ICH7_21 0x27df +#define PCI_DEVICE_ID_INTEL_ICH8_0 0x2810 +#define PCI_DEVICE_ID_INTEL_ICH8_1 0x2811 +#define PCI_DEVICE_ID_INTEL_ICH8_2 0x2812 +#define PCI_DEVICE_ID_INTEL_ICH8_3 0x2814 +#define PCI_DEVICE_ID_INTEL_ICH8_4 0x2815 +#define PCI_DEVICE_ID_INTEL_ICH8_5 0x283e +#define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 +#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a +#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b +#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c +#define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433 +#define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 +#define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 +#define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580 +#define PCI_DEVICE_ID_INTEL_82855GM_IG 0x3582 +#define PCI_DEVICE_ID_INTEL_E7520_MCH 0x3590 +#define PCI_DEVICE_ID_INTEL_E7320_MCH 0x3592 +#define PCI_DEVICE_ID_INTEL_MCH_PA 0x3595 +#define PCI_DEVICE_ID_INTEL_MCH_PA1 0x3596 +#define PCI_DEVICE_ID_INTEL_MCH_PB 0x3597 +#define PCI_DEVICE_ID_INTEL_MCH_PB1 0x3598 +#define PCI_DEVICE_ID_INTEL_MCH_PC 0x3599 +#define PCI_DEVICE_ID_INTEL_MCH_PC1 0x359a +#define PCI_DEVICE_ID_INTEL_E7525_MCH 0x359e +#define PCI_DEVICE_ID_INTEL_IOAT_CNB 0x360b +#define PCI_DEVICE_ID_INTEL_FBD_CNB 0x360c +#define PCI_DEVICE_ID_INTEL_ICH10_0 0x3a14 +#define PCI_DEVICE_ID_INTEL_ICH10_1 0x3a16 +#define PCI_DEVICE_ID_INTEL_ICH10_2 0x3a18 +#define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a +#define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 +#define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 +#define PCI_DEVICE_ID_INTEL_PCH_LPC_MIN 0x3b00 +#define PCI_DEVICE_ID_INTEL_PCH_LPC_MAX 0x3b1f +#define PCI_DEVICE_ID_INTEL_PCH_SMBUS 0x3b30 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f +#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 +#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 +#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 +#define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030 +#define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 +#define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 +#define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff +#define PCI_DEVICE_ID_INTEL_TOLAPAI_0 0x5031 +#define PCI_DEVICE_ID_INTEL_TOLAPAI_1 0x5032 +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 +#define PCI_DEVICE_ID_INTEL_82437VX 0x7030 +#define PCI_DEVICE_ID_INTEL_82439TX 0x7100 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120 +#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 +#define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122 +#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 +#define PCI_DEVICE_ID_INTEL_82810E_MC 0x7124 +#define PCI_DEVICE_ID_INTEL_82810E_IG 0x7125 +#define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 +#define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 +#define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 +#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 +#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#define PCI_DEVICE_ID_INTEL_440MX_6 0x7196 +#define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198 +#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199 +#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b +#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 +#define PCI_DEVICE_ID_INTEL_82443GX_2 0x71a2 +#define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601 +#define PCI_DEVICE_ID_INTEL_SCH_LPC 0x8119 +#define PCI_DEVICE_ID_INTEL_SCH_IDE 0x811a +#define PCI_DEVICE_ID_INTEL_82454GX 0x84c4 +#define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 +#define PCI_DEVICE_ID_INTEL_82451NX 0x84ca +#define PCI_DEVICE_ID_INTEL_82454NX 0x84cb +#define PCI_DEVICE_ID_INTEL_84460GX 0x84ea +#define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500 +#define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 +#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 + +#define PCI_VENDOR_ID_SCALEMP 0x8686 +#define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 + +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 +#define PCI_DEVICE_ID_COMPUTONE_PG 0x0302 +#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e +#define PCI_SUBDEVICE_ID_COMPUTONE_PG4 0x0001 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG8 0x0002 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG6 0x0003 + +#define PCI_VENDOR_ID_KTI 0x8e2e + +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_DEVICE_ID_ADAPTEC_7810 0x1078 +#define PCI_DEVICE_ID_ADAPTEC_7821 0x2178 +#define PCI_DEVICE_ID_ADAPTEC_38602 0x3860 +#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 +#define PCI_DEVICE_ID_ADAPTEC_7855 0x5578 +#define PCI_DEVICE_ID_ADAPTEC_3860 0x6038 +#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 +#define PCI_DEVICE_ID_ADAPTEC_7860 0x6078 +#define PCI_DEVICE_ID_ADAPTEC_7861 0x6178 +#define PCI_DEVICE_ID_ADAPTEC_7870 0x7078 +#define PCI_DEVICE_ID_ADAPTEC_7871 0x7178 +#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278 +#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378 +#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478 +#define PCI_DEVICE_ID_ADAPTEC_7895 0x7895 +#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078 +#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178 +#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278 +#define PCI_DEVICE_ID_ADAPTEC_7883 0x8378 +#define PCI_DEVICE_ID_ADAPTEC_7884 0x8478 +#define PCI_DEVICE_ID_ADAPTEC_7885 0x8578 +#define PCI_DEVICE_ID_ADAPTEC_7886 0x8678 +#define PCI_DEVICE_ID_ADAPTEC_7887 0x8778 +#define PCI_DEVICE_ID_ADAPTEC_7888 0x8878 + +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010 +#define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011 +#define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013 +#define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f +#define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050 +#define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051 +#define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f +#define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080 +#define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081 +#define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083 +#define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f +#define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0 +#define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1 +#define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3 +#define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf +#define PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN 0x0500 +#define PCI_DEVICE_ID_ADAPTEC2_SCAMP 0x0503 + +#define PCI_VENDOR_ID_HOLTEK 0x9412 +#define PCI_DEVICE_ID_HOLTEK_6565 0x6565 + +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_DEVICE_ID_NETMOS_9705 0x9705 +#define PCI_DEVICE_ID_NETMOS_9715 0x9715 +#define PCI_DEVICE_ID_NETMOS_9735 0x9735 +#define PCI_DEVICE_ID_NETMOS_9745 0x9745 +#define PCI_DEVICE_ID_NETMOS_9755 0x9755 +#define PCI_DEVICE_ID_NETMOS_9805 0x9805 +#define PCI_DEVICE_ID_NETMOS_9815 0x9815 +#define PCI_DEVICE_ID_NETMOS_9835 0x9835 +#define PCI_DEVICE_ID_NETMOS_9845 0x9845 +#define PCI_DEVICE_ID_NETMOS_9855 0x9855 + +#define PCI_VENDOR_ID_3COM_2 0xa727 + +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410 + +#define PCI_SUBVENDOR_ID_EXSYS 0xd84d +#define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 +#define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055 + +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#define PCI_DEVICE_ID_TIGERJET_300 0x0001 +#define PCI_DEVICE_ID_TIGERJET_100 0x0002 + +#define PCI_VENDOR_ID_XILINX_RME 0xea60 +#define PCI_DEVICE_ID_RME_DIGI32 0x9896 +#define PCI_DEVICE_ID_RME_DIGI32_PRO 0x9897 +#define PCI_DEVICE_ID_RME_DIGI32_8 0x9898 + +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 +#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 + +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0 diff --git a/qemu/roms/seabios/src/hw/pci_regs.h b/qemu/roms/seabios/src/hw/pci_regs.h new file mode 100644 index 000000000..e5effd47e --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci_regs.h @@ -0,0 +1,556 @@ +/* + * pci_regs.h + * + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares <mj@ucw.cz> + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification + */ + +#ifndef LINUX_PCI_REGS_H +#define LINUX_PCI_REGS_H + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +#define PCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03UL) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_DBG 0x0A /* Debug port */ +#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Vital Product Data */ + +#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ +#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +#define PCI_MSI_MASK_BIT 16 /* Mask bits register */ + +/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ +#define PCI_MSIX_FLAGS 2 +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_FLAGS_BITMASK (1 << 0) + +/* CompactPCI Hotswap Register */ + +#define PCI_CHSWP_CSR 2 /* Control and Status Register */ +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ + +/* PCI-X registers */ + +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ +#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ +#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ + /* Max # of outstanding split transactions */ +#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ +#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ +#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ +#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ +#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ +#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ +#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ +#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ +#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_STATUS 4 /* PCI-X capabilities */ +#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ + +/* PCI Express capability registers */ + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ +#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCAP_ASPMS 0xc00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_L0SEL 0x7000 /* L0s Exit Latency */ +#define PCI_EXP_LNKCAP_L1EL 0x38000 /* L1 Exit Latency */ +#define PCI_EXP_LNKCAP_CLKPM 0x40000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_RL 0x20 /* Retrain Link */ +#define PCI_EXP_LNKCTL_CCC 0x40 /* Common Clock COnfiguration */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_LT 0x800 /* Link Training */ +#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ +#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ +#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define PCI_EXT_CAP_ID_ERR 1 +#define PCI_EXT_CAP_ID_VC 2 +#define PCI_EXT_CAP_ID_DSN 3 +#define PCI_EXT_CAP_ID_PWR 4 +#define PCI_EXT_CAP_ID_ARI 14 + +/* Advanced Error Reporting */ +#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ +#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ +#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ +#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ +#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ +#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ +#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ +#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ +#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ + /* Same bits as above */ +#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ +#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ +#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ +#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ +#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ +#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ +#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ +#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ +/* Correctable Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 +/* Non-fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 +/* Fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 +#define PCI_ERR_ROOT_STATUS 48 +#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ +/* Multi ERR_COR Received */ +#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 +/* ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 +/* Multi ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 +#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ +#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ +#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_COR_SRC 52 +#define PCI_ERR_ROOT_SRC 54 + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +/* + * Hypertransport sub capability types + * + * Unfortunately there are both 3 bit and 5 bit capability types defined + * in the HT spec, catering for that is a little messy. You probably don't + * want to use these directly, just use pci_find_ht_capability() and it + * will do the right thing for you. + */ +#define HT_3BIT_CAP_MASK 0xE0 +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ + +#define HT_5BIT_CAP_MASK 0xF8 +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_MSI_FLAGS 0x02 /* Offset to flags */ +#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ +#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ +#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ +#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ +#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ +#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + +/* Alternative Routing-ID Interpretation */ +#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ +#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ +#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ +#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ +#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ +#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ +#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ +#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ + +#endif /* LINUX_PCI_REGS_H */ diff --git a/qemu/roms/seabios/src/hw/pic.c b/qemu/roms/seabios/src/hw/pic.c new file mode 100644 index 000000000..6ff696765 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pic.c @@ -0,0 +1,101 @@ +// Helpers for working with i8259 interrupt controller. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_IVT +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "pic.h" // pic_* + +u16 +pic_irqmask_read(void) +{ + return inb(PORT_PIC1_DATA) | (inb(PORT_PIC2_DATA) << 8); +} + +void +pic_irqmask_write(u16 mask) +{ + outb(mask, PORT_PIC1_DATA); + outb(mask >> 8, PORT_PIC2_DATA); +} + +void +pic_irqmask_mask(u16 off, u16 on) +{ + u8 pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8; + outb((inb(PORT_PIC1_DATA) & ~pic1off) | pic1on, PORT_PIC1_DATA); + outb((inb(PORT_PIC2_DATA) & ~pic2off) | pic2on, PORT_PIC2_DATA); +} + +void +pic_reset(u8 irq0, u8 irq8) +{ + // Send ICW1 (select OCW1 + will send ICW4) + outb(0x11, PORT_PIC1_CMD); + outb(0x11, PORT_PIC2_CMD); + // Send ICW2 (base irqs: 0x08-0x0f for irq0-7, 0x70-0x77 for irq8-15) + outb(irq0, PORT_PIC1_DATA); + outb(irq8, PORT_PIC2_DATA); + // Send ICW3 (cascaded pic ids) + outb(0x04, PORT_PIC1_DATA); + outb(0x02, PORT_PIC2_DATA); + // Send ICW4 (enable 8086 mode) + outb(0x01, PORT_PIC1_DATA); + outb(0x01, PORT_PIC2_DATA); + // Mask all irqs (except cascaded PIC2 irq) + pic_irqmask_write(PIC_IRQMASK_DEFAULT); +} + +void +pic_setup(void) +{ + dprintf(3, "init pic\n"); + pic_reset(BIOS_HWIRQ0_VECTOR, BIOS_HWIRQ8_VECTOR); +} + +void +enable_hwirq(int hwirq, struct segoff_s func) +{ + pic_irqmask_mask(1 << hwirq, 0); + int vector; + if (hwirq < 8) + vector = BIOS_HWIRQ0_VECTOR + hwirq; + else + vector = BIOS_HWIRQ8_VECTOR + hwirq - 8; + SET_IVT(vector, func); +} + +static u8 +pic_isr1_read(void) +{ + // 0x0b == select OCW1 + read ISR + outb(0x0b, PORT_PIC1_CMD); + return inb(PORT_PIC1_CMD); +} + +static u8 +pic_isr2_read(void) +{ + // 0x0b == select OCW1 + read ISR + outb(0x0b, PORT_PIC2_CMD); + return inb(PORT_PIC2_CMD); +} + +// Handler for otherwise unused hardware irqs. +void VISIBLE16 +handle_hwpic1(struct bregs *regs) +{ + dprintf(DEBUG_ISR_hwpic1, "handle_hwpic1 irq=%x\n", pic_isr1_read()); + pic_eoi1(); +} + +void VISIBLE16 +handle_hwpic2(struct bregs *regs) +{ + dprintf(DEBUG_ISR_hwpic2, "handle_hwpic2 irq=%x\n", pic_isr2_read()); + pic_eoi2(); +} diff --git a/qemu/roms/seabios/src/hw/pic.h b/qemu/roms/seabios/src/hw/pic.h new file mode 100644 index 000000000..6947b6e81 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pic.h @@ -0,0 +1,56 @@ +// Helpers for working with i8259 interrupt controller. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __PIC_H +#define __PIC_H + +#include "x86.h" // outb + +#define PORT_PIC1_CMD 0x0020 +#define PORT_PIC1_DATA 0x0021 +#define PORT_PIC2_CMD 0x00a0 +#define PORT_PIC2_DATA 0x00a1 + +// PORT_PIC1 bitdefs +#define PIC1_IRQ0 (1<<0) +#define PIC1_IRQ1 (1<<1) +#define PIC1_IRQ2 (1<<2) +#define PIC1_IRQ5 (1<<5) +#define PIC1_IRQ6 (1<<6) +// PORT_PIC2 bitdefs +#define PIC2_IRQ8 (1<<8) +#define PIC2_IRQ12 (1<<12) +#define PIC2_IRQ13 (1<<13) +#define PIC2_IRQ14 (1<<14) + +#define PIC_IRQMASK_DEFAULT ((u16)~PIC1_IRQ2) + +#define BIOS_HWIRQ0_VECTOR 0x08 +#define BIOS_HWIRQ8_VECTOR 0x70 + +static inline void +pic_eoi1(void) +{ + // Send eoi (select OCW2 + eoi) + outb(0x20, PORT_PIC1_CMD); +} + +static inline void +pic_eoi2(void) +{ + // Send eoi (select OCW2 + eoi) + outb(0x20, PORT_PIC2_CMD); + pic_eoi1(); +} + +u16 pic_irqmask_read(void); +void pic_irqmask_write(u16 mask); +void pic_irqmask_mask(u16 off, u16 on); +void pic_reset(u8 irq0, u8 irq8); +void pic_setup(void); +void enable_hwirq(int hwirq, struct segoff_s func); + +#endif // pic.h diff --git a/qemu/roms/seabios/src/hw/ps2port.c b/qemu/roms/seabios/src/hw/ps2port.c new file mode 100644 index 000000000..04995c881 --- /dev/null +++ b/qemu/roms/seabios/src/hw/ps2port.c @@ -0,0 +1,507 @@ +// Support for handling the PS/2 mouse/keyboard ports. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Several ideas taken from code Copyright (c) 1999-2004 Vojtech Pavlik +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "output.h" // dprintf +#include "pic.h" // pic_eoi1 +#include "ps2port.h" // ps2_kbd_command +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // udelay +#include "x86.h" // inb + + +/**************************************************************** + * Low level i8042 commands. + ****************************************************************/ + +// Timeout value. +#define I8042_CTL_TIMEOUT 10000 + +#define I8042_BUFFER_SIZE 16 + +static int +i8042_wait_read(void) +{ + dprintf(7, "i8042_wait_read\n"); + int i; + for (i=0; i<I8042_CTL_TIMEOUT; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (status & I8042_STR_OBF) + return 0; + udelay(50); + } + warn_timeout(); + return -1; +} + +static int +i8042_wait_write(void) +{ + dprintf(7, "i8042_wait_write\n"); + int i; + for (i=0; i<I8042_CTL_TIMEOUT; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (! (status & I8042_STR_IBF)) + return 0; + udelay(50); + } + warn_timeout(); + return -1; +} + +static int +i8042_flush(void) +{ + dprintf(7, "i8042_flush\n"); + int i; + for (i=0; i<I8042_BUFFER_SIZE; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (! (status & I8042_STR_OBF)) + return 0; + udelay(50); + u8 data = inb(PORT_PS2_DATA); + dprintf(7, "i8042 flushed %x (status=%x)\n", data, status); + } + + warn_timeout(); + return -1; +} + +static int +__i8042_command(int command, u8 *param) +{ + int receive = (command >> 8) & 0xf; + int send = (command >> 12) & 0xf; + + // Send the command. + int ret = i8042_wait_write(); + if (ret) + return ret; + outb(command, PORT_PS2_STATUS); + + // Send parameters (if any). + int i; + for (i = 0; i < send; i++) { + ret = i8042_wait_write(); + if (ret) + return ret; + outb(param[i], PORT_PS2_DATA); + } + + // Receive parameters (if any). + for (i = 0; i < receive; i++) { + ret = i8042_wait_read(); + if (ret) + return ret; + param[i] = inb(PORT_PS2_DATA); + dprintf(7, "i8042 param=%x\n", param[i]); + } + + return 0; +} + +static int +i8042_command(int command, u8 *param) +{ + dprintf(7, "i8042_command cmd=%x\n", command); + int ret = __i8042_command(command, param); + if (ret) + dprintf(2, "i8042 command %x failed\n", command); + return ret; +} + +static int +i8042_kbd_write(u8 c) +{ + dprintf(7, "i8042_kbd_write c=%d\n", c); + int ret = i8042_wait_write(); + if (! ret) + outb(c, PORT_PS2_DATA); + return ret; +} + +static int +i8042_aux_write(u8 c) +{ + return i8042_command(I8042_CMD_AUX_SEND, &c); +} + +void +i8042_reboot(void) +{ + if (! CONFIG_PS2PORT) + return; + int i; + for (i=0; i<10; i++) { + i8042_wait_write(); + udelay(50); + outb(0xfe, PORT_PS2_STATUS); /* pulse reset low */ + udelay(50); + } +} + + +/**************************************************************** + * Device commands. + ****************************************************************/ + +#define PS2_RET_ACK 0xfa +#define PS2_RET_NAK 0xfe + +static int +ps2_recvbyte(int aux, int needack, int timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + u8 status = inb(PORT_PS2_STATUS); + if (status & I8042_STR_OBF) { + u8 data = inb(PORT_PS2_DATA); + dprintf(7, "ps2 read %x\n", data); + + if (!!(status & I8042_STR_AUXDATA) == aux) { + if (!needack) + return data; + if (data == PS2_RET_ACK) + return data; + if (data == PS2_RET_NAK) { + dprintf(1, "Got ps2 nak (status=%x)\n", status); + return data; + } + } + + // This data not part of command - just discard it. + dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status); + } + + if (timer_check(end)) { + // Don't warn on second byte of a reset + if (timeout > 100) + warn_timeout(); + return -1; + } + yield(); + } +} + +static int +ps2_sendbyte(int aux, u8 command, int timeout) +{ + dprintf(7, "ps2_sendbyte aux=%d cmd=%x\n", aux, command); + int ret; + if (aux) + ret = i8042_aux_write(command); + else + ret = i8042_kbd_write(command); + if (ret) + return ret; + + // Read ack. + ret = ps2_recvbyte(aux, 1, timeout); + if (ret < 0) + return ret; + if (ret != PS2_RET_ACK) + return -1; + + return 0; +} + +u8 Ps2ctr VARLOW; + +static int +__ps2_command(int aux, int command, u8 *param) +{ + int ret2; + int receive = (command >> 8) & 0xf; + int send = (command >> 12) & 0xf; + + // Disable interrupts and keyboard/mouse. + u8 ps2ctr = GET_LOW(Ps2ctr); + u8 newctr = ((ps2ctr | I8042_CTR_AUXDIS | I8042_CTR_KBDDIS) + & ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT)); + dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr); + int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); + if (ret) + return ret; + + // Flush any interrupts already pending. + yield(); + + // Enable port command is being sent to. + if (aux) + newctr &= ~I8042_CTR_AUXDIS; + else + newctr &= ~I8042_CTR_KBDDIS; + ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); + if (ret) + goto fail; + + if (command == ATKBD_CMD_RESET_BAT) { + // Reset is special wrt timeouts and bytes received. + + // Send command. + ret = ps2_sendbyte(aux, command, 1000); + if (ret) + goto fail; + + // Receive parameters. + ret = ps2_recvbyte(aux, 0, 4000); + if (ret < 0) + goto fail; + param[0] = ret; + ret = ps2_recvbyte(aux, 0, 100); + if (ret < 0) + // Some devices only respond with one byte on reset. + ret = 0; + param[1] = ret; + } else if (command == ATKBD_CMD_GETID) { + // Getid is special wrt bytes received. + + // Send command. + ret = ps2_sendbyte(aux, command, 200); + if (ret) + goto fail; + + // Receive parameters. + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[0] = ret; + if (ret == 0xab || ret == 0xac || ret == 0x2b || ret == 0x5d + || ret == 0x60 || ret == 0x47) { + // These ids (keyboards) return two bytes. + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[1] = ret; + } else { + param[1] = 0; + } + } else { + // Send command. + ret = ps2_sendbyte(aux, command, 200); + if (ret) + goto fail; + + // Send parameters (if any). + int i; + for (i = 0; i < send; i++) { + ret = ps2_sendbyte(aux, param[i], 200); + if (ret) + goto fail; + } + + // Receive parameters (if any). + for (i = 0; i < receive; i++) { + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[i] = ret; + } + } + + ret = 0; + +fail: + // Restore interrupts and keyboard/mouse. + ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr); + if (ret2) + return ret2; + + return ret; +} + +static int +ps2_command(int aux, int command, u8 *param) +{ + dprintf(7, "ps2_command aux=%d cmd=%x\n", aux, command); + int ret = __ps2_command(aux, command, param); + if (ret) + dprintf(2, "ps2 command %x failed (aux=%d)\n", command, aux); + return ret; +} + +int +ps2_kbd_command(int command, u8 *param) +{ + if (! CONFIG_PS2PORT) + return -1; + return ps2_command(0, command, param); +} + +int +ps2_mouse_command(int command, u8 *param) +{ + if (! CONFIG_PS2PORT) + return -1; + + // Update ps2ctr for mouse enable/disable. + if (command == PSMOUSE_CMD_ENABLE || command == PSMOUSE_CMD_DISABLE) { + u8 ps2ctr = GET_LOW(Ps2ctr); + if (command == PSMOUSE_CMD_ENABLE) + ps2ctr = (ps2ctr | I8042_CTR_AUXINT) & ~I8042_CTR_AUXDIS; + else + ps2ctr = (ps2ctr | I8042_CTR_AUXDIS) & ~I8042_CTR_AUXINT; + SET_LOW(Ps2ctr, ps2ctr); + } + + return ps2_command(1, command, param); +} + + +/**************************************************************** + * IRQ handlers + ****************************************************************/ + +// INT74h : PS/2 mouse hardware interrupt +void VISIBLE16 +handle_74(void) +{ + if (! CONFIG_PS2PORT) + return; + + debug_isr(DEBUG_ISR_74); + + u8 v = inb(PORT_PS2_STATUS); + if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA)) + != (I8042_STR_OBF|I8042_STR_AUXDATA)) { + dprintf(1, "ps2 mouse irq but no mouse data.\n"); + goto done; + } + v = inb(PORT_PS2_DATA); + + if (!(GET_LOW(Ps2ctr) & I8042_CTR_AUXINT)) + // Interrupts not enabled. + goto done; + + process_mouse(v); + +done: + pic_eoi2(); +} + +// INT09h : Keyboard Hardware Service Entry Point +void VISIBLE16 +handle_09(void) +{ + if (! CONFIG_PS2PORT) + return; + + debug_isr(DEBUG_ISR_09); + + // read key from keyboard controller + u8 v = inb(PORT_PS2_STATUS); + if (v & I8042_STR_AUXDATA) { + dprintf(1, "ps2 keyboard irq but found mouse data?!\n"); + goto done; + } + v = inb(PORT_PS2_DATA); + + if (!(GET_LOW(Ps2ctr) & I8042_CTR_KBDINT)) + // Interrupts not enabled. + goto done; + + process_key(v); + + // Some old programs expect ISR to turn keyboard back on. + i8042_command(I8042_CMD_KBD_ENABLE, NULL); + +done: + pic_eoi1(); +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +static void +ps2_keyboard_setup(void *data) +{ + /* flush incoming keys */ + int ret = i8042_flush(); + if (ret) + return; + + // Controller self-test. + u8 param[2]; + ret = i8042_command(I8042_CMD_CTL_TEST, param); + if (ret) + return; + if (param[0] != 0x55) { + dprintf(1, "i8042 self test failed (got %x not 0x55)\n", param[0]); + return; + } + + // Controller keyboard test. + ret = i8042_command(I8042_CMD_KBD_TEST, param); + if (ret) + return; + if (param[0] != 0x00) { + dprintf(1, "i8042 keyboard test failed (got %x not 0x00)\n", param[0]); + return; + } + + // Disable keyboard and mouse events. + SET_LOW(Ps2ctr, I8042_CTR_KBDDIS | I8042_CTR_AUXDIS); + + + /* ------------------- keyboard side ------------------------*/ + /* reset keyboard and self test (keyboard side) */ + int spinupdelay = romfile_loadint("etc/ps2-keyboard-spinup", 0); + u32 end = timer_calc(spinupdelay); + for (;;) { + ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param); + if (!ret) + break; + if (timer_check(end)) { + if (spinupdelay) + warn_timeout(); + return; + } + yield(); + } + if (param[0] != 0xaa) { + dprintf(1, "keyboard self test failed (got %x not 0xaa)\n", param[0]); + return; + } + + /* Disable keyboard */ + ret = ps2_kbd_command(ATKBD_CMD_RESET_DIS, NULL); + if (ret) + return; + + // Set scancode command (mode 2) + param[0] = 0x02; + ret = ps2_kbd_command(ATKBD_CMD_SSCANSET, param); + if (ret) + return; + + // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ + SET_LOW(Ps2ctr, I8042_CTR_AUXDIS | I8042_CTR_XLATE | I8042_CTR_KBDINT); + + /* Enable keyboard */ + ret = ps2_kbd_command(ATKBD_CMD_ENABLE, NULL); + if (ret) + return; + + dprintf(1, "PS2 keyboard initialized\n"); +} + +void +ps2port_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_PS2PORT) + return; + dprintf(3, "init ps2port\n"); + + enable_hwirq(1, FUNC16(entry_09)); + enable_hwirq(12, FUNC16(entry_74)); + + run_thread(ps2_keyboard_setup, NULL); +} diff --git a/qemu/roms/seabios/src/hw/ps2port.h b/qemu/roms/seabios/src/hw/ps2port.h new file mode 100644 index 000000000..e5d9014b7 --- /dev/null +++ b/qemu/roms/seabios/src/hw/ps2port.h @@ -0,0 +1,66 @@ +// Basic ps2 port (keyboard/mouse) command handling. +#ifndef __PS2PORT_H +#define __PS2PORT_H + +#include "types.h" // u8 + +#define PORT_PS2_DATA 0x0060 +#define PORT_PS2_STATUS 0x0064 + +// Standard commands. +#define I8042_CMD_CTL_RCTR 0x0120 +#define I8042_CMD_CTL_WCTR 0x1060 +#define I8042_CMD_CTL_TEST 0x01aa + +#define I8042_CMD_KBD_TEST 0x01ab +#define I8042_CMD_KBD_DISABLE 0x00ad +#define I8042_CMD_KBD_ENABLE 0x00ae + +#define I8042_CMD_AUX_DISABLE 0x00a7 +#define I8042_CMD_AUX_ENABLE 0x00a8 +#define I8042_CMD_AUX_SEND 0x10d4 + +// Keyboard commands +#define ATKBD_CMD_SETLEDS 0x10ed +#define ATKBD_CMD_SSCANSET 0x10f0 +#define ATKBD_CMD_GETID 0x02f2 +#define ATKBD_CMD_ENABLE 0x00f4 +#define ATKBD_CMD_RESET_DIS 0x00f5 +#define ATKBD_CMD_RESET_BAT 0x02ff + +// Mouse commands +#define PSMOUSE_CMD_SETSCALE11 0x00e6 +#define PSMOUSE_CMD_SETSCALE21 0x00e7 +#define PSMOUSE_CMD_SETRES 0x10e8 +#define PSMOUSE_CMD_GETINFO 0x03e9 +#define PSMOUSE_CMD_GETID 0x02f2 +#define PSMOUSE_CMD_SETRATE 0x10f3 +#define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_DISABLE 0x00f5 +#define PSMOUSE_CMD_RESET_BAT 0x02ff + +// Status register bits. +#define I8042_STR_PARITY 0x80 +#define I8042_STR_TIMEOUT 0x40 +#define I8042_STR_AUXDATA 0x20 +#define I8042_STR_KEYLOCK 0x10 +#define I8042_STR_CMDDAT 0x08 +#define I8042_STR_MUXERR 0x04 +#define I8042_STR_IBF 0x02 +#define I8042_STR_OBF 0x01 + +// Control register bits. +#define I8042_CTR_KBDINT 0x01 +#define I8042_CTR_AUXINT 0x02 +#define I8042_CTR_IGNKEYLOCK 0x08 +#define I8042_CTR_KBDDIS 0x10 +#define I8042_CTR_AUXDIS 0x20 +#define I8042_CTR_XLATE 0x40 + +// ps2port.c +void i8042_reboot(void); +int ps2_kbd_command(int command, u8 *param); +int ps2_mouse_command(int command, u8 *param); +void ps2port_setup(void); + +#endif // ps2port.h diff --git a/qemu/roms/seabios/src/hw/pvscsi.c b/qemu/roms/seabios/src/hw/pvscsi.c new file mode 100644 index 000000000..601a551db --- /dev/null +++ b/qemu/roms/seabios/src/hw/pvscsi.c @@ -0,0 +1,354 @@ +// QEMU VMWARE Paravirtualized SCSI boot support. +// +// Copyright (c) 2013 Ravello Systems LTD (http://ravellosystems.com) +// +// Authors: +// Evgeny Budilovsky <evgeny.budilovsky@ravellosystems.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VMWARE_PVSCSI +#include "pci_regs.h" // PCI_VENDOR_ID +#include "pvscsi.h" // pvscsi_setup +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-ring.h" // PAGE_SHIFT, virt_to_phys +#include "x86.h" // writel + +#define MASK(n) ((1 << (n)) - 1) + +#define SIMPLE_QUEUE_TAG 0x20 + +#define PVSCSI_INTR_CMPL_0 (1 << 0) +#define PVSCSI_INTR_CMPL_1 (1 << 1) +#define PVSCSI_INTR_CMPL_MASK MASK(2) + +#define PVSCSI_INTR_MSG_0 (1 << 2) +#define PVSCSI_INTR_MSG_1 (1 << 3) +#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2) +#define PVSCSI_INTR_ALL_SUPPORTED MASK(4) + +#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0) +#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1) +#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2) +#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3) +#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4) + +enum PVSCSIRegOffset { + PVSCSI_REG_OFFSET_COMMAND = 0x0, + PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4, + PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8, + PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100, + PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104, + PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108, + PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c, + PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c, + PVSCSI_REG_OFFSET_INTR_MASK = 0x2010, + PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014, + PVSCSI_REG_OFFSET_DEBUG = 0x3018, + PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018, +}; + +enum PVSCSICommands { + PVSCSI_CMD_FIRST = 0, + PVSCSI_CMD_ADAPTER_RESET = 1, + PVSCSI_CMD_ISSUE_SCSI = 2, + PVSCSI_CMD_SETUP_RINGS = 3, + PVSCSI_CMD_RESET_BUS = 4, + PVSCSI_CMD_RESET_DEVICE = 5, + PVSCSI_CMD_ABORT_CMD = 6, + PVSCSI_CMD_CONFIG = 7, + PVSCSI_CMD_SETUP_MSG_RING = 8, + PVSCSI_CMD_DEVICE_UNPLUG = 9, + PVSCSI_CMD_LAST = 10 +}; + +#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32 +struct PVSCSICmdDescSetupRings { + u32 reqRingNumPages; + u32 cmpRingNumPages; + u64 ringsStatePPN; + u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; + u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; +} PACKED; + +struct PVSCSIRingCmpDesc { + u64 context; + u64 dataLen; + u32 senseLen; + u16 hostStatus; + u16 scsiStatus; + u32 pad[2]; +} PACKED; + +struct PVSCSIRingsState { + u32 reqProdIdx; + u32 reqConsIdx; + u32 reqNumEntriesLog2; + + u32 cmpProdIdx; + u32 cmpConsIdx; + u32 cmpNumEntriesLog2; + + u8 pad[104]; + + u32 msgProdIdx; + u32 msgConsIdx; + u32 msgNumEntriesLog2; +} PACKED; + +struct PVSCSIRingReqDesc { + u64 context; + u64 dataAddr; + u64 dataLen; + u64 senseAddr; + u32 senseLen; + u32 flags; + u8 cdb[16]; + u8 cdbLen; + u8 lun[8]; + u8 tag; + u8 bus; + u8 target; + u8 vcpuHint; + u8 unused[59]; +} PACKED; + +struct pvscsi_ring_dsc_s { + struct PVSCSIRingsState *ring_state; + struct PVSCSIRingReqDesc *ring_reqs; + struct PVSCSIRingCmpDesc *ring_cmps; +}; + +struct pvscsi_lun_s { + struct drive_s drive; + void *iobase; + u8 target; + u8 lun; + struct pvscsi_ring_dsc_s *ring_dsc; +}; + +static void +pvscsi_write_cmd_desc(void *iobase, u32 cmd, const void *desc, size_t len) +{ + const u32 *ptr = desc; + size_t i; + + len /= sizeof(*ptr); + writel(iobase + PVSCSI_REG_OFFSET_COMMAND, cmd); + for (i = 0; i < len; i++) + writel(iobase + PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]); +} + +static void +pvscsi_kick_rw_io(void *iobase) +{ + writel(iobase + PVSCSI_REG_OFFSET_KICK_RW_IO, 0); +} + +static void +pvscsi_wait_intr_cmpl(void *iobase) +{ + while (!(readl(iobase + PVSCSI_REG_OFFSET_INTR_STATUS) & PVSCSI_INTR_CMPL_MASK)) + usleep(5); + writel(iobase + PVSCSI_REG_OFFSET_INTR_STATUS, PVSCSI_INTR_CMPL_MASK); +} + +static void +pvscsi_init_rings(void *iobase, struct pvscsi_ring_dsc_s **ring_dsc) +{ + struct PVSCSICmdDescSetupRings cmd = {0,}; + + struct pvscsi_ring_dsc_s *dsc = memalign_low(sizeof(*dsc), PAGE_SIZE); + if (!dsc) { + warn_noalloc(); + return; + } + + dsc->ring_state = + (struct PVSCSIRingsState *)memalign_low(PAGE_SIZE, PAGE_SIZE); + dsc->ring_reqs = + (struct PVSCSIRingReqDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE); + dsc->ring_cmps = + (struct PVSCSIRingCmpDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE); + if (!dsc->ring_state || !dsc->ring_reqs || !dsc->ring_cmps) { + warn_noalloc(); + return; + } + memset(dsc->ring_state, 0, PAGE_SIZE); + memset(dsc->ring_reqs, 0, PAGE_SIZE); + memset(dsc->ring_cmps, 0, PAGE_SIZE); + + cmd.reqRingNumPages = 1; + cmd.cmpRingNumPages = 1; + cmd.ringsStatePPN = virt_to_phys(dsc->ring_state) >> PAGE_SHIFT; + cmd.reqRingPPNs[0] = virt_to_phys(dsc->ring_reqs) >> PAGE_SHIFT; + cmd.cmpRingPPNs[0] = virt_to_phys(dsc->ring_cmps) >> PAGE_SHIFT; + + pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_SETUP_RINGS, + &cmd, sizeof(cmd)); + *ring_dsc = dsc; +} + +static void pvscsi_fill_req(struct PVSCSIRingsState *s, + struct PVSCSIRingReqDesc *req, + u16 target, u16 lun, void *cdbcmd, u16 blocksize, + struct disk_op_s *op) +{ + req->bus = 0; + req->target = target; + memset(req->lun, 0, sizeof(req->lun)); + req->lun[1] = lun; + req->senseLen = 0; + req->senseAddr = 0; + req->cdbLen = 16; + req->vcpuHint = 0; + memcpy(req->cdb, cdbcmd, 16); + req->tag = SIMPLE_QUEUE_TAG; + req->flags = cdb_is_read(cdbcmd, blocksize) ? + PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE; + + req->dataLen = op->count * blocksize; + req->dataAddr = (u32)op->buf_fl; + s->reqProdIdx = s->reqProdIdx + 1; +} + +static u32 +pvscsi_get_rsp(struct PVSCSIRingsState *s, + struct PVSCSIRingCmpDesc *rsp) +{ + u32 status = rsp->hostStatus; + s->cmpConsIdx = s->cmpConsIdx + 1; + return status; +} + +static int +pvscsi_cmd(struct pvscsi_lun_s *plun, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + struct pvscsi_ring_dsc_s *ring_dsc = plun->ring_dsc; + struct PVSCSIRingsState *s = ring_dsc->ring_state; + u32 req_entries = s->reqNumEntriesLog2; + u32 cmp_entries = s->cmpNumEntriesLog2; + struct PVSCSIRingReqDesc *req; + struct PVSCSIRingCmpDesc *rsp; + u32 status; + + if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) { + dprintf(1, "pvscsi: ring full: reqProdIdx=%d cmpConsIdx=%d\n", + s->reqProdIdx, s->cmpConsIdx); + return DISK_RET_EBADTRACK; + } + + req = ring_dsc->ring_reqs + (s->reqProdIdx & MASK(req_entries)); + pvscsi_fill_req(s, req, target, lun, cdbcmd, blocksize, op); + + pvscsi_kick_rw_io(plun->iobase); + pvscsi_wait_intr_cmpl(plun->iobase); + + rsp = ring_dsc->ring_cmps + (s->cmpConsIdx & MASK(cmp_entries)); + status = pvscsi_get_rsp(s, rsp); + + return status == 0 ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; +} + +int +pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_PVSCSI) + return DISK_RET_EBADTRACK; + + struct pvscsi_lun_s *plun = + container_of(op->drive_gf, struct pvscsi_lun_s, drive); + + return pvscsi_cmd(plun, op, cdbcmd, plun->target, plun->lun, blocksize); +} + +static int +pvscsi_add_lun(struct pci_device *pci, void *iobase, + struct pvscsi_ring_dsc_s *ring_dsc, u8 target, u8 lun) +{ + struct pvscsi_lun_s *plun = malloc_fseg(sizeof(*plun)); + if (!plun) { + warn_noalloc(); + return -1; + } + memset(plun, 0, sizeof(*plun)); + plun->drive.type = DTYPE_PVSCSI; + plun->drive.cntl_id = pci->bdf; + plun->target = target; + plun->lun = lun; + plun->iobase = iobase; + plun->ring_dsc = ring_dsc; + + char *name = znprintf(16, "pvscsi %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&plun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(plun); + return -1; +} + +static void +pvscsi_scan_target(struct pci_device *pci, void *iobase, + struct pvscsi_ring_dsc_s *ring_dsc, u8 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + pvscsi_add_lun(pci, iobase, ring_dsc, target, 0); +} + +static void +init_pvscsi(struct pci_device *pci) +{ + struct pvscsi_ring_dsc_s *ring_dsc = NULL; + int i; + u16 bdf = pci->bdf; + void *iobase = (void*)(pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + dprintf(1, "found pvscsi at %02x:%02x.%x, io @ %p\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_ADAPTER_RESET, NULL, 0); + + pvscsi_init_rings(iobase, &ring_dsc); + for (i = 0; i < 7; i++) + pvscsi_scan_target(pci, iobase, ring_dsc, i); + + return; +} + +void +pvscsi_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_PVSCSI) + return; + + dprintf(3, "init pvscsi\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_VMWARE + || pci->device != PCI_DEVICE_ID_VMWARE_PVSCSI) + continue; + init_pvscsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/pvscsi.h b/qemu/roms/seabios/src/hw/pvscsi.h new file mode 100644 index 000000000..fde9f0b98 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pvscsi.h @@ -0,0 +1,8 @@ +#ifndef _PVSCSI_H_ +#define _PVSCSI_H_ + +struct disk_op_s; +int pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void pvscsi_setup(void); + +#endif /* _PVSCSI_H_ */ diff --git a/qemu/roms/seabios/src/hw/ramdisk.c b/qemu/roms/seabios/src/hw/ramdisk.c new file mode 100644 index 000000000..1177bc00a --- /dev/null +++ b/qemu/roms/seabios/src/hw/ramdisk.c @@ -0,0 +1,111 @@ +// Code for emulating a drive via high-memory accesses. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "malloc.h" // malloc_fseg +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "romfile.h" // romfile_findprefix +#include "stacks.h" // call16_int +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // process_ramdisk_op + +void +ramdisk_setup(void) +{ + if (!CONFIG_FLASH_FLOPPY) + return; + + // Find image. + struct romfile_s *file = romfile_findprefix("floppyimg/", NULL); + if (!file) + return; + const char *filename = file->name; + u32 size = file->size; + dprintf(3, "Found floppy file %s of size %d\n", filename, size); + int ftype = find_floppy_type(size); + if (ftype < 0) { + dprintf(3, "No floppy type found for ramdisk size\n"); + return; + } + + // Allocate ram for image. + void *pos = memalign_tmphigh(PAGE_SIZE, size); + if (!pos) { + warn_noalloc(); + return; + } + add_e820((u32)pos, size, E820_RESERVED); + + // Copy image into ram. + int ret = file->copy(file, pos, size); + if (ret < 0) + return; + + // Setup driver. + struct drive_s *drive = init_floppy((u32)pos, ftype); + if (!drive) + return; + drive->type = DTYPE_RAMDISK; + dprintf(1, "Mapping CBFS floppy %s to addr %p\n", filename, pos); + char *desc = znprintf(MAXDESCSIZE, "Ramdisk [%s]", &filename[10]); + boot_add_floppy(drive, desc, bootprio_find_named_rom(filename, 0)); +} + +static int +ramdisk_copy(struct disk_op_s *op, int iswrite) +{ + u32 offset = GET_GLOBALFLAT(op->drive_gf->cntl_id); + offset += (u32)op->lba * DISK_SECTOR_SIZE; + u64 opd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)op->buf_fl); + u64 ramd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE(offset); + + u64 gdt[6]; + if (iswrite) { + gdt[2] = opd; + gdt[3] = ramd; + } else { + gdt[2] = ramd; + gdt[3] = opd; + } + + // Call int 1587 to copy data. + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_CF|F_IF; + br.ah = 0x87; + br.es = GET_SEG(SS); + br.si = (u32)gdt; + br.cx = op->count * DISK_SECTOR_SIZE / 2; + call16_int(0x15, &br); + + if (br.flags & F_CF) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +int +process_ramdisk_op(struct disk_op_s *op) +{ + if (!CONFIG_FLASH_FLOPPY) + return 0; + + switch (op->command) { + case CMD_READ: + return ramdisk_copy(op, 0); + case CMD_WRITE: + return ramdisk_copy(op, 1); + case CMD_VERIFY: + case CMD_FORMAT: + case CMD_RESET: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} diff --git a/qemu/roms/seabios/src/hw/rtc.c b/qemu/roms/seabios/src/hw/rtc.c new file mode 100644 index 000000000..628d5429f --- /dev/null +++ b/qemu/roms/seabios/src/hw/rtc.c @@ -0,0 +1,93 @@ +// Support for MC146818 Real Time Clock chip. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "rtc.h" // rtc_read +#include "stacks.h" // yield +#include "util.h" // timer_calc +#include "x86.h" // inb + +u8 +rtc_read(u8 index) +{ + index |= NMI_DISABLE_BIT; + outb(index, PORT_CMOS_INDEX); + return inb(PORT_CMOS_DATA); +} + +void +rtc_write(u8 index, u8 val) +{ + index |= NMI_DISABLE_BIT; + outb(index, PORT_CMOS_INDEX); + outb(val, PORT_CMOS_DATA); +} + +void +rtc_mask(u8 index, u8 off, u8 on) +{ + outb(index, PORT_CMOS_INDEX); + u8 val = inb(PORT_CMOS_DATA); + outb((val & ~off) | on, PORT_CMOS_DATA); +} + +int +rtc_updating(void) +{ + // This function checks to see if the update-in-progress bit + // is set in CMOS Status Register A. If not, it returns 0. + // If it is set, it tries to wait until there is a transition + // to 0, and will return 0 if such a transition occurs. A -1 + // is returned only after timing out. The maximum period + // that this bit should be set is constrained to (1984+244) + // useconds, but we wait for longer just to be sure. + + if ((rtc_read(CMOS_STATUS_A) & RTC_A_UIP) == 0) + return 0; + u32 end = timer_calc(15); + for (;;) { + if ((rtc_read(CMOS_STATUS_A) & RTC_A_UIP) == 0) + return 0; + if (timer_check(end)) + // update-in-progress never transitioned to 0 + return -1; + yield(); + } +} + +void +rtc_setup(void) +{ + rtc_write(CMOS_STATUS_A, 0x26); // 32,768Khz src, 976.5625us updates + rtc_mask(CMOS_STATUS_B, ~RTC_B_DSE, RTC_B_24HR); + rtc_read(CMOS_STATUS_C); + rtc_read(CMOS_STATUS_D); +} + +int RTCusers VARLOW; + +void +rtc_use(void) +{ + int count = GET_LOW(RTCusers); + SET_LOW(RTCusers, count+1); + if (count) + return; + // Turn on the Periodic Interrupt timer + rtc_mask(CMOS_STATUS_B, 0, RTC_B_PIE); +} + +void +rtc_release(void) +{ + int count = GET_LOW(RTCusers); + SET_LOW(RTCusers, count-1); + if (count != 1) + return; + // Clear the Periodic Interrupt. + rtc_mask(CMOS_STATUS_B, RTC_B_PIE, 0); +} diff --git a/qemu/roms/seabios/src/hw/rtc.h b/qemu/roms/seabios/src/hw/rtc.h new file mode 100644 index 000000000..252e73a41 --- /dev/null +++ b/qemu/roms/seabios/src/hw/rtc.h @@ -0,0 +1,75 @@ +#ifndef __RTC_H +#define __RTC_H + +#define PORT_CMOS_INDEX 0x0070 +#define PORT_CMOS_DATA 0x0071 + +// PORT_CMOS_INDEX nmi disable bit +#define NMI_DISABLE_BIT 0x80 + +// Standard BIOS RTC chip entries +#define CMOS_RTC_SECONDS 0x00 +#define CMOS_RTC_SECONDS_ALARM 0x01 +#define CMOS_RTC_MINUTES 0x02 +#define CMOS_RTC_MINUTES_ALARM 0x03 +#define CMOS_RTC_HOURS 0x04 +#define CMOS_RTC_HOURS_ALARM 0x05 +#define CMOS_RTC_DAY_WEEK 0x06 +#define CMOS_RTC_DAY_MONTH 0x07 +#define CMOS_RTC_MONTH 0x08 +#define CMOS_RTC_YEAR 0x09 +#define CMOS_STATUS_A 0x0a +#define CMOS_STATUS_B 0x0b +#define CMOS_STATUS_C 0x0c +#define CMOS_STATUS_D 0x0d +#define CMOS_RESET_CODE 0x0f + +// QEMU cmos config fields. DO NOT ADD MORE. (All new content should +// be passed via the fw_cfg "file" interface.) +#define CMOS_FLOPPY_DRIVE_TYPE 0x10 +#define CMOS_DISK_DATA 0x12 +#define CMOS_EQUIPMENT_INFO 0x14 +#define CMOS_DISK_DRIVE1_TYPE 0x19 +#define CMOS_DISK_DRIVE2_TYPE 0x1a +#define CMOS_DISK_DRIVE1_CYL 0x1b +#define CMOS_DISK_DRIVE2_CYL 0x24 +#define CMOS_MEM_EXTMEM_LOW 0x30 +#define CMOS_MEM_EXTMEM_HIGH 0x31 +#define CMOS_CENTURY 0x32 +#define CMOS_MEM_EXTMEM2_LOW 0x34 +#define CMOS_MEM_EXTMEM2_HIGH 0x35 +#define CMOS_BIOS_BOOTFLAG1 0x38 +#define CMOS_BIOS_DISKTRANSFLAG 0x39 +#define CMOS_BIOS_BOOTFLAG2 0x3d +#define CMOS_MEM_HIGHMEM_LOW 0x5b +#define CMOS_MEM_HIGHMEM_MID 0x5c +#define CMOS_MEM_HIGHMEM_HIGH 0x5d +#define CMOS_BIOS_SMP_COUNT 0x5f + +// RTC register flags +#define RTC_A_UIP 0x80 + +#define RTC_B_SET 0x80 +#define RTC_B_PIE 0x40 +#define RTC_B_AIE 0x20 +#define RTC_B_UIE 0x10 +#define RTC_B_BIN 0x04 +#define RTC_B_24HR 0x02 +#define RTC_B_DSE 0x01 + +#ifndef __ASSEMBLY__ + +#include "types.h" // u8 + +// rtc.c +u8 rtc_read(u8 index); +void rtc_write(u8 index, u8 val); +void rtc_mask(u8 index, u8 off, u8 on); +int rtc_updating(void); +void rtc_setup(void); +void rtc_use(void); +void rtc_release(void); + +#endif // !__ASSEMBLY__ + +#endif // rtc.h diff --git a/qemu/roms/seabios/src/hw/sdcard.c b/qemu/roms/seabios/src/hw/sdcard.c new file mode 100644 index 000000000..6ff93c856 --- /dev/null +++ b/qemu/roms/seabios/src/hw/sdcard.c @@ -0,0 +1,321 @@ +// PCI SD Host Controller Interface +// +// Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // struct drive_s +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // malloc_fseg +#include "output.h" // znprintf +#include "pci.h" // pci_config_readl +#include "pci_ids.h" // PCI_CLASS_SYSTEM_SDHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "stacks.h" // wait_preempt +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // boot_add_hd +#include "x86.h" // writel + +// SDHCI MMIO registers +struct sdhci_s { + u32 sdma_addr; + u16 block_size; + u16 block_count; + u32 arg; + u16 transfer_mode; + u16 cmd; + u32 response[4]; + u32 data; + u32 present_state; + u8 host_control; + u8 power_control; + u8 block_gap_control; + u8 wakeup_control; + u16 clock_control; + u8 timeout_control; + u8 software_reset; + u16 irq_status; + u16 error_irq_status; + u16 irq_enable; + u16 error_irq_enable; + u16 irq_signal; + u16 error_signal; + u16 auto_cmd12; + u8 pad_3E[2]; + u64 cap; + u64 max_current; + u16 force_auto_cmd12; + u16 force_error; + u8 adma_error; + u8 pad_55[3]; + u64 adma_addr; + u8 pad_60[156]; + u16 slot_irq; + u16 controller_version; +} PACKED; + +// SDHCI commands +#define SC_ALL_SEND_CID ((2<<8) | 0x21) +#define SC_SEND_RELATIVE_ADDR ((3<<8) | 0x22) +#define SC_SELECT_DESELECT_CARD ((7<<8) | 0x23) +#define SC_READ_SINGLE ((17<<8) | 0x22) +#define SC_READ_MULTIPLE ((18<<8) | 0x22) +#define SC_WRITE_SINGLE ((24<<8) | 0x22) +#define SC_WRITE_MULTIPLE ((25<<8) | 0x22) +#define SC_APP_CMD ((55<<8) | 0x22) +#define SC_APP_SEND_OP_COND ((41<<8) | 0x22) + +// SDHCI irqs +#define SI_CMD_COMPLETE (1<<0) +#define SI_TRANS_DONE (1<<1) +#define SI_WRITE_READY (1<<4) +#define SI_READ_READY (1<<5) + +// SDHCI present_state flags +#define SP_CMD_INHIBIT (1<<0) +#define SP_DAT_INHIBIT (1<<1) + +// SDHCI transfer_mode flags +#define ST_BLOCKCOUNT (1<<1) +#define ST_AUTO_CMD12 (1<<2) +#define ST_READ (1<<4) +#define ST_MULTIPLE (1<<5) + +// SDHCI result flags +#define SR_OCR_CCS (1<<30) + +// SDHCI timeouts +#define SDHCI_PIO_TIMEOUT 1000 // XXX - these are just made up +#define SDHCI_TRANSFER_TIMEOUT 10000 + +// Internal 'struct drive_s' storage for a detected card +struct sddrive_s { + struct drive_s drive; + struct sdhci_s *regs; + int card_type; +}; + +// SD card types +#define SF_MMC 0 +#define SF_SDSC 1 +#define SF_SDHC 2 + +// Repeatedly read a u16 register until the specific value is found +static int +waitw(u16 *reg, u16 mask, u16 value, u32 end) +{ + for (;;) { + u16 v = readw(reg); + if ((v & mask) == value) + return 0; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +// Send a command to the card. +static int +sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) +{ + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); + u16 busyf = SP_CMD_INHIBIT | ((cmd & 0x03) == 0x03 ? SP_DAT_INHIBIT : 0); + int ret = waitw((u16*)®s->present_state, busyf, 0, end); + if (ret) + return ret; + // Send command + writel(®s->arg, *param); + writew(®s->cmd, cmd); + ret = waitw(®s->irq_status, SI_CMD_COMPLETE, SI_CMD_COMPLETE, end); + if (ret) + return ret; + writew(®s->irq_status, SI_CMD_COMPLETE); + // Read response + memcpy(param, regs->response, sizeof(regs->response)); + return 0; +} + +// Send an "app specific" command to the card. +static int +sdcard_pio_app(struct sdhci_s *regs, int cmd, u32 *param) +{ + u32 aparam[4] = {}; + int ret = sdcard_pio(regs, SC_APP_CMD, aparam); + if (ret) + return ret; + return sdcard_pio(regs, cmd, param); +} + +// Send a command to the card which transfers data. +static int +sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr + , void *data, int count) +{ + // Send command + writel(&drive->regs->block_size, DISK_SECTOR_SIZE); + writew(&drive->regs->block_count, count); // XXX - SC_SET_BLOCK_COUNT? + int isread = cmd != SC_WRITE_SINGLE && cmd != SC_WRITE_MULTIPLE; + u16 tmode = ((count > 1 ? ST_MULTIPLE|ST_AUTO_CMD12|ST_BLOCKCOUNT : 0) + | (isread ? ST_READ : 0)); + writew(&drive->regs->transfer_mode, tmode); + if (drive->card_type < SF_SDHC) + addr *= DISK_SECTOR_SIZE; + u32 param[4] = { addr }; + int ret = sdcard_pio(drive->regs, cmd, param); + if (ret) + return ret; + // Read/write data + u32 end = timer_calc(SDHCI_TRANSFER_TIMEOUT); + u16 cbit = isread ? SI_READ_READY : SI_WRITE_READY; + while (count--) { + ret = waitw(&drive->regs->irq_status, cbit, cbit, end); + if (ret) + return ret; + writew(&drive->regs->irq_status, cbit); + int i; + for (i=0; i<DISK_SECTOR_SIZE/4; i++) { + if (isread) + *(u32*)data = readl(&drive->regs->data); + else + writel(&drive->regs->data, *(u32*)data); + data += 4; + } + } + // Complete command + // XXX - SC_STOP_TRANSMISSION? + ret = waitw(&drive->regs->irq_status, SI_TRANS_DONE, SI_TRANS_DONE, end); + if (ret) + return ret; + writew(&drive->regs->irq_status, SI_TRANS_DONE); + return 0; +} + +// Read/write a block of data to/from the card. +static int +sdcard_readwrite(struct disk_op_s *op, int iswrite) +{ + struct sddrive_s *drive = container_of( + op->drive_gf, struct sddrive_s, drive); + int cmd = iswrite ? SC_WRITE_SINGLE : SC_READ_SINGLE; + if (op->count > 1) + cmd = iswrite ? SC_WRITE_MULTIPLE : SC_READ_MULTIPLE; + int ret = sdcard_pio_transfer(drive, cmd, op->lba, op->buf_fl, op->count); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +int VISIBLE32FLAT +process_sdcard_op(struct disk_op_s *op) +{ + if (!CONFIG_SDCARD) + return 0; + switch (op->command) { + case CMD_READ: + return sdcard_readwrite(op, 0); + case CMD_WRITE: + return sdcard_readwrite(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Initialize an SD card +static int +sdcard_card_setup(struct sdhci_s *regs) +{ + // XXX - works on QEMU; probably wont on real hardware! + u32 param[4] = { 0x01 }; + int ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + if (ret) + return ret; + int card_type = (param[0] & SR_OCR_CCS) ? SF_SDHC : SF_SDSC; + param[0] = 0x00; + ret = sdcard_pio(regs, SC_ALL_SEND_CID, param); + if (ret) + return ret; + param[0] = 0x01 << 16; + ret = sdcard_pio(regs, SC_SEND_RELATIVE_ADDR, param); + if (ret) + return ret; + u16 rca = param[0] >> 16; + param[0] = rca << 16; + ret = sdcard_pio(regs, SC_SELECT_DESELECT_CARD, param); + if (ret) + return ret; + return card_type; +} + +// Setup and configure an SD card controller +static void +sdcard_controller_setup(void *data) +{ + struct pci_device *pci = data; + u16 bdf = pci->bdf; + wait_preempt(); // Avoid pci_config_readl when preempting + struct sdhci_s *regs = (void*)pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + // Initialize controller + if (!runningOnQEMU()) + // XXX - this init logic will probably only work on qemu! + return; + writew(®s->irq_signal, 0); + writew(®s->irq_enable, 0xffff); + writew(®s->error_signal, 0); + writeb(®s->power_control, 0x0f); + writew(®s->clock_control, 0x0005); + + // Initialize card + int card_type = sdcard_card_setup(regs); + if (card_type < 0) + return; + + // Register drive + struct sddrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return; + } + memset(drive, 0, sizeof(*drive)); + drive->drive.type = DTYPE_SDCARD; + drive->drive.blksize = DISK_SECTOR_SIZE; + drive->drive.sectors = (u64)-1; // XXX + drive->regs = regs; + drive->card_type = card_type; + + dprintf(1, "Found SD Card at %02x:%02x.%x\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + char *desc = znprintf(MAXDESCSIZE, "SD Card"); // XXX + boot_add_hd(&drive->drive, desc, bootprio_find_pci_device(pci)); +} + +void +sdcard_setup(void) +{ + if (!CONFIG_SDCARD) + return; + + struct pci_device *pci; + foreachpci(pci) { + if (pci->class != PCI_CLASS_SYSTEM_SDHCI || pci->prog_if >= 2) + // Not an SDHCI controller following SDHCI spec + continue; + run_thread(sdcard_controller_setup, pci); + } +} diff --git a/qemu/roms/seabios/src/hw/serialio.c b/qemu/roms/seabios/src/hw/serialio.c new file mode 100644 index 000000000..6486fc086 --- /dev/null +++ b/qemu/roms/seabios/src/hw/serialio.c @@ -0,0 +1,89 @@ +// Low-level serial (and serial-like) device access. +// +// Copyright (C) 2008-1013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_DEBUG_SERIAL +#include "fw/paravirt.h" // RunningOnQEMU +#include "output.h" // dprintf +#include "serialio.h" // serial_debug_preinit +#include "x86.h" // outb + + +/**************************************************************** + * Serial port debug output + ****************************************************************/ + +#define DEBUG_TIMEOUT 100000 + +// Setup the debug serial port for output. +void +serial_debug_preinit(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + // setup for serial logging: 8N1 + u8 oldparam, newparam = 0x03; + oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + // Disable irqs + u8 oldier, newier = 0; + oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + + if (oldparam != newparam || oldier != newier) + dprintf(1, "Changing serial settings was %x/%x now %x/%x\n" + , oldparam, oldier, newparam, newier); +} + +// Write a character to the serial port. +static void +serial_debug(char c) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20) + if (!timeout--) + // Ran out of time. + return; + outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA); +} + +void +serial_debug_putc(char c) +{ + if (c == '\n') + serial_debug('\r'); + serial_debug(c); +} + +// Make sure all serial port writes have been completely sent. +void +serial_debug_flush(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60) + if (!timeout--) + // Ran out of time. + return; +} + + +/**************************************************************** + * QEMU debug port + ****************************************************************/ + +u16 DebugOutputPort VARFSEG = 0x402; + +// Write a character to the special debugging port. +void +qemu_debug_putc(char c) +{ + if (CONFIG_DEBUG_IO && runningOnQEMU()) + // Send character to debug port. + outb(c, GET_GLOBAL(DebugOutputPort)); +} diff --git a/qemu/roms/seabios/src/hw/serialio.h b/qemu/roms/seabios/src/hw/serialio.h new file mode 100644 index 000000000..88296fe7f --- /dev/null +++ b/qemu/roms/seabios/src/hw/serialio.h @@ -0,0 +1,29 @@ +#ifndef __SERIALIO_H +#define __SERIALIO_H + +#include "types.h" // u16 + +#define PORT_LPT2 0x0278 +#define PORT_SERIAL4 0x02e8 +#define PORT_SERIAL2 0x02f8 +#define PORT_LPT1 0x0378 +#define PORT_SERIAL3 0x03e8 +#define PORT_SERIAL1 0x03f8 + +// Serial port offsets +#define SEROFF_DATA 0 +#define SEROFF_DLL 0 +#define SEROFF_IER 1 +#define SEROFF_DLH 1 +#define SEROFF_IIR 2 +#define SEROFF_LCR 3 +#define SEROFF_LSR 5 +#define SEROFF_MSR 6 + +void serial_debug_preinit(void); +void serial_debug_putc(char c); +void serial_debug_flush(void); +extern u16 DebugOutputPort; +void qemu_debug_putc(char c); + +#endif // serialio.h diff --git a/qemu/roms/seabios/src/hw/timer.c b/qemu/roms/seabios/src/hw/timer.c new file mode 100644 index 000000000..5edc9fdbb --- /dev/null +++ b/qemu/roms/seabios/src/hw/timer.c @@ -0,0 +1,257 @@ +// Internal timer and Intel 8253 Programmable Interrupt Timer (PIT) support. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "stacks.h" // yield +#include "util.h" // timer_setup +#include "x86.h" // cpuid + +#define PORT_PIT_COUNTER0 0x0040 +#define PORT_PIT_COUNTER1 0x0041 +#define PORT_PIT_COUNTER2 0x0042 +#define PORT_PIT_MODE 0x0043 +#define PORT_PS2_CTRLB 0x0061 + +// Bits for PORT_PIT_MODE +#define PM_SEL_TIMER0 (0<<6) +#define PM_SEL_TIMER1 (1<<6) +#define PM_SEL_TIMER2 (2<<6) +#define PM_SEL_READBACK (3<<6) +#define PM_ACCESS_LATCH (0<<4) +#define PM_ACCESS_LOBYTE (1<<4) +#define PM_ACCESS_HIBYTE (2<<4) +#define PM_ACCESS_WORD (3<<4) +#define PM_MODE0 (0<<1) +#define PM_MODE1 (1<<1) +#define PM_MODE2 (2<<1) +#define PM_MODE3 (3<<1) +#define PM_MODE4 (4<<1) +#define PM_MODE5 (5<<1) +#define PM_CNT_BINARY (0<<0) +#define PM_CNT_BCD (1<<0) +#define PM_READ_COUNTER0 (1<<1) +#define PM_READ_COUNTER1 (1<<2) +#define PM_READ_COUNTER2 (1<<3) +#define PM_READ_STATUSVALUE (0<<4) +#define PM_READ_VALUE (1<<4) +#define PM_READ_STATUS (2<<4) + +// Bits for PORT_PS2_CTRLB +#define PPCB_T2GATE (1<<0) +#define PPCB_SPKR (1<<1) +#define PPCB_T2OUT (1<<5) + +#define PMTIMER_HZ 3579545 // Underlying Hz of the PM Timer +#define PMTIMER_TO_PIT 3 // Ratio of pmtimer rate to pit rate + +u32 TimerKHz VARFSEG; +u16 TimerPort VARFSEG; +u8 ShiftTSC VARFSEG; + + +/**************************************************************** + * Internal timer setup + ****************************************************************/ + +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms + +// Calibrate the CPU time-stamp-counter +static void +tsctimer_setup(void) +{ + // Setup "timer2" + u8 orig = inb(PORT_PS2_CTRLB); + outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE); + /* LSB of ticks */ + outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2); + /* MSB of ticks */ + outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2); + + u64 start = rdtscll(); + while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0) + ; + u64 end = rdtscll(); + + // Restore PORT_PS2_CTRLB + outb(orig, PORT_PS2_CTRLB); + + // Store calibrated cpu khz. + u64 diff = end - start; + dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n" + , (u32)start, (u32)end, (u32)diff); + u64 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT); + while (t >= (1<<24)) { + ShiftTSC++; + t = (t + 1) >> 1; + } + TimerKHz = DIV_ROUND_UP((u32)t, 1000 * PMTIMER_TO_PIT); + + dprintf(1, "CPU Mhz=%u\n", (TimerKHz << ShiftTSC) / 1000); +} + +// Setup internal timers. +void +timer_setup(void) +{ + if (CONFIG_PMTIMER && TimerPort) { + dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); + return; + } + + u32 eax, ebx, ecx, edx, cpuid_features = 0; + cpuid(0, &eax, &ebx, &ecx, &edx); + if (eax > 0) + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + + if (!(cpuid_features & CPUID_TSC)) { + TimerPort = PORT_PIT_COUNTER0; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); + dprintf(3, "386/486 class CPU. Using TSC emulation\n"); + return; + } + + tsctimer_setup(); +} + +void +pmtimer_setup(u16 ioport) +{ + if (!CONFIG_PMTIMER) + return; + dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); + TimerPort = ioport; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); +} + + +/**************************************************************** + * Internal timer reading + ****************************************************************/ + +u32 TimerLast VARLOW; + +// Add extra high bits to timers that have less than 32bits of precision. +static u32 +timer_adjust_bits(u32 value, u32 validbits) +{ + u32 last = GET_LOW(TimerLast); + value = (last & ~validbits) | (value & validbits); + if (value < last) + value += validbits + 1; + SET_LOW(TimerLast, value); + return value; +} + +// Sample the current timer value. +static u32 +timer_read(void) +{ + u16 port = GET_GLOBAL(TimerPort); + if (!port) + // Read from CPU TSC + return rdtscll() >> GET_GLOBAL(ShiftTSC); + if (CONFIG_PMTIMER && port != PORT_PIT_COUNTER0) + // Read from PMTIMER + return timer_adjust_bits(inl(port), 0xffffff); + // Read from PIT. + outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE); + u16 v = inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8); + return timer_adjust_bits(v, 0xffff); +} + +// Check if the current time is past a previously calculated end time. +int +timer_check(u32 end) +{ + return (s32)(timer_read() - end) > 0; +} + +static void +timer_delay(u32 diff) +{ + u32 start = timer_read(); + u32 end = start + diff; + while (!timer_check(end)) + cpu_relax(); +} + +static void +timer_sleep(u32 diff) +{ + u32 start = timer_read(); + u32 end = start + diff; + while (!timer_check(end)) + yield(); +} + +void ndelay(u32 count) { + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); +} +void udelay(u32 count) { + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); +} +void mdelay(u32 count) { + timer_delay(count * GET_GLOBAL(TimerKHz)); +} + +void nsleep(u32 count) { + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); +} +void usleep(u32 count) { + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); +} +void msleep(u32 count) { + timer_sleep(count * GET_GLOBAL(TimerKHz)); +} + +// Return the TSC value that is 'msecs' time in the future. +u32 +timer_calc(u32 msecs) +{ + return timer_read() + (GET_GLOBAL(TimerKHz) * msecs); +} +u32 +timer_calc_usec(u32 usecs) +{ + return timer_read() + DIV_ROUND_UP(GET_GLOBAL(TimerKHz) * usecs, 1000); +} + + +/**************************************************************** + * PIT setup + ****************************************************************/ + +#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer + +// Return the number of milliseconds in 'ticks' number of timer irqs. +u32 +ticks_to_ms(u32 ticks) +{ + u32 t = PIT_TICK_INTERVAL * 1000 * PMTIMER_TO_PIT * ticks; + return DIV_ROUND_UP(t, PMTIMER_HZ); +} + +// Return the number of timer irqs in 'ms' number of milliseconds. +u32 +ticks_from_ms(u32 ms) +{ + u32 t = DIV_ROUND_UP((u64)ms * PMTIMER_HZ, PIT_TICK_INTERVAL); + return DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT); +} + +void +pit_setup(void) +{ + // timer0: binary count, 16bit count, mode 2 + outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE); + // maximum count of 0000H = 18.2Hz + outb(0x0, PORT_PIT_COUNTER0); + outb(0x0, PORT_PIT_COUNTER0); +} diff --git a/qemu/roms/seabios/src/hw/usb-ehci.c b/qemu/roms/seabios/src/hw/usb-ehci.c new file mode 100644 index 000000000..41f8579ce --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ehci.c @@ -0,0 +1,648 @@ +// Code for handling EHCI USB controllers. +// +// Copyright (C) 2010-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "malloc.h" // free +#include "memmap.h" // PAGE_SIZE +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ehci.h" // struct ehci_qh +#include "util.h" // msleep +#include "x86.h" // readl + +struct usb_ehci_s { + struct usb_s usb; + struct ehci_caps *caps; + struct ehci_regs *regs; + struct ehci_qh *async_qh; + int checkports; +}; + +struct ehci_pipe { + struct ehci_qh qh; + struct ehci_qtd *next_td, *tds; + void *data; + struct usb_pipe pipe; +}; + +static int PendingEHCI; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +#define EHCI_TIME_POSTPOWER 20 +#define EHCI_TIME_POSTRESET 2 + +// Check if device attached to port +static int +ehci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + if (!(portsc & PORT_CONNECT)) + // No device present + return 0; + + if ((portsc & PORT_LINESTATUS_MASK) == PORT_LINESTATUS_KSTATE) { + // low speed device + writel(portreg, portsc | PORT_OWNER); + return -1; + } + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Begin reset on port + portsc = (portsc & ~PORT_PE) | PORT_RESET; + writel(portreg, portsc); + msleep(USB_TIME_DRSTR); + return 1; +} + +// Reset device on port +static int +ehci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + // Finish reset on port + portsc &= ~PORT_RESET; + writel(portreg, portsc); + msleep(EHCI_TIME_POSTRESET); + + portsc = readl(portreg); + if (!(portsc & PORT_CONNECT)) + // No longer connected + return -1; + if (!(portsc & PORT_PE)) { + // full speed device + writel(portreg, portsc | PORT_OWNER); + return -1; + } + + return USB_HIGHSPEED; +} + +// Disable port +static void +ehci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + writel(portreg, portsc & ~PORT_PE); +} + +static struct usbhub_op_s ehci_HubOp = { + .detect = ehci_hub_detect, + .reset = ehci_hub_reset, + .disconnect = ehci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_ehci_ports(struct usb_ehci_s *cntl) +{ + // Power up ports. + int i; + for (i=0; i<cntl->checkports; i++) { + u32 *portreg = &cntl->regs->portsc[i]; + u32 portsc = readl(portreg); + if (!(portsc & PORT_POWER)) { + portsc |= PORT_POWER; + writel(portreg, portsc); + } + } + msleep(EHCI_TIME_POSTPOWER); + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = cntl->checkports; + hub.op = &ehci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB async frame to start - for ensuring safe memory release. +static void +ehci_waittick(struct usb_ehci_s *cntl) +{ + if (MODE16) { + msleep(10); + return; + } + // Wait for access to "doorbell" + barrier(); + u32 cmd, sts; + u32 end = timer_calc(100); + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (!(sts & STS_IAA)) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_IAAD)) + break; + } + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ring "doorbell" + writel(&cntl->regs->usbcmd, cmd | CMD_IAAD); + // Wait for completion + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (sts & STS_IAA) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ack completion + writel(&cntl->regs->usbsts, STS_IAA); +} + +static void +ehci_free_pipes(struct usb_ehci_s *cntl) +{ + dprintf(7, "ehci_free_pipes %p\n", cntl); + + struct ehci_qh *start = cntl->async_qh; + struct ehci_qh *pos = start; + for (;;) { + struct ehci_qh *next = (void*)(pos->next & ~EHCI_PTR_BITS); + if (next == start) + break; + struct ehci_pipe *pipe = container_of(next, struct ehci_pipe, qh); + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) + pos->next = next->next; + else + pos = next; + } + ehci_waittick(cntl); + for (;;) { + struct usb_pipe *usbpipe = cntl->usb.freelist; + if (!usbpipe) + break; + cntl->usb.freelist = usbpipe->freenext; + struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe); + free(pipe); + } +} + +static void +configure_ehci(void *data) +{ + struct usb_ehci_s *cntl = data; + + // Allocate ram for schedule storage + struct ehci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); + struct ehci_qh *intr_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*intr_qh)); + struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh)); + if (!fl || !intr_qh || !async_qh) { + warn_noalloc(); + PendingEHCI--; + goto fail; + } + + // XXX - check for halted? + + // Reset the HC + u32 cmd = readl(&cntl->regs->usbcmd); + writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET); + u32 end = timer_calc(250); + for (;;) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_HCRESET)) + break; + if (timer_check(end)) { + warn_timeout(); + PendingEHCI--; + goto fail; + } + yield(); + } + + // Disable interrupts (just to be safe). + writel(&cntl->regs->usbintr, 0); + + // Set schedule to point to primary intr queue head + memset(intr_qh, 0, sizeof(*intr_qh)); + intr_qh->next = EHCI_PTR_TERM; + intr_qh->info2 = (0x01 << QH_SMASK_SHIFT); + intr_qh->token = QTD_STS_HALT; + intr_qh->qtd_next = intr_qh->alt_next = EHCI_PTR_TERM; + int i; + for (i=0; i<ARRAY_SIZE(fl->links); i++) + fl->links[i] = (u32)intr_qh | EHCI_PTR_QH; + writel(&cntl->regs->periodiclistbase, (u32)fl); + + // Set async list to point to primary async queue head + memset(async_qh, 0, sizeof(*async_qh)); + async_qh->next = (u32)async_qh | EHCI_PTR_QH; + async_qh->info1 = QH_HEAD; + async_qh->token = QTD_STS_HALT; + async_qh->qtd_next = async_qh->alt_next = EHCI_PTR_TERM; + cntl->async_qh = async_qh; + writel(&cntl->regs->asynclistbase, (u32)async_qh); + + // Enable queues + writel(&cntl->regs->usbcmd, cmd | CMD_ASE | CMD_PSE | CMD_RUN); + + // Set default of high speed for root hub. + writel(&cntl->regs->configflag, 1); + PendingEHCI--; + + // Find devices + int count = check_ehci_ports(cntl); + ehci_free_pipes(cntl); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + writel(&cntl->regs->usbcmd, cmd & ~CMD_RUN); + msleep(4); // 2ms to stop reading memory - XXX +fail: + free(fl); + free(intr_qh); + free(async_qh); + free(cntl); +} + +static void +ehci_controller_setup(struct pci_device *pci) +{ + wait_preempt(); // Avoid pci_config_readl when preempting + u16 bdf = pci->bdf; + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + struct ehci_caps *caps = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + u32 hcc_params = readl(&caps->hccparams); + + struct usb_ehci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_EHCI; + cntl->caps = caps; + cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK; + cntl->regs = (void*)caps + readb(&caps->caplength); + if (hcc_params & HCC_64BIT_ADDR) + cntl->regs->ctrldssegment = 0; + PendingEHCI++; + + dprintf(1, "EHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + // XXX - check for and disable SMM control? + + run_thread(configure_ehci, cntl); +} + +void +ehci_setup(void) +{ + if (! CONFIG_USB_EHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_EHCI) + ehci_controller_setup(pci); + } + + // Wait for all EHCI controllers to initialize. This forces OHCI/UHCI + // setup to always be after any EHCI ports are routed to EHCI. + while (PendingEHCI) + yield(); +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +// Setup fields in qh +static void +ehci_desc2pipe(struct ehci_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + + pipe->qh.info1 = ((pipe->pipe.maxpacket << QH_MAXPACKET_SHIFT) + | (pipe->pipe.speed << QH_SPEED_SHIFT) + | (pipe->pipe.ep << QH_EP_SHIFT) + | (pipe->pipe.devaddr << QH_DEVADDR_SHIFT)); + + pipe->qh.info2 = (1 << QH_MULT_SHIFT); + struct usbdevice_s *hubdev = usbdev->hub->usbdev; + if (hubdev) { + struct ehci_pipe *hpipe = container_of( + hubdev->defpipe, struct ehci_pipe, pipe); + if (hpipe->pipe.speed == USB_HIGHSPEED) + pipe->qh.info2 |= (((usbdev->port+1) << QH_HUBPORT_SHIFT) + | (hpipe->pipe.devaddr << QH_HUBADDR_SHIFT)); + else + pipe->qh.info2 = hpipe->qh.info2; + } + + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe->qh.info1 |= ((pipe->pipe.speed != USB_HIGHSPEED ? QH_CONTROL : 0) + | QH_TOGGLECONTROL); + else if (eptype == USB_ENDPOINT_XFER_INT) + pipe->qh.info2 |= (0x01 << QH_SMASK_SHIFT) | (0x1c << QH_CMASK_SHIFT); +} + +static struct usb_pipe * +ehci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_ehci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ehci_s, usb); + int frameexp = usb_get_period(usbdev, epdesc); + dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 10) + frameexp = 10; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); + struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { + warn_noalloc(); + goto fail; + } + memset(pipe, 0, sizeof(*pipe)); + memset(tds, 0, sizeof(*tds) * count); + memset(data, 0, maxpacket * count); + ehci_desc2pipe(pipe, usbdev, epdesc); + pipe->next_td = pipe->tds = tds; + pipe->data = data; + pipe->qh.qtd_next = (u32)tds; + + int i; + for (i=0; i<count; i++) { + struct ehci_qtd *td = &tds[i]; + td->qtd_next = (i==count-1 ? (u32)tds : (u32)&td[1]); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3)); + td->buf[0] = (u32)data + maxpacket * i; + } + + // Add to interrupt schedule. + struct ehci_framelist *fl = (void*)readl(&cntl->regs->periodiclistbase); + if (frameexp == 0) { + // Add to existing interrupt entry. + struct ehci_qh *intr_qh = (void*)(fl->links[0] & ~EHCI_PTR_BITS); + pipe->qh.next = intr_qh->next; + barrier(); + intr_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + } else { + int startpos = 1<<(frameexp-1); + pipe->qh.next = fl->links[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) + fl->links[i] = (u32)&pipe->qh | EHCI_PTR_QH; + } + + return &pipe->pipe; +fail: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +ehci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_EHCI) + return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return ehci_alloc_intr_pipe(usbdev, epdesc); + struct usb_ehci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ehci_s, usb); + dprintf(7, "ehci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); + + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe); + ehci_desc2pipe(pipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct ehci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = memalign_tmphigh(EHCI_QH_ALIGN, sizeof(*pipe)); + else + pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + ehci_desc2pipe(pipe, usbdev, epdesc); + pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM; + + // Add queue head to controller list. + struct ehci_qh *async_qh = cntl->async_qh; + pipe->qh.next = async_qh->next; + barrier(); + async_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + return &pipe->pipe; +} + +static void +ehci_reset_pipe(struct ehci_pipe *pipe) +{ + SET_LOWFLAT(pipe->qh.qtd_next, EHCI_PTR_TERM); + SET_LOWFLAT(pipe->qh.alt_next, EHCI_PTR_TERM); + barrier(); + SET_LOWFLAT(pipe->qh.token, GET_LOWFLAT(pipe->qh.token) & QTD_TOGGLE); +} + +static int +ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, u32 end) +{ + u32 status; + for (;;) { + status = td->token; + if (!(status & QTD_STS_ACTIVE)) + break; + if (timer_check(end)) { + u32 cur = GET_LOWFLAT(pipe->qh.current); + u32 tok = GET_LOWFLAT(pipe->qh.token); + u32 next = GET_LOWFLAT(pipe->qh.qtd_next); + warn_timeout(); + dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n" + , pipe, cur, tok, next, td, status); + ehci_reset_pipe(pipe); + struct usb_ehci_s *cntl = container_of( + GET_LOWFLAT(pipe->pipe.cntl), struct usb_ehci_s, usb); + ehci_waittick(cntl); + return -1; + } + yield(); + } + if (status & QTD_STS_HALT) { + dprintf(1, "ehci_wait_td error - status=%x\n", status); + ehci_reset_pipe(pipe); + return -2; + } + return 0; +} + +static void +ehci_fill_tdbuf(struct ehci_qtd *td, u32 dest, int transfer) +{ + u32 *pos = td->buf, end = dest + transfer; + for (; dest < end; dest = ALIGN_DOWN(dest + PAGE_SIZE, PAGE_SIZE)) + *pos++ = dest; +} + +#define STACKQTDS 6 + +int +ehci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) +{ + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + dprintf(7, "ehci_send_pipe qh=%p dir=%d data=%p size=%d\n" + , &pipe->qh, dir, data, datasize); + + // Allocate tds on stack (with required alignment) + u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1]; + struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN), *td = tds; + memset(tds, 0, sizeof(*tds) * STACKQTDS); + + // Setup transfer descriptors + u16 maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + u32 toggle = 0; + if (cmd) { + // Send setup pid on control transfers + td->qtd_next = (u32)MAKE_FLATPTR(GET_SEG(SS), td+1); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(USB_CONTROL_SETUP_SIZE) | QTD_STS_ACTIVE + | QTD_PID_SETUP | ehci_maxerr(3)); + ehci_fill_tdbuf(td, (u32)cmd, USB_CONTROL_SETUP_SIZE); + td++; + toggle = QTD_TOGGLE; + } + u32 dest = (u32)data, dataend = dest + datasize; + while (dest < dataend) { + // Send data pids + if (td >= &tds[STACKQTDS]) { + warn_noalloc(); + return -1; + } + int maxtransfer = 5*PAGE_SIZE - (dest & (PAGE_SIZE-1)); + int transfer = dataend - dest; + if (transfer > maxtransfer) + transfer = ALIGN_DOWN(maxtransfer, maxpacket); + td->qtd_next = (u32)MAKE_FLATPTR(GET_SEG(SS), td+1); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(transfer) | toggle | QTD_STS_ACTIVE + | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); + ehci_fill_tdbuf(td, dest, transfer); + td++; + dest += transfer; + } + if (cmd) { + // Send status pid on control transfers + if (td >= &tds[STACKQTDS]) { + warn_noalloc(); + return -1; + } + td->qtd_next = EHCI_PTR_TERM; + td->alt_next = EHCI_PTR_TERM; + td->token = (QTD_TOGGLE | QTD_STS_ACTIVE + | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3)); + td++; + } + + // Transfer data + (td-1)->qtd_next = EHCI_PTR_TERM; + barrier(); + SET_LOWFLAT(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + u32 end = timer_calc(usb_xfer_time(p, datasize)); + int i; + for (i=0, td=tds; i<STACKQTDS; i++, td++) { + int ret = ehci_wait_td(pipe, td, end); + if (ret) + return -1; + } + + return 0; +} + +int +ehci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + struct ehci_qtd *td = GET_LOWFLAT(pipe->next_td); + u32 token = GET_LOWFLAT(td->token); + if (token & QTD_STS_ACTIVE) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + int pos = td - GET_LOWFLAT(pipe->tds); + void *tddata = GET_LOWFLAT(pipe->data) + maxpacket * pos; + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata), maxpacket); + + // Reenable this td. + struct ehci_qtd *next = (void*)(GET_LOWFLAT(td->qtd_next) & ~EHCI_PTR_BITS); + SET_LOWFLAT(pipe->next_td, next); + SET_LOWFLAT(td->buf[0], (u32)tddata); + barrier(); + SET_LOWFLAT(td->token, (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3))); + + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-ehci.h b/qemu/roms/seabios/src/hw/usb-ehci.h new file mode 100644 index 000000000..88f7b6a1e --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ehci.h @@ -0,0 +1,176 @@ +#ifndef __USB_EHCI_H +#define __USB_EHCI_H + +// usb-ehci.c +void ehci_setup(void); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; +struct usb_pipe *ehci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int ehci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int ehci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * ehci structs and flags + ****************************************************************/ + +struct ehci_caps { + u8 caplength; + u8 reserved_01; + u16 hciversion; + u32 hcsparams; + u32 hccparams; + u64 portroute; +} PACKED; + +#define HCC_64BIT_ADDR 1 + +#define HCS_N_PORTS_MASK 0xf + +struct ehci_regs { + u32 usbcmd; + u32 usbsts; + u32 usbintr; + u32 frindex; + u32 ctrldssegment; + u32 periodiclistbase; + u32 asynclistbase; + u32 reserved[9]; + u32 configflag; + u32 portsc[0]; +} PACKED; + +#define CMD_PARK (1<<11) +#define CMD_PARK_CNT(c) (((c)>>8)&3) +#define CMD_LRESET (1<<7) +#define CMD_IAAD (1<<6) +#define CMD_ASE (1<<5) +#define CMD_PSE (1<<4) +#define CMD_HCRESET (1<<1) +#define CMD_RUN (1<<0) + +#define STS_ASS (1<<15) +#define STS_PSS (1<<14) +#define STS_RECL (1<<13) +#define STS_HALT (1<<12) +#define STS_IAA (1<<5) +#define STS_FATAL (1<<4) +#define STS_FLR (1<<3) +#define STS_PCD (1<<2) +#define STS_ERR (1<<1) +#define STS_INT (1<<0) + +#define FLAG_CF (1<<0) + +#define PORT_WKOC_E (1<<22) +#define PORT_WKDISC_E (1<<21) +#define PORT_WKCONN_E (1<<20) +#define PORT_TEST_PKT (0x4<<16) +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) +#define PORT_POWER (1<<12) +#define PORT_LINESTATUS_MASK (3<<10) +#define PORT_LINESTATUS_KSTATE (1<<10) +#define PORT_RESET (1<<8) +#define PORT_SUSPEND (1<<7) +#define PORT_RESUME (1<<6) +#define PORT_OCC (1<<5) +#define PORT_OC (1<<4) +#define PORT_PEC (1<<3) +#define PORT_PE (1<<2) +#define PORT_CSC (1<<1) +#define PORT_CONNECT (1<<0) +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) + + +#define EHCI_QH_ALIGN 128 // Can't span a 4K boundary, so increase from 32 + +struct ehci_qh { + u32 next; + u32 info1; + u32 info2; + u32 current; + + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + u32 buf_hi[5]; +} PACKED; + +#define QH_CONTROL (1 << 27) +#define QH_MAXPACKET_SHIFT 16 +#define QH_MAXPACKET_MASK (0x7ff << QH_MAXPACKET_SHIFT) +#define QH_HEAD (1 << 15) +#define QH_TOGGLECONTROL (1 << 14) +#define QH_SPEED_SHIFT 12 +#define QH_SPEED_MASK (0x3 << QH_SPEED_SHIFT) +#define QH_EP_SHIFT 8 +#define QH_EP_MASK (0xf << QH_EP_SHIFT) +#define QH_DEVADDR_SHIFT 0 +#define QH_DEVADDR_MASK (0x7f << QH_DEVADDR_SHIFT) + +#define QH_SMASK_SHIFT 0 +#define QH_SMASK_MASK (0xff << QH_SMASK_SHIFT) +#define QH_CMASK_SHIFT 8 +#define QH_CMASK_MASK (0xff << QH_CMASK_SHIFT) +#define QH_HUBADDR_SHIFT 16 +#define QH_HUBADDR_MASK (0x7f << QH_HUBADDR_SHIFT) +#define QH_HUBPORT_SHIFT 23 +#define QH_HUBPORT_MASK (0x7f << QH_HUBPORT_SHIFT) +#define QH_MULT_SHIFT 30 +#define QH_MULT_MASK (0x3 << QH_MULT_SHIFT) + +#define EHCI_PTR_BITS 0x001F +#define EHCI_PTR_TERM 0x0001 +#define EHCI_PTR_QH 0x0002 + + +#define EHCI_QTD_ALIGN 64 // Can't span a 4K boundary, so increase from 32 + +struct ehci_qtd { + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + u32 buf_hi[5]; + /* keep struct size a multiple of 64 bytes, as we're allocating + arrays. Without this padding, the second qtd could have the + wrong alignment. */ +} PACKED __aligned(EHCI_QTD_ALIGN); + +#define QTD_TOGGLE (1 << 31) +#define QTD_LENGTH_SHIFT 16 +#define QTD_LENGTH_MASK (0x7fff << QTD_LENGTH_SHIFT) +#define QTD_CERR_SHIFT 10 +#define QTD_CERR_MASK (0x3 << QTD_CERR_SHIFT) +#define QTD_IOC (1 << 15) +#define QTD_PID_OUT (0x0 << 8) +#define QTD_PID_IN (0x1 << 8) +#define QTD_PID_SETUP (0x2 << 8) +#define QTD_STS_ACTIVE (1 << 7) +#define QTD_STS_HALT (1 << 6) +#define QTD_STS_DBE (1 << 5) +#define QTD_STS_BABBLE (1 << 4) +#define QTD_STS_XACT (1 << 3) +#define QTD_STS_MMF (1 << 2) +#define QTD_STS_STS (1 << 1) +#define QTD_STS_PING (1 << 0) + +#define ehci_explen(len) (((len) << QTD_LENGTH_SHIFT) & QTD_LENGTH_MASK) + +#define ehci_maxerr(err) (((err) << QTD_CERR_SHIFT) & QTD_CERR_MASK) + + +struct ehci_framelist { + u32 links[1024]; +} PACKED; + +#endif // usb-ehci.h diff --git a/qemu/roms/seabios/src/hw/usb-hid.c b/qemu/roms/seabios/src/hw/usb-hid.c new file mode 100644 index 000000000..09a6cf455 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hid.c @@ -0,0 +1,432 @@ +// Code for handling USB Human Interface Devices (HID). +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "ps2port.h" // ATKBD_CMD_GETID +#include "usb.h" // usb_ctrlrequest +#include "usb-hid.h" // usb_keyboard_setup +#include "util.h" // process_key + +struct usb_pipe *keyboard_pipe VARFSEG; +struct usb_pipe *mouse_pipe VARFSEG; + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Send USB HID protocol message. +static int +set_protocol(struct usb_pipe *pipe, u16 val) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = HID_REQ_SET_PROTOCOL; + req.wValue = val; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + +// Send USB HID SetIdle request. +static int +set_idle(struct usb_pipe *pipe, int ms) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = HID_REQ_SET_IDLE; + req.wValue = (ms/4)<<8; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + +#define KEYREPEATWAITMS 500 +#define KEYREPEATMS 33 + +static int +usb_kbd_setup(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_KEYBOARD) + return -1; + if (keyboard_pipe) + // XXX - this enables the first found keyboard (could be random) + return -1; + + if (epdesc->wMaxPacketSize != 8) + return -1; + + // Enable "boot" protocol. + int ret = set_protocol(usbdev->defpipe, 0); + if (ret) + return -1; + // Periodically send reports to enable key repeat. + ret = set_idle(usbdev->defpipe, KEYREPEATMS); + if (ret) + return -1; + + keyboard_pipe = usb_alloc_pipe(usbdev, epdesc); + if (!keyboard_pipe) + return -1; + + dprintf(1, "USB keyboard initialized\n"); + return 0; +} + +static int +usb_mouse_setup(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_MOUSE) + return -1; + if (mouse_pipe) + // XXX - this enables the first found mouse (could be random) + return -1; + + if (epdesc->wMaxPacketSize < 3 || epdesc->wMaxPacketSize > 8) + return -1; + + // Enable "boot" protocol. + int ret = set_protocol(usbdev->defpipe, 0); + if (ret) + return -1; + + mouse_pipe = usb_alloc_pipe(usbdev, epdesc); + if (!mouse_pipe) + return -1; + + dprintf(1, "USB mouse initialized\n"); + return 0; +} + +// Initialize a found USB HID device (if applicable). +int +usb_hid_setup(struct usbdevice_s *usbdev) +{ + if (! CONFIG_USB_KEYBOARD && ! CONFIG_USB_MOUSE) + return -1; + dprintf(2, "usb_hid_setup %p\n", usbdev->defpipe); + + struct usb_interface_descriptor *iface = usbdev->iface; + if (iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT) + // Doesn't support boot protocol. + return -1; + + // Find intr in endpoint. + struct usb_endpoint_descriptor *epdesc = usb_find_desc( + usbdev, USB_ENDPOINT_XFER_INT, USB_DIR_IN); + if (!epdesc) { + dprintf(1, "No usb hid intr in?\n"); + return -1; + } + + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD) + return usb_kbd_setup(usbdev, epdesc); + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) + return usb_mouse_setup(usbdev, epdesc); + return -1; +} + + +/**************************************************************** + * Keyboard events + ****************************************************************/ + +// Mapping from USB key id to ps2 key sequence. +static u16 KeyToScanCode[] VAR16 = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020, + 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, + 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014, + 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003, + 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, + 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a, + 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034, + 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, + 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046, + 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d, + 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e, + 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047, + 0x0048, 0x0049, 0x0052, 0x0053 +}; + +// Mapping from USB modifier id to ps2 key sequence. +static u16 ModifierToScanCode[] VAR16 = { + //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui + 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c +}; + +#define RELEASEBIT 0x80 + +// Format of USB keyboard event data +struct keyevent { + u8 modifiers; + u8 reserved; + u8 keys[6]; +}; + +// Translate data from KeyToScanCode[] to calls to process_key(). +static void +prockeys(u16 keys) +{ + if (keys > 0xff) { + u8 key = keys>>8; + if (key == 0xe1) { + // Pause key + process_key(0xe1); + process_key(0x1d | (keys & RELEASEBIT)); + process_key(0x45 | (keys & RELEASEBIT)); + return; + } + process_key(key); + } + process_key(keys); +} + +// Handle a USB key press/release event. +static void +procscankey(u8 key, u8 flags) +{ + if (key >= ARRAY_SIZE(KeyToScanCode)) + return; + u16 keys = GET_GLOBAL(KeyToScanCode[key]); + if (keys) + prockeys(keys | flags); +} + +// Handle a USB modifier press/release event. +static void +procmodkey(u8 mods, u8 flags) +{ + int i; + for (i=0; mods; i++) + if (mods & (1<<i)) { + // Modifier key change. + prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags); + mods &= ~(1<<i); + } +} + +struct usbkeyinfo { + union { + struct { + u8 modifiers; + u8 repeatcount; + u8 keys[6]; + }; + u64 data; + }; +}; +struct usbkeyinfo LastUSBkey VARLOW; + +// Process USB keyboard data. +static void +handle_key(struct keyevent *data) +{ + dprintf(9, "Got key %x %x\n", data->modifiers, data->keys[0]); + + // Load old keys. + struct usbkeyinfo old; + old.data = GET_LOW(LastUSBkey.data); + + // Check for keys no longer pressed. + int addpos = 0; + int i; + for (i=0; i<ARRAY_SIZE(old.keys); i++) { + u8 key = old.keys[i]; + if (!key) + break; + int j; + for (j=0;; j++) { + if (j>=ARRAY_SIZE(data->keys)) { + // Key released. + procscankey(key, RELEASEBIT); + if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1]) + // Last pressed key released - disable repeat. + old.repeatcount = 0xff; + break; + } + if (data->keys[j] == key) { + // Key still pressed. + data->keys[j] = 0; + old.keys[addpos++] = key; + break; + } + } + } + procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT); + + // Process new keys + procmodkey(data->modifiers & ~old.modifiers, 0); + old.modifiers = data->modifiers; + for (i=0; i<ARRAY_SIZE(data->keys); i++) { + u8 key = data->keys[i]; + if (!key) + continue; + // New key pressed. + procscankey(key, 0); + old.keys[addpos++] = key; + old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1; + } + if (addpos < ARRAY_SIZE(old.keys)) + old.keys[addpos] = 0; + + // Check for key repeat event. + if (addpos) { + if (!old.repeatcount) + procscankey(old.keys[addpos-1], 0); + else if (old.repeatcount != 0xff) + old.repeatcount--; + } + + // Update old keys + SET_LOW(LastUSBkey.data, old.data); +} + +// Check if a USB keyboard event is pending and process it if so. +static void +usb_check_key(void) +{ + if (! CONFIG_USB_KEYBOARD) + return; + struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe); + if (!pipe) + return; + + for (;;) { + struct keyevent data; + int ret = usb_poll_intr(pipe, &data); + if (ret) + break; + handle_key(&data); + } +} + +// Test if USB keyboard is active. +inline int +usb_kbd_active(void) +{ + if (! CONFIG_USB_KEYBOARD) + return 0; + return GET_GLOBAL(keyboard_pipe) != NULL; +} + +// Handle a ps2 style keyboard command. +inline int +usb_kbd_command(int command, u8 *param) +{ + if (! CONFIG_USB_KEYBOARD) + return -1; + dprintf(9, "usb keyboard cmd=%x\n", command); + switch (command) { + case ATKBD_CMD_GETID: + // Return the id of a standard AT keyboard. + param[0] = 0xab; + param[1] = 0x83; + return 0; + default: + return -1; + } +} + + +/**************************************************************** + * Mouse events + ****************************************************************/ + +// Format of USB mouse event data +struct mouseevent { + u8 buttons; + u8 x, y; + u8 reserved[5]; +}; + +// Process USB mouse data. +static void +handle_mouse(struct mouseevent *data) +{ + dprintf(9, "Got mouse b=%x x=%x y=%x\n", data->buttons, data->x, data->y); + + s8 x = data->x, y = -data->y; + u8 flag = ((data->buttons & 0x7) | (1<<3) + | (x & 0x80 ? (1<<4) : 0) | (y & 0x80 ? (1<<5) : 0)); + process_mouse(flag); + process_mouse(x); + process_mouse(y); +} + +// Check if a USB mouse event is pending and process it if so. +static void +usb_check_mouse(void) +{ + if (! CONFIG_USB_MOUSE) + return; + struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe); + if (!pipe) + return; + + for (;;) { + struct mouseevent data; + int ret = usb_poll_intr(pipe, &data); + if (ret) + break; + handle_mouse(&data); + } +} + +// Test if USB mouse is active. +inline int +usb_mouse_active(void) +{ + if (! CONFIG_USB_MOUSE) + return 0; + return GET_GLOBAL(mouse_pipe) != NULL; +} + +// Handle a ps2 style mouse command. +inline int +usb_mouse_command(int command, u8 *param) +{ + if (! CONFIG_USB_MOUSE) + return -1; + dprintf(9, "usb mouse cmd=%x\n", command); + switch (command) { + case PSMOUSE_CMD_ENABLE: + case PSMOUSE_CMD_DISABLE: + case PSMOUSE_CMD_SETSCALE11: + return 0; + case PSMOUSE_CMD_SETSCALE21: + case PSMOUSE_CMD_SETRATE: + case PSMOUSE_CMD_SETRES: + // XXX + return 0; + case PSMOUSE_CMD_RESET_BAT: + case PSMOUSE_CMD_GETID: + // Return the id of a standard AT mouse. + param[0] = 0xaa; + param[1] = 0x00; + return 0; + + case PSMOUSE_CMD_GETINFO: + param[0] = 0x00; + param[1] = 4; + param[2] = 100; + return 0; + + default: + return -1; + } +} + +// Check for USB events pending - called periodically from timer interrupt. +void +usb_check_event(void) +{ + usb_check_key(); + usb_check_mouse(); +} diff --git a/qemu/roms/seabios/src/hw/usb-hid.h b/qemu/roms/seabios/src/hw/usb-hid.h new file mode 100644 index 000000000..ef34e7963 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hid.h @@ -0,0 +1,29 @@ +#ifndef __USB_HID_H +#define __USB_HID_H + +// usb-hid.c +struct usbdevice_s; +int usb_hid_setup(struct usbdevice_s *usbdev); +inline int usb_kbd_active(void); +inline int usb_kbd_command(int command, u8 *param); +inline int usb_mouse_active(void); +inline int usb_mouse_command(int command, u8 *param); +void usb_check_event(void); + + +/**************************************************************** + * hid flags + ****************************************************************/ + +#define USB_INTERFACE_SUBCLASS_BOOT 1 +#define USB_INTERFACE_PROTOCOL_KEYBOARD 1 +#define USB_INTERFACE_PROTOCOL_MOUSE 2 + +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B + +#endif // ush-hid.h diff --git a/qemu/roms/seabios/src/hw/usb-hub.c b/qemu/roms/seabios/src/hw/usb-hub.c new file mode 100644 index 000000000..54e341b73 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hub.c @@ -0,0 +1,205 @@ +// Code for handling standard USB hubs. +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_USB_HUB +#include "output.h" // dprintf +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-hub.h" // struct usb_hub_descriptor +#include "util.h" // timer_calc + +static int +get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + if (pipe->speed == USB_SUPERSPEED) + req.wValue = USB_DT_HUB3<<8; + else + req.wValue = USB_DT_HUB<<8; + req.wIndex = 0; + req.wLength = sizeof(*desc); + return usb_send_default_control(pipe, &req, desc); +} + +static int +set_hub_depth(struct usb_pipe *pipe, u16 depth) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE; + req.bRequest = HUB_REQ_SET_HUB_DEPTH; + req.wValue = depth; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + +static int +set_port_feature(struct usbhub_s *hub, int port, int feature) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_SET_FEATURE; + req.wValue = feature; + req.wIndex = port + 1; + req.wLength = 0; + mutex_lock(&hub->lock); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); + mutex_unlock(&hub->lock); + return ret; +} + +static int +clear_port_feature(struct usbhub_s *hub, int port, int feature) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_CLEAR_FEATURE; + req.wValue = feature; + req.wIndex = port + 1; + req.wLength = 0; + mutex_lock(&hub->lock); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); + mutex_unlock(&hub->lock); + return ret; +} + +static int +get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_GET_STATUS; + req.wValue = 0; + req.wIndex = port + 1; + req.wLength = sizeof(*sts); + mutex_lock(&hub->lock); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, sts); + mutex_unlock(&hub->lock); + return ret; +} + +// Check if device attached to port +static int +usb_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_port_status sts; + int ret = get_port_status(hub, port, &sts); + if (ret) { + dprintf(1, "Failure on hub port %d detect\n", port); + return -1; + } + return (sts.wPortStatus & USB_PORT_STAT_CONNECTION) ? 1 : 0; +} + +// Disable port +static void +usb_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE); + if (ret) + dprintf(1, "Failure on hub port %d disconnect\n", port); +} + +// Reset device on port +static int +usb_hub_reset(struct usbhub_s *hub, u32 port) +{ + int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET); + if (ret) + goto fail; + + // Wait for reset to complete. + struct usb_port_status sts; + u32 end = timer_calc(USB_TIME_DRST * 2); + for (;;) { + ret = get_port_status(hub, port, &sts); + if (ret) + goto fail; + if (!(sts.wPortStatus & USB_PORT_STAT_RESET) + && (hub->usbdev->speed != USB_SUPERSPEED + || !(sts.wPortStatus & USB_PORT_STAT_LINK_MASK))) + break; + if (timer_check(end)) { + warn_timeout(); + goto fail; + } + msleep(5); + } + + // Reset complete. + if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) + // Device no longer present + return -1; + + if (hub->usbdev->speed == USB_SUPERSPEED) + return USB_SUPERSPEED; + return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) + >> USB_PORT_STAT_SPEED_SHIFT); + +fail: + dprintf(1, "Failure on hub port %d reset\n", port); + usb_hub_disconnect(hub, port); + return -1; +} + +static struct usbhub_op_s HubOp = { + .detect = usb_hub_detect, + .reset = usb_hub_reset, + .disconnect = usb_hub_disconnect, +}; + +// Configure a usb hub and then find devices connected to it. +int +usb_hub_setup(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + if (!CONFIG_USB_HUB) + return -1; + + struct usb_hub_descriptor desc; + int ret = get_hub_desc(usbdev->defpipe, &desc); + if (ret) + return ret; + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.usbdev = usbdev; + hub.cntl = usbdev->defpipe->cntl; + hub.portcount = desc.bNbrPorts; + hub.op = &HubOp; + + if (usbdev->speed == USB_SUPERSPEED) { + int depth = 0; + struct usbdevice_s *parent = usbdev->hub->usbdev; + while (parent) { + depth++; + parent = parent->hub->usbdev; + } + + ret = set_hub_depth(usbdev->defpipe, depth); + if (ret) + return ret; + } + + // Turn on power to ports. + int port; + for (port=0; port<desc.bNbrPorts; port++) { + ret = set_port_feature(&hub, port, USB_PORT_FEAT_POWER); + if (ret) + return ret; + } + // Wait for port power to stabilize. + msleep(desc.bPwrOn2PwrGood * 2); + + usb_enumerate(&hub); + + dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount); + if (hub.devcount) + return 0; + return -1; +} diff --git a/qemu/roms/seabios/src/hw/usb-hub.h b/qemu/roms/seabios/src/hw/usb-hub.h new file mode 100644 index 000000000..880378ca1 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hub.h @@ -0,0 +1,64 @@ +#ifndef __USB_HUB_H +#define __USB_HUB_H + +// usb-hub.c +struct usbdevice_s; +int usb_hub_setup(struct usbdevice_s *usbdev); + + +/**************************************************************** + * hub flags + ****************************************************************/ + +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) +#define USB_DT_HUB3 (USB_TYPE_CLASS | 0x0a) + +#define HUB_REQ_SET_HUB_DEPTH 0x0C + +struct usb_hub_descriptor { + u8 bDescLength; + u8 bDescriptorType; + u8 bNbrPorts; + u16 wHubCharacteristics; + u8 bPwrOn2PwrGood; + u8 bHubContrCurrent; + // Variable length fields for DeviceRemovable[], PortPwrCtrlMask[] follow. +} PACKED; + +#define USB_PORT_FEAT_CONNECTION 0 +#define USB_PORT_FEAT_ENABLE 1 +#define USB_PORT_FEAT_SUSPEND 2 +#define USB_PORT_FEAT_OVER_CURRENT 3 +#define USB_PORT_FEAT_RESET 4 +#define USB_PORT_FEAT_POWER 8 +#define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_C_CONNECTION 16 +#define USB_PORT_FEAT_C_ENABLE 17 +#define USB_PORT_FEAT_C_SUSPEND 18 +#define USB_PORT_FEAT_C_OVER_CURRENT 19 +#define USB_PORT_FEAT_C_RESET 20 +#define USB_PORT_FEAT_TEST 21 +#define USB_PORT_FEAT_INDICATOR 22 +#define USB_PORT_FEAT_C_PORT_L1 23 + +struct usb_port_status { + u16 wPortStatus; + u16 wPortChange; +} PACKED; + +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +#define USB_PORT_STAT_LINK_SHIFT 5 +#define USB_PORT_STAT_LINK_MASK (0x7 << USB_PORT_STAT_LINK_SHIFT) +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_SPEED_SHIFT 9 +#define USB_PORT_STAT_SPEED_MASK (0x3 << USB_PORT_STAT_SPEED_SHIFT) +#define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_PORT_STAT_TEST 0x0800 +#define USB_PORT_STAT_INDICATOR 0x1000 + +#endif // ush-hid.h diff --git a/qemu/roms/seabios/src/hw/usb-msc.c b/qemu/roms/seabios/src/hw/usb-msc.c new file mode 100644 index 000000000..d90319f51 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-msc.c @@ -0,0 +1,220 @@ +// Code for handling USB Mass Storage Controller devices. +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // DTYPE_USB +#include "blockcmd.h" // cdb_read +#include "config.h" // CONFIG_USB_MSC +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-msc.h" // usb_msc_setup +#include "util.h" // bootprio_find_usb + +struct usbdrive_s { + struct drive_s drive; + struct usb_pipe *bulkin, *bulkout; + int lun; +}; + + +/**************************************************************** + * Bulk-only drive command processing + ****************************************************************/ + +#define USB_CDB_SIZE 12 + +#define CBW_SIGNATURE 0x43425355 // USBC + +struct cbw_s { + u32 dCBWSignature; + u32 dCBWTag; + u32 dCBWDataTransferLength; + u8 bmCBWFlags; + u8 bCBWLUN; + u8 bCBWCBLength; + u8 CBWCB[16]; +} PACKED; + +#define CSW_SIGNATURE 0x53425355 // USBS + +struct csw_s { + u32 dCSWSignature; + u32 dCSWTag; + u32 dCSWDataResidue; + u8 bCSWStatus; +} PACKED; + +static int +usb_msc_send(struct usbdrive_s *udrive_gf, int dir, void *buf, u32 bytes) +{ + struct usb_pipe *pipe; + if (dir == USB_DIR_OUT) + pipe = GET_GLOBALFLAT(udrive_gf->bulkout); + else + pipe = GET_GLOBALFLAT(udrive_gf->bulkin); + return usb_send_bulk(pipe, dir, buf, bytes); +} + +// Low-level usb command transmit function. +int +usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_USB_MSC) + return 0; + + dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n" + , op->drive_gf, 0, op->count, blocksize, op->buf_fl); + struct usbdrive_s *udrive_gf = container_of( + op->drive_gf, struct usbdrive_s, drive); + + // Setup command block wrapper. + u32 bytes = blocksize * op->count; + struct cbw_s cbw; + memset(&cbw, 0, sizeof(cbw)); + memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE); + cbw.dCBWSignature = CBW_SIGNATURE; + cbw.dCBWTag = 999; // XXX + cbw.dCBWDataTransferLength = bytes; + cbw.bmCBWFlags = cdb_is_read(cdbcmd, blocksize) ? USB_DIR_IN : USB_DIR_OUT; + cbw.bCBWLUN = GET_GLOBALFLAT(udrive_gf->lun); + cbw.bCBWCBLength = USB_CDB_SIZE; + + // Transfer cbw to device. + int ret = usb_msc_send(udrive_gf, USB_DIR_OUT + , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw)); + if (ret) + goto fail; + + // Transfer data to/from device. + if (bytes) { + ret = usb_msc_send(udrive_gf, cbw.bmCBWFlags, op->buf_fl, bytes); + if (ret) + goto fail; + } + + // Transfer csw info. + struct csw_s csw; + ret = usb_msc_send(udrive_gf, USB_DIR_IN + , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw)); + if (ret) + goto fail; + + if (!csw.bCSWStatus) + return DISK_RET_SUCCESS; + if (csw.bCSWStatus == 2) + goto fail; + + if (blocksize) + op->count -= csw.dCSWDataResidue / blocksize; + return DISK_RET_EBADTRACK; + +fail: + // XXX - reset connection + dprintf(1, "USB transmission failed\n"); + return DISK_RET_EBADTRACK; +} + +static int +usb_msc_maxlun(struct usb_pipe *pipe) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = 0xfe; + req.wValue = 0; + req.wIndex = 0; + req.wLength = 1; + unsigned char maxlun; + int ret = usb_send_default_control(pipe, &req, &maxlun); + if (ret) + return 0; + return maxlun; +} + +static int +usb_msc_lun_setup(struct usb_pipe *inpipe, struct usb_pipe *outpipe, + struct usbdevice_s *usbdev, int lun) +{ + // Allocate drive structure. + struct usbdrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return -1; + } + memset(drive, 0, sizeof(*drive)); + if (usb_32bit_pipe(inpipe)) + drive->drive.type = DTYPE_USB_32; + else + drive->drive.type = DTYPE_USB; + drive->bulkin = inpipe; + drive->bulkout = outpipe; + drive->lun = lun; + + int prio = bootprio_find_usb(usbdev, lun); + int ret = scsi_drive_setup(&drive->drive, "USB MSC", prio); + if (ret) { + dprintf(1, "Unable to configure USB MSC drive.\n"); + free(drive); + return -1; + } + return 0; +} + +/**************************************************************** + * Setup + ****************************************************************/ + +// Configure a usb msc device. +int +usb_msc_setup(struct usbdevice_s *usbdev) +{ + if (!CONFIG_USB_MSC) + return -1; + + // Verify right kind of device + struct usb_interface_descriptor *iface = usbdev->iface; + if ((iface->bInterfaceSubClass != US_SC_SCSI && + iface->bInterfaceSubClass != US_SC_ATAPI_8070 && + iface->bInterfaceSubClass != US_SC_ATAPI_8020) + || iface->bInterfaceProtocol != US_PR_BULK) { + dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + // Find bulk in and bulk out endpoints. + struct usb_pipe *inpipe = NULL, *outpipe = NULL; + struct usb_endpoint_descriptor *indesc = usb_find_desc( + usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_IN); + struct usb_endpoint_descriptor *outdesc = usb_find_desc( + usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT); + if (!indesc || !outdesc) + goto fail; + inpipe = usb_alloc_pipe(usbdev, indesc); + outpipe = usb_alloc_pipe(usbdev, outdesc); + if (!inpipe || !outpipe) + goto fail; + + int maxlun = usb_msc_maxlun(usbdev->defpipe); + int lun, pipesused = 0; + for (lun = 0; lun < maxlun + 1; lun++) { + int ret = usb_msc_lun_setup(inpipe, outpipe, usbdev, lun); + if (!ret) + pipesused = 1; + } + + if (!pipesused) + goto fail; + + return 0; +fail: + dprintf(1, "Unable to configure USB MSC device.\n"); + usb_free_pipe(usbdev, inpipe); + usb_free_pipe(usbdev, outpipe); + return -1; +} diff --git a/qemu/roms/seabios/src/hw/usb-msc.h b/qemu/roms/seabios/src/hw/usb-msc.h new file mode 100644 index 000000000..c40d75556 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-msc.h @@ -0,0 +1,10 @@ +#ifndef __USB_MSC_H +#define __USB_MSC_H + +// usb-msc.c +struct disk_op_s; +int usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +struct usbdevice_s; +int usb_msc_setup(struct usbdevice_s *usbdev); + +#endif // ush-msc.h diff --git a/qemu/roms/seabios/src/hw/usb-ohci.c b/qemu/roms/seabios/src/hw/usb-ohci.c new file mode 100644 index 000000000..42f8a0681 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ohci.c @@ -0,0 +1,569 @@ +// Code for handling OHCI USB controllers. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "memmap.h" // PAGE_SIZE +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_OHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ohci.h" // struct ohci_hcca +#include "util.h" // msleep +#include "x86.h" // readl + +#define FIT (1 << 31) + +struct usb_ohci_s { + struct usb_s usb; + struct ohci_regs *regs; +}; + +struct ohci_pipe { + struct ohci_ed ed; + struct usb_pipe pipe; + struct ohci_regs *regs; + void *data; + int count; + struct ohci_td *tds; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +// Check if device attached to port +static int +ohci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + u32 sts = readl(&cntl->regs->roothub_portstatus[port]); + return (sts & RH_PS_CCS) ? 1 : 0; +} + +// Disable port +static void +ohci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); +} + +// Reset device on port +static int +ohci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS); + u32 sts; + u32 end = timer_calc(USB_TIME_DRSTR * 2); + for (;;) { + sts = readl(&cntl->regs->roothub_portstatus[port]); + if (!(sts & RH_PS_PRS)) + // XXX - need to ensure USB_TIME_DRSTR time in reset? + break; + if (timer_check(end)) { + // Timeout. + warn_timeout(); + ohci_hub_disconnect(hub, port); + return -1; + } + yield(); + } + + if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES)) + // Device no longer present + return -1; + + return !!(sts & RH_PS_LSDA); +} + +static struct usbhub_op_s ohci_HubOp = { + .detect = ohci_hub_detect, + .reset = ohci_hub_reset, + .disconnect = ohci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_ohci_ports(struct usb_ohci_s *cntl) +{ + ASSERT32FLAT(); + // Turn on power for all devices on roothub. + u32 rha = readl(&cntl->regs->roothub_a); + rha &= ~(RH_A_PSM | RH_A_OCPM); + writel(&cntl->regs->roothub_status, RH_HS_LPSC); + writel(&cntl->regs->roothub_b, RH_B_PPCM); + msleep((rha >> 24) * 2); + // XXX - need to sleep for USB_TIME_SIGATT if just powered up? + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = rha & RH_A_NDP; + hub.op = &ohci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB frame to start - for ensuring safe memory release. +static void +ohci_waittick(struct ohci_regs *regs) +{ + barrier(); + struct ohci_hcca *hcca = (void*)regs->hcca; + u32 startframe = hcca->frame_no; + u32 end = timer_calc(1000 * 5); + for (;;) { + if (hcca->frame_no != startframe) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } +} + +static void +ohci_free_pipes(struct usb_ohci_s *cntl) +{ + dprintf(7, "ohci_free_pipes %p\n", cntl); + + u32 creg = readl(&cntl->regs->control); + if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) { + writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); + ohci_waittick(cntl->regs); + } + + u32 *pos = &cntl->regs->ed_controlhead; + for (;;) { + struct ohci_ed *next = (void*)*pos; + if (!next) + break; + struct ohci_pipe *pipe = container_of(next, struct ohci_pipe, ed); + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) { + *pos = next->hwNextED; + free(pipe); + } else { + pos = &next->hwNextED; + } + } + + writel(&cntl->regs->ed_controlcurrent, 0); + writel(&cntl->regs->ed_bulkcurrent, 0); + writel(&cntl->regs->control, creg); + cntl->usb.freelist = NULL; +} + +static int +start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) +{ + u32 oldfminterval = readl(&cntl->regs->fminterval); + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC; + + // XXX - check if already running? + + // Do reset + writel(&cntl->regs->control, OHCI_USB_RESET | oldrwc); + readl(&cntl->regs->control); // flush writes + msleep(USB_TIME_DRSTR); + + // Do software init (min 10us, max 2ms) + u32 end = timer_calc_usec(10); + writel(&cntl->regs->cmdstatus, OHCI_HCR); + for (;;) { + u32 status = readl(&cntl->regs->cmdstatus); + if (! status & OHCI_HCR) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + } + + // Init memory + writel(&cntl->regs->ed_controlhead, 0); + writel(&cntl->regs->ed_bulkhead, 0); + writel(&cntl->regs->hcca, (u32)hcca); + + // Init fminterval + u32 fi = oldfminterval & 0x3fff; + writel(&cntl->regs->fminterval + , (((oldfminterval & FIT) ^ FIT) + | fi | (((6 * (fi - 210)) / 7) << 16))); + writel(&cntl->regs->periodicstart, ((9 * fi) / 10) & 0x3fff); + readl(&cntl->regs->control); // flush writes + + // XXX - verify that fminterval was setup correctly. + + // Go into operational state + writel(&cntl->regs->control + , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_BLE | OHCI_CTRL_PLE + | OHCI_USB_OPER | oldrwc)); + readl(&cntl->regs->control); // flush writes + + return 0; +} + +static void +stop_ohci(struct usb_ohci_s *cntl) +{ + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC; + writel(&cntl->regs->control, oldrwc); + readl(&cntl->regs->control); // flush writes +} + +static void +configure_ohci(void *data) +{ + struct usb_ohci_s *cntl = data; + + // Allocate memory + struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca)); + struct ohci_ed *intr_ed = malloc_high(sizeof(*intr_ed)); + if (!hcca || !intr_ed) { + warn_noalloc(); + goto free; + } + memset(hcca, 0, sizeof(*hcca)); + memset(intr_ed, 0, sizeof(*intr_ed)); + intr_ed->hwINFO = ED_SKIP; + int i; + for (i=0; i<ARRAY_SIZE(hcca->int_table); i++) + hcca->int_table[i] = (u32)intr_ed; + + int ret = start_ohci(cntl, hcca); + if (ret) + goto err; + + int count = check_ohci_ports(cntl); + ohci_free_pipes(cntl); + if (! count) + goto err; + return; + +err: + stop_ohci(cntl); +free: + free(hcca); + free(intr_ed); +} + +static void +ohci_controller_setup(struct pci_device *pci) +{ + struct usb_ohci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_OHCI; + + wait_preempt(); // Avoid pci_config_readl when preempting + u16 bdf = pci->bdf; + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + cntl->regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + + dprintf(1, "OHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + // Enable bus mastering and memory access. + pci_config_maskw(bdf, PCI_COMMAND + , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY); + + // XXX - check for and disable SMM control? + + // Disable interrupts + writel(&cntl->regs->intrdisable, ~0); + writel(&cntl->regs->intrstatus, ~0); + + run_thread(configure_ohci, cntl); +} + +void +ohci_setup(void) +{ + if (! CONFIG_USB_OHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI) + ohci_controller_setup(pci); + } +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +// Setup fields in ed +static void +ohci_desc2pipe(struct ohci_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->ed.hwINFO = (ED_SKIP | usbdev->devaddr | (pipe->pipe.ep << 7) + | (epdesc->wMaxPacketSize << 16) + | (usbdev->speed ? ED_LOWSPEED : 0)); + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + pipe->regs = cntl->regs; +} + +static struct usb_pipe * +ohci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + int frameexp = usb_get_period(usbdev, epdesc); + dprintf(7, "ohci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 5) + frameexp = 5; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms) + 1; + struct ohci_pipe *pipe = malloc_low(sizeof(*pipe)); + struct ohci_td *tds = malloc_low(sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) + goto err; + memset(pipe, 0, sizeof(*pipe)); + ohci_desc2pipe(pipe, usbdev, epdesc); + pipe->ed.hwINFO &= ~ED_SKIP; + pipe->data = data; + pipe->count = count; + pipe->tds = tds; + + struct ohci_ed *ed = &pipe->ed; + ed->hwHeadP = (u32)&tds[0]; + ed->hwTailP = (u32)&tds[count-1]; + + int i; + for (i=0; i<count-1; i++) { + tds[i].hwINFO = TD_DP_IN | TD_T_TOGGLE | TD_CC; + tds[i].hwCBP = (u32)data + maxpacket * i; + tds[i].hwNextTD = (u32)&tds[i+1]; + tds[i].hwBE = tds[i].hwCBP + maxpacket - 1; + } + + // Add to interrupt schedule. + struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + if (frameexp == 0) { + // Add to existing interrupt entry. + struct ohci_ed *intr_ed = (void*)hcca->int_table[0]; + ed->hwNextED = intr_ed->hwNextED; + barrier(); + intr_ed->hwNextED = (u32)ed; + } else { + int startpos = 1<<(frameexp-1); + ed->hwNextED = hcca->int_table[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(hcca->int_table); i+=ms) + hcca->int_table[i] = (u32)ed; + } + + return &pipe->pipe; + +err: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_OHCI) + return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return ohci_alloc_intr_pipe(usbdev, epdesc); + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + dprintf(7, "ohci_alloc_async_pipe %p\n", &cntl->usb); + + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + struct ohci_pipe *pipe = container_of(usbpipe, struct ohci_pipe, pipe); + ohci_desc2pipe(pipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct ohci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = malloc_tmphigh(sizeof(*pipe)); + else + pipe = malloc_low(sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + ohci_desc2pipe(pipe, usbdev, epdesc); + + // Add queue head to controller list. + u32 *head = &cntl->regs->ed_controlhead; + if (eptype != USB_ENDPOINT_XFER_CONTROL) + head = &cntl->regs->ed_bulkhead; + pipe->ed.hwNextED = *head; + barrier(); + *head = (u32)&pipe->ed; + return &pipe->pipe; +} + +static int +wait_ed(struct ohci_ed *ed, int timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + if ((ed->hwHeadP & ~(ED_C|ED_H)) == ed->hwTailP) + return 0; + if (timer_check(end)) { + warn_timeout(); + dprintf(1, "ohci ed info=%x tail=%x head=%x next=%x\n" + , ed->hwINFO, ed->hwTailP, ed->hwHeadP, ed->hwNextED); + return -1; + } + yield(); + } +} + +#define STACKOTDS 18 +#define OHCI_TD_ALIGN 16 + +int +ohci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) +{ + ASSERT32FLAT(); + if (! CONFIG_USB_OHCI) + return -1; + dprintf(7, "ohci_send_pipe %p\n", p); + struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); + + // Allocate tds on stack (with required alignment) + u8 tdsbuf[sizeof(struct ohci_td) * STACKOTDS + OHCI_TD_ALIGN - 1]; + struct ohci_td *tds = (void*)ALIGN((u32)tdsbuf, OHCI_TD_ALIGN), *td = tds; + memset(tds, 0, sizeof(*tds) * STACKOTDS); + + // Setup transfer descriptors + u16 maxpacket = pipe->pipe.maxpacket; + u32 toggle = 0, statuscmd = OHCI_BLF; + if (cmd) { + // Send setup pid on control transfers + td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; + td->hwCBP = (u32)cmd; + td->hwNextTD = (u32)&td[1]; + td->hwBE = (u32)cmd + USB_CONTROL_SETUP_SIZE - 1; + td++; + toggle = TD_T_DATA1; + statuscmd = OHCI_CLF; + } + u32 dest = (u32)data, dataend = dest + datasize; + while (dest < dataend) { + // Send data pids + if (td >= &tds[STACKOTDS]) { + warn_noalloc(); + return -1; + } + int maxtransfer = 2*PAGE_SIZE - (dest & (PAGE_SIZE-1)); + int transfer = dataend - dest; + if (transfer > maxtransfer) + transfer = ALIGN_DOWN(maxtransfer, maxpacket); + td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | toggle | TD_CC; + td->hwCBP = dest; + td->hwNextTD = (u32)&td[1]; + td->hwBE = dest + transfer - 1; + td++; + dest += transfer; + } + if (cmd) { + // Send status pid on control transfers + if (td >= &tds[STACKOTDS]) { + warn_noalloc(); + return -1; + } + td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; + td->hwCBP = 0; + td->hwNextTD = (u32)&td[1]; + td->hwBE = 0; + td++; + } + + // Transfer data + pipe->ed.hwHeadP = (u32)tds | (pipe->ed.hwHeadP & ED_C); + pipe->ed.hwTailP = (u32)td; + barrier(); + pipe->ed.hwINFO &= ~ED_SKIP; + writel(&pipe->regs->cmdstatus, statuscmd); + + int ret = wait_ed(&pipe->ed, usb_xfer_time(p, datasize)); + pipe->ed.hwINFO |= ED_SKIP; + if (ret) + ohci_waittick(pipe->regs); + return ret; +} + +int +ohci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_OHCI) + return -1; + + struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); + struct ohci_td *tds = GET_LOWFLAT(pipe->tds); + struct ohci_td *head = (void*)(GET_LOWFLAT(pipe->ed.hwHeadP) & ~(ED_C|ED_H)); + struct ohci_td *tail = (void*)GET_LOWFLAT(pipe->ed.hwTailP); + int count = GET_LOWFLAT(pipe->count); + int pos = (tail - tds + 1) % count; + struct ohci_td *next = &tds[pos]; + if (head == next) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + void *pipedata = GET_LOWFLAT((pipe->data)); + void *intrdata = pipedata + maxpacket * pos; + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(intrdata), maxpacket); + + // Reenable this td. + SET_LOWFLAT(tail->hwINFO, TD_DP_IN | TD_T_TOGGLE | TD_CC); + intrdata = pipedata + maxpacket * (tail-tds); + SET_LOWFLAT(tail->hwCBP, (u32)intrdata); + SET_LOWFLAT(tail->hwNextTD, (u32)next); + SET_LOWFLAT(tail->hwBE, (u32)intrdata + maxpacket - 1); + barrier(); + SET_LOWFLAT(pipe->ed.hwTailP, (u32)next); + + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-ohci.h b/qemu/roms/seabios/src/hw/usb-ohci.h new file mode 100644 index 000000000..5a275a334 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ohci.h @@ -0,0 +1,144 @@ +#ifndef __USB_OHCI_H +#define __USB_OHCI_H + +// usb-ohci.c +void ohci_setup(void); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; +struct usb_pipe *ohci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int ohci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int ohci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * ohci structs and flags + ****************************************************************/ + +struct ohci_ed { + u32 hwINFO; + u32 hwTailP; + u32 hwHeadP; + u32 hwNextED; +} PACKED; + +#define ED_ISO (1 << 15) +#define ED_SKIP (1 << 14) +#define ED_LOWSPEED (1 << 13) +#define ED_OUT (0x01 << 11) +#define ED_IN (0x02 << 11) + +#define ED_C (0x02) +#define ED_H (0x01) + +struct ohci_td { + u32 hwINFO; + u32 hwCBP; + u32 hwNextTD; + u32 hwBE; +} PACKED; + +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_DI 0x00E00000 + +#define TD_DONE 0x00020000 +#define TD_ISO 0x00010000 + +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_R 0x00040000 + +struct ohci_hcca { + u32 int_table[32]; + u32 frame_no; + u32 done_head; + u8 reserved[120]; +} PACKED; + +struct ohci_regs { + u32 revision; + u32 control; + u32 cmdstatus; + u32 intrstatus; + u32 intrenable; + u32 intrdisable; + + u32 hcca; + u32 ed_periodcurrent; + u32 ed_controlhead; + u32 ed_controlcurrent; + u32 ed_bulkhead; + u32 ed_bulkcurrent; + u32 donehead; + + u32 fminterval; + u32 fmremaining; + u32 fmnumber; + u32 periodicstart; + u32 lsthresh; + + u32 roothub_a; + u32 roothub_b; + u32 roothub_status; + u32 roothub_portstatus[15]; +} PACKED; + +#define OHCI_CTRL_CBSR (3 << 0) +#define OHCI_CTRL_PLE (1 << 2) +#define OHCI_CTRL_CLE (1 << 4) +#define OHCI_CTRL_BLE (1 << 5) +#define OHCI_CTRL_HCFS (3 << 6) +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_OPER (2 << 6) +#define OHCI_CTRL_RWC (1 << 9) + +#define OHCI_HCR (1 << 0) +#define OHCI_CLF (1 << 1) +#define OHCI_BLF (1 << 2) + +#define OHCI_INTR_MIE (1 << 31) + +#define RH_PS_CCS 0x00000001 +#define RH_PS_PES 0x00000002 +#define RH_PS_PSS 0x00000004 +#define RH_PS_POCI 0x00000008 +#define RH_PS_PRS 0x00000010 +#define RH_PS_PPS 0x00000100 +#define RH_PS_LSDA 0x00000200 +#define RH_PS_CSC 0x00010000 +#define RH_PS_PESC 0x00020000 +#define RH_PS_PSSC 0x00040000 +#define RH_PS_OCIC 0x00080000 +#define RH_PS_PRSC 0x00100000 + +#define RH_HS_LPS 0x00000001 +#define RH_HS_OCI 0x00000002 +#define RH_HS_DRWE 0x00008000 +#define RH_HS_LPSC 0x00010000 +#define RH_HS_OCIC 0x00020000 +#define RH_HS_CRWE 0x80000000 + +#define RH_B_DR 0x0000ffff +#define RH_B_PPCM 0xffff0000 + +#define RH_A_NDP (0xff << 0) +#define RH_A_PSM (1 << 8) +#define RH_A_NPS (1 << 9) +#define RH_A_DT (1 << 10) +#define RH_A_OCPM (1 << 11) +#define RH_A_NOCP (1 << 12) +#define RH_A_POTPGT (0xff << 24) + +#endif // usb-ohci.h diff --git a/qemu/roms/seabios/src/hw/usb-uas.c b/qemu/roms/seabios/src/hw/usb-uas.c new file mode 100644 index 000000000..6ef8d0912 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uas.c @@ -0,0 +1,274 @@ +// Code for handling usb attached scsi devices. +// +// only usb 2.0 for now. +// +// once we have xhci driver with usb 3.0 support this must +// be updated to use usb3 streams so booting from usb3 +// devices actually works. +// +// Authors: +// Gerd Hoffmann <kraxel@redhat.com> +// +// based on usb-msc.c which is written by: +// Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // DTYPE_USB +#include "blockcmd.h" // cdb_read +#include "config.h" // CONFIG_USB_UAS +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-uas.h" // usb_uas_init +#include "util.h" // bootprio_find_usb + +#define UAS_UI_COMMAND 0x01 +#define UAS_UI_SENSE 0x03 +#define UAS_UI_RESPONSE 0x04 +#define UAS_UI_TASK_MGMT 0x05 +#define UAS_UI_READ_READY 0x06 +#define UAS_UI_WRITE_READY 0x07 + +#define UAS_PIPE_ID_COMMAND 0x01 +#define UAS_PIPE_ID_STATUS 0x02 +#define UAS_PIPE_ID_DATA_IN 0x03 +#define UAS_PIPE_ID_DATA_OUT 0x04 + +typedef struct { + u8 id; + u8 reserved; + u16 tag; +} PACKED uas_ui_header; + +typedef struct { + u8 prio_taskattr; /* 6:3 priority, 2:0 task attribute */ + u8 reserved_1; + u8 add_cdb_length; /* 7:2 additional adb length (dwords) */ + u8 reserved_2; + u8 lun[8]; + u8 cdb[16]; + u8 add_cdb[]; +} PACKED uas_ui_command; + +typedef struct { + u16 status_qualifier; + u8 status; + u8 reserved[7]; + u16 sense_length; + u8 sense_data[18]; +} PACKED uas_ui_sense; + +typedef struct { + u16 add_response_info; + u8 response_code; +} PACKED uas_ui_response; + +typedef struct { + u8 function; + u8 reserved; + u16 task_tag; + u8 lun[8]; +} PACKED uas_ui_task_mgmt; + +typedef struct { + uas_ui_header hdr; + union { + uas_ui_command command; + uas_ui_sense sense; + uas_ui_task_mgmt task; + uas_ui_response response; + }; +} PACKED uas_ui; + +struct uasdrive_s { + struct drive_s drive; + struct usb_pipe *command, *status, *data_in, *data_out; + int lun; +}; + +int +uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_USB_UAS) + return DISK_RET_EBADTRACK; + + struct uasdrive_s *drive_gf = container_of( + op->drive_gf, struct uasdrive_s, drive); + + uas_ui ui; + memset(&ui, 0, sizeof(ui)); + ui.hdr.id = UAS_UI_COMMAND; + ui.hdr.tag = 0xdead; + ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun); + memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb)); + int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command), + USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui), + sizeof(ui.hdr) + sizeof(ui.command)); + if (ret) { + dprintf(1, "uas: command send fail"); + goto fail; + } + + memset(&ui, 0xff, sizeof(ui)); + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status), + USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui)); + if (ret) { + dprintf(1, "uas: status recv fail"); + goto fail; + } + + switch (ui.hdr.id) { + case UAS_UI_SENSE: + goto have_sense; + case UAS_UI_READ_READY: + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_in), + USB_DIR_IN, op->buf_fl, op->count * blocksize); + if (ret) { + dprintf(1, "uas: data read fail"); + goto fail; + } + break; + case UAS_UI_WRITE_READY: + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_out), + USB_DIR_OUT, op->buf_fl, op->count * blocksize); + if (ret) { + dprintf(1, "uas: data write fail"); + goto fail; + } + break; + default: + dprintf(1, "uas: unknown status ui id %d", ui.hdr.id); + goto fail; + } + + memset(&ui, 0xff, sizeof(ui)); + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status), + USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui)); + if (ret) { + dprintf(1, "uas: status recv fail"); + goto fail; + } + if (ui.hdr.id != UAS_UI_SENSE) { + dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id); + goto fail; + } + +have_sense: + if (ui.sense.status == 0) { + return DISK_RET_SUCCESS; + } + +fail: + return DISK_RET_EBADTRACK; +} + +static int +uas_lun_setup(struct usbdevice_s *usbdev, + struct usb_pipe *command, struct usb_pipe *status, + struct usb_pipe *data_in, struct usb_pipe *data_out, + int lun) +{ + // Allocate drive structure. + struct uasdrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return -1; + } + memset(drive, 0, sizeof(*drive)); + if (usb_32bit_pipe(data_in)) + drive->drive.type = DTYPE_UAS_32; + else + drive->drive.type = DTYPE_UAS; + drive->command = command; + drive->status = status; + drive->data_in = data_in; + drive->data_out = data_out; + drive->lun = lun; + + int prio = bootprio_find_usb(usbdev, lun); + int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio); + if (ret) { + free(drive); + return -1; + } + return 0; +} + +int +usb_uas_setup(struct usbdevice_s *usbdev) +{ + if (!CONFIG_USB_UAS) + return -1; + + // Verify right kind of device + struct usb_interface_descriptor *iface = usbdev->iface; + if (iface->bInterfaceSubClass != US_SC_SCSI || + iface->bInterfaceProtocol != US_PR_UAS) { + dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + /* find & allocate pipes */ + struct usb_endpoint_descriptor *ep = NULL; + struct usb_pipe *command = NULL; + struct usb_pipe *status = NULL; + struct usb_pipe *data_in = NULL; + struct usb_pipe *data_out = NULL; + u8 *desc = (u8*)iface; + while (desc) { + desc += desc[0]; + switch (desc[1]) { + case USB_DT_ENDPOINT: + ep = (void*)desc; + break; + case USB_DT_ENDPOINT_COMPANION: + /* No support (yet) for usb3 streams */ + dprintf(1, "Superspeed UAS devices not supported (yet)\n"); + goto fail; + case 0x24: + switch (desc[2]) { + case UAS_PIPE_ID_COMMAND: + command = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_STATUS: + status = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_DATA_IN: + data_in = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_DATA_OUT: + data_out = usb_alloc_pipe(usbdev, ep); + break; + default: + goto fail; + } + break; + default: + desc = NULL; + break; + } + } + if (!command || !status || !data_in || !data_out) + goto fail; + + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + int ret = uas_lun_setup(usbdev, command, status, data_in, data_out, 0); + if (ret < 0) { + dprintf(1, "Unable to configure UAS drive.\n"); + goto fail; + } + + return 0; + +fail: + usb_free_pipe(usbdev, command); + usb_free_pipe(usbdev, status); + usb_free_pipe(usbdev, data_in); + usb_free_pipe(usbdev, data_out); + return -1; +} diff --git a/qemu/roms/seabios/src/hw/usb-uas.h b/qemu/roms/seabios/src/hw/usb-uas.h new file mode 100644 index 000000000..ad91c5f60 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uas.h @@ -0,0 +1,9 @@ +#ifndef __USB_UAS_H +#define __USB_UAS_H + +struct disk_op_s; +int uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +struct usbdevice_s; +int usb_uas_setup(struct usbdevice_s *usbdev); + +#endif /* __USB_UAS_H */ diff --git a/qemu/roms/seabios/src/hw/usb-uhci.c b/qemu/roms/seabios/src/hw/usb-uhci.c new file mode 100644 index 000000000..69c33ee3b --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uhci.c @@ -0,0 +1,567 @@ +// Code for handling UHCI USB controllers. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_4 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-uhci.h" // USBLEGSUP +#include "util.h" // msleep +#include "x86.h" // outw + +struct usb_uhci_s { + struct usb_s usb; + u16 iobase; + struct uhci_qh *control_qh; + struct uhci_framelist *framelist; +}; + +struct uhci_pipe { + struct uhci_qh qh; + struct uhci_td *next_td; + struct usb_pipe pipe; + u16 iobase; + u8 toggle; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +// Check if device attached to a given port +static int +uhci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + u16 status = inw(ioport); + if (!(status & USBPORTSC_CCS)) + // No device found. + return 0; + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Begin reset on port + outw(USBPORTSC_PR, ioport); + msleep(USB_TIME_DRSTR); + return 1; +} + +// Reset device on port +static int +uhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + + // Finish reset on port + outw(0, ioport); + udelay(6); // 64 high-speed bit times + u16 status = inw(ioport); + if (!(status & USBPORTSC_CCS)) + // No longer connected + return -1; + outw(USBPORTSC_PE, ioport); + return !!(status & USBPORTSC_LSDA); +} + +// Disable port +static void +uhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + outw(0, ioport); +} + +static struct usbhub_op_s uhci_HubOp = { + .detect = uhci_hub_detect, + .reset = uhci_hub_reset, + .disconnect = uhci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_uhci_ports(struct usb_uhci_s *cntl) +{ + ASSERT32FLAT(); + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = 2; + hub.op = &uhci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB frame to start - for ensuring safe memory release. +static void +uhci_waittick(u16 iobase) +{ + barrier(); + u16 startframe = inw(iobase + USBFRNUM); + u32 end = timer_calc(1000 * 5); + for (;;) { + if (inw(iobase + USBFRNUM) != startframe) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } +} + +static void +uhci_free_pipes(struct usb_uhci_s *cntl) +{ + dprintf(7, "uhci_free_pipes %p\n", cntl); + + struct uhci_qh *pos = (void*)(cntl->framelist->links[0] & ~UHCI_PTR_BITS); + for (;;) { + u32 link = pos->link; + if (link == UHCI_PTR_TERM) + break; + struct uhci_qh *next = (void*)(link & ~UHCI_PTR_BITS); + struct uhci_pipe *pipe = container_of(next, struct uhci_pipe, qh); + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) + pos->link = next->link; + else + pos = next; + } + uhci_waittick(cntl->iobase); + for (;;) { + struct usb_pipe *usbpipe = cntl->usb.freelist; + if (!usbpipe) + break; + cntl->usb.freelist = usbpipe->freenext; + struct uhci_pipe *pipe = container_of(usbpipe, struct uhci_pipe, pipe); + free(pipe); + } +} + +static void +reset_uhci(struct usb_uhci_s *cntl, u16 bdf) +{ + // XXX - don't reset if not needed. + + // Reset PIRQ and SMI + pci_config_writew(bdf, USBLEGSUP, USBLEGSUP_RWC); + + // Reset the HC + outw(USBCMD_HCRESET, cntl->iobase + USBCMD); + udelay(5); + + // Disable interrupts and commands (just to be safe). + outw(0, cntl->iobase + USBINTR); + outw(0, cntl->iobase + USBCMD); +} + +static void +configure_uhci(void *data) +{ + struct usb_uhci_s *cntl = data; + + // Allocate ram for schedule storage + struct uhci_td *term_td = malloc_high(sizeof(*term_td)); + struct uhci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); + struct uhci_pipe *intr_pipe = malloc_high(sizeof(*intr_pipe)); + struct uhci_pipe *term_pipe = malloc_high(sizeof(*term_pipe)); + if (!term_td || !fl || !intr_pipe || !term_pipe) { + warn_noalloc(); + goto fail; + } + + // Work around for PIIX errata + memset(term_td, 0, sizeof(*term_td)); + term_td->link = UHCI_PTR_TERM; + term_td->token = (uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT) + | USB_PID_IN); + memset(term_pipe, 0, sizeof(*term_pipe)); + term_pipe->qh.element = (u32)term_td; + term_pipe->qh.link = UHCI_PTR_TERM; + term_pipe->pipe.cntl = &cntl->usb; + + // Set schedule to point to primary intr queue head + memset(intr_pipe, 0, sizeof(*intr_pipe)); + intr_pipe->qh.element = UHCI_PTR_TERM; + intr_pipe->qh.link = (u32)&term_pipe->qh | UHCI_PTR_QH; + intr_pipe->pipe.cntl = &cntl->usb; + int i; + for (i=0; i<ARRAY_SIZE(fl->links); i++) + fl->links[i] = (u32)&intr_pipe->qh | UHCI_PTR_QH; + cntl->framelist = fl; + cntl->control_qh = &intr_pipe->qh; + barrier(); + + // Set the frame length to the default: 1 ms exactly + outb(USBSOF_DEFAULT, cntl->iobase + USBSOF); + + // Store the frame list base address + outl((u32)fl->links, cntl->iobase + USBFLBASEADD); + + // Set the current frame number + outw(0, cntl->iobase + USBFRNUM); + + // Mark as configured and running with a 64-byte max packet. + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->iobase + USBCMD); + + // Find devices + int count = check_uhci_ports(cntl); + uhci_free_pipes(cntl); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + outw(0, cntl->iobase + USBCMD); +fail: + free(term_td); + free(fl); + free(intr_pipe); + free(term_pipe); + free(cntl); +} + +static void +uhci_controller_setup(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + struct usb_uhci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + wait_preempt(); // Avoid pci_config_readl when preempting + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_UHCI; + cntl->iobase = (pci_config_readl(bdf, PCI_BASE_ADDRESS_4) + & PCI_BASE_ADDRESS_IO_MASK); + + dprintf(1, "UHCI init on dev %02x:%02x.%x (io=%x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->iobase); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + reset_uhci(cntl, bdf); + + run_thread(configure_uhci, cntl); +} + +void +uhci_setup(void) +{ + if (! CONFIG_USB_UHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI) + uhci_controller_setup(pci); + } +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +static struct usb_pipe * +uhci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_uhci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_uhci_s, usb); + int frameexp = usb_get_period(usbdev, epdesc); + dprintf(7, "uhci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 10) + frameexp = 10; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); + count = ALIGN(count, 2); + struct uhci_pipe *pipe = malloc_low(sizeof(*pipe)); + struct uhci_td *tds = malloc_low(sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { + warn_noalloc(); + goto fail; + } + memset(pipe, 0, sizeof(*pipe)); + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + int lowspeed = pipe->pipe.speed; + int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); + pipe->qh.element = (u32)tds; + pipe->next_td = &tds[0]; + pipe->iobase = cntl->iobase; + + int toggle = 0; + int i; + for (i=0; i<count; i++) { + tds[i].link = (i==count-1 ? (u32)&tds[0] : (u32)&tds[i+1]); + tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + tds[i].token = (uhci_explen(maxpacket) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | USB_PID_IN); + tds[i].buffer = data + maxpacket * i; + toggle ^= TD_TOKEN_TOGGLE; + } + + // Add to interrupt schedule. + struct uhci_framelist *fl = cntl->framelist; + if (frameexp == 0) { + // Add to existing interrupt entry. + struct uhci_qh *intr_qh = (void*)(fl->links[0] & ~UHCI_PTR_BITS); + pipe->qh.link = intr_qh->link; + barrier(); + intr_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; + if (cntl->control_qh == intr_qh) + cntl->control_qh = &pipe->qh; + } else { + int startpos = 1<<(frameexp-1); + pipe->qh.link = fl->links[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) + fl->links[i] = (u32)&pipe->qh | UHCI_PTR_QH; + } + + return &pipe->pipe; +fail: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +uhci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_UHCI) + return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return uhci_alloc_intr_pipe(usbdev, epdesc); + struct usb_uhci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_uhci_s, usb); + dprintf(7, "uhci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); + + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + usb_desc2pipe(usbpipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct uhci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = malloc_tmphigh(sizeof(*pipe)); + else + pipe = malloc_low(sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->qh.element = UHCI_PTR_TERM; + pipe->iobase = cntl->iobase; + + // Add queue head to controller list. + struct uhci_qh *control_qh = cntl->control_qh; + pipe->qh.link = control_qh->link; + barrier(); + control_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + cntl->control_qh = &pipe->qh; + return &pipe->pipe; +} + +static int +wait_pipe(struct uhci_pipe *pipe, u32 end) +{ + for (;;) { + u32 el_link = GET_LOWFLAT(pipe->qh.element); + if (el_link & UHCI_PTR_TERM) + return 0; + if (timer_check(end)) { + warn_timeout(); + u16 iobase = GET_LOWFLAT(pipe->iobase); + struct uhci_td *td = (void*)(el_link & ~UHCI_PTR_BITS); + dprintf(1, "Timeout on wait_pipe %p (td=%p s=%x c=%x/%x)\n" + , pipe, (void*)el_link, GET_LOWFLAT(td->status) + , inw(iobase + USBCMD) + , inw(iobase + USBSTS)); + SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(iobase); + return -1; + } + yield(); + } +} + +static int +wait_td(struct uhci_td *td, u32 end) +{ + u32 status; + for (;;) { + status = td->status; + if (!(status & TD_CTRL_ACTIVE)) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + if (status & TD_CTRL_ANY_ERROR) { + dprintf(1, "wait_td error - status=%x\n", status); + return -2; + } + return 0; +} + +#define STACKTDS 16 +#define TDALIGN 16 + +int +uhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) +{ + if (! CONFIG_USB_UHCI) + return -1; + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + dprintf(7, "uhci_send_pipe qh=%p dir=%d data=%p size=%d\n" + , &pipe->qh, dir, data, datasize); + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + int lowspeed = GET_LOWFLAT(pipe->pipe.speed); + int devaddr = (GET_LOWFLAT(pipe->pipe.devaddr) + | (GET_LOWFLAT(pipe->pipe.ep) << 7)); + int toggle = GET_LOWFLAT(pipe->toggle) ? TD_TOKEN_TOGGLE : 0; + + // Allocate 16 tds on stack (16byte aligned) + u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1]; + struct uhci_td *tds = (void*)ALIGN((u32)tdsbuf, TDALIGN); + memset(tds, 0, sizeof(*tds) * STACKTDS); + int tdpos = 0; + + // Enable tds + u32 end = timer_calc(usb_xfer_time(p, datasize)); + barrier(); + SET_LOWFLAT(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + + // Setup transfer descriptors + if (cmd) { + // Send setup pid on control transfers + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + u32 nexttd = (u32)MAKE_FLATPTR(GET_SEG(SS), &tds[tdpos % STACKTDS]); + td->link = nexttd | UHCI_PTR_DEPTH; + td->token = (uhci_explen(USB_CONTROL_SETUP_SIZE) + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_SETUP); + td->buffer = (void*)cmd; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle = TD_TOKEN_TOGGLE; + } + while (datasize) { + // Send data pids + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td, end); + if (ret) + goto fail; + + int transfer = datasize; + if (transfer > maxpacket) + transfer = maxpacket; + u32 nexttd = (u32)MAKE_FLATPTR(GET_SEG(SS), &tds[tdpos % STACKTDS]); + td->link = ((transfer==datasize && !cmd) + ? UHCI_PTR_TERM : (nexttd | UHCI_PTR_DEPTH)); + td->token = (uhci_explen(transfer) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_IN : USB_PID_OUT)); + td->buffer = data; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle ^= TD_TOKEN_TOGGLE; + + data += transfer; + datasize -= transfer; + } + if (cmd) { + // Send status pid on control transfers + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td, end); + if (ret) + goto fail; + td->link = UHCI_PTR_TERM; + td->token = (uhci_explen(0) | TD_TOKEN_TOGGLE + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_OUT : USB_PID_IN)); + td->buffer = 0; + barrier(); + td->status = (uhci_maxerr(0) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + } + SET_LOWFLAT(pipe->toggle, !!toggle); + return wait_pipe(pipe, end); +fail: + dprintf(1, "uhci_send_bulk failed\n"); + SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(GET_LOWFLAT(pipe->iobase)); + return -1; +} + +int +uhci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_UHCI) + return -1; + + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + struct uhci_td *td = GET_LOWFLAT(pipe->next_td); + u32 status = GET_LOWFLAT(td->status); + u32 token = GET_LOWFLAT(td->token); + if (status & TD_CTRL_ACTIVE) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + void *tddata = GET_LOWFLAT(td->buffer); + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata) + , uhci_expected_length(token)); + + // Reenable this td. + struct uhci_td *next = (void*)(GET_LOWFLAT(td->link) & ~UHCI_PTR_BITS); + SET_LOWFLAT(pipe->next_td, next); + barrier(); + SET_LOWFLAT(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS) + | TD_CTRL_ACTIVE)); + + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-uhci.h b/qemu/roms/seabios/src/hw/usb-uhci.h new file mode 100644 index 000000000..bff70c667 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uhci.h @@ -0,0 +1,128 @@ +#ifndef __USB_UHCI_H +#define __USB_UHCI_H + +// usb-uhci.c +void uhci_setup(void); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; +struct usb_pipe *uhci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int uhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int uhci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * uhci structs and flags + ****************************************************************/ + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status + * ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */ +#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error: PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error: + * the schedule is buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 +#define USBSOF_DEFAULT 64 /* Frame length is exactly 1 ms */ + +struct uhci_framelist { + u32 links[1024]; +} PACKED; + +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_C_ERR_SHIFT 27 +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIMEO | \ + TD_CTRL_BITSTUFF) +#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT) + +#define TD_TOKEN_DEVADDR_SHIFT 8 +#define TD_TOKEN_TOGGLE_SHIFT 19 +#define TD_TOKEN_TOGGLE (1 << 19) +#define TD_TOKEN_EXPLEN_SHIFT 21 +#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */ +#define TD_TOKEN_PID_MASK 0xFF + +#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \ + TD_TOKEN_EXPLEN_SHIFT) + +#define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + \ + 1) & TD_TOKEN_EXPLEN_MASK) + +struct uhci_td { + u32 link; + u32 status; + u32 token; + void *buffer; +} PACKED; + +struct uhci_qh { + u32 link; + u32 element; +} PACKED; + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 +#define UHCI_PTR_BREADTH 0x0000 + +#endif // usb-uhci.h diff --git a/qemu/roms/seabios/src/hw/usb-xhci.c b/qemu/roms/seabios/src/hw/usb-xhci.c new file mode 100644 index 000000000..fd58334dc --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-xhci.c @@ -0,0 +1,1139 @@ +// Code for handling XHCI "Super speed" USB controllers. +// +// Copyright (C) 2013 Gerd Hoffmann <kraxel@redhat.com> +// Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // memalign_low +#include "memmap.h" // PAGE_SIZE +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_XHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memcpy +#include "usb.h" // struct usb_s +#include "usb-xhci.h" // struct ehci_qh +#include "util.h" // timer_calc +#include "x86.h" // readl + +// -------------------------------------------------------------- +// configuration + +#define XHCI_RING_ITEMS 16 +#define XHCI_RING_SIZE (XHCI_RING_ITEMS*sizeof(struct xhci_trb)) + +/* + * xhci_ring structs are allocated with XHCI_RING_SIZE alignment, + * then we can get it from a trb pointer (provided by evt ring). + */ +#define XHCI_RING(_trb) \ + ((struct xhci_ring*)((u32)(_trb) & ~(XHCI_RING_SIZE-1))) + +// -------------------------------------------------------------- +// bit definitions + +#define XHCI_CMD_RS (1<<0) +#define XHCI_CMD_HCRST (1<<1) +#define XHCI_CMD_INTE (1<<2) +#define XHCI_CMD_HSEE (1<<3) +#define XHCI_CMD_LHCRST (1<<7) +#define XHCI_CMD_CSS (1<<8) +#define XHCI_CMD_CRS (1<<9) +#define XHCI_CMD_EWE (1<<10) +#define XHCI_CMD_EU3S (1<<11) + +#define XHCI_STS_HCH (1<<0) +#define XHCI_STS_HSE (1<<2) +#define XHCI_STS_EINT (1<<3) +#define XHCI_STS_PCD (1<<4) +#define XHCI_STS_SSS (1<<8) +#define XHCI_STS_RSS (1<<9) +#define XHCI_STS_SRE (1<<10) +#define XHCI_STS_CNR (1<<11) +#define XHCI_STS_HCE (1<<12) + +#define XHCI_PORTSC_CCS (1<<0) +#define XHCI_PORTSC_PED (1<<1) +#define XHCI_PORTSC_OCA (1<<3) +#define XHCI_PORTSC_PR (1<<4) +#define XHCI_PORTSC_PLS_SHIFT 5 +#define XHCI_PORTSC_PLS_MASK 0xf +#define XHCI_PORTSC_PP (1<<9) +#define XHCI_PORTSC_SPEED_SHIFT 10 +#define XHCI_PORTSC_SPEED_MASK 0xf +#define XHCI_PORTSC_SPEED_FULL (1<<10) +#define XHCI_PORTSC_SPEED_LOW (2<<10) +#define XHCI_PORTSC_SPEED_HIGH (3<<10) +#define XHCI_PORTSC_SPEED_SUPER (4<<10) +#define XHCI_PORTSC_PIC_SHIFT 14 +#define XHCI_PORTSC_PIC_MASK 0x3 +#define XHCI_PORTSC_LWS (1<<16) +#define XHCI_PORTSC_CSC (1<<17) +#define XHCI_PORTSC_PEC (1<<18) +#define XHCI_PORTSC_WRC (1<<19) +#define XHCI_PORTSC_OCC (1<<20) +#define XHCI_PORTSC_PRC (1<<21) +#define XHCI_PORTSC_PLC (1<<22) +#define XHCI_PORTSC_CEC (1<<23) +#define XHCI_PORTSC_CAS (1<<24) +#define XHCI_PORTSC_WCE (1<<25) +#define XHCI_PORTSC_WDE (1<<26) +#define XHCI_PORTSC_WOE (1<<27) +#define XHCI_PORTSC_DR (1<<30) +#define XHCI_PORTSC_WPR (1<<31) + +#define TRB_C (1<<0) +#define TRB_TYPE_SHIFT 10 +#define TRB_TYPE_MASK 0x3f +#define TRB_TYPE(t) (((t) >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK) + +#define TRB_EV_ED (1<<2) + +#define TRB_TR_ENT (1<<1) +#define TRB_TR_ISP (1<<2) +#define TRB_TR_NS (1<<3) +#define TRB_TR_CH (1<<4) +#define TRB_TR_IOC (1<<5) +#define TRB_TR_IDT (1<<6) +#define TRB_TR_TBC_SHIFT 7 +#define TRB_TR_TBC_MASK 0x3 +#define TRB_TR_BEI (1<<9) +#define TRB_TR_TLBPC_SHIFT 16 +#define TRB_TR_TLBPC_MASK 0xf +#define TRB_TR_FRAMEID_SHIFT 20 +#define TRB_TR_FRAMEID_MASK 0x7ff +#define TRB_TR_SIA (1<<31) + +#define TRB_TR_DIR (1<<16) + +#define TRB_CR_SLOTID_SHIFT 24 +#define TRB_CR_SLOTID_MASK 0xff +#define TRB_CR_EPID_SHIFT 16 +#define TRB_CR_EPID_MASK 0x1f + +#define TRB_CR_BSR (1<<9) +#define TRB_CR_DC (1<<9) + +#define TRB_LK_TC (1<<1) + +#define TRB_INTR_SHIFT 22 +#define TRB_INTR_MASK 0x3ff +#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) + +typedef enum TRBType { + TRB_RESERVED = 0, + TR_NORMAL, + TR_SETUP, + TR_DATA, + TR_STATUS, + TR_ISOCH, + TR_LINK, + TR_EVDATA, + TR_NOOP, + CR_ENABLE_SLOT, + CR_DISABLE_SLOT, + CR_ADDRESS_DEVICE, + CR_CONFIGURE_ENDPOINT, + CR_EVALUATE_CONTEXT, + CR_RESET_ENDPOINT, + CR_STOP_ENDPOINT, + CR_SET_TR_DEQUEUE, + CR_RESET_DEVICE, + CR_FORCE_EVENT, + CR_NEGOTIATE_BW, + CR_SET_LATENCY_TOLERANCE, + CR_GET_PORT_BANDWIDTH, + CR_FORCE_HEADER, + CR_NOOP, + ER_TRANSFER = 32, + ER_COMMAND_COMPLETE, + ER_PORT_STATUS_CHANGE, + ER_BANDWIDTH_REQUEST, + ER_DOORBELL, + ER_HOST_CONTROLLER, + ER_DEVICE_NOTIFICATION, + ER_MFINDEX_WRAP, +} TRBType; + +typedef enum TRBCCode { + CC_INVALID = 0, + CC_SUCCESS, + CC_DATA_BUFFER_ERROR, + CC_BABBLE_DETECTED, + CC_USB_TRANSACTION_ERROR, + CC_TRB_ERROR, + CC_STALL_ERROR, + CC_RESOURCE_ERROR, + CC_BANDWIDTH_ERROR, + CC_NO_SLOTS_ERROR, + CC_INVALID_STREAM_TYPE_ERROR, + CC_SLOT_NOT_ENABLED_ERROR, + CC_EP_NOT_ENABLED_ERROR, + CC_SHORT_PACKET, + CC_RING_UNDERRUN, + CC_RING_OVERRUN, + CC_VF_ER_FULL, + CC_PARAMETER_ERROR, + CC_BANDWIDTH_OVERRUN, + CC_CONTEXT_STATE_ERROR, + CC_NO_PING_RESPONSE_ERROR, + CC_EVENT_RING_FULL_ERROR, + CC_INCOMPATIBLE_DEVICE_ERROR, + CC_MISSED_SERVICE_ERROR, + CC_COMMAND_RING_STOPPED, + CC_COMMAND_ABORTED, + CC_STOPPED, + CC_STOPPED_LENGTH_INVALID, + CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, + CC_ISOCH_BUFFER_OVERRUN = 31, + CC_EVENT_LOST_ERROR, + CC_UNDEFINED_ERROR, + CC_INVALID_STREAM_ID_ERROR, + CC_SECONDARY_BANDWIDTH_ERROR, + CC_SPLIT_TRANSACTION_ERROR +} TRBCCode; + +enum { + PLS_U0 = 0, + PLS_U1 = 1, + PLS_U2 = 2, + PLS_U3 = 3, + PLS_DISABLED = 4, + PLS_RX_DETECT = 5, + PLS_INACTIVE = 6, + PLS_POLLING = 7, + PLS_RECOVERY = 8, + PLS_HOT_RESET = 9, + PLS_COMPILANCE_MODE = 10, + PLS_TEST_MODE = 11, + PLS_RESUME = 15, +}; + +#define xhci_get_field(data, field) \ + (((data) >> field##_SHIFT) & field##_MASK) + +// -------------------------------------------------------------- +// state structs + +struct xhci_ring { + struct xhci_trb ring[XHCI_RING_ITEMS]; + struct xhci_trb evt; + u32 eidx; + u32 nidx; + u32 cs; + struct mutex_s lock; +}; + +struct usb_xhci_s { + struct usb_s usb; + + /* devinfo */ + u32 baseaddr; + u32 xcap; + u32 ports; + u32 slots; + u8 context64; + + /* xhci registers */ + struct xhci_caps *caps; + struct xhci_op *op; + struct xhci_pr *pr; + struct xhci_ir *ir; + struct xhci_db *db; + + /* xhci data structures */ + struct xhci_devlist *devs; + struct xhci_ring *cmds; + struct xhci_ring *evts; + struct xhci_er_seg *eseg; +}; + +struct xhci_pipe { + struct xhci_ring reqs; + + struct usb_pipe pipe; + u32 slotid; + u32 epid; + void *buf; + int bufused; +}; + +// -------------------------------------------------------------- +// tables + +static const char *speed_name[16] = { + [ 0 ] = " - ", + [ 1 ] = "Full", + [ 2 ] = "Low", + [ 3 ] = "High", + [ 4 ] = "Super", +}; + +static const int speed_from_xhci[16] = { + [ 0 ] = -1, + [ 1 ] = USB_FULLSPEED, + [ 2 ] = USB_LOWSPEED, + [ 3 ] = USB_HIGHSPEED, + [ 4 ] = USB_SUPERSPEED, + [ 5 ... 15 ] = -1, +}; + +static const int speed_to_xhci[] = { + [ USB_FULLSPEED ] = 1, + [ USB_LOWSPEED ] = 2, + [ USB_HIGHSPEED ] = 3, + [ USB_SUPERSPEED ] = 4, +}; + +static const int eptype_to_xhci_in[] = { + [ USB_ENDPOINT_XFER_CONTROL] = 4, + [ USB_ENDPOINT_XFER_ISOC ] = 5, + [ USB_ENDPOINT_XFER_BULK ] = 6, + [ USB_ENDPOINT_XFER_INT ] = 7, +}; + +static const int eptype_to_xhci_out[] = { + [ USB_ENDPOINT_XFER_CONTROL] = 4, + [ USB_ENDPOINT_XFER_ISOC ] = 1, + [ USB_ENDPOINT_XFER_BULK ] = 2, + [ USB_ENDPOINT_XFER_INT ] = 3, +}; + +static int wait_bit(u32 *reg, u32 mask, int value, u32 timeout) +{ + u32 end = timer_calc(timeout); + + while ((readl(reg) & mask) != value) { + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + return 0; +} + + +/**************************************************************** + * Root hub + ****************************************************************/ + +#define XHCI_TIME_POSTPOWER 20 + +// Check if device attached to port +static void +xhci_print_port_state(int loglevel, const char *prefix, u32 port, u32 portsc) +{ + u32 pls = xhci_get_field(portsc, XHCI_PORTSC_PLS); + u32 speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED); + + dprintf(loglevel, "%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n", + prefix, port + 1, portsc, + (portsc & XHCI_PORTSC_PP) ? " powered," : "", + (portsc & XHCI_PORTSC_PED) ? " enabled," : "", + pls, speed, speed_name[speed]); +} + +static int +xhci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + return (portsc & XHCI_PORTSC_CCS) ? 1 : 0; +} + +// Reset device on port +static int +xhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + int rc; + + switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { + case PLS_U0: + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + case PLS_POLLING: + xhci_print_port_state(3, __func__, port, portsc); + portsc |= XHCI_PORTSC_PR; + writel(&xhci->pr[port].portsc, portsc); + if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0) + return -1; + portsc = readl(&xhci->pr[port].portsc); + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + default: + rc = -1; + break; + } + + xhci_print_port_state(1, "XHCI", port, portsc); + return rc; +} + +static void +xhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + // XXX - should turn the port power off. +} + +static struct usbhub_op_s xhci_hub_ops = { + .detect = xhci_hub_detect, + .reset = xhci_hub_reset, + .disconnect = xhci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +xhci_check_ports(struct usb_xhci_s *xhci) +{ + // Wait for port power to stabilize. + msleep(XHCI_TIME_POSTPOWER); + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &xhci->usb; + hub.portcount = xhci->ports; + hub.op = &xhci_hub_ops; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +static void +xhci_free_pipes(struct usb_xhci_s *xhci) +{ + // XXX - should walk list of pipes and free unused pipes. +} + +static void +configure_xhci(void *data) +{ + struct usb_xhci_s *xhci = data; + u32 reg; + + xhci->devs = memalign_high(64, sizeof(*xhci->devs) * (xhci->slots + 1)); + xhci->eseg = memalign_high(64, sizeof(*xhci->eseg)); + xhci->cmds = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->cmds)); + xhci->evts = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->evts)); + if (!xhci->devs || !xhci->cmds || !xhci->evts || !xhci->eseg) { + warn_noalloc(); + goto fail; + } + memset(xhci->devs, 0, sizeof(*xhci->devs) * (xhci->slots + 1)); + memset(xhci->cmds, 0, sizeof(*xhci->cmds)); + memset(xhci->evts, 0, sizeof(*xhci->evts)); + memset(xhci->eseg, 0, sizeof(*xhci->eseg)); + + reg = readl(&xhci->op->usbcmd); + if (reg & XHCI_CMD_RS) { + reg &= ~XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + if (wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32) != 0) + goto fail; + } + + dprintf(3, "%s: resetting\n", __func__); + writel(&xhci->op->usbcmd, XHCI_CMD_HCRST); + if (wait_bit(&xhci->op->usbcmd, XHCI_CMD_HCRST, 0, 100) != 0) + goto fail; + if (wait_bit(&xhci->op->usbsts, XHCI_STS_CNR, 0, 100) != 0) + goto fail; + + writel(&xhci->op->config, xhci->slots); + writel(&xhci->op->dcbaap_low, (u32)xhci->devs); + writel(&xhci->op->dcbaap_high, 0); + writel(&xhci->op->crcr_low, (u32)xhci->cmds | 1); + writel(&xhci->op->crcr_high, 0); + xhci->cmds->cs = 1; + + xhci->eseg->ptr_low = (u32)xhci->evts; + xhci->eseg->ptr_high = 0; + xhci->eseg->size = XHCI_RING_ITEMS; + writel(&xhci->ir->erstsz, 1); + writel(&xhci->ir->erdp_low, (u32)xhci->evts); + writel(&xhci->ir->erdp_high, 0); + writel(&xhci->ir->erstba_low, (u32)xhci->eseg); + writel(&xhci->ir->erstba_high, 0); + xhci->evts->cs = 1; + + reg = readl(&xhci->caps->hcsparams2); + u32 spb = reg >> 27; + if (spb) { + dprintf(3, "%s: setup %d scratch pad buffers\n", __func__, spb); + u64 *spba = memalign_high(64, sizeof(*spba) * spb); + void *pad = memalign_high(PAGE_SIZE, PAGE_SIZE * spb); + if (!spba || !pad) { + warn_noalloc(); + free(spba); + free(pad); + goto fail; + } + int i; + for (i = 0; i < spb; i++) + spba[i] = (u32)pad + (i * PAGE_SIZE); + xhci->devs[0].ptr_low = (u32)spba; + xhci->devs[0].ptr_high = 0; + } + + reg = readl(&xhci->op->usbcmd); + reg |= XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + + // Find devices + int count = xhci_check_ports(xhci); + xhci_free_pipes(xhci); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + dprintf(1, "XHCI no devices found\n"); + reg = readl(&xhci->op->usbcmd); + reg &= ~XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32); + +fail: + free(xhci->eseg); + free(xhci->evts); + free(xhci->cmds); + free(xhci->devs); + free(xhci); +} + +static void +xhci_controller_setup(struct pci_device *pci) +{ + struct usb_xhci_s *xhci = malloc_high(sizeof(*xhci)); + if (!xhci) { + warn_noalloc(); + return; + } + memset(xhci, 0, sizeof(*xhci)); + + wait_preempt(); // Avoid pci_config_readl when preempting + xhci->baseaddr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK; + xhci->caps = (void*)(xhci->baseaddr); + xhci->op = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength)); + xhci->pr = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength) + 0x400); + xhci->db = (void*)(xhci->baseaddr + readl(&xhci->caps->dboff)); + xhci->ir = (void*)(xhci->baseaddr + readl(&xhci->caps->rtsoff) + 0x20); + + u32 hcs1 = readl(&xhci->caps->hcsparams1); + u32 hcc = readl(&xhci->caps->hccparams); + xhci->ports = (hcs1 >> 24) & 0xff; + xhci->slots = hcs1 & 0xff; + xhci->xcap = ((hcc >> 16) & 0xffff) << 2; + xhci->context64 = (hcc & 0x04) ? 1 : 0; + + xhci->usb.pci = pci; + xhci->usb.type = USB_TYPE_XHCI; + + dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots" + ", %d byte contexts\n" + , pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf) + , pci_bdf_to_fn(pci->bdf), xhci->caps + , xhci->ports, xhci->slots, xhci->context64 ? 64 : 32); + + if (xhci->xcap) { + u32 off, addr = xhci->baseaddr + xhci->xcap; + do { + struct xhci_xcap *xcap = (void*)addr; + u32 ports, name, cap = readl(&xcap->cap); + switch (cap & 0xff) { + case 0x02: + name = readl(&xcap->data[0]); + ports = readl(&xcap->data[1]); + dprintf(1, "XHCI protocol %c%c%c%c %x.%02x" + ", %d ports (offset %d), def %x\n" + , (name >> 0) & 0xff + , (name >> 8) & 0xff + , (name >> 16) & 0xff + , (name >> 24) & 0xff + , (cap >> 24) & 0xff + , (cap >> 16) & 0xff + , (ports >> 8) & 0xff + , (ports >> 0) & 0xff + , ports >> 16); + break; + default: + dprintf(1, "XHCI extcap 0x%x @ %x\n", cap & 0xff, addr); + break; + } + off = (cap >> 8) & 0xff; + addr += off << 2; + } while (off > 0); + } + + u32 pagesize = readl(&xhci->op->pagesize); + if (PAGE_SIZE != (pagesize<<12)) { + dprintf(1, "XHCI driver does not support page size code %d\n" + , pagesize<<12); + free(xhci); + return; + } + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + run_thread(configure_xhci, xhci); +} + +void +xhci_setup(void) +{ + if (! CONFIG_USB_XHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI) + xhci_controller_setup(pci); + } +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +static void xhci_doorbell(struct usb_xhci_s *xhci, u32 slotid, u32 value) +{ + struct xhci_db *db = xhci->db; + void *addr = &db[slotid].doorbell; + writel(addr, value); +} + +static void xhci_process_events(struct usb_xhci_s *xhci) +{ + struct xhci_ring *evts = xhci->evts; + + for (;;) { + /* check for event */ + u32 nidx = evts->nidx; + u32 cs = evts->cs; + struct xhci_trb *etrb = evts->ring + nidx; + u32 control = etrb->control; + if ((control & TRB_C) != (cs ? 1 : 0)) + return; + + /* process event */ + u32 evt_type = TRB_TYPE(control); + u32 evt_cc = (etrb->status >> 24) & 0xff; + switch (evt_type) { + case ER_TRANSFER: + case ER_COMMAND_COMPLETE: + { + struct xhci_trb *rtrb = (void*)etrb->ptr_low; + struct xhci_ring *ring = XHCI_RING(rtrb); + struct xhci_trb *evt = &ring->evt; + u32 eidx = rtrb - ring->ring + 1; + dprintf(5, "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n", + __func__, ring, rtrb, evt, evt_type, eidx, evt_cc); + memcpy(evt, etrb, sizeof(*etrb)); + ring->eidx = eidx; + break; + } + case ER_PORT_STATUS_CHANGE: + { + u32 portid = (etrb->ptr_low >> 24) & 0xff; + dprintf(3, "%s: status change port #%d\n", + __func__, portid); + break; + } + default: + dprintf(1, "%s: unknown event, type %d, cc %d\n", + __func__, evt_type, evt_cc); + break; + } + + /* move ring index, notify xhci */ + nidx++; + if (nidx == XHCI_RING_ITEMS) { + nidx = 0; + cs = cs ? 0 : 1; + evts->cs = cs; + } + evts->nidx = nidx; + struct xhci_ir *ir = xhci->ir; + u32 erdp = (u32)(evts->ring + nidx); + writel(&ir->erdp_low, erdp); + writel(&ir->erdp_high, 0); + } +} + +static int xhci_ring_busy(struct xhci_ring *ring) +{ + u32 eidx = ring->eidx; + u32 nidx = ring->nidx; + return (eidx != nidx); +} + +static int xhci_event_wait(struct usb_xhci_s *xhci, + struct xhci_ring *ring, + u32 timeout) +{ + u32 end = timer_calc(timeout); + + for (;;) { + xhci_process_events(xhci); + if (!xhci_ring_busy(ring)) { + u32 status = ring->evt.status; + return (status >> 24) & 0xff; + } + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +static void xhci_trb_queue(struct xhci_ring *ring, + struct xhci_trb *trb) +{ + u32 nidx = ring->nidx; + u32 cs = ring->cs; + struct xhci_trb *dst; + u32 control; + + if (nidx == XHCI_RING_ITEMS-1) { + dst = ring->ring + nidx; + control = (TR_LINK << 10); // trb type + control |= TRB_LK_TC; + control |= (cs ? TRB_C : 0); + dst->ptr_low = (u32)&ring[0]; + dst->ptr_high = 0; + dst->status = 0; + dst->control = control; + nidx = 0; + cs = cs ? 0 : 1; + ring->nidx = nidx; + ring->cs = cs; + + dprintf(5, "%s: ring %p [linked]\n", __func__, ring); + } + + dst = ring->ring + nidx; + control = trb->control | (cs ? TRB_C : 0); + + dst->ptr_low = trb->ptr_low; + dst->ptr_high = trb->ptr_high; + dst->status = trb->status; + dst->control = control; + nidx++; + ring->nidx = nidx; + + dprintf(5, "%s: ring %p [nidx %d, len %d]\n", + __func__, ring, nidx, + trb->status & 0xffff); +} + +static int xhci_cmd_submit(struct usb_xhci_s *xhci, + struct xhci_trb *cmd) +{ + int rc; + + mutex_lock(&xhci->cmds->lock); + xhci_trb_queue(xhci->cmds, cmd); + xhci_doorbell(xhci, 0, 0); + rc = xhci_event_wait(xhci, xhci->cmds, 1000); + mutex_unlock(&xhci->cmds->lock); + return rc; +} + +static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci) +{ + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (CR_ENABLE_SLOT << 10) + }; + dprintf(3, "%s:\n", __func__); + int cc = xhci_cmd_submit(xhci, &cmd); + if (cc != CC_SUCCESS) + return -1; + return (xhci->cmds->evt.control >> 24) & 0xff; +} + +#if 0 +static int xhci_cmd_disable_slot(struct usb_xhci_s *xhci, u32 slotid) +{ + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_DISABLE_SLOT << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); +} +#endif + +static int xhci_cmd_address_device(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_ADDRESS_DEVICE << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); +} + +static int xhci_cmd_configure_endpoint(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); +} + +static int xhci_cmd_evaluate_context(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_EVALUATE_CONTEXT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); +} + +static struct xhci_inctx * +xhci_alloc_inctx(struct usbdevice_s *usbdev, int maxepid) +{ + struct usb_xhci_s *xhci = container_of( + usbdev->hub->cntl, struct usb_xhci_s, usb); + int size = (sizeof(struct xhci_inctx) * 33) << xhci->context64; + struct xhci_inctx *in = memalign_tmphigh(2048 << xhci->context64, size); + if (!in) { + warn_noalloc(); + return NULL; + } + memset(in, 0, size); + + struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64]; + slot->ctx[0] |= maxepid << 27; // context entries + slot->ctx[0] |= speed_to_xhci[usbdev->speed] << 20; + + // Set high-speed hub flags. + struct usbdevice_s *hubdev = usbdev->hub->usbdev; + if (hubdev) { + if (usbdev->speed == USB_LOWSPEED || usbdev->speed == USB_FULLSPEED) { + struct xhci_pipe *hpipe = container_of( + hubdev->defpipe, struct xhci_pipe, pipe); + if (hubdev->speed == USB_HIGHSPEED) { + slot->ctx[2] |= hpipe->slotid; + slot->ctx[2] |= (usbdev->port+1) << 8; + } else { + struct xhci_slotctx *hslot = (void*)xhci->devs[hpipe->slotid].ptr_low; + slot->ctx[2] = hslot->ctx[2]; + } + } + u32 route = 0; + while (usbdev->hub->usbdev) { + route <<= 4; + route |= (usbdev->port+1) & 0xf; + usbdev = usbdev->hub->usbdev; + } + slot->ctx[0] |= route; + } + + slot->ctx[1] |= (usbdev->port+1) << 16; + + return in; +} + +static int xhci_config_hub(struct usbhub_s *hub) +{ + struct usb_xhci_s *xhci = container_of( + hub->cntl, struct usb_xhci_s, usb); + struct xhci_pipe *pipe = container_of( + hub->usbdev->defpipe, struct xhci_pipe, pipe); + struct xhci_slotctx *hdslot = (void*)xhci->devs[pipe->slotid].ptr_low; + if ((hdslot->ctx[3] >> 27) == 3) + // Already configured + return 0; + struct xhci_inctx *in = xhci_alloc_inctx(hub->usbdev, 1); + if (!in) + return -1; + in->add = 0x01; + struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64]; + slot->ctx[0] |= 1 << 26; + slot->ctx[1] |= hub->portcount << 24; + + int cc = xhci_cmd_configure_endpoint(xhci, pipe->slotid, in); + free(in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: configure hub: failed (cc %d)\n", __func__, cc); + return -1; + } + return 0; +} + +static struct usb_pipe * +xhci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + struct usb_xhci_s *xhci = container_of( + usbdev->hub->cntl, struct usb_xhci_s, usb); + struct xhci_pipe *pipe; + u32 epid; + + if (epdesc->bEndpointAddress == 0) { + epid = 1; + } else { + epid = (epdesc->bEndpointAddress & 0x0f) * 2; + epid += (epdesc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + } + + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = memalign_high(XHCI_RING_SIZE, sizeof(*pipe)); + else + pipe = memalign_low(XHCI_RING_SIZE, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->epid = epid; + pipe->reqs.cs = 1; + if (eptype == USB_ENDPOINT_XFER_INT) + pipe->buf = malloc_high(pipe->pipe.maxpacket); + + // Allocate input context and initialize endpoint info. + struct xhci_inctx *in = xhci_alloc_inctx(usbdev, epid); + if (!in) + goto fail; + in->add = 0x01 | (1 << epid); + struct xhci_epctx *ep = (void*)&in[(pipe->epid+1) << xhci->context64]; + if (eptype == USB_ENDPOINT_XFER_INT) + ep->ctx[0] = (usb_get_period(usbdev, epdesc) + 3) << 16; + ep->ctx[1] |= eptype << 3; + if (epdesc->bEndpointAddress & USB_DIR_IN + || eptype == USB_ENDPOINT_XFER_CONTROL) + ep->ctx[1] |= 1 << 5; + ep->ctx[1] |= pipe->pipe.maxpacket << 16; + ep->deq_low = (u32)&pipe->reqs.ring[0]; + ep->deq_low |= 1; // dcs + ep->length = pipe->pipe.maxpacket; + + dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, + usbdev, &pipe->reqs, pipe->slotid, pipe->epid); + if (pipe->epid == 1) { + if (usbdev->hub->usbdev) { + // Make sure parent hub is configured. + int ret = xhci_config_hub(usbdev->hub); + if (ret) + goto fail; + } + // Enable slot. + u32 size = (sizeof(struct xhci_slotctx) * 32) << xhci->context64; + struct xhci_slotctx *dev = memalign_high(1024 << xhci->context64, size); + if (!dev) { + warn_noalloc(); + goto fail; + } + int slotid = xhci_cmd_enable_slot(xhci); + if (slotid < 0) { + dprintf(1, "%s: enable slot: failed\n", __func__); + free(dev); + goto fail; + } + dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid); + memset(dev, 0, size); + pipe->slotid = usbdev->slotid = slotid; + xhci->devs[slotid].ptr_low = (u32)dev; + xhci->devs[slotid].ptr_high = 0; + + // Send set_address command. + int cc = xhci_cmd_address_device(xhci, slotid, in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc); + goto fail; + } + } else { + pipe->slotid = usbdev->slotid; + // Send configure command. + int cc = xhci_cmd_configure_endpoint(xhci, pipe->slotid, in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: configure endpoint: failed (cc %d)\n", __func__, cc); + goto fail; + } + } + free(in); + return &pipe->pipe; + +fail: + free(pipe); + free(in); + return NULL; +} + +struct usb_pipe * +xhci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (!CONFIG_USB_XHCI) + return NULL; + if (!epdesc) { + usb_add_freelist(upipe); + return NULL; + } + if (!upipe) + return xhci_alloc_pipe(usbdev, epdesc); + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + int oldmaxpacket = upipe->maxpacket; + usb_desc2pipe(upipe, usbdev, epdesc); + struct xhci_pipe *pipe = container_of(upipe, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, + usbdev, &pipe->reqs, pipe->slotid, pipe->epid); + if (eptype != USB_ENDPOINT_XFER_CONTROL || upipe->maxpacket == oldmaxpacket) + return upipe; + + // maxpacket has changed on control endpoint - update controller. + dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n", + __func__, oldmaxpacket, pipe->pipe.maxpacket); + struct xhci_inctx *in = xhci_alloc_inctx(usbdev, 1); + if (!in) + return upipe; + in->add = (1 << 1); + struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; + ep->ctx[1] |= (pipe->pipe.maxpacket << 16); + int cc = xhci_cmd_evaluate_context(xhci, pipe->slotid, in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", + __func__, cc); + } + free(in); + + return upipe; +} + +static void xhci_xfer_queue(struct xhci_pipe *pipe, + void *data, int datalen, u32 flags) +{ + struct xhci_trb trb; + memset(&trb, 0, sizeof(trb)); + if (flags & TRB_TR_IDT) + memcpy(&trb.ptr_low, data, datalen); + else + trb.ptr_low = (u32)data; + trb.status = datalen; + trb.control = flags; + xhci_trb_queue(&pipe->reqs, &trb); +} + +static void xhci_xfer_kick(struct xhci_pipe *pipe) +{ + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + u32 slotid = pipe->slotid; + u32 epid = pipe->epid; + + dprintf(5, "%s: ring %p, slotid %d, epid %d\n", + __func__, &pipe->reqs, slotid, epid); + xhci_doorbell(xhci, slotid, epid); +} + +static void xhci_xfer_normal(struct xhci_pipe *pipe, + void *data, int datalen) +{ + xhci_xfer_queue(pipe, data, datalen, (TR_NORMAL << 10) | TRB_TR_IOC); + xhci_xfer_kick(pipe); +} + +int +xhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datalen) +{ + if (!CONFIG_USB_XHCI) + return -1; + struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + + if (cmd) { + const struct usb_ctrlrequest *req = cmd; + if (req->bRequest == USB_REQ_SET_ADDRESS) + // Set address command sent during xhci_alloc_pipe. + return 0; + + xhci_xfer_queue(pipe, (void*)req, USB_CONTROL_SETUP_SIZE + , (TR_SETUP << 10) | TRB_TR_IDT + | ((datalen ? (dir ? 3 : 2) : 0) << 16)); + if (datalen) + xhci_xfer_queue(pipe, data, datalen, (TR_DATA << 10) + | ((dir ? 1 : 0) << 16)); + xhci_xfer_queue(pipe, NULL, 0, (TR_STATUS << 10) | TRB_TR_IOC + | ((dir ? 0 : 1) << 16)); + xhci_xfer_kick(pipe); + } else { + xhci_xfer_normal(pipe, data, datalen); + } + + int cc = xhci_event_wait(xhci, &pipe->reqs, usb_xfer_time(p, datalen)); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: xfer failed (cc %d)\n", __func__, cc); + return -1; + } + + return 0; +} + +int VISIBLE32FLAT +xhci_poll_intr(struct usb_pipe *p, void *data) +{ + if (!CONFIG_USB_XHCI) + return -1; + + struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + u32 len = pipe->pipe.maxpacket; + void *buf = pipe->buf; + int bufused = pipe->bufused; + + if (!bufused) { + xhci_xfer_normal(pipe, buf, len); + bufused = 1; + pipe->bufused = bufused; + return -1; + } + + xhci_process_events(xhci); + if (xhci_ring_busy(&pipe->reqs)) + return -1; + dprintf(5, "%s: st %x ct %x [ %p <= %p / %d ]\n", __func__, + pipe->reqs.evt.status, + pipe->reqs.evt.control, + data, buf, len); + memcpy(data, buf, len); + xhci_xfer_normal(pipe, buf, len); + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-xhci.h b/qemu/roms/seabios/src/hw/usb-xhci.h new file mode 100644 index 000000000..c768c5b58 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-xhci.h @@ -0,0 +1,133 @@ +#ifndef __USB_XHCI_H +#define __USB_XHCI_H + +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; + +// -------------------------------------------------------------- + +// usb-xhci.c +void xhci_setup(void); +struct usb_pipe *xhci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int xhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int xhci_poll_intr(struct usb_pipe *p, void *data); + +// -------------------------------------------------------------- +// register interface + +// capabilities +struct xhci_caps { + u8 caplength; + u8 reserved_01; + u16 hciversion; + u32 hcsparams1; + u32 hcsparams2; + u32 hcsparams3; + u32 hccparams; + u32 dboff; + u32 rtsoff; +} PACKED; + +// extended capabilities +struct xhci_xcap { + u32 cap; + u32 data[]; +} PACKED; + +// operational registers +struct xhci_op { + u32 usbcmd; + u32 usbsts; + u32 pagesize; + u32 reserved_01[2]; + u32 dnctl; + u32 crcr_low; + u32 crcr_high; + u32 reserved_02[4]; + u32 dcbaap_low; + u32 dcbaap_high; + u32 config; +} PACKED; + +// port registers +struct xhci_pr { + u32 portsc; + u32 portpmsc; + u32 portli; + u32 reserved_01; +} PACKED; + +// doorbell registers +struct xhci_db { + u32 doorbell; +} PACKED; + +// runtime registers +struct xhci_rts { + u32 mfindex; +} PACKED; + +// interrupter registers +struct xhci_ir { + u32 iman; + u32 imod; + u32 erstsz; + u32 reserved_01; + u32 erstba_low; + u32 erstba_high; + u32 erdp_low; + u32 erdp_high; +} PACKED; + +// -------------------------------------------------------------- +// memory data structs + +// slot context +struct xhci_slotctx { + u32 ctx[4]; + u32 reserved_01[4]; +} PACKED; + +// endpoint context +struct xhci_epctx { + u32 ctx[2]; + u32 deq_low; + u32 deq_high; + u32 length; + u32 reserved_01[3]; +} PACKED; + +// device context array element +struct xhci_devlist { + u32 ptr_low; + u32 ptr_high; +} PACKED; + +// input context +struct xhci_inctx { + u32 del; + u32 add; + u32 reserved_01[6]; +} PACKED; + +// transfer block (ring element) +struct xhci_trb { + u32 ptr_low; + u32 ptr_high; + u32 status; + u32 control; +} PACKED; + +// event ring segment +struct xhci_er_seg { + u32 ptr_low; + u32 ptr_high; + u32 size; + u32 reserved_01; +} PACKED; + +#endif // usb-xhci.h diff --git a/qemu/roms/seabios/src/hw/usb.c b/qemu/roms/seabios/src/hw/usb.c new file mode 100644 index 000000000..1b4ea8bed --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb.c @@ -0,0 +1,500 @@ +// Main code for handling USB controllers and devices. +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ehci.h" // ehci_setup +#include "usb-xhci.h" // xhci_setup +#include "usb-hid.h" // usb_keyboard_setup +#include "usb-hub.h" // usb_hub_setup +#include "usb-msc.h" // usb_msc_setup +#include "usb-ohci.h" // ohci_setup +#include "usb-uas.h" // usb_uas_setup +#include "usb-uhci.h" // uhci_setup +#include "util.h" // msleep +#include "x86.h" // __fls + + +/**************************************************************** + * Controller function wrappers + ****************************************************************/ + +// Allocate, update, or free a usb pipe. +static struct usb_pipe * +usb_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe + , struct usb_endpoint_descriptor *epdesc) +{ + switch (usbdev->hub->cntl->type) { + default: + case USB_TYPE_UHCI: + return uhci_realloc_pipe(usbdev, pipe, epdesc); + case USB_TYPE_OHCI: + return ohci_realloc_pipe(usbdev, pipe, epdesc); + case USB_TYPE_EHCI: + return ehci_realloc_pipe(usbdev, pipe, epdesc); + case USB_TYPE_XHCI: + return xhci_realloc_pipe(usbdev, pipe, epdesc); + } +} + +// Send a message on a control pipe using the default control descriptor. +static int +usb_send_pipe(struct usb_pipe *pipe_fl, int dir, const void *cmd + , void *data, int datasize) +{ + switch (GET_LOWFLAT(pipe_fl->type)) { + default: + case USB_TYPE_UHCI: + return uhci_send_pipe(pipe_fl, dir, cmd, data, datasize); + case USB_TYPE_OHCI: + if (MODESEGMENT) + return -1; + return ohci_send_pipe(pipe_fl, dir, cmd, data, datasize); + case USB_TYPE_EHCI: + return ehci_send_pipe(pipe_fl, dir, cmd, data, datasize); + case USB_TYPE_XHCI: + if (MODESEGMENT) + return -1; + return xhci_send_pipe(pipe_fl, dir, cmd, data, datasize); + } +} + +int +usb_poll_intr(struct usb_pipe *pipe_fl, void *data) +{ + ASSERT16(); + switch (GET_LOWFLAT(pipe_fl->type)) { + default: + case USB_TYPE_UHCI: + return uhci_poll_intr(pipe_fl, data); + case USB_TYPE_OHCI: + return ohci_poll_intr(pipe_fl, data); + case USB_TYPE_EHCI: + return ehci_poll_intr(pipe_fl, data); + case USB_TYPE_XHCI: ; + extern void _cfunc32flat_xhci_poll_intr(void); + return call32_params(_cfunc32flat_xhci_poll_intr, (u32)pipe_fl + , (u32)MAKE_FLATPTR(GET_SEG(SS), (u32)data), 0, -1); + } +} + +int usb_32bit_pipe(struct usb_pipe *pipe_fl) +{ + return (CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI) + || (CONFIG_USB_OHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_OHCI); +} + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Allocate a usb pipe. +struct usb_pipe * +usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + return usb_realloc_pipe(usbdev, NULL, epdesc); +} + +// Free an allocated control or bulk pipe. +void +usb_free_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe) +{ + if (!pipe) + return; + usb_realloc_pipe(usbdev, pipe, NULL); +} + +// Send a message to the default control pipe of a device. +int +usb_send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req + , void *data) +{ + return usb_send_pipe(pipe, req->bRequestType & USB_DIR_IN, req + , data, req->wLength); +} + +// Send a message to a bulk endpoint +int +usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize) +{ + return usb_send_pipe(pipe_fl, dir, NULL, data, datasize); +} + +// Check if a pipe for a given controller is on the freelist +int +usb_is_freelist(struct usb_s *cntl, struct usb_pipe *pipe) +{ + return pipe->cntl != cntl; +} + +// Add a pipe to the controller's freelist +void +usb_add_freelist(struct usb_pipe *pipe) +{ + if (!pipe) + return; + struct usb_s *cntl = pipe->cntl; + pipe->freenext = cntl->freelist; + cntl->freelist = pipe; +} + +// Check for an available pipe on the freelist. +struct usb_pipe * +usb_get_freelist(struct usb_s *cntl, u8 eptype) +{ + struct usb_pipe **pfree = &cntl->freelist; + for (;;) { + struct usb_pipe *pipe = *pfree; + if (!pipe) + return NULL; + if (pipe->eptype == eptype) { + *pfree = pipe->freenext; + return pipe; + } + pfree = &pipe->freenext; + } +} + +// Fill "pipe" endpoint info from an endpoint descriptor. +void +usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + pipe->cntl = usbdev->hub->cntl; + pipe->type = usbdev->hub->cntl->type; + pipe->ep = epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + pipe->devaddr = usbdev->devaddr; + pipe->speed = usbdev->speed; + pipe->maxpacket = epdesc->wMaxPacketSize; + pipe->eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; +} + +// Find the exponential period of the requested interrupt end point. +int +usb_get_period(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + int period = epdesc->bInterval; + if (usbdev->speed != USB_HIGHSPEED) + return (period <= 0) ? 0 : __fls(period); + return (period <= 4) ? 0 : period - 4; +} + +// Maximum time (in ms) a data transfer should take +int +usb_xfer_time(struct usb_pipe *pipe, int datalen) +{ + // Use the maximum command time (5 seconds), except for + // set_address commands where we don't want to stall the boot if + // the device doesn't actually exist. Add 100ms to account for + // any controller delays. + if (!GET_LOWFLAT(pipe->devaddr)) + return USB_TIME_STATUS + 100; + return USB_TIME_COMMAND + 100; +} + +// Find the first endpoint of a given type in an interface description. +struct usb_endpoint_descriptor * +usb_find_desc(struct usbdevice_s *usbdev, int type, int dir) +{ + struct usb_endpoint_descriptor *epdesc = (void*)&usbdev->iface[1]; + for (;;) { + if ((void*)epdesc >= (void*)usbdev->iface + usbdev->imax + || epdesc->bDescriptorType == USB_DT_INTERFACE) { + return NULL; + } + if (epdesc->bDescriptorType == USB_DT_ENDPOINT + && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir + && (epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type) + return epdesc; + epdesc = (void*)epdesc + epdesc->bLength; + } +} + +// Get the first 8 bytes of the device descriptor. +static int +get_device_info8(struct usb_pipe *pipe, struct usb_device_descriptor *dinfo) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = USB_DT_DEVICE<<8; + req.wIndex = 0; + req.wLength = 8; + return usb_send_default_control(pipe, &req, dinfo); +} + +static struct usb_config_descriptor * +get_device_config(struct usb_pipe *pipe) +{ + struct usb_config_descriptor cfg; + + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = USB_DT_CONFIG<<8; + req.wIndex = 0; + req.wLength = sizeof(cfg); + int ret = usb_send_default_control(pipe, &req, &cfg); + if (ret) + return NULL; + + void *config = malloc_tmphigh(cfg.wTotalLength); + if (!config) + return NULL; + req.wLength = cfg.wTotalLength; + ret = usb_send_default_control(pipe, &req, config); + if (ret) { + free(config); + return NULL; + } + //hexdump(config, cfg.wTotalLength); + return config; +} + +static int +set_configuration(struct usb_pipe *pipe, u16 val) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_SET_CONFIGURATION; + req.wValue = val; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + + +/**************************************************************** + * Initialization and enumeration + ****************************************************************/ + +static const int speed_to_ctlsize[] = { + [ USB_FULLSPEED ] = 8, + [ USB_LOWSPEED ] = 8, + [ USB_HIGHSPEED ] = 64, + [ USB_SUPERSPEED ] = 512, +}; + +// Assign an address to a device in the default state on the given +// controller. +static int +usb_set_address(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + struct usb_s *cntl = usbdev->hub->cntl; + dprintf(3, "set_address %p\n", cntl); + if (cntl->maxaddr >= USB_MAXADDR) + return -1; + + msleep(USB_TIME_RSTRCY); + + // Create a pipe for the default address. + struct usb_endpoint_descriptor epdesc = { + .wMaxPacketSize = speed_to_ctlsize[usbdev->speed], + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }; + usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc); + if (!usbdev->defpipe) + return -1; + + // Send set_address command. + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_SET_ADDRESS; + req.wValue = cntl->maxaddr + 1; + req.wIndex = 0; + req.wLength = 0; + int ret = usb_send_default_control(usbdev->defpipe, &req, NULL); + if (ret) { + usb_free_pipe(usbdev, usbdev->defpipe); + return -1; + } + + msleep(USB_TIME_SETADDR_RECOVERY); + + cntl->maxaddr++; + usbdev->devaddr = cntl->maxaddr; + usbdev->defpipe = usb_realloc_pipe(usbdev, usbdev->defpipe, &epdesc); + if (!usbdev->defpipe) + return -1; + return 0; +} + +// Called for every found device - see if a driver is available for +// this device and do setup if so. +static int +configure_usb_device(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + dprintf(3, "config_usb: %p\n", usbdev->defpipe); + + // Set the max packet size for endpoint 0 of this device. + struct usb_device_descriptor dinfo; + int ret = get_device_info8(usbdev->defpipe, &dinfo); + if (ret) + return 0; + u16 maxpacket = dinfo.bMaxPacketSize0; + if (dinfo.bcdUSB >= 0x0300) + maxpacket = 1 << dinfo.bMaxPacketSize0; + dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%d\n" + , dinfo.bcdUSB, dinfo.bDeviceClass, dinfo.bDeviceSubClass + , dinfo.bDeviceProtocol, maxpacket); + if (maxpacket < 8) + return 0; + struct usb_endpoint_descriptor epdesc = { + .wMaxPacketSize = maxpacket, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }; + usbdev->defpipe = usb_realloc_pipe(usbdev, usbdev->defpipe, &epdesc); + if (!usbdev->defpipe) + return -1; + + // Get configuration + struct usb_config_descriptor *config = get_device_config(usbdev->defpipe); + if (!config) + return 0; + + // Determine if a driver exists for this device - only look at the + // first interface of the first configuration. + struct usb_interface_descriptor *iface = (void*)(&config[1]); + if (iface->bInterfaceClass != USB_CLASS_HID + && iface->bInterfaceClass != USB_CLASS_MASS_STORAGE + && iface->bInterfaceClass != USB_CLASS_HUB) + // Not a supported device. + goto fail; + + // Set the configuration. + ret = set_configuration(usbdev->defpipe, config->bConfigurationValue); + if (ret) + goto fail; + + // Configure driver. + usbdev->config = config; + usbdev->iface = iface; + usbdev->imax = (void*)config + config->wTotalLength - (void*)iface; + if (iface->bInterfaceClass == USB_CLASS_HUB) + ret = usb_hub_setup(usbdev); + else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE) { + if (iface->bInterfaceProtocol == US_PR_BULK) + ret = usb_msc_setup(usbdev); + if (iface->bInterfaceProtocol == US_PR_UAS) + ret = usb_uas_setup(usbdev); + } else + ret = usb_hid_setup(usbdev); + if (ret) + goto fail; + + free(config); + return 1; +fail: + free(config); + return 0; +} + +static void +usb_hub_port_setup(void *data) +{ + struct usbdevice_s *usbdev = data; + struct usbhub_s *hub = usbdev->hub; + u32 port = usbdev->port; + + for (;;) { + // Detect if device present (and possibly start reset) + int ret = hub->op->detect(hub, port); + if (ret > 0) + // Device connected. + break; + if (ret < 0 || timer_check(hub->detectend)) + // No device found. + goto done; + msleep(5); + } + + // XXX - wait USB_TIME_ATTDB time? + + // Reset port and determine device speed + mutex_lock(&hub->cntl->resetlock); + int ret = hub->op->reset(hub, port); + if (ret < 0) + // Reset failed + goto resetfail; + usbdev->speed = ret; + + // Set address of port + ret = usb_set_address(usbdev); + if (ret) { + hub->op->disconnect(hub, port); + goto resetfail; + } + mutex_unlock(&hub->cntl->resetlock); + + // Configure the device + int count = configure_usb_device(usbdev); + usb_free_pipe(usbdev, usbdev->defpipe); + if (!count) + hub->op->disconnect(hub, port); + hub->devcount += count; +done: + hub->threads--; + free(usbdev); + return; + +resetfail: + mutex_unlock(&hub->cntl->resetlock); + goto done; +} + +void +usb_enumerate(struct usbhub_s *hub) +{ + u32 portcount = hub->portcount; + hub->threads = portcount; + hub->detectend = timer_calc(USB_TIME_SIGATT); + + // Launch a thread for every port. + int i; + for (i=0; i<portcount; i++) { + struct usbdevice_s *usbdev = malloc_tmphigh(sizeof(*usbdev)); + if (!usbdev) { + warn_noalloc(); + continue; + } + memset(usbdev, 0, sizeof(*usbdev)); + usbdev->hub = hub; + usbdev->port = i; + run_thread(usb_hub_port_setup, usbdev); + } + + // Wait for threads to complete. + while (hub->threads) + yield(); +} + +void +__usb_setup(void *data) +{ + dprintf(3, "init usb\n"); + xhci_setup(); + ehci_setup(); + uhci_setup(); + ohci_setup(); +} + +void +usb_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_USB) + return; + run_thread(__usb_setup, NULL); +} diff --git a/qemu/roms/seabios/src/hw/usb.h b/qemu/roms/seabios/src/hw/usb.h new file mode 100644 index 000000000..efb5e6f9c --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb.h @@ -0,0 +1,254 @@ +// USB functions and data. +#ifndef __USB_H +#define __USB_H + +#include "stacks.h" // struct mutex_s + +// Information on a USB end point. +struct usb_pipe { + union { + struct usb_s *cntl; + struct usb_pipe *freenext; + }; + u8 type; + u8 ep; + u8 devaddr; + u8 speed; + u16 maxpacket; + u8 eptype; +}; + +// Common information for usb devices. +struct usbdevice_s { + struct usbhub_s *hub; + struct usb_pipe *defpipe; + u32 slotid; + u32 port; + struct usb_config_descriptor *config; + struct usb_interface_descriptor *iface; + int imax; + u8 speed; + u8 devaddr; +}; + +// Common information for usb controllers. +struct usb_s { + struct usb_pipe *freelist; + struct mutex_s resetlock; + struct pci_device *pci; + u8 type; + u8 maxaddr; +}; + +// Information for enumerating USB hubs +struct usbhub_s { + struct usbhub_op_s *op; + struct usbdevice_s *usbdev; + struct usb_s *cntl; + struct mutex_s lock; + u32 detectend; + u32 port; + u32 threads; + u32 portcount; + u32 devcount; +}; + +// Hub callback (32bit) info +struct usbhub_op_s { + int (*detect)(struct usbhub_s *hub, u32 port); + int (*reset)(struct usbhub_s *hub, u32 port); + void (*disconnect)(struct usbhub_s *hub, u32 port); +}; + +#define USB_TYPE_UHCI 1 +#define USB_TYPE_OHCI 2 +#define USB_TYPE_EHCI 3 +#define USB_TYPE_XHCI 4 + +#define USB_FULLSPEED 0 +#define USB_LOWSPEED 1 +#define USB_HIGHSPEED 2 +#define USB_SUPERSPEED 3 + +#define USB_MAXADDR 127 + + +/**************************************************************** + * usb structs and flags + ****************************************************************/ + +// USB mandated timings (in ms) +#define USB_TIME_SIGATT 100 +#define USB_TIME_ATTDB 100 +#define USB_TIME_DRST 10 +#define USB_TIME_DRSTR 50 +#define USB_TIME_RSTRCY 10 + +#define USB_TIME_STATUS 50 +#define USB_TIME_DATAIN 500 +#define USB_TIME_COMMAND 5000 + +#define USB_TIME_SETADDR_RECOVERY 2 + +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SETUP 0x2d + +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +struct usb_ctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} PACKED; + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_ENDPOINT_COMPANION 0x30 + +struct usb_device_descriptor { + u8 bLength; + u8 bDescriptorType; + + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u16 idVendor; + u16 idProduct; + u16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} PACKED; + +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 + +struct usb_config_descriptor { + u8 bLength; + u8 bDescriptorType; + + u16 wTotalLength; + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; +} PACKED; + +struct usb_interface_descriptor { + u8 bLength; + u8 bDescriptorType; + + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; +} PACKED; + +struct usb_endpoint_descriptor { + u8 bLength; + u8 bDescriptorType; + + u8 bEndpointAddress; + u8 bmAttributes; + u16 wMaxPacketSize; + u8 bInterval; +} PACKED; + +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 + +#define USB_CONTROL_SETUP_SIZE 8 + + +/**************************************************************** + * usb mass storage flags + ****************************************************************/ + +#define US_SC_ATAPI_8020 0x02 +#define US_SC_ATAPI_8070 0x05 +#define US_SC_SCSI 0x06 + +#define US_PR_BULK 0x50 /* bulk-only transport */ +#define US_PR_UAS 0x62 /* usb attached scsi */ + +/**************************************************************** + * function defs + ****************************************************************/ + +// usb.c +int usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize); +int usb_poll_intr(struct usb_pipe *pipe, void *data); +int usb_32bit_pipe(struct usb_pipe *pipe_fl); +struct usb_pipe *usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +void usb_free_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe); +int usb_send_default_control(struct usb_pipe *pipe + , const struct usb_ctrlrequest *req, void *data); +int usb_is_freelist(struct usb_s *cntl, struct usb_pipe *pipe); +void usb_add_freelist(struct usb_pipe *pipe); +struct usb_pipe *usb_get_freelist(struct usb_s *cntl, u8 eptype); +void usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +int usb_get_period(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +int usb_xfer_time(struct usb_pipe *pipe, int datalen); +struct usb_endpoint_descriptor *usb_find_desc(struct usbdevice_s *usbdev + , int type, int dir); +void usb_enumerate(struct usbhub_s *hub); +void usb_setup(void); + +#endif // usb.h diff --git a/qemu/roms/seabios/src/hw/virtio-blk.c b/qemu/roms/seabios/src/hw/virtio-blk.c new file mode 100644 index 000000000..e2dbd3c94 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-blk.c @@ -0,0 +1,177 @@ +// Virtio block boot support. +// +// Copyright (C) 2010 Red Hat Inc. +// +// Authors: +// Gleb Natapov <gnatapov@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "config.h" // CONFIG_* +#include "block.h" // struct drive_s +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-pci.h" +#include "virtio-ring.h" +#include "virtio-blk.h" + +struct virtiodrive_s { + struct drive_s drive; + struct vring_virtqueue *vq; + u16 ioaddr; +}; + +static int +virtio_blk_op(struct disk_op_s *op, int write) +{ + struct virtiodrive_s *vdrive_gf = + container_of(op->drive_gf, struct virtiodrive_s, drive); + struct vring_virtqueue *vq = GET_GLOBALFLAT(vdrive_gf->vq); + struct virtio_blk_outhdr hdr = { + .type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN, + .ioprio = 0, + .sector = op->lba, + }; + u8 status = VIRTIO_BLK_S_UNSUPP; + struct vring_list sg[] = { + { + .addr = MAKE_FLATPTR(GET_SEG(SS), &hdr), + .length = sizeof(hdr), + }, + { + .addr = op->buf_fl, + .length = GET_GLOBALFLAT(vdrive_gf->drive.blksize) * op->count, + }, + { + .addr = MAKE_FLATPTR(GET_SEG(SS), &status), + .length = sizeof(status), + }, + }; + + /* Add to virtqueue and kick host */ + if (write) + vring_add_buf(vq, sg, 2, 1, 0, 0); + else + vring_add_buf(vq, sg, 1, 2, 0, 0); + vring_kick(GET_GLOBALFLAT(vdrive_gf->ioaddr), vq, 1); + + /* Wait for reply */ + while (!vring_more_used(vq)) + usleep(5); + + /* Reclaim virtqueue element */ + vring_get_buf(vq, NULL); + + /* Clear interrupt status register. Avoid leaving interrupts stuck if + * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. + */ + vp_get_isr(GET_GLOBALFLAT(vdrive_gf->ioaddr)); + + return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; +} + +int +process_virtio_blk_op(struct disk_op_s *op) +{ + if (! CONFIG_VIRTIO_BLK) + return 0; + switch (op->command) { + case CMD_READ: + return virtio_blk_op(op, 0); + case CMD_WRITE: + return virtio_blk_op(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + +static void +init_virtio_blk(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf), + pci_bdf_to_dev(bdf)); + struct virtiodrive_s *vdrive = malloc_fseg(sizeof(*vdrive)); + if (!vdrive) { + warn_noalloc(); + return; + } + memset(vdrive, 0, sizeof(*vdrive)); + vdrive->drive.type = DTYPE_VIRTIO_BLK; + vdrive->drive.cntl_id = bdf; + + u16 ioaddr = vp_init_simple(bdf); + vdrive->ioaddr = ioaddr; + if (vp_find_vq(ioaddr, 0, &vdrive->vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-blk %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + struct virtio_blk_config cfg; + vp_get(ioaddr, 0, &cfg, sizeof(cfg)); + + u32 f = vp_get_features(ioaddr); + vdrive->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ? + cfg.blk_size : DISK_SECTOR_SIZE; + + vdrive->drive.sectors = cfg.capacity; + dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize, (u32)vdrive->drive.sectors); + + if (vdrive->drive.blksize != DISK_SECTOR_SIZE) { + dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize); + goto fail; + } + + vdrive->drive.pchs.cylinder = cfg.cylinders; + vdrive->drive.pchs.head = cfg.heads; + vdrive->drive.pchs.sector = cfg.sectors; + char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + + boot_add_hd(&vdrive->drive, desc, bootprio_find_pci_device(pci)); + + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + return; + +fail: + vp_reset(ioaddr); + free(vdrive->vq); + free(vdrive); +} + +void +virtio_blk_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_VIRTIO_BLK) + return; + + dprintf(3, "init virtio-blk\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET + || pci->device != PCI_DEVICE_ID_VIRTIO_BLK) + continue; + init_virtio_blk(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/virtio-blk.h b/qemu/roms/seabios/src/hw/virtio-blk.h new file mode 100644 index 000000000..b233c744b --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-blk.h @@ -0,0 +1,43 @@ +#ifndef _VIRTIO_BLK_H +#define _VIRTIO_BLK_H + +struct virtio_blk_config +{ + u64 capacity; + u32 size_max; + u32 seg_max; + u16 cylinders; + u8 heads; + u8 sectors; + u32 blk_size; + u8 physical_block_exp; + u8 alignment_offset; + u16 min_io_size; + u32 opt_io_size; +} __attribute__((packed)); + +#define VIRTIO_BLK_F_BLK_SIZE 6 + +/* These two define direction. */ +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 + +/* This is the first element of the read scatter-gather list. */ +struct virtio_blk_outhdr { + /* VIRTIO_BLK_T* */ + u32 type; + /* io priority. */ + u32 ioprio; + /* Sector (ie. 512 byte offset) */ + u64 sector; +}; + +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 + +struct disk_op_s; +int process_virtio_blk_op(struct disk_op_s *op); +void virtio_blk_setup(void); + +#endif /* _VIRTIO_BLK_H */ diff --git a/qemu/roms/seabios/src/hw/virtio-pci.c b/qemu/roms/seabios/src/hw/virtio-pci.c new file mode 100644 index 000000000..b9b3ab1e3 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-pci.c @@ -0,0 +1,97 @@ +/* virtio-pci.c - pci interface for virtio interface + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier <Laurent.Vivier@bull.net> + * + * some parts from Linux Virtio PCI driver + * + * Copyright IBM Corp. 2007 + * Authors: Anthony Liguori <aliguori@us.ibm.com> + * + * Adopted for Seabios: Gleb Natapov <gleb@redhat.com> + * + * This work is licensed under the terms of the GNU LGPLv3 + * See the COPYING file in the top-level directory. + */ + +#include "config.h" // CONFIG_DEBUG_LEVEL +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // pci_config_readl +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "virtio-pci.h" +#include "virtio-ring.h" + +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue **p_vq) +{ + u16 num; + + ASSERT32FLAT(); + struct vring_virtqueue *vq = *p_vq = memalign_low(PAGE_SIZE, sizeof(*vq)); + if (!vq) { + warn_noalloc(); + goto fail; + } + memset(vq, 0, sizeof(*vq)); + + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* check if the queue is available */ + + num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); + if (!num) { + dprintf(1, "ERROR: queue size is 0\n"); + goto fail; + } + + if (num > MAX_QUEUE_NUM) { + dprintf(1, "ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); + goto fail; + } + + /* check if the queue is already active */ + + if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { + dprintf(1, "ERROR: queue already active\n"); + goto fail; + } + + vq->queue_index = queue_index; + + /* initialize the queue */ + + struct vring * vr = &vq->vring; + vring_init(vr, num, (unsigned char*)&vq->queue); + + /* activate the queue + * + * NOTE: vr->desc is initialized by vring_init() + */ + + outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT, + ioaddr + VIRTIO_PCI_QUEUE_PFN); + + return num; + +fail: + free(vq); + *p_vq = NULL; + return -1; +} + +u16 vp_init_simple(u16 bdf) +{ + u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) & + PCI_BASE_ADDRESS_IO_MASK; + + vp_reset(ioaddr); + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER ); + return ioaddr; +} diff --git a/qemu/roms/seabios/src/hw/virtio-pci.h b/qemu/roms/seabios/src/hw/virtio-pci.h new file mode 100644 index 000000000..bc04b039e --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-pci.h @@ -0,0 +1,105 @@ +#ifndef _VIRTIO_PCI_H +#define _VIRTIO_PCI_H + +#include "x86.h" // inl + +/* A 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES 0 + +/* A 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES 4 + +/* A 32-bit r/w PFN for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_PFN 8 + +/* A 16-bit r/o queue size for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_NUM 12 + +/* A 16-bit r/w queue selector */ +#define VIRTIO_PCI_QUEUE_SEL 14 + +/* A 16-bit r/w queue notifier */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 + +/* An 8-bit device status register. */ +#define VIRTIO_PCI_STATUS 18 + +/* An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. */ +#define VIRTIO_PCI_ISR 19 + +/* The bit of the ISR which indicates a device configuration change. */ +#define VIRTIO_PCI_ISR_CONFIG 0x2 + +/* The remaining space is defined by each driver as the per-driver + * configuration space */ +#define VIRTIO_PCI_CONFIG 20 + +/* Virtio ABI version, this must match exactly */ +#define VIRTIO_PCI_ABI_VERSION 0 + +static inline u32 vp_get_features(unsigned int ioaddr) +{ + return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); +} + +static inline void vp_set_features(unsigned int ioaddr, u32 features) +{ + outl(features, ioaddr + VIRTIO_PCI_GUEST_FEATURES); +} + +static inline void vp_get(unsigned int ioaddr, unsigned offset, + void *buf, unsigned len) +{ + u8 *ptr = buf; + unsigned i; + + for (i = 0; i < len; i++) + ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i); +} + +static inline u8 vp_get_status(unsigned int ioaddr) +{ + return inb(ioaddr + VIRTIO_PCI_STATUS); +} + +static inline void vp_set_status(unsigned int ioaddr, u8 status) +{ + if (status == 0) /* reset */ + return; + outb(status, ioaddr + VIRTIO_PCI_STATUS); +} + +static inline u8 vp_get_isr(unsigned int ioaddr) +{ + return inb(ioaddr + VIRTIO_PCI_ISR); +} + +static inline void vp_reset(unsigned int ioaddr) +{ + outb(0, ioaddr + VIRTIO_PCI_STATUS); + (void)inb(ioaddr + VIRTIO_PCI_ISR); +} + +static inline void vp_notify(unsigned int ioaddr, int queue_index) +{ + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); +} + +static inline void vp_del_vq(unsigned int ioaddr, int queue_index) +{ + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* deactivate the queue */ + + outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); +} + +struct vring_virtqueue; +u16 vp_init_simple(u16 bdf); +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue **p_vq); +#endif /* _VIRTIO_PCI_H_ */ diff --git a/qemu/roms/seabios/src/hw/virtio-ring.c b/qemu/roms/seabios/src/hw/virtio-ring.c new file mode 100644 index 000000000..97e0b3487 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-ring.c @@ -0,0 +1,149 @@ +/* virtio-pci.c - virtio ring management + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier <Laurent.Vivier@bull.net> + * + * some parts from Linux Virtio Ring + * + * Copyright Rusty Russell IBM Corporation 2007 + * + * Adopted for Seabios: Gleb Natapov <gleb@redhat.com> + * + * This work is licensed under the terms of the GNU LGPLv3 + * See the COPYING file in the top-level directory. + * + * + */ + +#include "biosvar.h" // GET_GLOBAL +#include "output.h" // panic +#include "virtio-ring.h" +#include "virtio-pci.h" + +#define BUG() do { \ + panic("BUG: failure at %d/%s()!\n", __LINE__, __func__); \ + } while (0) +#define BUG_ON(condition) do { if (condition) BUG(); } while (0) + +/* + * vring_more_used + * + * is there some used buffers ? + * + */ + +int vring_more_used(struct vring_virtqueue *vq) +{ + struct vring_used *used = GET_LOWFLAT(vq->vring.used); + int more = GET_LOWFLAT(vq->last_used_idx) != GET_LOWFLAT(used->idx); + /* Make sure ring reads are done after idx read above. */ + smp_rmb(); + return more; +} + +/* + * vring_free + * + * put at the begin of the free list the current desc[head] + */ + +void vring_detach(struct vring_virtqueue *vq, unsigned int head) +{ + struct vring *vr = &vq->vring; + struct vring_desc *desc = GET_LOWFLAT(vr->desc); + unsigned int i; + + /* find end of given descriptor */ + + i = head; + while (GET_LOWFLAT(desc[i].flags) & VRING_DESC_F_NEXT) + i = GET_LOWFLAT(desc[i].next); + + /* link it with free list and point to it */ + + SET_LOWFLAT(desc[i].next, GET_LOWFLAT(vq->free_head)); + SET_LOWFLAT(vq->free_head, head); +} + +/* + * vring_get_buf + * + * get a buffer from the used list + * + */ + +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len) +{ + struct vring *vr = &vq->vring; + struct vring_used_elem *elem; + struct vring_used *used = GET_LOWFLAT(vq->vring.used); + u32 id; + int ret; + +// BUG_ON(!vring_more_used(vq)); + + elem = &used->ring[GET_LOWFLAT(vq->last_used_idx) % GET_LOWFLAT(vr->num)]; + id = GET_LOWFLAT(elem->id); + if (len != NULL) + *len = GET_LOWFLAT(elem->len); + + ret = GET_LOWFLAT(vq->vdata[id]); + + vring_detach(vq, id); + + SET_LOWFLAT(vq->last_used_idx, GET_LOWFLAT(vq->last_used_idx) + 1); + + return ret; +} + +void vring_add_buf(struct vring_virtqueue *vq, + struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added) +{ + struct vring *vr = &vq->vring; + int i, av, head, prev; + struct vring_desc *desc = GET_LOWFLAT(vr->desc); + struct vring_avail *avail = GET_LOWFLAT(vr->avail); + + BUG_ON(out + in == 0); + + prev = 0; + head = GET_LOWFLAT(vq->free_head); + for (i = head; out; i = GET_LOWFLAT(desc[i].next), out--) { + SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT); + SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); + SET_LOWFLAT(desc[i].len, list->length); + prev = i; + list++; + } + for ( ; in; i = GET_LOWFLAT(desc[i].next), in--) { + SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT|VRING_DESC_F_WRITE); + SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); + SET_LOWFLAT(desc[i].len, list->length); + prev = i; + list++; + } + SET_LOWFLAT(desc[prev].flags, + GET_LOWFLAT(desc[prev].flags) & ~VRING_DESC_F_NEXT); + + SET_LOWFLAT(vq->free_head, i); + + SET_LOWFLAT(vq->vdata[head], index); + + av = (GET_LOWFLAT(avail->idx) + num_added) % GET_LOWFLAT(vr->num); + SET_LOWFLAT(avail->ring[av], head); +} + +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) +{ + struct vring *vr = &vq->vring; + struct vring_avail *avail = GET_LOWFLAT(vr->avail); + + /* Make sure idx update is done after ring write. */ + smp_wmb(); + SET_LOWFLAT(avail->idx, GET_LOWFLAT(avail->idx) + num_added); + + vp_notify(ioaddr, GET_LOWFLAT(vq->queue_index)); +} diff --git a/qemu/roms/seabios/src/hw/virtio-ring.h b/qemu/roms/seabios/src/hw/virtio-ring.h new file mode 100644 index 000000000..b7a7aafb2 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-ring.h @@ -0,0 +1,131 @@ +#ifndef _VIRTIO_RING_H +#define _VIRTIO_RING_H + +#include "types.h" // u64 +#include "memmap.h" // PAGE_SIZE + +#define PAGE_SHIFT 12 +#define PAGE_MASK (PAGE_SIZE-1) + +#define virt_to_phys(v) (unsigned long)(v) +#define phys_to_virt(p) (void*)(p) +/* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */ +#define smp_rmb() barrier() +#define smp_wmb() barrier() + +/* Status byte for guest to report progress, and synchronize features. */ +/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +/* We have found a driver for the device. */ +#define VIRTIO_CONFIG_S_DRIVER 2 +/* Driver has used its parts of the config, and is happy */ +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* We've given up on this device. */ +#define VIRTIO_CONFIG_S_FAILED 0x80 + +#define MAX_QUEUE_NUM (128) + +#define VRING_DESC_F_NEXT 1 +#define VRING_DESC_F_WRITE 2 + +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +#define VRING_USED_F_NO_NOTIFY 1 + +struct vring_desc +{ + u64 addr; + u32 len; + u16 flags; + u16 next; +}; + +struct vring_avail +{ + u16 flags; + u16 idx; + u16 ring[0]; +}; + +struct vring_used_elem +{ + u32 id; + u32 len; +}; + +struct vring_used +{ + u16 flags; + u16 idx; + struct vring_used_elem ring[]; +}; + +struct vring { + unsigned int num; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +}; + +#define vring_size(num) \ + (((((sizeof(struct vring_desc) * num) + \ + (sizeof(struct vring_avail) + sizeof(u16) * num)) \ + + PAGE_MASK) & ~PAGE_MASK) + \ + (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) + +typedef unsigned char virtio_queue_t[vring_size(MAX_QUEUE_NUM)]; + +struct vring_virtqueue { + virtio_queue_t queue; + struct vring vring; + u16 free_head; + u16 last_used_idx; + u16 vdata[MAX_QUEUE_NUM]; + /* PCI */ + int queue_index; +}; + +struct vring_list { + char *addr; + unsigned int length; +}; + +static inline void vring_init(struct vring *vr, + unsigned int num, unsigned char *queue) +{ + unsigned int i; + unsigned long pa; + + ASSERT32FLAT(); + vr->num = num; + + /* physical address of desc must be page aligned */ + + pa = virt_to_phys(queue); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->desc = phys_to_virt(pa); + + vr->avail = (struct vring_avail *)&vr->desc[num]; + /* disable interrupts */ + vr->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; + + /* physical address of used must be page aligned */ + + pa = virt_to_phys(&vr->avail->ring[num]); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->used = phys_to_virt(pa); + + for (i = 0; i < num - 1; i++) + vr->desc[i].next = i + 1; + vr->desc[i].next = 0; +} + +int vring_more_used(struct vring_virtqueue *vq); +void vring_detach(struct vring_virtqueue *vq, unsigned int head); +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); +void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added); +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); + +#endif /* _VIRTIO_RING_H_ */ diff --git a/qemu/roms/seabios/src/hw/virtio-scsi.c b/qemu/roms/seabios/src/hw/virtio-scsi.c new file mode 100644 index 000000000..8f966875b --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-scsi.c @@ -0,0 +1,187 @@ +// Virtio SCSI boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-pci.h" +#include "virtio-ring.h" +#include "virtio-scsi.h" + +struct virtio_lun_s { + struct drive_s drive; + struct pci_device *pci; + struct vring_virtqueue *vq; + u16 ioaddr; + u16 target; + u16 lun; +}; + +static int +virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + struct virtio_scsi_req_cmd req; + struct virtio_scsi_resp_cmd resp; + struct vring_list sg[3]; + + memset(&req, 0, sizeof(req)); + req.lun[0] = 1; + req.lun[1] = target; + req.lun[2] = (lun >> 8) | 0x40; + req.lun[3] = (lun & 0xff); + memcpy(req.cdb, cdbcmd, 16); + + u32 len = op->count * blocksize; + int datain = cdb_is_read(cdbcmd, blocksize); + int in_num = (datain ? 2 : 1); + int out_num = (len ? 3 : 2) - in_num; + + sg[0].addr = MAKE_FLATPTR(GET_SEG(SS), &req); + sg[0].length = sizeof(req); + + sg[out_num].addr = MAKE_FLATPTR(GET_SEG(SS), &resp); + sg[out_num].length = sizeof(resp); + + if (len) { + int data_idx = (datain ? 2 : 1); + sg[data_idx].addr = op->buf_fl; + sg[data_idx].length = len; + } + + /* Add to virtqueue and kick host */ + vring_add_buf(vq, sg, out_num, in_num, 0, 0); + vring_kick(ioaddr, vq, 1); + + /* Wait for reply */ + while (!vring_more_used(vq)) + usleep(5); + + /* Reclaim virtqueue element */ + vring_get_buf(vq, NULL); + + /* Clear interrupt status register. Avoid leaving interrupts stuck if + * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. + */ + vp_get_isr(ioaddr); + + if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) { + return DISK_RET_SUCCESS; + } + return DISK_RET_EBADTRACK; +} + +int +virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + struct virtio_lun_s *vlun_gf = + container_of(op->drive_gf, struct virtio_lun_s, drive); + + return virtio_scsi_cmd(GET_GLOBALFLAT(vlun_gf->ioaddr), + GET_GLOBALFLAT(vlun_gf->vq), op, cdbcmd, + GET_GLOBALFLAT(vlun_gf->target), + GET_GLOBALFLAT(vlun_gf->lun), + blocksize); +} + +static int +virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr, + struct vring_virtqueue *vq, u16 target, u16 lun) +{ + struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun)); + if (!vlun) { + warn_noalloc(); + return -1; + } + memset(vlun, 0, sizeof(*vlun)); + vlun->drive.type = DTYPE_VIRTIO_SCSI; + vlun->drive.cntl_id = pci->bdf; + vlun->pci = pci; + vlun->ioaddr = ioaddr; + vlun->vq = vq; + vlun->target = target; + vlun->lun = lun; + + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&vlun->drive, "virtio-scsi", prio); + if (ret) + goto fail; + return 0; + +fail: + free(vlun); + return -1; +} + +static int +virtio_scsi_scan_target(struct pci_device *pci, u16 ioaddr, + struct vring_virtqueue *vq, u16 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + int ret = virtio_scsi_add_lun(pci, ioaddr, vq, target, 0); + return ret < 0 ? 0 : 1; +} + +static void +init_virtio_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf), + pci_bdf_to_dev(bdf)); + struct vring_virtqueue *vq = NULL; + u16 ioaddr = vp_init_simple(bdf); + if (vp_find_vq(ioaddr, 2, &vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-scsi %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + + int i, tot; + for (tot = 0, i = 0; i < 256; i++) + tot += virtio_scsi_scan_target(pci, ioaddr, vq, i); + + if (!tot) + goto fail; + + return; + +fail: + vp_reset(ioaddr); + free(vq); +} + +void +virtio_scsi_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_VIRTIO_SCSI) + return; + + dprintf(3, "init virtio-scsi\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET + || pci->device != PCI_DEVICE_ID_VIRTIO_SCSI) + continue; + init_virtio_scsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/virtio-scsi.h b/qemu/roms/seabios/src/hw/virtio-scsi.h new file mode 100644 index 000000000..96c3701d2 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-scsi.h @@ -0,0 +1,47 @@ +#ifndef _VIRTIO_SCSI_H +#define _VIRTIO_SCSI_H + +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 + +struct virtio_scsi_config +{ + u32 num_queues; + u32 seg_max; + u32 max_sectors; + u32 cmd_per_lun; + u32 event_info_size; + u32 sense_size; + u32 cdb_size; + u16 max_channel; + u16 max_target; + u32 max_lun; +} __attribute__((packed)); + +/* This is the first element of the "out" scatter-gather list. */ +struct virtio_scsi_req_cmd { + u8 lun[8]; + u64 id; + u8 task_attr; + u8 prio; + u8 crn; + char cdb[VIRTIO_SCSI_CDB_SIZE]; +} __attribute__((packed)); + +/* This is the first element of the "in" scatter-gather list. */ +struct virtio_scsi_resp_cmd { + u32 sense_len; + u32 residual; + u16 status_qualifier; + u8 status; + u8 response; + u8 sense[VIRTIO_SCSI_SENSE_SIZE]; +} __attribute__((packed)); + +#define VIRTIO_SCSI_S_OK 0 + +struct disk_op_s; +int virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void virtio_scsi_setup(void); + +#endif /* _VIRTIO_SCSI_H */ diff --git a/qemu/roms/seabios/src/jpeg.c b/qemu/roms/seabios/src/jpeg.c new file mode 100644 index 000000000..c2138edab --- /dev/null +++ b/qemu/roms/seabios/src/jpeg.c @@ -0,0 +1,1055 @@ +/* + * Copyright (C) 2001, Novell Inc. + * Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Novell nor the names of the contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * a tiny jpeg decoder. + * + * written in August 2001 by Michael Schroeder <mls@suse.de> + * + */ + +#define __LITTLE_ENDIAN +#include "malloc.h" +#include "string.h" +#include "util.h" +#define ISHIFT 11 + +#define IFIX(a) ((int)((a) * (1 << ISHIFT) + .5)) +#define IMULT(a, b) (((a) * (b)) >> ISHIFT) +#define ITOINT(a) ((a) >> ISHIFT) + +#ifndef __P +# define __P(x) x +#endif + +/* special markers */ +#define M_BADHUFF -1 +#define M_EOF 0x80 + +struct in { + unsigned char *p; + unsigned int bits; + int left; + int marker; + + int (*func) __P((void *)); + void *data; +}; + +/*********************************/ +struct dec_hufftbl; +struct enc_hufftbl; + +union hufftblp { + struct dec_hufftbl *dhuff; + struct enc_hufftbl *ehuff; +}; + +struct scan { + int dc; /* old dc value */ + + union hufftblp hudc; + union hufftblp huac; + int next; /* when to switch to next scan */ + + int cid; /* component id */ + int hv; /* horiz/vert, copied from comp */ + int tq; /* quant tbl, copied from comp */ +}; + +/*********************************/ + +#define DECBITS 10 /* seems to be the optimum */ + +struct dec_hufftbl { + int maxcode[17]; + int valptr[16]; + unsigned char vals[256]; + unsigned int llvals[1 << DECBITS]; +}; + +static void decode_mcus __P((struct in *, int *, int, struct scan *, int *)); +static int dec_readmarker __P((struct in *)); +static void dec_makehuff __P((struct dec_hufftbl *, int *, unsigned char *)); + +static void setinput __P((struct in *, unsigned char *)); +/*********************************/ + +#undef PREC +#define PREC int + +static void idctqtab __P((unsigned char *, PREC *)); +static void idct __P((int *, int *, PREC *, PREC, int)); +static void scaleidctqtab __P((PREC *, PREC)); + +/*********************************/ + +static void initcol __P((PREC[][64])); + +static void col221111 __P((int *, unsigned char *, int)); +static void col221111_16 __P((int *, unsigned char *, int)); +static void col221111_32 __P((int *, unsigned char *, int)); + +/*********************************/ + +#define ERR_NO_SOI 1 +#define ERR_NOT_8BIT 2 +#define ERR_HEIGHT_MISMATCH 3 +#define ERR_WIDTH_MISMATCH 4 +#define ERR_BAD_WIDTH_OR_HEIGHT 5 +#define ERR_TOO_MANY_COMPPS 6 +#define ERR_ILLEGAL_HV 7 +#define ERR_QUANT_TABLE_SELECTOR 8 +#define ERR_NOT_YCBCR_221111 9 +#define ERR_UNKNOWN_CID_IN_SCAN 10 +#define ERR_NOT_SEQUENTIAL_DCT 11 +#define ERR_WRONG_MARKER 12 +#define ERR_NO_EOI 13 +#define ERR_BAD_TABLES 14 +#define ERR_DEPTH_MISMATCH 15 + +/*********************************/ + +#define M_SOI 0xd8 +#define M_APP0 0xe0 +#define M_DQT 0xdb +#define M_SOF0 0xc0 +#define M_DHT 0xc4 +#define M_DRI 0xdd +#define M_SOS 0xda +#define M_RST0 0xd0 +#define M_EOI 0xd9 +#define M_COM 0xfe + +struct comp { + int cid; + int hv; + int tq; +}; + +#define MAXCOMP 4 +struct jpginfo { + int nc; /* number of components */ + int ns; /* number of scans */ + int dri; /* restart interval */ + int nm; /* mcus til next marker */ + int rm; /* next restart marker */ +}; + +struct jpeg_decdata { + int dcts[6 * 64 + 16]; + int out[64 * 6]; + int dquant[3][64]; + + unsigned char *datap; + struct jpginfo info; + struct comp comps[MAXCOMP]; + struct scan dscans[MAXCOMP]; + unsigned char quant[4][64]; + struct dec_hufftbl dhuff[4]; + struct in in; + + int height, width; +}; + +static int getbyte(struct jpeg_decdata *jpeg) +{ + return *jpeg->datap++; +} + +static int getword(struct jpeg_decdata *jpeg) +{ + int c1, c2; + c1 = *jpeg->datap++; + c2 = *jpeg->datap++; + return c1 << 8 | c2; +} + +static int readtables(struct jpeg_decdata *jpeg, int till) +{ + int m, l, i, j, lq, pq, tq; + int tc, th, tt; + + for (;;) { + if (getbyte(jpeg) != 0xff) + return -1; + if ((m = getbyte(jpeg)) == till) + break; + + switch (m) { + case 0xc2: + return 0; + + case M_DQT: + lq = getword(jpeg); + while (lq > 2) { + pq = getbyte(jpeg); + tq = pq & 15; + if (tq > 3) + return -1; + pq >>= 4; + if (pq != 0) + return -1; + for (i = 0; i < 64; i++) + jpeg->quant[tq][i] = getbyte(jpeg); + lq -= 64 + 1; + } + break; + + case M_DHT: + l = getword(jpeg); + while (l > 2) { + int hufflen[16], k; + unsigned char huffvals[256]; + + tc = getbyte(jpeg); + th = tc & 15; + tc >>= 4; + tt = tc * 2 + th; + if (tc > 1 || th > 1) + return -1; + for (i = 0; i < 16; i++) + hufflen[i] = getbyte(jpeg); + l -= 1 + 16; + k = 0; + for (i = 0; i < 16; i++) { + for (j = 0; j < hufflen[i]; j++) + huffvals[k++] = getbyte(jpeg); + l -= hufflen[i]; + } + dec_makehuff(jpeg->dhuff + tt, hufflen, huffvals); + } + break; + + case M_DRI: + l = getword(jpeg); + jpeg->info.dri = getword(jpeg); + break; + + default: + l = getword(jpeg); + while (l-- > 2) + getbyte(jpeg); + break; + } + } + return 0; +} + +static void dec_initscans(struct jpeg_decdata *jpeg) +{ + int i; + + jpeg->info.nm = jpeg->info.dri + 1; + jpeg->info.rm = M_RST0; + for (i = 0; i < jpeg->info.ns; i++) + jpeg->dscans[i].dc = 0; +} + +static int dec_checkmarker(struct jpeg_decdata *jpeg) +{ + int i; + + if (dec_readmarker(&jpeg->in) != jpeg->info.rm) + return -1; + jpeg->info.nm = jpeg->info.dri; + jpeg->info.rm = (jpeg->info.rm + 1) & ~0x08; + for (i = 0; i < jpeg->info.ns; i++) + jpeg->dscans[i].dc = 0; + return 0; +} + +struct jpeg_decdata *jpeg_alloc(void) +{ + struct jpeg_decdata *jpeg = malloc_tmphigh(sizeof(*jpeg)); + return jpeg; +} + +int jpeg_decode(struct jpeg_decdata *jpeg, unsigned char *buf) +{ + int i, j, m, tac, tdc; + + if (!jpeg || !buf) + return -1; + jpeg->datap = buf; + if (getbyte(jpeg) != 0xff) + return ERR_NO_SOI; + if (getbyte(jpeg) != M_SOI) + return ERR_NO_SOI; + if (readtables(jpeg, M_SOF0)) + return ERR_BAD_TABLES; + getword(jpeg); + i = getbyte(jpeg); + if (i != 8) + return ERR_NOT_8BIT; + jpeg->height = getword(jpeg); + jpeg->width = getword(jpeg); + if ((jpeg->height & 15) || (jpeg->width & 15)) + return ERR_BAD_WIDTH_OR_HEIGHT; + jpeg->info.nc = getbyte(jpeg); + if (jpeg->info.nc > MAXCOMP) + return ERR_TOO_MANY_COMPPS; + for (i = 0; i < jpeg->info.nc; i++) { + int h, v; + jpeg->comps[i].cid = getbyte(jpeg); + jpeg->comps[i].hv = getbyte(jpeg); + v = jpeg->comps[i].hv & 15; + h = jpeg->comps[i].hv >> 4; + jpeg->comps[i].tq = getbyte(jpeg); + if (h > 3 || v > 3) + return ERR_ILLEGAL_HV; + if (jpeg->comps[i].tq > 3) + return ERR_QUANT_TABLE_SELECTOR; + } + if (readtables(jpeg, M_SOS)) + return ERR_BAD_TABLES; + getword(jpeg); + jpeg->info.ns = getbyte(jpeg); + if (jpeg->info.ns != 3) + return ERR_NOT_YCBCR_221111; + for (i = 0; i < 3; i++) { + jpeg->dscans[i].cid = getbyte(jpeg); + tdc = getbyte(jpeg); + tac = tdc & 15; + tdc >>= 4; + if (tdc > 1 || tac > 1) + return ERR_QUANT_TABLE_SELECTOR; + for (j = 0; j < jpeg->info.nc; j++) + if (jpeg->comps[j].cid == jpeg->dscans[i].cid) + break; + if (j == jpeg->info.nc) + return ERR_UNKNOWN_CID_IN_SCAN; + jpeg->dscans[i].hv = jpeg->comps[j].hv; + jpeg->dscans[i].tq = jpeg->comps[j].tq; + jpeg->dscans[i].hudc.dhuff = &jpeg->dhuff[tdc]; + jpeg->dscans[i].huac.dhuff = &jpeg->dhuff[2 + tac]; + } + + i = getbyte(jpeg); + j = getbyte(jpeg); + m = getbyte(jpeg); + + if (i != 0 || j != 63 || m != 0) + return ERR_NOT_SEQUENTIAL_DCT; + + if (jpeg->dscans[0].cid != 1 || jpeg->dscans[1].cid != 2 + || jpeg->dscans[2].cid != 3) + return ERR_NOT_YCBCR_221111; + + if (jpeg->dscans[0].hv != 0x22 || jpeg->dscans[1].hv != 0x11 + || jpeg->dscans[2].hv != 0x11) + return ERR_NOT_YCBCR_221111; + + idctqtab(jpeg->quant[jpeg->dscans[0].tq], jpeg->dquant[0]); + idctqtab(jpeg->quant[jpeg->dscans[1].tq], jpeg->dquant[1]); + idctqtab(jpeg->quant[jpeg->dscans[2].tq], jpeg->dquant[2]); + initcol(jpeg->dquant); + setinput(&jpeg->in, jpeg->datap); + +#if 0 + /* landing zone */ + img[len] = 0; + img[len + 1] = 0xff; + img[len + 2] = M_EOF; +#endif + + dec_initscans(jpeg); + + return 0; +} + +void jpeg_get_size(struct jpeg_decdata *jpeg, int *width, int *height) +{ + *width = jpeg->width; + *height = jpeg->height; +} + +int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest) +{ + int m, mcusx, mcusy, mx, my, mloffset, jpgbpl; + int max[6]; + + if (jpeg->height != height) + return ERR_HEIGHT_MISMATCH; + if (jpeg->width != width) + return ERR_WIDTH_MISMATCH; + + jpgbpl = width * depth / 8; + mloffset = bytes_per_line_dest > jpgbpl ? bytes_per_line_dest : jpgbpl; + + mcusx = jpeg->width >> 4; + mcusy = jpeg->height >> 4; + + jpeg->dscans[0].next = 6 - 4; + jpeg->dscans[1].next = 6 - 4 - 1; + jpeg->dscans[2].next = 6 - 4 - 1 - 1; /* 411 encoding */ + for (my = 0; my < mcusy; my++) { + for (mx = 0; mx < mcusx; mx++) { + if (jpeg->info.dri && !--jpeg->info.nm) + if (dec_checkmarker(jpeg)) + return ERR_WRONG_MARKER; + + decode_mcus(&jpeg->in, jpeg->dcts, 6, jpeg->dscans, max); + idct(jpeg->dcts, jpeg->out, jpeg->dquant[0], + IFIX(128.5), max[0]); + idct(jpeg->dcts + 64, jpeg->out + 64, jpeg->dquant[0], + IFIX(128.5), max[1]); + idct(jpeg->dcts + 128, jpeg->out + 128, jpeg->dquant[0], + IFIX(128.5), max[2]); + idct(jpeg->dcts + 192, jpeg->out + 192, jpeg->dquant[0], + IFIX(128.5), max[3]); + idct(jpeg->dcts + 256, jpeg->out + 256, jpeg->dquant[1], + IFIX(0.5), max[4]); + idct(jpeg->dcts + 320, jpeg->out + 320, jpeg->dquant[2], + IFIX(0.5), max[5]); + + switch (depth) { + case 32: + col221111_32(jpeg->out, + pic + (my * 16 * mloffset + mx * 16 * 4), + mloffset); + break; + case 24: + col221111(jpeg->out, + pic + (my * 16 * mloffset + mx * 16 * 3), + mloffset); + break; + case 16: + col221111_16(jpeg->out, + pic + (my * 16 * mloffset + mx * 16 * 2), + mloffset); + break; + default: + return ERR_DEPTH_MISMATCH; + break; + } + } + } + + m = dec_readmarker(&jpeg->in); + if (m != M_EOI) + return ERR_NO_EOI; + + return 0; +} + +/****************************************************************/ +/************** huffman decoder ***************/ +/****************************************************************/ + +static int fillbits __P((struct in *, int, unsigned int)); +static int dec_rec2 __P((struct in *, struct dec_hufftbl *, int *, int, int)); + +static void setinput(struct in *in, unsigned char *p) +{ + in->p = p; + in->left = 0; + in->bits = 0; + in->marker = 0; +} + +static int fillbits(struct in *in, int le, unsigned int bi) +{ + int b, m; + + if (in->marker) { + if (le <= 16) + in->bits = bi << 16, le += 16; + return le; + } + while (le <= 24) { + b = *in->p++; + if (b == 0xff && (m = *in->p++) != 0) { + if (m == M_EOF) { + if (in->func && (m = in->func(in->data)) == 0) + continue; + } + in->marker = m; + if (le <= 16) + bi = bi << 16, le += 16; + break; + } + bi = bi << 8 | b; + le += 8; + } + in->bits = bi; /* tmp... 2 return values needed */ + return le; +} + +static int dec_readmarker(struct in *in) +{ + int m; + + in->left = fillbits(in, in->left, in->bits); + if ((m = in->marker) == 0) + return 0; + in->left = 0; + in->marker = 0; + return m; +} + +#define LEBI_DCL int le, bi +#define LEBI_GET(in) (le = in->left, bi = in->bits) +#define LEBI_PUT(in) (in->left = le, in->bits = bi) + +#define GETBITS(in, n) ( \ + (le < (n) ? le = fillbits(in, le, bi), bi = in->bits : 0), \ + (le -= (n)), \ + bi >> le & ((1 << (n)) - 1) \ +) + +#define UNGETBITS(in, n) ( \ + le += (n) \ +) + + +static int dec_rec2(struct in *in, struct dec_hufftbl *hu, int *runp, + int c, int i) +{ + LEBI_DCL; + + LEBI_GET(in); + if (i) { + UNGETBITS(in, i & 127); + *runp = i >> 8 & 15; + i >>= 16; + } else { + for (i = DECBITS; + (c = ((c << 1) | GETBITS(in, 1))) >= (hu->maxcode[i]); i++); + if (i >= 16) { + in->marker = M_BADHUFF; + return 0; + } + i = hu->vals[hu->valptr[i] + c - hu->maxcode[i - 1] * 2]; + *runp = i >> 4; + i &= 15; + } + if (i == 0) { /* sigh, 0xf0 is 11 bit */ + LEBI_PUT(in); + return 0; + } + /* receive part */ + c = GETBITS(in, i); + if (c < (1 << (i - 1))) + c += (-1 << i) + 1; + LEBI_PUT(in); + return c; +} + +#define DEC_REC(in, hu, r, i) ( \ + r = GETBITS(in, DECBITS), \ + i = hu->llvals[r], \ + i & 128 ? \ + ( \ + UNGETBITS(in, i & 127), \ + r = i >> 8 & 15, \ + i >> 16 \ + ) \ + : \ + ( \ + LEBI_PUT(in), \ + i = dec_rec2(in, hu, &r, r, i), \ + LEBI_GET(in), \ + i \ + ) \ +) + +static void decode_mcus(struct in *in, int *dct, int n, struct scan *sc, + int *maxp) +{ + struct dec_hufftbl *hu; + int i, r, t; + LEBI_DCL; + + memset(dct, 0, n * 64 * sizeof(*dct)); + LEBI_GET(in); + while (n-- > 0) { + hu = sc->hudc.dhuff; + *dct++ = (sc->dc += DEC_REC(in, hu, r, t)); + + hu = sc->huac.dhuff; + i = 63; + while (i > 0) { + t = DEC_REC(in, hu, r, t); + if (t == 0 && r == 0) { + dct += i; + break; + } + dct += r; + *dct++ = t; + i -= r + 1; + } + *maxp++ = 64 - i; + if (n == sc->next) + sc++; + } + LEBI_PUT(in); +} + +static void dec_makehuff(struct dec_hufftbl *hu, int *hufflen, + unsigned char *huffvals) +{ + int code, k, i, j, d, x, c, v; + for (i = 0; i < (1 << DECBITS); i++) + hu->llvals[i] = 0; + + /* + * llvals layout: + * + * value v already known, run r, backup u bits: + * vvvvvvvvvvvvvvvv 0000 rrrr 1 uuuuuuu + * value unknown, size b bits, run r, backup u bits: + * 000000000000bbbb 0000 rrrr 0 uuuuuuu + * value and size unknown: + * 0000000000000000 0000 0000 0 0000000 + */ + + code = 0; + k = 0; + for (i = 0; i < 16; i++, code <<= 1) { /* sizes */ + hu->valptr[i] = k; + for (j = 0; j < hufflen[i]; j++) { + hu->vals[k] = *huffvals++; + if (i < DECBITS) { + c = code << (DECBITS - 1 - i); + v = hu->vals[k] & 0x0f; /* size */ + for (d = 1 << (DECBITS - 1 - i); --d >= 0;) { + if (v + i < DECBITS) { /* both fit in table */ + x = d >> (DECBITS - 1 - v - i); + if (v && x < (1 << (v - 1))) + x += (-1 << v) + 1; + x = x << 16 | (hu->vals[k] & 0xf0) << 4 | + (DECBITS - (i + 1 + v)) | 128; + } else + x = v << 16 | (hu->vals[k] & 0xf0) << 4 | + (DECBITS - (i + 1)); + hu->llvals[c | d] = x; + } + } + code++; + k++; + } + hu->maxcode[i] = code; + } + hu->maxcode[16] = 0x20000; /* always terminate decode */ +} + +/****************************************************************/ +/************** idct ***************/ +/****************************************************************/ + +#define ONE ((PREC)IFIX(1.)) +#define S2 ((PREC)IFIX(0.382683432)) +#define C2 ((PREC)IFIX(0.923879532)) +#define C4 ((PREC)IFIX(0.707106781)) + +#define S22 ((PREC)IFIX(2 * 0.382683432)) +#define C22 ((PREC)IFIX(2 * 0.923879532)) +#define IC4 ((PREC)IFIX(1 / 0.707106781)) + +#define C3IC1 ((PREC)IFIX(0.847759065)) /* c3/c1 */ +#define C5IC1 ((PREC)IFIX(0.566454497)) /* c5/c1 */ +#define C7IC1 ((PREC)IFIX(0.198912367)) /* c7/c1 */ + +#define XPP(a,b) (t = a + b, b = a - b, a = t) +#define XMP(a,b) (t = a - b, b = a + b, a = t) +#define XPM(a,b) (t = a + b, b = b - a, a = t) + +#define ROT(a,b,s,c) ( t = IMULT(a + b, s), \ + a = IMULT(a, c - s) + t, \ + b = IMULT(b, c + s) - t) + +#define IDCT \ +( \ + XPP(t0, t1), \ + XMP(t2, t3), \ + t2 = IMULT(t2, IC4) - t3, \ + XPP(t0, t3), \ + XPP(t1, t2), \ + XMP(t4, t7), \ + XPP(t5, t6), \ + XMP(t5, t7), \ + t5 = IMULT(t5, IC4), \ + ROT(t4, t6, S22, C22), \ + t6 -= t7, \ + t5 -= t6, \ + t4 -= t5, \ + XPP(t0, t7), \ + XPP(t1, t6), \ + XPP(t2, t5), \ + XPP(t3, t4) \ +) + +static unsigned char zig2[64] = { + 0, 2, 3, 9, 10, 20, 21, 35, + 14, 16, 25, 31, 39, 46, 50, 57, + 5, 7, 12, 18, 23, 33, 37, 48, + 27, 29, 41, 44, 52, 55, 59, 62, + 15, 26, 30, 40, 45, 51, 56, 58, + 1, 4, 8, 11, 19, 22, 34, 36, + 28, 42, 43, 53, 54, 60, 61, 63, + 6, 13, 17, 24, 32, 38, 47, 49 +}; + +static void idct(int *in, int *out, PREC * quant, PREC off, int max) +{ + PREC t0, t1, t2, t3, t4, t5, t6, t7, t; + PREC tmp[64], *tmpp; + int i, j; + unsigned char *zig2p; + + t0 = off; + if (max == 1) { + t0 += in[0] * quant[0]; + for (i = 0; i < 64; i++) + out[i] = ITOINT(t0); + return; + } + zig2p = zig2; + tmpp = tmp; + for (i = 0; i < 8; i++) { + j = *zig2p++; + t0 += in[j] * quant[j]; + j = *zig2p++; + t5 = in[j] * quant[j]; + j = *zig2p++; + t2 = in[j] * quant[j]; + j = *zig2p++; + t7 = in[j] * quant[j]; + j = *zig2p++; + t1 = in[j] * quant[j]; + j = *zig2p++; + t4 = in[j] * quant[j]; + j = *zig2p++; + t3 = in[j] * quant[j]; + j = *zig2p++; + t6 = in[j] * quant[j]; + IDCT; + tmpp[0 * 8] = t0; + tmpp[1 * 8] = t1; + tmpp[2 * 8] = t2; + tmpp[3 * 8] = t3; + tmpp[4 * 8] = t4; + tmpp[5 * 8] = t5; + tmpp[6 * 8] = t6; + tmpp[7 * 8] = t7; + tmpp++; + t0 = 0; + } + for (i = 0; i < 8; i++) { + t0 = tmp[8 * i + 0]; + t1 = tmp[8 * i + 1]; + t2 = tmp[8 * i + 2]; + t3 = tmp[8 * i + 3]; + t4 = tmp[8 * i + 4]; + t5 = tmp[8 * i + 5]; + t6 = tmp[8 * i + 6]; + t7 = tmp[8 * i + 7]; + IDCT; + out[8 * i + 0] = ITOINT(t0); + out[8 * i + 1] = ITOINT(t1); + out[8 * i + 2] = ITOINT(t2); + out[8 * i + 3] = ITOINT(t3); + out[8 * i + 4] = ITOINT(t4); + out[8 * i + 5] = ITOINT(t5); + out[8 * i + 6] = ITOINT(t6); + out[8 * i + 7] = ITOINT(t7); + } +} + +static unsigned char zig[64] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +static PREC aaidct[8] = { + IFIX(0.3535533906), IFIX(0.4903926402), + IFIX(0.4619397663), IFIX(0.4157348062), + IFIX(0.3535533906), IFIX(0.2777851165), + IFIX(0.1913417162), IFIX(0.0975451610) +}; + + +static void idctqtab(unsigned char *qin, PREC * qout) +{ + int i, j; + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + qout[zig[i * 8 + j]] = qin[zig[i * 8 + j]] * + IMULT(aaidct[i], aaidct[j]); +} + +static void scaleidctqtab(PREC * q, PREC sc) +{ + int i; + + for (i = 0; i < 64; i++) + q[i] = IMULT(q[i], sc); +} + +/****************************************************************/ +/************** color decoder ***************/ +/****************************************************************/ + +#define ROUND + +/* + * YCbCr Color transformation: + * + * y:0..255 Cb:-128..127 Cr:-128..127 + * + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + * => + * Cr *= 1.40200; + * Cb *= 1.77200; + * Cg = 0.19421 * Cb + .50937 * Cr; + * R = Y + Cr; + * G = Y - Cg; + * B = Y + Cb; + * + * => + * Cg = (50 * Cb + 130 * Cr + 128) >> 8; + */ + +static void initcol(PREC q[][64]) +{ + scaleidctqtab(q[1], IFIX(1.77200)); + scaleidctqtab(q[2], IFIX(1.40200)); +} + +/* This is optimized for the stupid sun SUNWspro compiler. */ +#define STORECLAMP(a,x) \ +( \ + (a) = (x), \ + (unsigned int)(x) >= 256 ? \ + ((a) = (x) < 0 ? 0 : 255) \ + : \ + 0 \ +) + +#define CLAMP(x) ((unsigned int)(x) >= 256 ? ((x) < 0 ? 0 : 255) : (x)) + +#ifdef ROUND + +#define CBCRCG(yin, xin) \ +( \ + cb = outc[0 +yin*8+xin], \ + cr = outc[64+yin*8+xin], \ + cg = (50 * cb + 130 * cr + 128) >> 8 \ +) + +#else + +#define CBCRCG(yin, xin) \ +( \ + cb = outc[0 +yin*8+xin], \ + cr = outc[64+yin*8+xin], \ + cg = (3 * cb + 8 * cr) >> 4 \ +) + +#endif + +#ifdef __LITTLE_ENDIAN +#define PIC(yin, xin, p, xout) \ +( \ + y = outy[(yin) * 8 + xin], \ + STORECLAMP(p[(xout) * 3 + 2], y + cr), \ + STORECLAMP(p[(xout) * 3 + 1], y - cg), \ + STORECLAMP(p[(xout) * 3 + 0], y + cb) \ +) +#else +#define PIC(yin, xin, p, xout) \ +( \ + y = outy[(yin) * 8 + xin], \ + STORECLAMP(p[(xout) * 3 + 0], y + cr), \ + STORECLAMP(p[(xout) * 3 + 1], y - cg), \ + STORECLAMP(p[(xout) * 3 + 2], y + cb) \ +) +#endif + +#ifdef __LITTLE_ENDIAN +#define PIC_16(yin, xin, p, xout, add) \ +( \ + y = outy[(yin) * 8 + xin], \ + y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 8) | \ + ((CLAMP(y - cg + add ) & 0xfc) << 3) | \ + ((CLAMP(y + cb + add*2+1) ) >> 3), \ + p[(xout) * 2 + 0] = y & 0xff, \ + p[(xout) * 2 + 1] = y >> 8 \ +) +#else +#ifdef CONFIG_PPC +#define PIC_16(yin, xin, p, xout, add) \ +( \ + y = outy[(yin) * 8 + xin], \ + y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 7) | \ + ((CLAMP(y - cg + add*2+1) & 0xf8) << 2) | \ + ((CLAMP(y + cb + add*2+1) ) >> 3), \ + p[(xout) * 2 + 0] = y >> 8, \ + p[(xout) * 2 + 1] = y & 0xff \ +) +#else +#define PIC_16(yin, xin, p, xout, add) \ +( \ + y = outy[(yin) * 8 + xin], \ + y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 8) | \ + ((CLAMP(y - cg + add ) & 0xfc) << 3) | \ + ((CLAMP(y + cb + add*2+1) ) >> 3), \ + p[(xout) * 2 + 0] = y >> 8, \ + p[(xout) * 2 + 1] = y & 0xff \ +) +#endif +#endif + +#define PIC_32(yin, xin, p, xout) \ +( \ + y = outy[(yin) * 8 + xin], \ + STORECLAMP(p[(xout) * 4 + 0], y + cr), \ + STORECLAMP(p[(xout) * 4 + 1], y - cg), \ + STORECLAMP(p[(xout) * 4 + 2], y + cb), \ + p[(xout) * 4 + 3] = 0 \ +) + +#define PIC221111(xin) \ +( \ + CBCRCG(0, xin), \ + PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0), \ + PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1), \ + PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0), \ + PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1) \ +) + +#define PIC221111_16(xin) \ +( \ + CBCRCG(0, xin), \ + PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0, 3), \ + PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1, 0), \ + PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0, 1), \ + PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1, 2) \ +) + +#define PIC221111_32(xin) \ +( \ + CBCRCG(0, xin), \ + PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0), \ + PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1), \ + PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0), \ + PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1) \ +) + +static void col221111(int *out, unsigned char *pic, int width) +{ + int i, j, k; + unsigned char *pic0, *pic1; + int *outy, *outc; + int cr, cg, cb, y; + + pic0 = pic; + pic1 = pic + width; + outy = out; + outc = out + 64 * 4; + for (i = 2; i > 0; i--) { + for (j = 4; j > 0; j--) { + for (k = 0; k < 8; k++) { + PIC221111(k); + } + outc += 8; + outy += 16; + pic0 += 2 * width; + pic1 += 2 * width; + } + outy += 64 * 2 - 16 * 4; + } +} + +static void col221111_16(int *out, unsigned char *pic, int width) +{ + int i, j, k; + unsigned char *pic0, *pic1; + int *outy, *outc; + int cr, cg, cb, y; + + pic0 = pic; + pic1 = pic + width; + outy = out; + outc = out + 64 * 4; + for (i = 2; i > 0; i--) { + for (j = 4; j > 0; j--) { + for (k = 0; k < 8; k++) { + PIC221111_16(k); + } + outc += 8; + outy += 16; + pic0 += 2 * width; + pic1 += 2 * width; + } + outy += 64 * 2 - 16 * 4; + } +} + +static void col221111_32(int *out, unsigned char *pic, int width) +{ + int i, j, k; + unsigned char *pic0, *pic1; + int *outy, *outc; + int cr, cg, cb, y; + + pic0 = pic; + pic1 = pic + width; + outy = out; + outc = out + 64 * 4; + for (i = 2; i > 0; i--) { + for (j = 4; j > 0; j--) { + for (k = 0; k < 8; k++) { + PIC221111_32(k); + } + outc += 8; + outy += 16; + pic0 += 2 * width; + pic1 += 2 * width; + } + outy += 64 * 2 - 16 * 4; + } +} diff --git a/qemu/roms/seabios/src/kbd.c b/qemu/roms/seabios/src/kbd.c new file mode 100644 index 000000000..a5a1ad9a5 --- /dev/null +++ b/qemu/roms/seabios/src/kbd.c @@ -0,0 +1,576 @@ +// 16bit code to handle keyboard requests. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "hw/ps2port.h" // ps2_kbd_command +#include "hw/usb-hid.h" // usb_kbd_command +#include "output.h" // debug_enter +#include "stacks.h" // yield +#include "string.h" // memset +#include "util.h" // kbd_init + +// Bit definitions for BDA kbd_flag[012] +#define KF0_RSHIFT (1<<0) +#define KF0_LSHIFT (1<<1) +#define KF0_CTRLACTIVE (1<<2) +#define KF0_ALTACTIVE (1<<3) +#define KF0_SCROLLACTIVE (1<<4) +#define KF0_NUMACTIVE (1<<5) +#define KF0_CAPSACTIVE (1<<6) + +#define KF1_LCTRL (1<<0) +#define KF1_LALT (1<<1) +#define KF1_PAUSEACTIVE (1<<3) +#define KF1_SCROLL (1<<4) +#define KF1_NUM (1<<5) +#define KF1_CAPS (1<<6) + +#define KF2_LAST_E1 (1<<0) +#define KF2_LAST_E0 (1<<1) +#define KF2_RCTRL (1<<2) +#define KF2_RALT (1<<3) +#define KF2_101KBD (1<<4) + +void +kbd_init(void) +{ + dprintf(3, "init keyboard\n"); + u16 x = offsetof(struct bios_data_area_s, kbd_buf); + SET_BDA(kbd_flag2, KF2_101KBD); + SET_BDA(kbd_buf_head, x); + SET_BDA(kbd_buf_tail, x); + SET_BDA(kbd_buf_start_offset, x); + + SET_BDA(kbd_buf_end_offset + , x + FIELD_SIZEOF(struct bios_data_area_s, kbd_buf)); +} + +static u8 +enqueue_key(u8 scan_code, u8 ascii_code) +{ + u16 buffer_start = GET_BDA(kbd_buf_start_offset); + u16 buffer_end = GET_BDA(kbd_buf_end_offset); + + u16 buffer_head = GET_BDA(kbd_buf_head); + u16 buffer_tail = GET_BDA(kbd_buf_tail); + + u16 temp_tail = buffer_tail; + buffer_tail += 2; + if (buffer_tail >= buffer_end) + buffer_tail = buffer_start; + + if (buffer_tail == buffer_head) + return 0; + + SET_FARVAR(SEG_BDA, *(u8*)(temp_tail+0), ascii_code); + SET_FARVAR(SEG_BDA, *(u8*)(temp_tail+1), scan_code); + SET_BDA(kbd_buf_tail, buffer_tail); + return 1; +} + +static void +dequeue_key(struct bregs *regs, int incr, int extended) +{ + yield(); + u16 buffer_head; + u16 buffer_tail; + for (;;) { + buffer_head = GET_BDA(kbd_buf_head); + buffer_tail = GET_BDA(kbd_buf_tail); + + if (buffer_head != buffer_tail) + break; + if (!incr) { + regs->flags |= F_ZF; + return; + } + yield_toirq(); + } + + u8 ascii_code = GET_FARVAR(SEG_BDA, *(u8*)(buffer_head+0)); + u8 scan_code = GET_FARVAR(SEG_BDA, *(u8*)(buffer_head+1)); + if ((ascii_code == 0xF0 && scan_code != 0) + || (ascii_code == 0xE0 && !extended)) + ascii_code = 0; + regs->ax = (scan_code << 8) | ascii_code; + + if (!incr) { + regs->flags &= ~F_ZF; + return; + } + u16 buffer_start = GET_BDA(kbd_buf_start_offset); + u16 buffer_end = GET_BDA(kbd_buf_end_offset); + + buffer_head += 2; + if (buffer_head >= buffer_end) + buffer_head = buffer_start; + SET_BDA(kbd_buf_head, buffer_head); +} + +static int +kbd_command(int command, u8 *param) +{ + if (usb_kbd_active()) + return usb_kbd_command(command, param); + return ps2_kbd_command(command, param); +} + +// read keyboard input +static void +handle_1600(struct bregs *regs) +{ + dequeue_key(regs, 1, 0); +} + +// check keyboard status +static void +handle_1601(struct bregs *regs) +{ + dequeue_key(regs, 0, 0); +} + +// get shift flag status +static void +handle_1602(struct bregs *regs) +{ + yield(); + regs->al = GET_BDA(kbd_flag0); +} + +// store key-stroke into buffer +static void +handle_1605(struct bregs *regs) +{ + regs->al = !enqueue_key(regs->ch, regs->cl); +} + +// GET KEYBOARD FUNCTIONALITY +static void +handle_1609(struct bregs *regs) +{ + // bit Bochs Description + // 7 0 reserved + // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support) + // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support) + // 4 1 INT 16/AH=0Ah supported + // 3 0 INT 16/AX=0306h supported + // 2 0 INT 16/AX=0305h supported + // 1 0 INT 16/AX=0304h supported + // 0 0 INT 16/AX=0300h supported + // + regs->al = 0x30; +} + +// GET KEYBOARD ID +static void noinline +handle_160a(struct bregs *regs) +{ + u8 param[2]; + int ret = kbd_command(ATKBD_CMD_GETID, param); + if (ret) { + regs->bx = 0; + return; + } + regs->bx = (param[1] << 8) | param[0]; +} + +// read MF-II keyboard input +static void +handle_1610(struct bregs *regs) +{ + dequeue_key(regs, 1, 1); +} + +// check MF-II keyboard status +static void +handle_1611(struct bregs *regs) +{ + dequeue_key(regs, 0, 1); +} + +// get extended keyboard status +static void +handle_1612(struct bregs *regs) +{ + yield(); + regs->al = GET_BDA(kbd_flag0); + regs->ah = ((GET_BDA(kbd_flag1) & ~(KF2_RCTRL|KF2_RALT)) + | (GET_BDA(kbd_flag2) & (KF2_RCTRL|KF2_RALT))); + //BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX); +} + +static void +handle_166f(struct bregs *regs) +{ + if (regs->al == 0x08) + // unsupported, aka normal keyboard + regs->ah = 2; +} + +// keyboard capability check called by DOS 5.0+ keyb +static void +handle_1692(struct bregs *regs) +{ + // function int16 ah=0x10-0x12 supported + regs->ah = 0x80; +} + +// 122 keys capability check called by DOS 5.0+ keyb +static void +handle_16a2(struct bregs *regs) +{ + // don't change AH : function int16 ah=0x20-0x22 NOT supported +} + +static void +handle_16XX(struct bregs *regs) +{ + warn_unimplemented(regs); +} + +static void noinline +set_leds(void) +{ + u8 shift_flags = (GET_BDA(kbd_flag0) >> 4) & 0x07; + u8 kbd_led = GET_BDA(kbd_led); + u8 led_flags = kbd_led & 0x07; + if (shift_flags == led_flags) + return; + + int ret = kbd_command(ATKBD_CMD_SETLEDS, &shift_flags); + if (ret) + // Error + return; + kbd_led = (kbd_led & ~0x07) | shift_flags; + SET_BDA(kbd_led, kbd_led); +} + +// INT 16h Keyboard Service Entry Point +void VISIBLE16 +handle_16(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_16); + if (! CONFIG_KEYBOARD) + return; + + // XXX - set_leds should be called from irq handler + set_leds(); + + switch (regs->ah) { + case 0x00: handle_1600(regs); break; + case 0x01: handle_1601(regs); break; + case 0x02: handle_1602(regs); break; + case 0x05: handle_1605(regs); break; + case 0x09: handle_1609(regs); break; + case 0x0a: handle_160a(regs); break; + case 0x10: handle_1610(regs); break; + case 0x11: handle_1611(regs); break; + case 0x12: handle_1612(regs); break; + case 0x92: handle_1692(regs); break; + case 0xa2: handle_16a2(regs); break; + case 0x6f: handle_166f(regs); break; + default: handle_16XX(regs); break; + } +} + +#define none 0 +#define MNUM KF0_NUMACTIVE +#define MCAP KF0_CAPSACTIVE + +static struct scaninfo { + u16 normal; + u16 shift; + u16 control; + u16 alt; + u8 lock_flags; +} scan_to_scanascii[] VAR16 = { + { none, none, none, none, none }, + { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */ + { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */ + { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */ + { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */ + { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */ + { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */ + { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */ + { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */ + { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */ + { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */ + { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */ + { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */ + { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */ + { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */ + { 0x0f09, 0x0f00, none, none, none }, /* tab */ + { 0x1071, 0x1051, 0x1011, 0x1000, MCAP }, /* Q */ + { 0x1177, 0x1157, 0x1117, 0x1100, MCAP }, /* W */ + { 0x1265, 0x1245, 0x1205, 0x1200, MCAP }, /* E */ + { 0x1372, 0x1352, 0x1312, 0x1300, MCAP }, /* R */ + { 0x1474, 0x1454, 0x1414, 0x1400, MCAP }, /* T */ + { 0x1579, 0x1559, 0x1519, 0x1500, MCAP }, /* Y */ + { 0x1675, 0x1655, 0x1615, 0x1600, MCAP }, /* U */ + { 0x1769, 0x1749, 0x1709, 0x1700, MCAP }, /* I */ + { 0x186f, 0x184f, 0x180f, 0x1800, MCAP }, /* O */ + { 0x1970, 0x1950, 0x1910, 0x1900, MCAP }, /* P */ + { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */ + { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */ + { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */ + { none, none, none, none, none }, /* L Ctrl */ + { 0x1e61, 0x1e41, 0x1e01, 0x1e00, MCAP }, /* A */ + { 0x1f73, 0x1f53, 0x1f13, 0x1f00, MCAP }, /* S */ + { 0x2064, 0x2044, 0x2004, 0x2000, MCAP }, /* D */ + { 0x2166, 0x2146, 0x2106, 0x2100, MCAP }, /* F */ + { 0x2267, 0x2247, 0x2207, 0x2200, MCAP }, /* G */ + { 0x2368, 0x2348, 0x2308, 0x2300, MCAP }, /* H */ + { 0x246a, 0x244a, 0x240a, 0x2400, MCAP }, /* J */ + { 0x256b, 0x254b, 0x250b, 0x2500, MCAP }, /* K */ + { 0x266c, 0x264c, 0x260c, 0x2600, MCAP }, /* L */ + { 0x273b, 0x273a, none, none, none }, /* ;: */ + { 0x2827, 0x2822, none, none, none }, /* '" */ + { 0x2960, 0x297e, none, none, none }, /* `~ */ + { none, none, none, none, none }, /* L shift */ + { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */ + { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, MCAP }, /* Z */ + { 0x2d78, 0x2d58, 0x2d18, 0x2d00, MCAP }, /* X */ + { 0x2e63, 0x2e43, 0x2e03, 0x2e00, MCAP }, /* C */ + { 0x2f76, 0x2f56, 0x2f16, 0x2f00, MCAP }, /* V */ + { 0x3062, 0x3042, 0x3002, 0x3000, MCAP }, /* B */ + { 0x316e, 0x314e, 0x310e, 0x3100, MCAP }, /* N */ + { 0x326d, 0x324d, 0x320d, 0x3200, MCAP }, /* M */ + { 0x332c, 0x333c, none, none, none }, /* ,< */ + { 0x342e, 0x343e, none, none, none }, /* .> */ + { 0x352f, 0x353f, none, none, none }, /* /? */ + { none, none, none, none, none }, /* R Shift */ + { 0x372a, 0x372a, none, none, none }, /* * */ + { none, none, none, none, none }, /* L Alt */ + { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */ + { none, none, none, none, none }, /* caps lock */ + { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */ + { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */ + { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */ + { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */ + { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */ + { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */ + { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */ + { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */ + { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */ + { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */ + { none, none, none, none, none }, /* Num Lock */ + { none, none, none, none, none }, /* Scroll Lock */ + { 0x4700, 0x4737, 0x7700, none, MNUM }, /* 7 Home */ + { 0x4800, 0x4838, none, none, MNUM }, /* 8 UP */ + { 0x4900, 0x4939, 0x8400, none, MNUM }, /* 9 PgUp */ + { 0x4a2d, 0x4a2d, none, none, none }, /* - */ + { 0x4b00, 0x4b34, 0x7300, none, MNUM }, /* 4 Left */ + { 0x4c00, 0x4c35, none, none, MNUM }, /* 5 */ + { 0x4d00, 0x4d36, 0x7400, none, MNUM }, /* 6 Right */ + { 0x4e2b, 0x4e2b, none, none, none }, /* + */ + { 0x4f00, 0x4f31, 0x7500, none, MNUM }, /* 1 End */ + { 0x5000, 0x5032, none, none, MNUM }, /* 2 Down */ + { 0x5100, 0x5133, 0x7600, none, MNUM }, /* 3 PgDn */ + { 0x5200, 0x5230, none, none, MNUM }, /* 0 Ins */ + { 0x5300, 0x532e, none, none, MNUM }, /* Del */ + { none, none, none, none, none }, + { none, none, none, none, none }, + { 0x565c, 0x567c, none, none, none }, /* \| */ + { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */ + { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */ +}; + +// Handle a ps2 style scancode read from the keyboard. +static void +__process_key(u8 scancode) +{ + u8 flags0 = GET_BDA(kbd_flag0); + u8 flags1 = GET_BDA(kbd_flag1); + u8 flags2 = GET_BDA(kbd_flag2); + + if (flags2 & KF2_LAST_E1) { + // Part of "pause" key (sequence is e1 1d 45 e1 9d c5) + if ((scancode & ~0x80) == 0x1d) + // Second key of sequence + return; + // Third key of sequence - clear flag. + flags2 &= ~KF2_LAST_E1; + SET_BDA(kbd_flag2, flags2); + + if (scancode == 0xc5) { + // Final key in sequence. + + // XXX - do actual pause. + } + return; + } + + // XXX - PrtScr should cause int 0x05 + // XXX - Ctrl+Break should cause int 0x1B + // XXX - SysReq should cause int 0x15/0x85 + + switch (scancode) { + case 0x00: + dprintf(1, "KBD: int09 handler: AL=0\n"); + return; + + case 0x3a: /* Caps Lock press */ + flags0 ^= KF0_CAPSACTIVE; + flags1 |= KF1_CAPS; + break; + case 0xba: /* Caps Lock release */ + flags1 &= ~KF1_CAPS; + break; + + case 0x2a: /* L Shift press */ + flags0 |= KF0_LSHIFT; + break; + case 0xaa: /* L Shift release */ + flags0 &= ~KF0_LSHIFT; + break; + + case 0x36: /* R Shift press */ + flags0 |= KF0_RSHIFT; + break; + case 0xb6: /* R Shift release */ + flags0 &= ~KF0_RSHIFT; + break; + + case 0x1d: /* Ctrl press */ + flags0 |= KF0_CTRLACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 |= KF2_RCTRL; + else + flags1 |= KF1_LCTRL; + break; + case 0x9d: /* Ctrl release */ + flags0 &= ~KF0_CTRLACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 &= ~KF2_RCTRL; + else + flags1 &= ~KF1_LCTRL; + break; + + case 0x38: /* Alt press */ + flags0 |= KF0_ALTACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 |= KF2_RALT; + else + flags1 |= KF1_LALT; + break; + case 0xb8: /* Alt release */ + flags0 &= ~KF0_ALTACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 &= ~KF2_RALT; + else + flags1 &= ~KF1_LALT; + break; + + case 0x45: /* Num Lock press */ + flags1 |= KF1_NUM; + flags0 ^= KF0_NUMACTIVE; + break; + case 0xc5: /* Num Lock release */ + flags1 &= ~KF1_NUM; + break; + + case 0x46: /* Scroll Lock press */ + flags1 |= KF1_SCROLL; + flags0 ^= KF0_SCROLLACTIVE; + break; + case 0xc6: /* Scroll Lock release */ + flags1 &= ~KF1_SCROLL; + break; + + case 0xe0: + // Extended key + flags2 |= KF2_LAST_E0; + SET_BDA(kbd_flag2, flags2); + return; + case 0xe1: + // Start of pause key sequence + flags2 |= KF2_LAST_E1; + break; + + default: + if (scancode & 0x80) + // toss key releases + break; + if (scancode == 0x53 + && ((flags0 & (KF0_CTRLACTIVE|KF0_ALTACTIVE)) + == (KF0_CTRLACTIVE|KF0_ALTACTIVE))) { + // Ctrl+alt+del - reset machine. + SET_BDA(soft_reset_flag, 0x1234); + reset(); + } + if (scancode >= ARRAY_SIZE(scan_to_scanascii)) { + dprintf(1, "KBD: int09h_handler(): unknown scancode read: 0x%02x!\n" + , scancode); + return; + } + u8 asciicode; + struct scaninfo *info = &scan_to_scanascii[scancode]; + if (flags0 & KF0_ALTACTIVE) { + asciicode = GET_GLOBAL(info->alt); + scancode = GET_GLOBAL(info->alt) >> 8; + } else if (flags0 & KF0_CTRLACTIVE) { + asciicode = GET_GLOBAL(info->control); + scancode = GET_GLOBAL(info->control) >> 8; + } else if (flags2 & KF2_LAST_E0 + && scancode >= 0x47 && scancode <= 0x53) { + /* extended keys handling */ + asciicode = 0xe0; + scancode = GET_GLOBAL(info->normal) >> 8; + } else if (flags0 & (KF0_RSHIFT|KF0_LSHIFT)) { + /* check if lock state should be ignored because a SHIFT + * key is pressed */ + + if (flags0 & GET_GLOBAL(info->lock_flags)) { + asciicode = GET_GLOBAL(info->normal); + scancode = GET_GLOBAL(info->normal) >> 8; + } else { + asciicode = GET_GLOBAL(info->shift); + scancode = GET_GLOBAL(info->shift) >> 8; + } + } else { + /* check if lock is on */ + if (flags0 & GET_GLOBAL(info->lock_flags)) { + asciicode = GET_GLOBAL(info->shift); + scancode = GET_GLOBAL(info->shift) >> 8; + } else { + asciicode = GET_GLOBAL(info->normal); + scancode = GET_GLOBAL(info->normal) >> 8; + } + } + if (scancode==0 && asciicode==0) + dprintf(1, "KBD: scancode & asciicode are zero?\n"); + enqueue_key(scancode, asciicode); + break; + } + flags2 &= ~KF2_LAST_E0; + + SET_BDA(kbd_flag0, flags0); + SET_BDA(kbd_flag1, flags1); + SET_BDA(kbd_flag2, flags2); +} + +void +process_key(u8 key) +{ + if (!CONFIG_KEYBOARD) + return; + + if (CONFIG_KBD_CALL_INT15_4F) { + // allow for keyboard intercept + struct bregs br; + memset(&br, 0, sizeof(br)); + br.eax = (0x4f << 8) | key; + br.flags = F_IF|F_CF; + call16_int(0x15, &br); + if (!(br.flags & F_CF)) + return; + key = br.eax; + } + __process_key(key); +} diff --git a/qemu/roms/seabios/src/list.h b/qemu/roms/seabios/src/list.h new file mode 100644 index 000000000..de656b9d6 --- /dev/null +++ b/qemu/roms/seabios/src/list.h @@ -0,0 +1,81 @@ +#ifndef __LIST_H +#define __LIST_H + +#include "types.h" // container_of + + +/**************************************************************** + * hlist - Double linked lists with a single pointer list head + ****************************************************************/ + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +static inline int +hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void +hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void +hlist_add(struct hlist_node *n, struct hlist_node **pprev) +{ + struct hlist_node *next = *pprev; + n->pprev = pprev; + n->next = next; + if (next) + next->pprev = &n->next; + *pprev = n; +} + +static inline void +hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + hlist_add(n, &h->first); +} + +static inline void +hlist_add_before(struct hlist_node *n, struct hlist_node *next) +{ + hlist_add(n, next->pprev); +} + +static inline void +hlist_add_after(struct hlist_node *n, struct hlist_node *prev) +{ + hlist_add(n, &prev->next); +} + +#define hlist_for_each_entry(pos, head, member) \ + for (pos = container_of((head)->first, typeof(*pos), member) \ + ; pos != container_of(NULL, typeof(*pos), member) \ + ; pos = container_of(pos->member.next, typeof(*pos), member)) + +#define hlist_for_each_entry_safe(pos, n, head, member) \ + for (pos = container_of((head)->first, typeof(*pos), member) \ + ; pos != container_of(NULL, typeof(*pos), member) \ + && ({ n = pos->member.next; 1; }) \ + ; pos = container_of(n, typeof(*pos), member)) + +#define hlist_for_each_entry_pprev(pos, pprev, head, member) \ + for (pprev = &(head)->first \ + ; *pprev && ({ pos=container_of(*pprev, typeof(*pos), member); 1; }) \ + ; pprev = &(*pprev)->next) + + +#endif // list.h diff --git a/qemu/roms/seabios/src/malloc.c b/qemu/roms/seabios/src/malloc.c new file mode 100644 index 000000000..c4cb17149 --- /dev/null +++ b/qemu/roms/seabios/src/malloc.c @@ -0,0 +1,541 @@ +// Internal dynamic memory allocations. +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "config.h" // BUILD_BIOS_ADDR +#include "list.h" // hlist_node +#include "malloc.h" // _malloc +#include "memmap.h" // struct e820entry +#include "output.h" // dprintf +#include "stacks.h" // wait_preempt +#include "std/optionrom.h" // OPTION_ROM_ALIGN +#include "string.h" // memset + +// Information on a reserved area. +struct allocinfo_s { + struct hlist_node node; + void *data, *dataend, *allocend; +}; + +// Information on a tracked memory allocation. +struct allocdetail_s { + struct allocinfo_s detailinfo; + struct allocinfo_s datainfo; + u32 handle; +}; + +// The various memory zones. +struct zone_s { + struct hlist_head head; +}; + +struct zone_s ZoneLow VARVERIFY32INIT, ZoneHigh VARVERIFY32INIT; +struct zone_s ZoneFSeg VARVERIFY32INIT; +struct zone_s ZoneTmpLow VARVERIFY32INIT, ZoneTmpHigh VARVERIFY32INIT; + +static struct zone_s *Zones[] VARVERIFY32INIT = { + &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh +}; + + +/**************************************************************** + * low-level memory reservations + ****************************************************************/ + +// Find and reserve space from a given zone +static void * +allocSpace(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill) +{ + struct allocinfo_s *info; + hlist_for_each_entry(info, &zone->head, node) { + void *dataend = info->dataend; + void *allocend = info->allocend; + void *newallocend = (void*)ALIGN_DOWN((u32)allocend - size, align); + if (newallocend >= dataend && newallocend <= allocend) { + // Found space - now reserve it. + if (!fill) + fill = newallocend; + fill->data = newallocend; + fill->dataend = newallocend + size; + fill->allocend = allocend; + + info->allocend = newallocend; + hlist_add_before(&fill->node, &info->node); + return newallocend; + } + } + return NULL; +} + +// Release space allocated with allocSpace() +static void +freeSpace(struct allocinfo_s *info) +{ + struct allocinfo_s *next = container_of_or_null( + info->node.next, struct allocinfo_s, node); + if (next && next->allocend == info->data) + next->allocend = info->allocend; + hlist_del(&info->node); +} + +// Add new memory to a zone +static void +addSpace(struct zone_s *zone, void *start, void *end) +{ + // Find position to add space + struct allocinfo_s *info; + struct hlist_node **pprev; + hlist_for_each_entry_pprev(info, pprev, &zone->head, node) { + if (info->data < start) + break; + } + + // Add space using temporary allocation info. + struct allocdetail_s tempdetail; + tempdetail.datainfo.data = tempdetail.datainfo.dataend = start; + tempdetail.datainfo.allocend = end; + hlist_add(&tempdetail.datainfo.node, pprev); + + // Allocate final allocation info. + struct allocdetail_s *detail = allocSpace( + &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL); + if (!detail) { + detail = allocSpace(&ZoneTmpLow, sizeof(*detail) + , MALLOC_MIN_ALIGN, NULL); + if (!detail) { + hlist_del(&tempdetail.datainfo.node); + warn_noalloc(); + return; + } + } + + // Replace temp alloc space with final alloc space + pprev = tempdetail.datainfo.node.pprev; + hlist_del(&tempdetail.datainfo.node); + memcpy(&detail->datainfo, &tempdetail.datainfo, sizeof(detail->datainfo)); + detail->handle = MALLOC_DEFAULT_HANDLE; + hlist_add(&detail->datainfo.node, pprev); +} + +// Search all zones for an allocation obtained from allocSpace() +static struct allocinfo_s * +findAlloc(void *data) +{ + int i; + for (i=0; i<ARRAY_SIZE(Zones); i++) { + struct allocinfo_s *info; + hlist_for_each_entry(info, &Zones[i]->head, node) { + if (info->data == data) + return info; + } + } + return NULL; +} + +// Return the last sentinal node of a zone +static struct allocinfo_s * +findLast(struct zone_s *zone) +{ + struct allocinfo_s *info, *last = NULL; + hlist_for_each_entry(info, &zone->head, node) { + last = info; + } + return last; +} + + +/**************************************************************** + * ebda movement + ****************************************************************/ + +// Move ebda +static int +relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size) +{ + u32 lowram = GET_BDA(mem_size_kb) * 1024; + if (oldebda != lowram) + // EBDA isn't at end of ram - give up. + return -1; + + // Do copy + memmove((void*)newebda, (void*)oldebda, ebda_size * 1024); + + // Update indexes + dprintf(1, "ebda moved from %x to %x\n", oldebda, newebda); + SET_BDA(mem_size_kb, newebda / 1024); + SET_BDA(ebda_seg, FLATPTR_TO_SEG(newebda)); + return 0; +} + +// Support expanding the ZoneLow dynamically. +static void * +zonelow_expand(u32 size, u32 align, struct allocinfo_s *fill) +{ + // Make sure to not move ebda while an optionrom is running. + if (unlikely(wait_preempt())) { + void *data = allocSpace(&ZoneLow, size, align, fill); + if (data) + return data; + } + + struct allocinfo_s *info = findLast(&ZoneLow); + if (!info) + return NULL; + u32 oldpos = (u32)info->allocend; + u32 newpos = ALIGN_DOWN(oldpos - size, align); + u32 bottom = (u32)info->dataend; + if (newpos >= bottom && newpos <= oldpos) + // Space already present. + return allocSpace(&ZoneLow, size, align, fill); + u16 ebda_seg = get_ebda_seg(); + u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0); + u8 ebda_size = GET_EBDA(ebda_seg, size); + u32 ebda_end = ebda_pos + ebda_size * 1024; + if (ebda_end != bottom) + // Something else is after ebda - can't use any existing space. + newpos = ALIGN_DOWN(ebda_end - size, align); + u32 newbottom = ALIGN_DOWN(newpos, 1024); + u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024); + if (newebda < BUILD_EBDA_MINIMUM) + // Not enough space. + return NULL; + + // Move ebda + int ret = relocate_ebda(newebda, ebda_pos, ebda_size); + if (ret) + return NULL; + + // Update zone + if (ebda_end == bottom) { + info->data = (void*)newbottom; + info->dataend = (void*)newbottom; + } else + addSpace(&ZoneLow, (void*)newbottom, (void*)ebda_end); + + return allocSpace(&ZoneLow, size, align, fill); +} + + +/**************************************************************** + * tracked memory allocations + ****************************************************************/ + +// Allocate memory from the given zone and track it as a PMM allocation +void * __malloc +_malloc(struct zone_s *zone, u32 size, u32 align) +{ + ASSERT32FLAT(); + if (!size) + return NULL; + + // Find and reserve space for bookkeeping. + struct allocdetail_s *detail = allocSpace( + &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL); + if (!detail) { + detail = allocSpace(&ZoneTmpLow, sizeof(*detail) + , MALLOC_MIN_ALIGN, NULL); + if (!detail) + return NULL; + } + detail->handle = MALLOC_DEFAULT_HANDLE; + + // Find and reserve space for main allocation + void *data = allocSpace(zone, size, align, &detail->datainfo); + if (!CONFIG_MALLOC_UPPERMEMORY && !data && zone == &ZoneLow) + data = zonelow_expand(size, align, &detail->datainfo); + if (!data) { + freeSpace(&detail->detailinfo); + return NULL; + } + + dprintf(8, "_malloc zone=%p size=%d align=%x ret=%p (detail=%p)\n" + , zone, size, align, data, detail); + + return data; +} + +// Free a data block allocated with _malloc +int +_free(void *data) +{ + ASSERT32FLAT(); + struct allocinfo_s *info = findAlloc(data); + if (!info || data == (void*)info || data == info->dataend) + return -1; + struct allocdetail_s *detail = container_of( + info, struct allocdetail_s, datainfo); + dprintf(8, "_free %p (detail=%p)\n", data, detail); + freeSpace(info); + freeSpace(&detail->detailinfo); + return 0; +} + +// Find the amount of free space in a given zone. +u32 +malloc_getspace(struct zone_s *zone) +{ + // XXX - doesn't account for ZoneLow being able to grow. + // XXX - results not reliable when CONFIG_THREAD_OPTIONROMS + u32 maxspace = 0; + struct allocinfo_s *info; + hlist_for_each_entry(info, &zone->head, node) { + u32 space = info->allocend - info->dataend; + if (space > maxspace) + maxspace = space; + } + + if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow) + return maxspace; + // Account for space needed for PMM tracking. + u32 reserve = ALIGN(sizeof(struct allocdetail_s), MALLOC_MIN_ALIGN); + if (maxspace <= reserve) + return 0; + return maxspace - reserve; +} + +// Set a handle associated with an allocation. +void +malloc_sethandle(void *data, u32 handle) +{ + ASSERT32FLAT(); + struct allocinfo_s *info = findAlloc(data); + if (!info || data == (void*)info || data == info->dataend) + return; + struct allocdetail_s *detail = container_of( + info, struct allocdetail_s, datainfo); + detail->handle = handle; +} + +// Find the data block allocated with _malloc with a given handle. +void * +malloc_findhandle(u32 handle) +{ + int i; + for (i=0; i<ARRAY_SIZE(Zones); i++) { + struct allocinfo_s *info; + hlist_for_each_entry(info, &Zones[i]->head, node) { + if (info->data != (void*)info) + continue; + struct allocdetail_s *detail = container_of( + info, struct allocdetail_s, detailinfo); + if (detail->handle == handle) + return detail->datainfo.data; + } + } + return NULL; +} + + +/**************************************************************** + * 0xc0000-0xf0000 management + ****************************************************************/ + +static u32 RomEnd = BUILD_ROM_START; +static struct allocinfo_s *RomBase; + +#define OPROM_HEADER_RESERVE 16 + +// Return the maximum memory position option roms may use. +u32 +rom_get_max(void) +{ + if (CONFIG_MALLOC_UPPERMEMORY) + return ALIGN_DOWN((u32)RomBase->allocend - OPROM_HEADER_RESERVE + , OPTION_ROM_ALIGN); + extern u8 final_readonly_start[]; + return (u32)final_readonly_start; +} + +// Return the end of the last deployed option rom. +u32 +rom_get_last(void) +{ + return RomEnd; +} + +// Request space for an optionrom in 0xc0000-0xf0000 area. +struct rom_header * +rom_reserve(u32 size) +{ + u32 newend = ALIGN(RomEnd + size, OPTION_ROM_ALIGN); + if (newend > rom_get_max()) + return NULL; + if (CONFIG_MALLOC_UPPERMEMORY) { + if (newend < (u32)zonelow_base) + newend = (u32)zonelow_base; + RomBase->data = RomBase->dataend = (void*)newend + OPROM_HEADER_RESERVE; + } + return (void*)RomEnd; +} + +// Confirm space as in use by an optionrom. +int +rom_confirm(u32 size) +{ + void *new = rom_reserve(size); + if (!new) { + warn_noalloc(); + return -1; + } + RomEnd = ALIGN(RomEnd + size, OPTION_ROM_ALIGN); + return 0; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +void +malloc_preinit(void) +{ + ASSERT32FLAT(); + dprintf(3, "malloc preinit\n"); + + // Don't declare any memory between 0xa0000 and 0x100000 + add_e820(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END, E820_HOLE); + + // Mark known areas as reserved. + add_e820(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED); + + // Populate temp high ram + u32 highram = 0; + int i; + for (i=e820_count-1; i>=0; i--) { + struct e820entry *en = &e820_list[i]; + u64 end = en->start + en->size; + if (end < 1024*1024) + break; + if (en->type != E820_RAM || end > 0xffffffff) + continue; + u32 s = en->start, e = end; + if (!highram) { + u32 newe = ALIGN_DOWN(e - BUILD_MAX_HIGHTABLE, MALLOC_MIN_ALIGN); + if (newe <= e && newe >= s) { + highram = newe; + e = newe; + } + } + addSpace(&ZoneTmpHigh, (void*)s, (void*)e); + } + + // Populate regions + addSpace(&ZoneTmpLow, (void*)BUILD_STACK_ADDR, (void*)BUILD_EBDA_MINIMUM); + if (highram) { + addSpace(&ZoneHigh, (void*)highram + , (void*)highram + BUILD_MAX_HIGHTABLE); + add_e820(highram, BUILD_MAX_HIGHTABLE, E820_RESERVED); + } +} + +void +csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size) +{ + ASSERT32FLAT(); + + if (hi_pmm_size > BUILD_MAX_HIGHTABLE) { + void *hi_pmm_end = (void *)hi_pmm + hi_pmm_size; + addSpace(&ZoneTmpHigh, (void *)hi_pmm, hi_pmm_end - BUILD_MAX_HIGHTABLE); + addSpace(&ZoneHigh, hi_pmm_end - BUILD_MAX_HIGHTABLE, hi_pmm_end); + } else { + addSpace(&ZoneTmpHigh, (void *)hi_pmm, (void *)hi_pmm + hi_pmm_size); + } + addSpace(&ZoneTmpLow, (void *)low_pmm, (void *)low_pmm + low_pmm_size); +} + +u32 LegacyRamSize VARFSEG; + +// Calculate the maximum ramsize (less than 4gig) from e820 map. +static void +calcRamSize(void) +{ + u32 rs = 0; + int i; + for (i=e820_count-1; i>=0; i--) { + struct e820entry *en = &e820_list[i]; + u64 end = en->start + en->size; + u32 type = en->type; + if (end <= 0xffffffff && (type == E820_ACPI || type == E820_RAM)) { + rs = end; + break; + } + } + LegacyRamSize = rs >= 1024*1024 ? rs : 1024*1024; +} + +// Update pointers after code relocation. +void +malloc_init(void) +{ + ASSERT32FLAT(); + dprintf(3, "malloc init\n"); + + if (CONFIG_RELOCATE_INIT) { + // Fixup malloc pointers after relocation + int i; + for (i=0; i<ARRAY_SIZE(Zones); i++) { + struct zone_s *zone = Zones[i]; + if (zone->head.first) + zone->head.first->pprev = &zone->head.first; + } + } + + // Initialize low-memory region + extern u8 varlow_start[], varlow_end[], final_varlow_start[]; + memmove(final_varlow_start, varlow_start, varlow_end - varlow_start); + if (CONFIG_MALLOC_UPPERMEMORY) { + addSpace(&ZoneLow, zonelow_base + OPROM_HEADER_RESERVE + , final_varlow_start); + RomBase = findLast(&ZoneLow); + } else { + addSpace(&ZoneLow, (void*)ALIGN_DOWN((u32)final_varlow_start, 1024) + , final_varlow_start); + } + + // Add space available in f-segment to ZoneFSeg + extern u8 zonefseg_start[], zonefseg_end[]; + memset(zonefseg_start, 0, zonefseg_end - zonefseg_start); + addSpace(&ZoneFSeg, zonefseg_start, zonefseg_end); + + calcRamSize(); +} + +void +malloc_prepboot(void) +{ + ASSERT32FLAT(); + dprintf(3, "malloc finalize\n"); + + u32 base = rom_get_max(); + memset((void*)RomEnd, 0, base-RomEnd); + if (CONFIG_MALLOC_UPPERMEMORY) { + // Place an optionrom signature around used low mem area. + struct rom_header *dummyrom = (void*)base; + dummyrom->signature = OPTION_ROM_SIGNATURE; + int size = (BUILD_BIOS_ADDR - base) / 512; + dummyrom->size = (size > 255) ? 255 : size; + } + + // Reserve more low-mem if needed. + u32 endlow = GET_BDA(mem_size_kb)*1024; + add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED); + + // Clear unused f-seg ram. + struct allocinfo_s *info = findLast(&ZoneFSeg); + memset(info->dataend, 0, info->allocend - info->dataend); + dprintf(1, "Space available for UMB: %x-%x, %x-%x\n" + , RomEnd, base, (u32)info->dataend, (u32)info->allocend); + + // Give back unused high ram. + info = findLast(&ZoneHigh); + if (info) { + u32 giveback = ALIGN_DOWN(info->allocend - info->dataend, PAGE_SIZE); + add_e820((u32)info->dataend, giveback, E820_RAM); + dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback); + } + + calcRamSize(); +} diff --git a/qemu/roms/seabios/src/malloc.h b/qemu/roms/seabios/src/malloc.h new file mode 100644 index 000000000..2bcb5bf6d --- /dev/null +++ b/qemu/roms/seabios/src/malloc.h @@ -0,0 +1,71 @@ +#ifndef __MALLOC_H +#define __MALLOC_H + +#include "types.h" // u32 + +// malloc.c +extern struct zone_s ZoneLow, ZoneHigh, ZoneFSeg, ZoneTmpLow, ZoneTmpHigh; +u32 rom_get_max(void); +u32 rom_get_last(void); +struct rom_header *rom_reserve(u32 size); +int rom_confirm(u32 size); +void csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, + u32 hi_pmm_size); +void malloc_preinit(void); +extern u32 LegacyRamSize; +void malloc_init(void); +void malloc_prepboot(void); +void *_malloc(struct zone_s *zone, u32 size, u32 align); +int _free(void *data); +u32 malloc_getspace(struct zone_s *zone); +void malloc_sethandle(void *data, u32 handle); +void *malloc_findhandle(u32 handle); + +#define MALLOC_DEFAULT_HANDLE 0xFFFFFFFF +// Minimum alignment of malloc'd memory +#define MALLOC_MIN_ALIGN 16 +// Helper functions for memory allocation. +static inline void *malloc_low(u32 size) { + return _malloc(&ZoneLow, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_high(u32 size) { + return _malloc(&ZoneHigh, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_fseg(u32 size) { + return _malloc(&ZoneFSeg, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_tmplow(u32 size) { + return _malloc(&ZoneTmpLow, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_tmphigh(u32 size) { + return _malloc(&ZoneTmpHigh, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_tmp(u32 size) { + void *ret = malloc_tmphigh(size); + if (ret) + return ret; + return malloc_tmplow(size); +} +static inline void *memalign_low(u32 align, u32 size) { + return _malloc(&ZoneLow, size, align); +} +static inline void *memalign_high(u32 align, u32 size) { + return _malloc(&ZoneHigh, size, align); +} +static inline void *memalign_tmplow(u32 align, u32 size) { + return _malloc(&ZoneTmpLow, size, align); +} +static inline void *memalign_tmphigh(u32 align, u32 size) { + return _malloc(&ZoneTmpHigh, size, align); +} +static inline void *memalign_tmp(u32 align, u32 size) { + void *ret = memalign_tmphigh(align, size); + if (ret) + return ret; + return memalign_tmplow(align, size); +} +static inline void free(void *data) { + _free(data); +} + +#endif // malloc.h diff --git a/qemu/roms/seabios/src/memmap.c b/qemu/roms/seabios/src/memmap.c new file mode 100644 index 000000000..e03f8d0bf --- /dev/null +++ b/qemu/roms/seabios/src/memmap.c @@ -0,0 +1,144 @@ +// Support for building memory maps suitable for int 15 e820 calls. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // BUILD_MAX_E820 +#include "memmap.h" // struct e820entry +#include "output.h" // dprintf +#include "string.h" // memmove + + +/**************************************************************** + * e820 memory map + ****************************************************************/ + +// Info on e820 map location and size. +struct e820entry e820_list[BUILD_MAX_E820] VARFSEG; +int e820_count VARFSEG; + +// Remove an entry from the e820_list. +static void +remove_e820(int i) +{ + e820_count--; + memmove(&e820_list[i], &e820_list[i+1] + , sizeof(e820_list[0]) * (e820_count - i)); +} + +// Insert an entry in the e820_list at the given position. +static void +insert_e820(int i, u64 start, u64 size, u32 type) +{ + if (e820_count >= BUILD_MAX_E820) { + warn_noalloc(); + return; + } + + memmove(&e820_list[i+1], &e820_list[i] + , sizeof(e820_list[0]) * (e820_count - i)); + e820_count++; + struct e820entry *e = &e820_list[i]; + e->start = start; + e->size = size; + e->type = type; +} + +static const char * +e820_type_name(u32 type) +{ + switch (type) { + case E820_RAM: return "RAM"; + case E820_RESERVED: return "RESERVED"; + case E820_ACPI: return "ACPI"; + case E820_NVS: return "NVS"; + case E820_UNUSABLE: return "UNUSABLE"; + case E820_HOLE: return "HOLE"; + default: return "UNKNOWN"; + } +} + +// Show the current e820_list. +static void +dump_map(void) +{ + dprintf(1, "e820 map has %d items:\n", e820_count); + int i; + for (i=0; i<e820_count; i++) { + struct e820entry *e = &e820_list[i]; + u64 e_end = e->start + e->size; + dprintf(1, " %d: %016llx - %016llx = %d %s\n", i + , e->start, e_end, e->type, e820_type_name(e->type)); + } +} + +// Add a new entry to the list. This scans for overlaps and keeps the +// list sorted. +void +add_e820(u64 start, u64 size, u32 type) +{ + dprintf(8, "Add to e820 map: %08x %08x %d\n", (u32)start, (u32)size, type); + + if (! size) + // Huh? Nothing to do. + return; + + // Find position of new item (splitting existing item if needed). + u64 end = start + size; + int i; + for (i=0; i<e820_count; i++) { + struct e820entry *e = &e820_list[i]; + u64 e_end = e->start + e->size; + if (start > e_end) + continue; + // Found position - check if an existing item needs to be split. + if (start > e->start) { + if (type == e->type) { + // Same type - merge them. + size += start - e->start; + start = e->start; + } else { + // Split existing item. + e->size = start - e->start; + i++; + if (e_end > end) + insert_e820(i, end, e_end - end, e->type); + } + } + break; + } + // Remove/adjust existing items that are overlapping. + while (i<e820_count) { + struct e820entry *e = &e820_list[i]; + if (end < e->start) + // No overlap - done. + break; + u64 e_end = e->start + e->size; + if (end >= e_end) { + // Existing item completely overlapped - remove it. + remove_e820(i); + continue; + } + // Not completely overlapped - adjust its start. + e->start = end; + e->size = e_end - end; + if (type == e->type) { + // Same type - merge them. + size += e->size; + remove_e820(i); + } + break; + } + // Insert new item. + if (type != E820_HOLE) + insert_e820(i, start, size, type); + //dump_map(); +} + +// Report on final memory locations. +void +memmap_prepboot(void) +{ + dump_map(); +} diff --git a/qemu/roms/seabios/src/memmap.h b/qemu/roms/seabios/src/memmap.h new file mode 100644 index 000000000..7bda56e2b --- /dev/null +++ b/qemu/roms/seabios/src/memmap.h @@ -0,0 +1,29 @@ +#ifndef __E820MAP_H +#define __E820MAP_H + +#include "types.h" // u64 + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 +#define E820_HOLE ((u32)-1) // Useful for removing entries + +struct e820entry { + u64 start; + u64 size; + u32 type; +}; + +void add_e820(u64 start, u64 size, u32 type); +void memmap_prepboot(void); + +// A typical OS page size +#define PAGE_SIZE 4096 + +// e820 map storage +extern struct e820entry e820_list[]; +extern int e820_count; + +#endif // e820map.h diff --git a/qemu/roms/seabios/src/misc.c b/qemu/roms/seabios/src/misc.c new file mode 100644 index 000000000..8caaf31d8 --- /dev/null +++ b/qemu/roms/seabios/src/misc.c @@ -0,0 +1,203 @@ +// Code for misc 16bit handlers and variables. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "bregs.h" // struct bregs +#include "hw/pic.h" // enable_hwirq +#include "output.h" // debug_enter +#include "stacks.h" // call16_int +#include "string.h" // memset + +#define PORT_MATH_CLEAR 0x00f0 + +// Indicator if POST phase has been started (and if it has completed). +int HaveRunPost VARFSEG; + +int +in_post(void) +{ + return GET_GLOBAL(HaveRunPost) == 1; +} + + +/**************************************************************** + * Misc 16bit ISRs + ****************************************************************/ + +// INT 12h Memory Size Service Entry Point +void VISIBLE16 +handle_12(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_12); + regs->ax = GET_BDA(mem_size_kb); +} + +// INT 11h Equipment List Service Entry Point +void VISIBLE16 +handle_11(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_11); + regs->ax = GET_BDA(equipment_list_flags); +} + +// INT 05h Print Screen Service Entry Point +void VISIBLE16 +handle_05(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_05); +} + +// INT 10h Video Support Service Entry Point +void VISIBLE16 +handle_10(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_10); + // dont do anything, since the VGA BIOS handles int10h requests +} + +// NMI handler +void VISIBLE16 +handle_02(void) +{ + debug_isr(DEBUG_ISR_02); +} + +void +mathcp_setup(void) +{ + dprintf(3, "math cp init\n"); + // 80x87 coprocessor installed + set_equipment_flags(0x02, 0x02); + enable_hwirq(13, FUNC16(entry_75)); +} + +// INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION +void VISIBLE16 +handle_75(void) +{ + debug_isr(DEBUG_ISR_75); + + // clear irq13 + outb(0, PORT_MATH_CLEAR); + // clear interrupt + pic_eoi2(); + // legacy nmi call + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x02, &br); +} + + +/**************************************************************** + * BIOS_CONFIG_TABLE + ****************************************************************/ + +// DMA channel 3 used by hard disk BIOS +#define CBT_F1_DMA3USED (1<<7) +// 2nd interrupt controller (8259) installed +#define CBT_F1_2NDPIC (1<<6) +// Real-Time Clock installed +#define CBT_F1_RTC (1<<5) +// INT 15/AH=4Fh called upon INT 09h +#define CBT_F1_INT154F (1<<4) +// wait for external event (INT 15/AH=41h) supported +#define CBT_F1_WAITEXT (1<<3) +// extended BIOS area allocated (usually at top of RAM) +#define CBT_F1_EBDA (1<<2) +// bus is Micro Channel instead of ISA +#define CBT_F1_MCA (1<<1) +// system has dual bus (Micro Channel + ISA) +#define CBT_F1_MCAISA (1<<0) + +// INT 16/AH=09h (keyboard functionality) supported +#define CBT_F2_INT1609 (1<<6) + +struct bios_config_table_s BIOS_CONFIG_TABLE VARFSEGFIXED(0xe6f5) = { + .size = sizeof(BIOS_CONFIG_TABLE) - 2, + .model = BUILD_MODEL_ID, + .submodel = BUILD_SUBMODEL_ID, + .biosrev = BUILD_BIOS_REVISION, + .feature1 = ( + CBT_F1_2NDPIC | CBT_F1_RTC | CBT_F1_EBDA + | (CONFIG_KBD_CALL_INT15_4F ? CBT_F1_INT154F : 0)), + .feature2 = CBT_F2_INT1609, + .feature3 = 0, + .feature4 = 0, + .feature5 = 0, +}; + + +/**************************************************************** + * GDT and IDT tables + ****************************************************************/ + +// Real mode IDT descriptor +struct descloc_s rmode_IDT_info VARFSEG = { + .length = sizeof(struct rmode_IVT) - 1, + .addr = (u32)MAKE_FLATPTR(SEG_IVT, 0), +}; + +// Dummy IDT that forces a machine shutdown if an irq happens in +// protected mode. +u8 dummy_IDT VARFSEG; + +// Protected mode IDT descriptor +struct descloc_s pmode_IDT_info VARFSEG = { + .length = sizeof(dummy_IDT) - 1, + .addr = (u32)&dummy_IDT, +}; + +// GDT +u64 rombios32_gdt[] VARFSEG __aligned(8) = { + // First entry can't be used. + 0x0000000000000000LL, + // 32 bit flat code segment (SEG32_MODE32_CS) + GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_B, + // 32 bit flat data segment (SEG32_MODE32_DS) + GDT_GRANLIMIT(0xffffffff) | GDT_DATA | GDT_B, + // 16 bit code segment base=0xf0000 limit=0xffff (SEG32_MODE16_CS) + GDT_LIMIT(BUILD_BIOS_SIZE-1) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR), + // 16 bit data segment base=0x0 limit=0xffff (SEG32_MODE16_DS) + GDT_LIMIT(0x0ffff) | GDT_DATA, + // 16 bit code segment base=0xf0000 limit=0xffffffff (SEG32_MODE16BIG_CS) + GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR), + // 16 bit data segment base=0 limit=0xffffffff (SEG32_MODE16BIG_DS) + GDT_GRANLIMIT(0xffffffff) | GDT_DATA, +}; + +// GDT descriptor +struct descloc_s rombios32_gdt_48 VARFSEG = { + .length = sizeof(rombios32_gdt) - 1, + .addr = (u32)rombios32_gdt, +}; + + +/**************************************************************** + * Misc fixed vars + ****************************************************************/ + +// BIOS build date +char BiosDate[] VARFSEGFIXED(0xfff5) = "06/23/99"; + +u8 BiosModelId VARFSEGFIXED(0xfffe) = BUILD_MODEL_ID; + +u8 BiosChecksum VARFSEGFIXED(0xffff); + +struct floppy_dbt_s diskette_param_table VARFSEGFIXED(0xefc7); + +// Old Fixed Disk Parameter Table (newer tables are in the ebda). +struct fdpt_s OldFDPT VARFSEGFIXED(0xe401); + +// XXX - Baud Rate Generator Table +u8 BaudTable[16] VARFSEGFIXED(0xe729); + +// XXX - Initial Interrupt Vector Offsets Loaded by POST +u8 InitVectors[13] VARFSEGFIXED(0xfef3); + +// XXX - INT 1D - SYSTEM DATA - VIDEO PARAMETER TABLES +u8 VideoParams[88] VARFSEGFIXED(0xf0a4); diff --git a/qemu/roms/seabios/src/mouse.c b/qemu/roms/seabios/src/mouse.c new file mode 100644 index 000000000..6d1f5b77e --- /dev/null +++ b/qemu/roms/seabios/src/mouse.c @@ -0,0 +1,343 @@ +// 16bit code to handle mouse events. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_EBDA +#include "bregs.h" // struct bregs +#include "hw/ps2port.h" // ps2_mouse_command +#include "hw/usb-hid.h" // usb_mouse_command +#include "output.h" // dprintf +#include "stacks.h" // stack_hop_back +#include "util.h" // mouse_init + +void +mouse_init(void) +{ + if (! CONFIG_MOUSE) + return; + dprintf(3, "init mouse\n"); + // pointing device installed + set_equipment_flags(0x04, 0x04); +} + +static int +mouse_command(int command, u8 *param) +{ + if (usb_mouse_active()) + return usb_mouse_command(command, param); + return ps2_mouse_command(command, param); +} + +#define RET_SUCCESS 0x00 +#define RET_EINVFUNCTION 0x01 +#define RET_EINVINPUT 0x02 +#define RET_EINTERFACE 0x03 +#define RET_ENEEDRESEND 0x04 +#define RET_ENOHANDLER 0x05 + +// Disable Mouse +static void +mouse_15c20000(struct bregs *regs) +{ + int ret = mouse_command(PSMOUSE_CMD_DISABLE, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Enable Mouse +static void +mouse_15c20001(struct bregs *regs) +{ + u16 ebda_seg = get_ebda_seg(); + u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); + if ((mouse_flags_2 & 0x80) == 0) { + set_code_invalid(regs, RET_ENOHANDLER); + return; + } + + int ret = mouse_command(PSMOUSE_CMD_ENABLE, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +static void +mouse_15c200XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EINVFUNCTION); +} + +// Disable/Enable Mouse +static void +mouse_15c200(struct bregs *regs) +{ + switch (regs->bh) { + case 0x00: mouse_15c20000(regs); break; + case 0x01: mouse_15c20001(regs); break; + default: mouse_15c200XX(regs); break; + } +} + +// Reset Mouse +static void +mouse_15c201(struct bregs *regs) +{ + u8 param[2]; + int ret = mouse_command(PSMOUSE_CMD_RESET_BAT, param); + if (ret) { + set_code_invalid(regs, RET_ENEEDRESEND); + return; + } + regs->bl = param[0]; + regs->bh = param[1]; + set_code_success(regs); +} + +// Set Sample Rate +static void +mouse_15c202(struct bregs *regs) +{ + static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200}; + if (regs->bh >= ARRAY_SIZE(sample_rates)) { + set_code_invalid(regs, RET_EINVINPUT); + return; + } + u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]); + int ret = mouse_command(PSMOUSE_CMD_SETRATE, &mouse_data1); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Set Resolution +static void +mouse_15c203(struct bregs *regs) +{ + // BH: + // 0 = 25 dpi, 1 count per millimeter + // 1 = 50 dpi, 2 counts per millimeter + // 2 = 100 dpi, 4 counts per millimeter + // 3 = 200 dpi, 8 counts per millimeter + if (regs->bh >= 4) { + set_code_invalid(regs, RET_EINVINPUT); + return; + } + u8 param = regs->bh; + int ret = mouse_command(PSMOUSE_CMD_SETRES, ¶m); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Get Device ID +static void +mouse_15c204(struct bregs *regs) +{ + u8 param[2]; + int ret = mouse_command(PSMOUSE_CMD_GETID, param); + if (ret) { + set_code_invalid(regs, RET_ENEEDRESEND); + return; + } + regs->bh = param[0]; + set_code_success(regs); +} + +// Initialize Mouse +static void +mouse_15c205(struct bregs *regs) +{ + if (regs->bh != 3) { + set_code_invalid(regs, RET_EINTERFACE); + return; + } + u16 ebda_seg = get_ebda_seg(); + SET_EBDA(ebda_seg, mouse_flag1, 0x00); + SET_EBDA(ebda_seg, mouse_flag2, regs->bh); + + // Reset Mouse + mouse_15c201(regs); +} + +// Return Status +static void +mouse_15c20600(struct bregs *regs) +{ + u8 param[3]; + int ret = mouse_command(PSMOUSE_CMD_GETINFO, param); + if (ret) { + set_code_invalid(regs, RET_ENEEDRESEND); + return; + } + regs->bl = param[0]; + regs->cl = param[1]; + regs->dl = param[2]; + set_code_success(regs); +} + +// Set Scaling Factor to 1:1 +static void +mouse_15c20601(struct bregs *regs) +{ + int ret = mouse_command(PSMOUSE_CMD_SETSCALE11, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Set Scaling Factor to 2:1 +static void +mouse_15c20602(struct bregs *regs) +{ + int ret = mouse_command(PSMOUSE_CMD_SETSCALE21, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +static void +mouse_15c206XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EINVFUNCTION); +} + +// Return Status & Set Scaling Factor... +static void +mouse_15c206(struct bregs *regs) +{ + switch (regs->bh) { + case 0x00: mouse_15c20600(regs); break; + case 0x01: mouse_15c20601(regs); break; + case 0x02: mouse_15c20602(regs); break; + default: mouse_15c206XX(regs); break; + } +} + +// Set Mouse Handler Address +static void +mouse_15c207(struct bregs *regs) +{ + struct segoff_s farptr = SEGOFF(regs->es, regs->bx); + u16 ebda_seg = get_ebda_seg(); + u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); + if (! farptr.segoff) { + /* remove handler */ + if ((mouse_flags_2 & 0x80) != 0) { + mouse_flags_2 &= ~0x80; + mouse_command(PSMOUSE_CMD_DISABLE, NULL); + } + } else { + /* install handler */ + mouse_flags_2 |= 0x80; + } + SET_EBDA(ebda_seg, mouse_flag2, mouse_flags_2); + SET_EBDA(ebda_seg, far_call_pointer, farptr); + set_code_success(regs); +} + +static void +mouse_15c2XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EINVFUNCTION); +} + +void +handle_15c2(struct bregs *regs) +{ + //debug_stub(regs); + + if (! CONFIG_MOUSE) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + + switch (regs->al) { + case 0x00: mouse_15c200(regs); break; + case 0x01: mouse_15c201(regs); break; + case 0x02: mouse_15c202(regs); break; + case 0x03: mouse_15c203(regs); break; + case 0x04: mouse_15c204(regs); break; + case 0x05: mouse_15c205(regs); break; + case 0x06: mouse_15c206(regs); break; + case 0x07: mouse_15c207(regs); break; + default: mouse_15c2XX(regs); break; + } +} + +void VISIBLE16 +invoke_mouse_handler(void) +{ + if (!CONFIG_MOUSE) + return; + if (need_hop_back()) { + extern void _cfunc16_invoke_mouse_handler(void); + stack_hop_back(0, 0, _cfunc16_invoke_mouse_handler); + return; + } + ASSERT16(); + u16 ebda_seg = get_ebda_seg(); + u16 status = GET_EBDA(ebda_seg, mouse_data[0]); + u16 X = GET_EBDA(ebda_seg, mouse_data[1]); + u16 Y = GET_EBDA(ebda_seg, mouse_data[2]); + + struct segoff_s func = GET_EBDA(ebda_seg, far_call_pointer); + dprintf(16, "mouse farcall s=%04x x=%04x y=%04x func=%04x:%04x\n" + , status, X, Y, func.seg, func.offset); + + asm volatile( + "pushl %%ebp\n" + "sti\n" + + "pushl %0\n" + "pushw %w1\n" // status + "pushw %w2\n" // X + "pushw %w3\n" // Y + "pushw $0\n" // Z + "lcallw *8(%%esp)\n" + "addl $12, %%esp\n" + + "cli\n" + "cld\n" + "popl %%ebp" + : "+a"(func.segoff), "+c"(status), "+d"(X), "+b"(Y) + : + : "edi", "esi", "cc", "memory"); +} + +void +process_mouse(u8 data) +{ + if (!CONFIG_MOUSE) + return; + + u16 ebda_seg = get_ebda_seg(); + u8 mouse_flags_1 = GET_EBDA(ebda_seg, mouse_flag1); + u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); + + if (! (mouse_flags_2 & 0x80)) + // far call handler not installed + return; + + u8 package_count = mouse_flags_2 & 0x07; + u8 index = mouse_flags_1 & 0x07; + SET_EBDA(ebda_seg, mouse_data[index], data); + + if ((index+1) < package_count) { + mouse_flags_1++; + SET_EBDA(ebda_seg, mouse_flag1, mouse_flags_1); + return; + } + + SET_EBDA(ebda_seg, mouse_flag1, 0); + invoke_mouse_handler(); +} diff --git a/qemu/roms/seabios/src/optionroms.c b/qemu/roms/seabios/src/optionroms.c new file mode 100644 index 000000000..93d9d2fe6 --- /dev/null +++ b/qemu/roms/seabios/src/optionroms.c @@ -0,0 +1,475 @@ +// Option rom scanning code. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "farptr.h" // FLATPTR_TO_SEG +#include "hw/pci.h" // foreachpci +#include "hw/pci_ids.h" // PCI_CLASS_DISPLAY_VGA +#include "hw/pci_regs.h" // PCI_ROM_ADDRESS +#include "malloc.h" // rom_confirm +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "stacks.h" // farcall16big +#include "std/optionrom.h" // struct rom_header +#include "std/pnpbios.h" // PNP_SIGNATURE +#include "string.h" // memset +#include "util.h" // get_pnp_offset + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Execute a given option rom. +static void +__callrom(struct rom_header *rom, u16 offset, u16 bdf) +{ + u16 seg = FLATPTR_TO_SEG(rom); + dprintf(1, "Running option rom at %04x:%04x\n", seg, offset); + + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.ax = bdf; + br.bx = 0xffff; + br.dx = 0xffff; + br.es = SEG_BIOS; + br.di = get_pnp_offset(); + br.code = SEGOFF(seg, offset); + start_preempt(); + farcall16big(&br); + finish_preempt(); +} + +// Execute a given option rom at the standard entry vector. +void +callrom(struct rom_header *rom, u16 bdf) +{ + __callrom(rom, OPTION_ROM_INITVECTOR, bdf); +} + +// Execute a BCV option rom registered via add_bcv(). +void +call_bcv(u16 seg, u16 ip) +{ + __callrom(MAKE_FLATPTR(seg, 0), ip, 0); +} + +static int EnforceChecksum; + +// Verify that an option rom looks valid +static int +is_valid_rom(struct rom_header *rom) +{ + dprintf(6, "Checking rom %p (sig %x size %d)\n" + , rom, rom->signature, rom->size); + if (rom->signature != OPTION_ROM_SIGNATURE) + return 0; + if (! rom->size) + return 0; + u32 len = rom->size * 512; + u8 sum = checksum(rom, len); + if (sum != 0) { + dprintf(1, "Found option rom with bad checksum: loc=%p len=%d sum=%x\n" + , rom, len, sum); + if (EnforceChecksum) + return 0; + } + return 1; +} + +// Check if a valid option rom has a pnp struct; return it if so. +static struct pnp_data * +get_pnp_rom(struct rom_header *rom) +{ + struct pnp_data *pnp = (void*)((u8*)rom + rom->pnpoffset); + if (pnp->signature != PNP_SIGNATURE) + return NULL; + return pnp; +} + +// Check for multiple pnp option rom headers. +static struct pnp_data * +get_pnp_next(struct rom_header *rom, struct pnp_data *pnp) +{ + if (! pnp->nextoffset) + return NULL; + pnp = (void*)((u8*)rom + pnp->nextoffset); + if (pnp->signature != PNP_SIGNATURE) + return NULL; + return pnp; +} + +// Check if a valid option rom has a pci struct; return it if so. +static struct pci_data * +get_pci_rom(struct rom_header *rom) +{ + struct pci_data *pd = (void*)((u32)rom + rom->pcioffset); + if (pd->signature != PCI_ROM_SIGNATURE) + return NULL; + if (rom->pcioffset & 3) + dprintf(1, "WARNING! Found unaligned PCI rom (vd=%04x:%04x)\n" + , pd->vendor, pd->device); + return pd; +} + +// Run rom init code and note rom size. +static int +init_optionrom(struct rom_header *rom, u16 bdf, int isvga) +{ + if (! is_valid_rom(rom)) + return -1; + struct rom_header *newrom = rom_reserve(rom->size * 512); + if (!newrom) { + warn_noalloc(); + return -1; + } + if (newrom != rom) + memmove(newrom, rom, rom->size * 512); + + if (isvga || get_pnp_rom(newrom)) + // Only init vga and PnP roms here. + callrom(newrom, bdf); + + return rom_confirm(newrom->size * 512); +} + +#define RS_PCIROM (1LL<<33) + +static void +setRomSource(u64 *sources, struct rom_header *rom, u64 source) +{ + if (sources) + sources[((u32)rom - BUILD_ROM_START) / OPTION_ROM_ALIGN] = source; +} + +static int +getRomPriority(u64 *sources, struct rom_header *rom, int instance) +{ + u64 source = sources[((u32)rom - BUILD_ROM_START) / OPTION_ROM_ALIGN]; + if (!source) + return -1; + if (source & RS_PCIROM) + return bootprio_find_pci_rom((void*)(u32)source, instance); + struct romfile_s *file = (void*)(u32)source; + return bootprio_find_named_rom(file->name, instance); +} + + +/**************************************************************** + * Roms in CBFS + ****************************************************************/ + +static struct rom_header * +deploy_romfile(struct romfile_s *file) +{ + u32 size = file->size; + struct rom_header *rom = rom_reserve(size); + if (!rom) { + warn_noalloc(); + return NULL; + } + int ret = file->copy(file, rom, size); + if (ret <= 0) + return NULL; + return rom; +} + +// Check if an option rom is at a hardcoded location or in CBFS. +static struct rom_header * +lookup_hardcode(struct pci_device *pci) +{ + char fname[17]; + snprintf(fname, sizeof(fname), "pci%04x,%04x.rom" + , pci->vendor, pci->device); + struct romfile_s *file = romfile_find(fname); + if (file) + return deploy_romfile(file); + return NULL; +} + +// Run all roms in a given CBFS directory. +static void +run_file_roms(const char *prefix, int isvga, u64 *sources) +{ + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix(prefix, file); + if (!file) + break; + struct rom_header *rom = deploy_romfile(file); + if (rom) { + setRomSource(sources, rom, (u32)file); + init_optionrom(rom, 0, isvga); + } + } +} + + +/**************************************************************** + * PCI roms + ****************************************************************/ + +// Verify device is a vga device with legacy address decoding enabled. +int +is_pci_vga(struct pci_device *pci) +{ + if (pci->class != PCI_CLASS_DISPLAY_VGA) + return 0; + u16 cmd = pci_config_readw(pci->bdf, PCI_COMMAND); + if (!(cmd & PCI_COMMAND_IO && cmd & PCI_COMMAND_MEMORY)) + return 0; + while (pci->parent) { + pci = pci->parent; + u32 ctrl = pci_config_readb(pci->bdf, PCI_BRIDGE_CONTROL); + if (!(ctrl & PCI_BRIDGE_CTL_VGA)) + return 0; + } + return 1; +} + +// Copy a rom to its permanent location below 1MiB +static struct rom_header * +copy_rom(struct rom_header *rom) +{ + u32 romsize = rom->size * 512; + struct rom_header *newrom = rom_reserve(romsize); + if (!newrom) { + warn_noalloc(); + return NULL; + } + dprintf(4, "Copying option rom (size %d) from %p to %p\n" + , romsize, rom, newrom); + iomemcpy(newrom, rom, romsize); + return newrom; +} + +// Map the option rom of a given PCI device. +static struct rom_header * +map_pcirom(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(6, "Attempting to map option rom on dev %02x:%02x.%x\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + + if ((pci->header_type & 0x7f) != PCI_HEADER_TYPE_NORMAL) { + dprintf(6, "Skipping non-normal pci device (type=%x)\n" + , pci->header_type); + return NULL; + } + + u32 orig = pci_config_readl(bdf, PCI_ROM_ADDRESS); + pci_config_writel(bdf, PCI_ROM_ADDRESS, ~PCI_ROM_ADDRESS_ENABLE); + u32 sz = pci_config_readl(bdf, PCI_ROM_ADDRESS); + + dprintf(6, "Option rom sizing returned %x %x\n", orig, sz); + orig &= ~PCI_ROM_ADDRESS_ENABLE; + if (!sz || sz == 0xffffffff) + goto fail; + + if (orig == sz || (u32)(orig + 4*1024*1024) < 20*1024*1024) { + // Don't try to map to a pci addresses at its max, in the last + // 4MiB of ram, or the first 16MiB of ram. + dprintf(6, "Preset rom address doesn't look valid\n"); + goto fail; + } + + // Looks like a rom - enable it. + pci_config_writel(bdf, PCI_ROM_ADDRESS, orig | PCI_ROM_ADDRESS_ENABLE); + + struct rom_header *rom = (void*)orig; + for (;;) { + dprintf(5, "Inspecting possible rom at %p (vd=%04x:%04x" + " bdf=%02x:%02x.%x)\n" + , rom, pci->vendor, pci->device + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + if (rom->signature != OPTION_ROM_SIGNATURE) { + dprintf(6, "No option rom signature (got %x)\n", rom->signature); + goto fail; + } + struct pci_data *pd = get_pci_rom(rom); + if (! pd) { + dprintf(6, "No valid pci signature found\n"); + goto fail; + } + + if (pd->vendor == pci->vendor && pd->device == pci->device + && pd->type == PCIROM_CODETYPE_X86) + // A match + break; + dprintf(6, "Didn't match dev/ven (got %04x:%04x) or type (got %d)\n" + , pd->vendor, pd->device, pd->type); + if (pd->indicator & 0x80) { + dprintf(6, "No more images left\n"); + goto fail; + } + rom = (void*)((u32)rom + pd->ilen * 512); + } + + rom = copy_rom(rom); + pci_config_writel(bdf, PCI_ROM_ADDRESS, orig); + return rom; +fail: + // Not valid - restore original and exit. + pci_config_writel(bdf, PCI_ROM_ADDRESS, orig); + return NULL; +} + +// Attempt to map and initialize the option rom on a given PCI device. +static int +init_pcirom(struct pci_device *pci, int isvga, u64 *sources) +{ + u16 bdf = pci->bdf; + dprintf(4, "Attempting to init PCI bdf %02x:%02x.%x (vd %04x:%04x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf) + , pci->vendor, pci->device); + struct rom_header *rom = lookup_hardcode(pci); + if (! rom) + rom = map_pcirom(pci); + if (! rom) + // No ROM present. + return -1; + setRomSource(sources, rom, RS_PCIROM | (u32)pci); + return init_optionrom(rom, bdf, isvga); +} + + +/**************************************************************** + * Non-VGA option rom init + ****************************************************************/ + +void +optionrom_setup(void) +{ + if (! CONFIG_OPTIONROMS) + return; + + dprintf(1, "Scan for option roms\n"); + u64 sources[(BUILD_BIOS_ADDR - BUILD_ROM_START) / OPTION_ROM_ALIGN]; + memset(sources, 0, sizeof(sources)); + u32 post_vga = rom_get_last(); + + if (CONFIG_OPTIONROMS_DEPLOYED) { + // Option roms are already deployed on the system. + u32 pos = post_vga; + while (pos < rom_get_max()) { + int ret = init_optionrom((void*)pos, 0, 0); + if (ret) + pos += OPTION_ROM_ALIGN; + else + pos = rom_get_last(); + } + } else { + // Find and deploy PCI roms. + struct pci_device *pci; + foreachpci(pci) { + if (pci->class == PCI_CLASS_DISPLAY_VGA || pci->have_driver) + continue; + init_pcirom(pci, 0, sources); + } + + // Find and deploy CBFS roms not associated with a device. + run_file_roms("genroms/", 0, sources); + } + rom_reserve(0); + + // All option roms found and deployed - now build BEV/BCV vectors. + + u32 pos = post_vga; + while (pos < rom_get_last()) { + struct rom_header *rom = (void*)pos; + if (! is_valid_rom(rom)) { + pos += OPTION_ROM_ALIGN; + continue; + } + pos += ALIGN(rom->size * 512, OPTION_ROM_ALIGN); + struct pnp_data *pnp = get_pnp_rom(rom); + if (! pnp) { + // Legacy rom. + boot_add_bcv(FLATPTR_TO_SEG(rom), OPTION_ROM_INITVECTOR, 0 + , getRomPriority(sources, rom, 0)); + continue; + } + // PnP rom - check for BEV and BCV boot capabilities. + int instance = 0; + while (pnp) { + if (pnp->bev) + boot_add_bev(FLATPTR_TO_SEG(rom), pnp->bev, pnp->productname + , getRomPriority(sources, rom, instance++)); + else if (pnp->bcv) + boot_add_bcv(FLATPTR_TO_SEG(rom), pnp->bcv, pnp->productname + , getRomPriority(sources, rom, instance++)); + else + break; + pnp = get_pnp_next(rom, pnp); + } + } +} + + +/**************************************************************** + * VGA init + ****************************************************************/ + +static int S3ResumeVga; +int ScreenAndDebug; +struct rom_header *VgaROM; + +// Call into vga code to turn on console. +void +vgarom_setup(void) +{ + if (! CONFIG_OPTIONROMS) + return; + + dprintf(1, "Scan for VGA option rom\n"); + + // Load some config settings that impact VGA. + EnforceChecksum = romfile_loadint("etc/optionroms-checksum", 1); + S3ResumeVga = romfile_loadint("etc/s3-resume-vga-init", CONFIG_QEMU); + ScreenAndDebug = romfile_loadint("etc/screen-and-debug", 1); + + if (CONFIG_OPTIONROMS_DEPLOYED) { + // Option roms are already deployed on the system. + init_optionrom((void*)BUILD_ROM_START, 0, 1); + } else { + // Clear option rom memory + memset((void*)BUILD_ROM_START, 0, rom_get_max() - BUILD_ROM_START); + + // Find and deploy PCI VGA rom. + struct pci_device *pci; + foreachpci(pci) { + if (!is_pci_vga(pci)) + continue; + vgahook_setup(pci); + init_pcirom(pci, 1, NULL); + break; + } + + // Find and deploy CBFS vga-style roms not associated with a device. + run_file_roms("vgaroms/", 1, NULL); + } + rom_reserve(0); + + if (rom_get_last() == BUILD_ROM_START) + // No VGA rom found + return; + + VgaROM = (void*)BUILD_ROM_START; + enable_vga_console(); +} + +void +s3_resume_vga(void) +{ + if (!S3ResumeVga) + return; + if (!VgaROM || ! is_valid_rom(VgaROM)) + return; + callrom(VgaROM, 0); +} diff --git a/qemu/roms/seabios/src/output.c b/qemu/roms/seabios/src/output.c new file mode 100644 index 000000000..45397b3f6 --- /dev/null +++ b/qemu/roms/seabios/src/output.c @@ -0,0 +1,564 @@ +// Raw screen writing and debug output code. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include <stdarg.h> // va_list + +#include "farptr.h" // GET_VAR +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL +#include "hw/serialio.h" // serial_debug_putc +#include "malloc.h" // malloc_tmp +#include "output.h" // dprintf +#include "stacks.h" // call16_int +#include "string.h" // memset +#include "util.h" // ScreenAndDebug + +struct putcinfo { + void (*func)(struct putcinfo *info, char c); +}; + + +/**************************************************************** + * Debug output + ****************************************************************/ + +void +debug_banner(void) +{ + dprintf(1, "SeaBIOS (version %s)\n", VERSION); +} + +// Write a character to debug port(s). +static void +debug_putc(struct putcinfo *action, char c) +{ + if (! CONFIG_DEBUG_LEVEL) + return; + qemu_debug_putc(c); + if (!MODESEGMENT) + coreboot_debug_putc(c); + serial_debug_putc(c); +} + +// Flush any pending output to debug port(s). +static void +debug_flush(void) +{ + serial_debug_flush(); +} + +// In segmented mode just need a dummy variable (debug_putc is always +// used anyway), and in 32bit flat mode need a pointer to the 32bit +// instance of debug_putc(). +#if MODE16 +static struct putcinfo debuginfo VAR16; +#elif MODESEGMENT +static struct putcinfo debuginfo VAR32SEG; +#else +static struct putcinfo debuginfo = { debug_putc }; +#endif + + +/**************************************************************** + * Screen writing + ****************************************************************/ + +// Show a character on the screen. +static void +screenc(char c) +{ + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.ah = 0x0e; + br.al = c; + br.bl = 0x07; + call16_int(0x10, &br); +} + +// Handle a character from a printf request. +static void +screen_putc(struct putcinfo *action, char c) +{ + if (ScreenAndDebug) + debug_putc(&debuginfo, c); + if (c == '\n') + screenc('\r'); + screenc(c); +} + +static struct putcinfo screeninfo = { screen_putc }; + + +/**************************************************************** + * Xprintf code + ****************************************************************/ + +// Output a character. +static void +putc(struct putcinfo *action, char c) +{ + if (MODESEGMENT) { + // Only debugging output supported in segmented mode. + debug_putc(action, c); + return; + } + + void (*func)(struct putcinfo *info, char c) = GET_GLOBAL(action->func); + func(action, c); +} + +// Ouptut a string. +static void +puts(struct putcinfo *action, const char *s) +{ + if (!MODESEGMENT && !s) + s = "(NULL)"; + for (; *s; s++) + putc(action, *s); +} + +// Output a string that is in the CS segment. +static void +puts_cs(struct putcinfo *action, const char *s) +{ + char *vs = (char*)s; + for (;; vs++) { + char c = GET_GLOBAL(*vs); + if (!c) + break; + putc(action, c); + } +} + +// Output an unsigned integer. +static void +putuint(struct putcinfo *action, u32 val) +{ + char buf[12]; + char *d = &buf[sizeof(buf) - 1]; + *d-- = '\0'; + for (;;) { + *d = (val % 10) + '0'; + val /= 10; + if (!val) + break; + d--; + } + puts(action, d); +} + +// Output a single digit hex character. +static inline void +putsinglehex(struct putcinfo *action, u32 val) +{ + if (val <= 9) + val = '0' + val; + else + val = 'a' + val - 10; + putc(action, val); +} + +// Output an integer in hexadecimal with a specified width. +static void +puthex(struct putcinfo *action, u32 val, int width) +{ + switch (width) { + default: putsinglehex(action, (val >> 28) & 0xf); + case 7: putsinglehex(action, (val >> 24) & 0xf); + case 6: putsinglehex(action, (val >> 20) & 0xf); + case 5: putsinglehex(action, (val >> 16) & 0xf); + case 4: putsinglehex(action, (val >> 12) & 0xf); + case 3: putsinglehex(action, (val >> 8) & 0xf); + case 2: putsinglehex(action, (val >> 4) & 0xf); + case 1: putsinglehex(action, (val >> 0) & 0xf); + } +} + +// Output an integer in hexadecimal with a minimum width. +static void +putprettyhex(struct putcinfo *action, u32 val, int width, char padchar) +{ + u32 tmp = val; + int count = 1; + while (tmp >>= 4) + count++; + width -= count; + while (width-- > 0) + putc(action, padchar); + puthex(action, val, count); +} + +static inline int +isdigit(u8 c) +{ + return ((u8)(c - '0')) < 10; +} + +static void +bvprintf(struct putcinfo *action, const char *fmt, va_list args) +{ + const char *s = fmt; + for (;; s++) { + char c = GET_GLOBAL(*(u8*)s); + if (!c) + break; + if (c != '%') { + putc(action, c); + continue; + } + const char *n = s+1; + int field_width = 0; + char padchar = ' '; + u8 is64 = 0; + for (;;) { + c = GET_GLOBAL(*(u8*)n); + if (!isdigit(c)) + break; + if (!field_width && (c == '0')) + padchar = '0'; + else + field_width = field_width * 10 + c - '0'; + n++; + } + if (c == 'l') { + // Ignore long format indicator + n++; + c = GET_GLOBAL(*(u8*)n); + } + if (c == 'l') { + is64 = 1; + n++; + c = GET_GLOBAL(*(u8*)n); + } + s32 val; + const char *sarg; + switch (c) { + case '%': + putc(action, '%'); + break; + case 'd': + val = va_arg(args, s32); + if (is64) + va_arg(args, s32); + if (val < 0) { + putc(action, '-'); + val = -val; + } + putuint(action, val); + break; + case 'u': + val = va_arg(args, s32); + if (is64) + va_arg(args, s32); + putuint(action, val); + break; + case 'p': + val = va_arg(args, s32); + putc(action, '0'); + putc(action, 'x'); + puthex(action, val, 8); + break; + case 'x': + val = va_arg(args, s32); + if (is64) { + u32 upper = va_arg(args, s32); + if (upper) { + putprettyhex(action, upper, field_width - 8, padchar); + puthex(action, val, 8); + break; + } + } + putprettyhex(action, val, field_width, padchar); + break; + case 'c': + val = va_arg(args, int); + putc(action, val); + break; + case '.': + // Hack to support "%.s" - meaning string on stack. + if (GET_GLOBAL(*(u8*)(n+1)) != 's') + break; + n++; + sarg = va_arg(args, const char *); + puts(action, sarg); + break; + case 's': + sarg = va_arg(args, const char *); + puts_cs(action, sarg); + break; + default: + putc(action, '%'); + n = s; + } + s = n; + } +} + +void +panic(const char *fmt, ...) +{ + if (CONFIG_DEBUG_LEVEL) { + va_list args; + va_start(args, fmt); + bvprintf(&debuginfo, fmt, args); + va_end(args); + debug_flush(); + } + + // XXX - use PANIC PORT. + irq_disable(); + for (;;) + hlt(); +} + +void +__dprintf(const char *fmt, ...) +{ + if (!MODESEGMENT && CONFIG_THREADS && CONFIG_DEBUG_LEVEL >= DEBUG_thread + && *fmt != '\\' && *fmt != '/') { + struct thread_info *cur = getCurThread(); + if (cur != &MainThread) { + // Show "thread id" for this debug message. + debug_putc(&debuginfo, '|'); + puthex(&debuginfo, (u32)cur, 8); + debug_putc(&debuginfo, '|'); + debug_putc(&debuginfo, ' '); + } + } + + va_list args; + va_start(args, fmt); + bvprintf(&debuginfo, fmt, args); + va_end(args); + debug_flush(); +} + +void +printf(const char *fmt, ...) +{ + ASSERT32FLAT(); + va_list args; + va_start(args, fmt); + bvprintf(&screeninfo, fmt, args); + va_end(args); + if (ScreenAndDebug) + debug_flush(); +} + + +/**************************************************************** + * snprintf + ****************************************************************/ + +struct snprintfinfo { + struct putcinfo info; + char *str, *end; +}; + +static void +putc_str(struct putcinfo *info, char c) +{ + struct snprintfinfo *sinfo = container_of(info, struct snprintfinfo, info); + if (sinfo->str >= sinfo->end) + return; + *sinfo->str = c; + sinfo->str++; +} + +// Build a formatted string. Note, this function returns the actual +// number of bytes used (not including null) even in the overflow +// case. +int +snprintf(char *str, size_t size, const char *fmt, ...) +{ + ASSERT32FLAT(); + if (!size) + return 0; + struct snprintfinfo sinfo = { { putc_str }, str, str + size }; + va_list args; + va_start(args, fmt); + bvprintf(&sinfo.info, fmt, args); + va_end(args); + char *end = sinfo.str; + if (end >= sinfo.end) + end = sinfo.end - 1; + *end = '\0'; + return end - str; +} + +// Build a formatted string - malloc'ing the memory. +char * +znprintf(size_t size, const char *fmt, ...) +{ + ASSERT32FLAT(); + if (!size) + return NULL; + char *str = malloc_tmp(size); + if (!str) { + warn_noalloc(); + return NULL; + } + struct snprintfinfo sinfo = { { putc_str }, str, str + size }; + va_list args; + va_start(args, fmt); + bvprintf(&sinfo.info, fmt, args); + va_end(args); + char *end = sinfo.str; + if (end >= sinfo.end) + end = sinfo.end - 1; + *end = '\0'; + return str; +} + + +/**************************************************************** + * Misc helpers + ****************************************************************/ + +void +hexdump(const void *d, int len) +{ + int count=0; + while (len > 0) { + if (count % 8 == 0) { + putc(&debuginfo, '\n'); + puthex(&debuginfo, count*4, 8); + putc(&debuginfo, ':'); + } else { + putc(&debuginfo, ' '); + } + puthex(&debuginfo, *(u32*)d, 8); + count++; + len-=4; + d+=4; + } + putc(&debuginfo, '\n'); + debug_flush(); +} + +static void +dump_regs(struct bregs *regs) +{ + if (!regs) { + dprintf(1, " NULL\n"); + return; + } + dprintf(1, " a=%08x b=%08x c=%08x d=%08x ds=%04x es=%04x ss=%04x\n" + , regs->eax, regs->ebx, regs->ecx, regs->edx + , regs->ds, regs->es, GET_SEG(SS)); + dprintf(1, " si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x f=%04x\n" + , regs->esi, regs->edi, regs->ebp, (u32)®s[1] + , regs->code.seg, regs->code.offset, regs->flags); +} + +// Report entry to an Interrupt Service Routine (ISR). +void +__debug_isr(const char *fname) +{ + puts_cs(&debuginfo, fname); + putc(&debuginfo, '\n'); + debug_flush(); +} + +// Function called on handler startup. +void +__debug_enter(struct bregs *regs, const char *fname) +{ + dprintf(1, "enter %s:\n", fname); + dump_regs(regs); +} + +// Send debugging output info. +void +__debug_stub(struct bregs *regs, int lineno, const char *fname) +{ + dprintf(1, "stub %s:%d:\n", fname, lineno); + dump_regs(regs); +} + +// Report on an invalid parameter. +void +__warn_invalid(struct bregs *regs, int lineno, const char *fname) +{ + if (CONFIG_DEBUG_LEVEL >= DEBUG_invalid) { + dprintf(1, "invalid %s:%d:\n", fname, lineno); + dump_regs(regs); + } +} + +// Report on an unimplemented feature. +void +__warn_unimplemented(struct bregs *regs, int lineno, const char *fname) +{ + if (CONFIG_DEBUG_LEVEL >= DEBUG_unimplemented) { + dprintf(1, "unimplemented %s:%d:\n", fname, lineno); + dump_regs(regs); + } +} + +// Report a detected internal inconsistency. +void +__warn_internalerror(int lineno, const char *fname) +{ + dprintf(1, "WARNING - internal error detected at %s:%d!\n" + , fname, lineno); +} + +// Report on an allocation failure. +void +__warn_noalloc(int lineno, const char *fname) +{ + dprintf(1, "WARNING - Unable to allocate resource at %s:%d!\n" + , fname, lineno); +} + +// Report on a timeout exceeded. +void +__warn_timeout(int lineno, const char *fname) +{ + dprintf(1, "WARNING - Timeout at %s:%d!\n", fname, lineno); +} + +// Report a handler reporting an invalid parameter to the caller. +void +__set_invalid(struct bregs *regs, int lineno, const char *fname) +{ + __warn_invalid(regs, lineno, fname); + set_invalid_silent(regs); +} + +// Report a call of an unimplemented function. +void +__set_unimplemented(struct bregs *regs, int lineno, const char *fname) +{ + __warn_unimplemented(regs, lineno, fname); + set_invalid_silent(regs); +} + +// Report a handler reporting an invalid parameter code to the +// caller. Note, the lineno and return code are encoded in the same +// parameter as gcc does a better job of scheduling function calls +// when there are 3 or less parameters. +void +__set_code_invalid(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + u32 lineno = linecode >> 8; + __warn_invalid(regs, lineno, fname); + set_code_invalid_silent(regs, code); +} + +// Report a call of an unimplemented function. +void +__set_code_unimplemented(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + u32 lineno = linecode >> 8; + __warn_unimplemented(regs, lineno, fname); + set_code_invalid_silent(regs, code); +} diff --git a/qemu/roms/seabios/src/output.h b/qemu/roms/seabios/src/output.h new file mode 100644 index 000000000..14288cf50 --- /dev/null +++ b/qemu/roms/seabios/src/output.h @@ -0,0 +1,68 @@ +#ifndef __OUTPUT_H +#define __OUTPUT_H + +#include "config.h" // CONFIG_DEBUG_LEVEL +#include "types.h" // u32 + +// output.c +void debug_banner(void); +void panic(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))) __noreturn; +void printf(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); +int snprintf(char *str, size_t size, const char *fmt, ...) + __attribute__ ((format (printf, 3, 4))); +char * znprintf(size_t size, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +void __dprintf(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); +struct bregs; +void __debug_enter(struct bregs *regs, const char *fname); +void __debug_isr(const char *fname); +void __debug_stub(struct bregs *regs, int lineno, const char *fname); +void __warn_invalid(struct bregs *regs, int lineno, const char *fname); +void __warn_unimplemented(struct bregs *regs, int lineno, const char *fname); +void __warn_internalerror(int lineno, const char *fname); +void __warn_noalloc(int lineno, const char *fname); +void __warn_timeout(int lineno, const char *fname); +void __set_invalid(struct bregs *regs, int lineno, const char *fname); +void __set_unimplemented(struct bregs *regs, int lineno, const char *fname); +void __set_code_invalid(struct bregs *regs, u32 linecode, const char *fname); +void __set_code_unimplemented(struct bregs *regs, u32 linecode + , const char *fname); +void hexdump(const void *d, int len); + +#define dprintf(lvl, fmt, args...) do { \ + if (CONFIG_DEBUG_LEVEL && (lvl) <= CONFIG_DEBUG_LEVEL) \ + __dprintf((fmt) , ##args ); \ + } while (0) +#define debug_enter(regs, lvl) do { \ + if ((lvl) && (lvl) <= CONFIG_DEBUG_LEVEL) \ + __debug_enter((regs), __func__); \ + } while (0) +#define debug_isr(lvl) do { \ + if ((lvl) && (lvl) <= CONFIG_DEBUG_LEVEL) \ + __debug_isr(__func__); \ + } while (0) +#define debug_stub(regs) \ + __debug_stub((regs), __LINE__, __func__) +#define warn_invalid(regs) \ + __warn_invalid((regs), __LINE__, __func__) +#define warn_unimplemented(regs) \ + __warn_unimplemented((regs), __LINE__, __func__) +#define warn_internalerror() \ + __warn_internalerror(__LINE__, __func__) +#define warn_noalloc() \ + __warn_noalloc(__LINE__, __func__) +#define warn_timeout() \ + __warn_timeout(__LINE__, __func__) +#define set_invalid(regs) \ + __set_invalid((regs), __LINE__, __func__) +#define set_code_invalid(regs, code) \ + __set_code_invalid((regs), (code) | (__LINE__ << 8), __func__) +#define set_unimplemented(regs) \ + __set_unimplemented((regs), __LINE__, __func__) +#define set_code_unimplemented(regs, code) \ + __set_code_unimplemented((regs), (code) | (__LINE__ << 8), __func__) + +#endif // output.h diff --git a/qemu/roms/seabios/src/pcibios.c b/qemu/roms/seabios/src/pcibios.c new file mode 100644 index 000000000..7e5d972d9 --- /dev/null +++ b/qemu/roms/seabios/src/pcibios.c @@ -0,0 +1,240 @@ +// PCI BIOS (int 1a/b1) calls +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "output.h" // dprintf +#include "std/pirtable.h" // struct pir_header +#include "string.h" // checksum +#include "util.h" // handle_1ab1 + +// romlayout.S +extern void entry_bios32(void); +extern void entry_pcibios32(void); + +#define RET_FUNC_NOT_SUPPORTED 0x81 +#define RET_BAD_VENDOR_ID 0x83 +#define RET_DEVICE_NOT_FOUND 0x86 +#define RET_BUFFER_TOO_SMALL 0x89 + +// installation check +static void +handle_1ab101(struct bregs *regs) +{ + regs->al = 0x01; // Flags - "Config Mechanism #1" supported. + regs->bx = 0x0210; // PCI version 2.10 + regs->cl = GET_GLOBAL(MaxPCIBus); + regs->edx = 0x20494350; // "PCI " + regs->edi = (u32)entry_pcibios32 + BUILD_BIOS_ADDR; + set_code_success(regs); +} + +// find pci device +static void +handle_1ab102(struct bregs *regs) +{ + u32 id = (regs->cx << 16) | regs->dx; + int count = regs->si; + int bus = -1; + while (bus < GET_GLOBAL(MaxPCIBus)) { + bus++; + int bdf; + foreachbdf(bdf, bus) { + u32 v = pci_config_readl(bdf, PCI_VENDOR_ID); + if (v != id) + continue; + if (count--) + continue; + regs->bx = bdf; + set_code_success(regs); + return; + } + } + set_code_invalid(regs, RET_DEVICE_NOT_FOUND); +} + +// find class code +static void +handle_1ab103(struct bregs *regs) +{ + int count = regs->si; + u32 classprog = regs->ecx; + int bus = -1; + while (bus < GET_GLOBAL(MaxPCIBus)) { + bus++; + int bdf; + foreachbdf(bdf, bus) { + u32 v = pci_config_readl(bdf, PCI_CLASS_REVISION); + if ((v>>8) != classprog) + continue; + if (count--) + continue; + regs->bx = bdf; + set_code_success(regs); + return; + } + } + set_code_invalid(regs, RET_DEVICE_NOT_FOUND); +} + +// read configuration byte +static void +handle_1ab108(struct bregs *regs) +{ + regs->cl = pci_config_readb(regs->bx, regs->di); + set_code_success(regs); +} + +// read configuration word +static void +handle_1ab109(struct bregs *regs) +{ + regs->cx = pci_config_readw(regs->bx, regs->di); + set_code_success(regs); +} + +// read configuration dword +static void +handle_1ab10a(struct bregs *regs) +{ + regs->ecx = pci_config_readl(regs->bx, regs->di); + set_code_success(regs); +} + +// write configuration byte +static void +handle_1ab10b(struct bregs *regs) +{ + pci_config_writeb(regs->bx, regs->di, regs->cl); + set_code_success(regs); +} + +// write configuration word +static void +handle_1ab10c(struct bregs *regs) +{ + pci_config_writew(regs->bx, regs->di, regs->cx); + set_code_success(regs); +} + +// write configuration dword +static void +handle_1ab10d(struct bregs *regs) +{ + pci_config_writel(regs->bx, regs->di, regs->ecx); + set_code_success(regs); +} + +// get irq routing options +static void +handle_1ab10e(struct bregs *regs) +{ + struct pir_header *pirtable_gf = GET_GLOBAL(PirAddr); + if (! pirtable_gf) { + set_code_invalid(regs, RET_FUNC_NOT_SUPPORTED); + return; + } + struct pir_header *pirtable_g = GLOBALFLAT2GLOBAL(pirtable_gf); + + struct param_s { + u16 size; + u16 buf_off; + u16 buf_seg; + } *param_far = (void*)(regs->di+0); + + // Validate and update size. + u16 bufsize = GET_FARVAR(regs->es, param_far->size); + u16 pirsize = GET_GLOBAL(pirtable_g->size) - sizeof(struct pir_header); + SET_FARVAR(regs->es, param_far->size, pirsize); + if (bufsize < pirsize) { + set_code_invalid(regs, RET_BUFFER_TOO_SMALL); + return; + } + + // Get dest buffer. + void *buf_far = (void*)(GET_FARVAR(regs->es, param_far->buf_off)+0); + u16 buf_seg = GET_FARVAR(regs->es, param_far->buf_seg); + + // Memcpy pir table slots to dest buffer. + memcpy_far(buf_seg, buf_far + , get_global_seg() + , (void*)(pirtable_g->slots) + get_global_offset() + , pirsize); + + // XXX - bochs bios sets bx to (1 << 9) | (1 << 11) + regs->bx = GET_GLOBAL(pirtable_g->exclusive_irqs); + set_code_success(regs); +} + +static void +handle_1ab1XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_FUNC_NOT_SUPPORTED); +} + +void +handle_1ab1(struct bregs *regs) +{ + //debug_stub(regs); + + if (! CONFIG_PCIBIOS) { + set_invalid(regs); + return; + } + + switch (regs->al) { + case 0x01: handle_1ab101(regs); break; + case 0x02: handle_1ab102(regs); break; + case 0x03: handle_1ab103(regs); break; + case 0x08: handle_1ab108(regs); break; + case 0x09: handle_1ab109(regs); break; + case 0x0a: handle_1ab10a(regs); break; + case 0x0b: handle_1ab10b(regs); break; + case 0x0c: handle_1ab10c(regs); break; + case 0x0d: handle_1ab10d(regs); break; + case 0x0e: handle_1ab10e(regs); break; + default: handle_1ab1XX(regs); break; + } +} + +// Entry point for pci bios functions. +void VISIBLE16 VISIBLE32SEG +handle_pcibios(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_pcibios); + handle_1ab1(regs); +} + + +/**************************************************************** + * 32bit interface + ****************************************************************/ + +struct bios32_s { + u32 signature; + u32 entry; + u8 version; + u8 length; + u8 checksum; + u8 reserved[5]; +} PACKED; + +struct bios32_s BIOS32HEADER __aligned(16) VARFSEG = { + .signature = 0x5f32335f, // _32_ + .length = sizeof(BIOS32HEADER) / 16, +}; + +void +bios32_init(void) +{ + dprintf(3, "init bios32\n"); + + BIOS32HEADER.entry = (u32)entry_bios32; + BIOS32HEADER.checksum -= checksum(&BIOS32HEADER, sizeof(BIOS32HEADER)); +} diff --git a/qemu/roms/seabios/src/pmm.c b/qemu/roms/seabios/src/pmm.c new file mode 100644 index 000000000..304faab2c --- /dev/null +++ b/qemu/roms/seabios/src/pmm.c @@ -0,0 +1,163 @@ +// Post memory manager (PMM) calls +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // FUNC16 +#include "config.h" // CONFIG_* +#include "malloc.h" // _malloc +#include "output.h" // dprintf +#include "std/pmm.h" // PMM_SIGNATURE +#include "string.h" // checksum +#include "util.h" // pmm_init +#include "x86.h" // __ffs + +extern struct pmmheader PMMHEADER; + +#if CONFIG_PMM +struct pmmheader PMMHEADER __aligned(16) VARFSEG = { + .signature = PMM_SIGNATURE, + .version = 0x01, + .length = sizeof(PMMHEADER), +}; +#endif + +// PMM - allocate +static u32 +handle_pmm00(u16 *args) +{ + u32 length = *(u32*)&args[1], handle = *(u32*)&args[3]; + u16 flags = args[5]; + dprintf(3, "pmm00: length=%x handle=%x flags=%x\n" + , length, handle, flags); + struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh; + if (flags & 8) { + // Permanent memory request. + lowzone = &ZoneLow; + highzone = &ZoneHigh; + } + if (!length) { + // Memory size request + switch (flags & 3) { + default: + case 0: + return 0; + case 1: + return malloc_getspace(lowzone); + case 2: + return malloc_getspace(highzone); + case 3: { + u32 spacelow = malloc_getspace(lowzone); + u32 spacehigh = malloc_getspace(highzone); + if (spacelow > spacehigh) + return spacelow; + return spacehigh; + } + } + } + u32 size = length * 16; + if ((s32)size <= 0) + return 0; + u32 align = MALLOC_MIN_ALIGN; + if (flags & 4) { + align = 1<<__ffs(size); + if (align < MALLOC_MIN_ALIGN) + align = MALLOC_MIN_ALIGN; + } + void *data; + switch (flags & 3) { + default: + case 0: + return 0; + case 1: + data = _malloc(lowzone, size, align); + break; + case 2: + data = _malloc(highzone, size, align); + break; + case 3: { + data = _malloc(lowzone, size, align); + if (!data) + data = _malloc(highzone, size, align); + } + } + if (data && handle != MALLOC_DEFAULT_HANDLE) + malloc_sethandle(data, handle); + return (u32)data; +} + +// PMM - find +static u32 +handle_pmm01(u16 *args) +{ + u32 handle = *(u32*)&args[1]; + dprintf(3, "pmm01: handle=%x\n", handle); + if (handle == MALLOC_DEFAULT_HANDLE) + return 0; + return (u32)malloc_findhandle(handle); +} + +// PMM - deallocate +static u32 +handle_pmm02(u16 *args) +{ + u32 buffer = *(u32*)&args[1]; + dprintf(3, "pmm02: buffer=%x\n", buffer); + int ret = _free((void*)buffer); + if (ret) + // Error + return 1; + return 0; +} + +static u32 +handle_pmmXX(u16 *args) +{ + return PMM_FUNCTION_NOT_SUPPORTED; +} + +u32 VISIBLE32INIT +handle_pmm(u16 *args) +{ + ASSERT32FLAT(); + if (! CONFIG_PMM) + return PMM_FUNCTION_NOT_SUPPORTED; + + u16 arg1 = args[0]; + dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1); + + u32 ret; + switch (arg1) { + case 0x00: ret = handle_pmm00(args); break; + case 0x01: ret = handle_pmm01(args); break; + case 0x02: ret = handle_pmm02(args); break; + default: ret = handle_pmmXX(args); break; + } + + return ret; +} + +void +pmm_init(void) +{ + if (! CONFIG_PMM) + return; + + dprintf(3, "init PMM\n"); + + PMMHEADER.entry = FUNC16(entry_pmm); + PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER)); +} + +void +pmm_prepboot(void) +{ + if (! CONFIG_PMM) + return; + + dprintf(3, "finalize PMM\n"); + + PMMHEADER.signature = 0; + PMMHEADER.entry.segoff = 0; +} diff --git a/qemu/roms/seabios/src/pnpbios.c b/qemu/roms/seabios/src/pnpbios.c new file mode 100644 index 000000000..95ce21f8f --- /dev/null +++ b/qemu/roms/seabios/src/pnpbios.c @@ -0,0 +1,88 @@ +// PNP BIOS calls +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // BUILD_BIOS_ADDR +#include "farptr.h" // SET_FARVAR +#include "output.h" // dprintf +#include "std/pnpbios.h" // PNP_SIGNATURE +#include "string.h" // checksum +#include "util.h" // pnp_init + +extern struct pnpheader PNPHEADER; +extern char pnp_string[]; + +#if CONFIG_PNPBIOS +struct pnpheader PNPHEADER __aligned(16) VARFSEG = { + .signature = PNP_SIGNATURE, + .version = 0x10, + .length = sizeof(PNPHEADER), + .real_cs = SEG_BIOS, + .prot_base = BUILD_BIOS_ADDR, + .real_ds = SEG_BIOS, + .prot_database = BUILD_BIOS_ADDR, +}; +#else +// We need a copy of this string in the 0xf000 segment, but we are not +// actually a PnP BIOS, so make sure it is *not* aligned, so OSes will +// not see it if they scan. +char pnp_string[] __aligned(2) VARFSEG = " $PnP"; +#endif + +// BBS - Get Version and Installation Check +static u16 +handle_pnp60(u16 *args) +{ + u16 version_ptr = args[1]; + u16 version_seg = args[2]; + SET_FARVAR(version_seg, *(u16*)(version_ptr+0), 0x0101); + return 0; +} + +static u16 +handle_pnpXX(u16 *args) +{ + return FUNCTION_NOT_SUPPORTED; +} + +u16 VISIBLE16 +handle_pnp(u16 *args) +{ + if (! CONFIG_PNPBIOS) + return FUNCTION_NOT_SUPPORTED; + + u16 arg1 = args[0]; + dprintf(DEBUG_HDL_pnp, "pnp call arg1=%x\n", arg1); + + switch (arg1) { + case 0x60: return handle_pnp60(args); + default: return handle_pnpXX(args); + } +} + +u16 +get_pnp_offset(void) +{ + if (! CONFIG_PNPBIOS) + return (u32)pnp_string + 1 - BUILD_BIOS_ADDR; + return (u32)&PNPHEADER - BUILD_BIOS_ADDR; +} + +// romlayout.S +extern void entry_pnp_real(void); +extern void entry_pnp_prot(void); + +void +pnp_init(void) +{ + if (! CONFIG_PNPBIOS) + return; + + dprintf(3, "init PNPBIOS table\n"); + + PNPHEADER.real_ip = (u32)entry_pnp_real - BUILD_BIOS_ADDR; + PNPHEADER.prot_ip = (u32)entry_pnp_prot - BUILD_BIOS_ADDR; + PNPHEADER.checksum -= checksum(&PNPHEADER, sizeof(PNPHEADER)); +} diff --git a/qemu/roms/seabios/src/post.c b/qemu/roms/seabios/src/post.c new file mode 100644 index 000000000..9ea5620c9 --- /dev/null +++ b/qemu/roms/seabios/src/post.c @@ -0,0 +1,336 @@ +// 32bit code to Power On Self Test (POST) a machine. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // qemu_cfg_preinit +#include "fw/xen.h" // xen_preinit +#include "hw/ahci.h" // ahci_setup +#include "hw/ata.h" // ata_setup +#include "hw/esp-scsi.h" // esp_scsi_setup +#include "hw/lsi-scsi.h" // lsi_scsi_setup +#include "hw/megasas.h" // megasas_setup +#include "hw/pvscsi.h" // pvscsi_setup +#include "hw/pic.h" // pic_setup +#include "hw/ps2port.h" // ps2port_setup +#include "hw/rtc.h" // rtc_write +#include "hw/serialio.h" // serial_debug_preinit +#include "hw/usb.h" // usb_setup +#include "hw/virtio-blk.h" // virtio_blk_setup +#include "hw/virtio-scsi.h" // virtio_scsi_setup +#include "malloc.h" // malloc_init +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "string.h" // memset +#include "util.h" // kbd_init + + +/**************************************************************** + * BIOS initialization and hardware setup + ****************************************************************/ + +static void +ivt_init(void) +{ + dprintf(3, "init ivt\n"); + + // Setup reset-vector entry point (controls legacy reboots). + HaveRunPost = 1; + rtc_write(CMOS_RESET_CODE, 0); + + // Initialize all vectors to the default handler. + int i; + for (i=0; i<256; i++) + SET_IVT(i, FUNC16(entry_iret_official)); + + // Initialize all hw vectors to a default hw handler. + for (i=BIOS_HWIRQ0_VECTOR; i<BIOS_HWIRQ0_VECTOR+8; i++) + SET_IVT(i, FUNC16(entry_hwpic1)); + for (i=BIOS_HWIRQ8_VECTOR; i<BIOS_HWIRQ8_VECTOR+8; i++) + SET_IVT(i, FUNC16(entry_hwpic2)); + + // Initialize software handlers. + SET_IVT(0x02, FUNC16(entry_02)); + SET_IVT(0x10, FUNC16(entry_10)); + SET_IVT(0x11, FUNC16(entry_11)); + SET_IVT(0x12, FUNC16(entry_12)); + SET_IVT(0x13, FUNC16(entry_13_official)); + SET_IVT(0x14, FUNC16(entry_14)); + SET_IVT(0x15, FUNC16(entry_15_official)); + SET_IVT(0x16, FUNC16(entry_16)); + SET_IVT(0x17, FUNC16(entry_17)); + SET_IVT(0x18, FUNC16(entry_18)); + SET_IVT(0x19, FUNC16(entry_19_official)); + SET_IVT(0x1a, FUNC16(entry_1a_official)); + SET_IVT(0x40, FUNC16(entry_40)); + + // INT 60h-66h reserved for user interrupt + for (i=0x60; i<=0x66; i++) + SET_IVT(i, SEGOFF(0, 0)); + + // set vector 0x79 to zero + // this is used by 'gardian angel' protection system + SET_IVT(0x79, SEGOFF(0, 0)); +} + +static void +bda_init(void) +{ + dprintf(3, "init bda\n"); + + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + memset(bda, 0, sizeof(*bda)); + + int esize = EBDA_SIZE_START; + u16 ebda_seg = EBDA_SEGMENT_START; + extern u8 final_varlow_start[]; + if (!CONFIG_MALLOC_UPPERMEMORY) + ebda_seg = FLATPTR_TO_SEG(ALIGN_DOWN((u32)final_varlow_start, 1024) + - EBDA_SIZE_START*1024); + SET_BDA(ebda_seg, ebda_seg); + + SET_BDA(mem_size_kb, ebda_seg / (1024/16)); + + // Init ebda + struct extended_bios_data_area_s *ebda = get_ebda_ptr(); + memset(ebda, 0, sizeof(*ebda)); + ebda->size = esize; + + add_e820((u32)ebda, BUILD_LOWRAM_END-(u32)ebda, E820_RESERVED); + + // Init extra stack + StackPos = (void*)(&ExtraStack[BUILD_EXTRA_STACK_SIZE] - zonelow_base); +} + +void +interface_init(void) +{ + // Running at new code address - do code relocation fixups + malloc_init(); + + // Setup romfile items. + qemu_cfg_init(); + coreboot_cbfs_init(); + + // Setup ivt/bda/ebda + ivt_init(); + bda_init(); + + // Other interfaces + thread_init(); + boot_init(); + bios32_init(); + pmm_init(); + pnp_init(); + kbd_init(); + mouse_init(); +} + +// Initialize hardware devices +void +device_hardware_setup(void) +{ + usb_setup(); + ps2port_setup(); + lpt_setup(); + serial_setup(); + + floppy_setup(); + ata_setup(); + ahci_setup(); + sdcard_setup(); + cbfs_payload_setup(); + ramdisk_setup(); + virtio_blk_setup(); + virtio_scsi_setup(); + lsi_scsi_setup(); + esp_scsi_setup(); + megasas_setup(); + pvscsi_setup(); +} + +static void +platform_hardware_setup(void) +{ + // Enable CPU caching + setcr0(getcr0() & ~(CR0_CD|CR0_NW)); + + // Make sure legacy DMA isn't running. + dma_setup(); + + // Init base pc hardware. + pic_setup(); + mathcp_setup(); + timer_setup(); + clock_setup(); + + // Platform specific setup + qemu_platform_setup(); + coreboot_platform_setup(); +} + +void +prepareboot(void) +{ + // Run BCVs + bcv_prepboot(); + + // Finalize data structures before boot + cdrom_prepboot(); + pmm_prepboot(); + malloc_prepboot(); + memmap_prepboot(); + + HaveRunPost = 2; + + // Setup bios checksum. + BiosChecksum -= checksum((u8*)BUILD_BIOS_ADDR, BUILD_BIOS_SIZE); +} + +// Begin the boot process by invoking an int0x19 in 16bit mode. +void VISIBLE32FLAT +startBoot(void) +{ + // Clear low-memory allocations (required by PMM spec). + memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR); + + dprintf(3, "Jump to int19\n"); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x19, &br); +} + +// Main setup code. +static void +maininit(void) +{ + // Initialize internal interfaces. + interface_init(); + + // Setup platform devices. + platform_hardware_setup(); + + // Start hardware initialization (if threads allowed during optionroms) + if (threads_during_optionroms()) + device_hardware_setup(); + + // Run vga option rom + vgarom_setup(); + + // Do hardware initialization (if running synchronously) + if (!threads_during_optionroms()) { + device_hardware_setup(); + wait_threads(); + } + + // Run option roms + optionrom_setup(); + + // Allow user to modify overall boot order. + interactive_bootmenu(); + wait_threads(); + + // Prepare for boot. + prepareboot(); + + // Write protect bios memory. + make_bios_readonly(); + + // Invoke int 19 to start boot process. + startBoot(); +} + + +/**************************************************************** + * POST entry and code relocation + ****************************************************************/ + +// Update given relocs for the code at 'dest' with a given 'delta' +static void +updateRelocs(void *dest, u32 *rstart, u32 *rend, u32 delta) +{ + u32 *reloc; + for (reloc = rstart; reloc < rend; reloc++) + *((u32*)(dest + *reloc)) += delta; +} + +// Relocate init code and then call a function at its new address. +// The passed function should be in the "init" section and must not +// return. +void __noreturn +reloc_preinit(void *f, void *arg) +{ + void (*func)(void *) __noreturn = f; + if (!CONFIG_RELOCATE_INIT) + func(arg); + // Symbols populated by the build. + extern u8 code32flat_start[]; + extern u8 _reloc_min_align; + extern u32 _reloc_abs_start[], _reloc_abs_end[]; + extern u32 _reloc_rel_start[], _reloc_rel_end[]; + extern u32 _reloc_init_start[], _reloc_init_end[]; + extern u8 code32init_start[], code32init_end[]; + + // Allocate space for init code. + u32 initsize = code32init_end - code32init_start; + u32 codealign = (u32)&_reloc_min_align; + void *codedest = memalign_tmp(codealign, initsize); + if (!codedest) + panic("No space for init relocation.\n"); + + // Copy code and update relocs (init absolute, init relative, and runtime) + dprintf(1, "Relocating init from %p to %p (size %d)\n" + , code32init_start, codedest, initsize); + s32 delta = codedest - (void*)code32init_start; + memcpy(codedest, code32init_start, initsize); + updateRelocs(codedest, _reloc_abs_start, _reloc_abs_end, delta); + updateRelocs(codedest, _reloc_rel_start, _reloc_rel_end, -delta); + updateRelocs(code32flat_start, _reloc_init_start, _reloc_init_end, delta); + if (f >= (void*)code32init_start && f < (void*)code32init_end) + func = f + delta; + + // Call function in relocated code. + barrier(); + func(arg); +} + +// Setup for code relocation and then relocate. +void VISIBLE32INIT +dopost(void) +{ + // Detect ram and setup internal malloc. + qemu_preinit(); + coreboot_preinit(); + malloc_preinit(); + + // Relocate initialization code and call maininit(). + reloc_preinit(maininit, NULL); +} + +// Entry point for Power On Self Test (POST) - the BIOS initilization +// phase. This function makes the memory at 0xc0000-0xfffff +// read/writable and then calls dopost(). +void VISIBLE32FLAT +handle_post(void) +{ + if (!CONFIG_QEMU && !CONFIG_COREBOOT) + return; + + serial_debug_preinit(); + debug_banner(); + + // Check if we are running under Xen. + xen_preinit(); + + // Allow writes to modify bios area (0xf0000) + make_bios_writable(); + + // Now that memory is read/writable - start post process. + dopost(); +} diff --git a/qemu/roms/seabios/src/resume.c b/qemu/roms/seabios/src/resume.c new file mode 100644 index 000000000..19031747c --- /dev/null +++ b/qemu/roms/seabios/src/resume.c @@ -0,0 +1,158 @@ +// Code for handling calls to "post" that are resume related. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "farptr.h" // FLATPTR_TO_SEGOFF +#include "hw/pci.h" // pci_reboot +#include "hw/pic.h" // pic_eoi2 +#include "hw/ps2port.h" // i8042_reboot +#include "hw/rtc.h" // rtc_read +#include "output.h" // dprintf +#include "stacks.h" // farcall16big +#include "std/bda.h" // struct bios_data_area_s +#include "string.h" // memset +#include "util.h" // dma_setup + +// Handler for post calls that look like a resume. +void VISIBLE16 +handle_resume(void) +{ + ASSERT16(); + int status = rtc_read(CMOS_RESET_CODE); + rtc_write(CMOS_RESET_CODE, 0); + dprintf(1, "In resume (status=%d)\n", status); + + dma_setup(); + + switch (status) { + case 0x01 ... 0x04: + case 0x06 ... 0x09: + panic("Unimplemented shutdown status: %02x\n", status); + + case 0x05: + // flush keyboard (issue EOI) and jump via 40h:0067h + pic_eoi2(); + // NO BREAK + case 0x0a: +#define BDA_JUMP (((struct bios_data_area_s *)0)->jump) + // resume execution by jump via 40h:0067h + asm volatile( + "movw %w1, %%ds\n" + "ljmpw *%0\n" + : : "m"(BDA_JUMP), "r"(SEG_BDA) + ); + break; + + case 0x0b: + // resume execution via IRET via 40h:0067h + asm volatile( + "movw %w1, %%ds\n" + "lssw %0, %%sp\n" + "iretw\n" + : : "m"(BDA_JUMP), "r"(SEG_BDA) + ); + break; + + case 0x0c: + // resume execution via RETF via 40h:0067h + asm volatile( + "movw %w1, %%ds\n" + "lssw %0, %%sp\n" + "lretw\n" + : : "m"(BDA_JUMP), "r"(SEG_BDA) + ); + break; + + default: + break; + } + + // Not a 16bit resume - do remaining checks in 32bit mode + asm volatile( + "movw %w1, %%ss\n" + "movl %0, %%esp\n" + "movl $_cfunc32flat_handle_resume32, %%edx\n" + "jmp transition32\n" + : : "i"(BUILD_S3RESUME_STACK_ADDR), "r"(0), "a"(status) + ); +} + +// Handle an S3 resume event +static void +s3_resume(void) +{ + if (!CONFIG_S3_RESUME) + return; + + u32 s3_resume_vector = find_resume_vector(); + if (!s3_resume_vector) { + dprintf(1, "No resume vector set!\n"); + return; + } + + pic_setup(); + smm_setup(); + + pci_resume(); + + s3_resume_vga(); + + make_bios_readonly(); + + // Invoke the resume vector. + struct bregs br; + memset(&br, 0, sizeof(br)); + dprintf(1, "Jump to resume vector (%x)\n", s3_resume_vector); + br.code = FLATPTR_TO_SEGOFF((void*)s3_resume_vector); + farcall16big(&br); +} + +u8 HaveAttemptedReboot VARLOW; + +// Attempt to invoke a hard-reboot. +static void +tryReboot(void) +{ + if (HaveAttemptedReboot) { + // Hard reboot has failed - try to shutdown machine. + dprintf(1, "Unable to hard-reboot machine - attempting shutdown.\n"); + apm_shutdown(); + } + HaveAttemptedReboot = 1; + + dprintf(1, "Attempting a hard reboot\n"); + + // Setup for reset on qemu. + qemu_prep_reset(); + + // Reboot using ACPI RESET_REG + acpi_reboot(); + + // Try keyboard controller reboot. + i8042_reboot(); + + // Try PCI 0xcf9 reboot + pci_reboot(); + + // Try triple fault + asm volatile("int3"); + + panic("Could not reboot"); +} + +void VISIBLE32FLAT +handle_resume32(int status) +{ + ASSERT32FLAT(); + dprintf(1, "In 32bit resume\n"); + + if (status == 0xfe) + s3_resume(); + + // Must be a soft reboot - invoke a hard reboot. + tryReboot(); +} diff --git a/qemu/roms/seabios/src/romfile.c b/qemu/roms/seabios/src/romfile.c new file mode 100644 index 000000000..42261a624 --- /dev/null +++ b/qemu/roms/seabios/src/romfile.c @@ -0,0 +1,100 @@ +// Access to pseudo "file" interface for configuration information. +// +// Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // struct romfile_s +#include "string.h" // memcmp + +static struct romfile_s *RomfileRoot VARVERIFY32INIT; + +void +romfile_add(struct romfile_s *file) +{ + dprintf(3, "Add romfile: %s (size=%d)\n", file->name, file->size); + file->next = RomfileRoot; + RomfileRoot = file; +} + +// Search for the specified file. +static struct romfile_s * +__romfile_findprefix(const char *prefix, int prefixlen, struct romfile_s *prev) +{ + struct romfile_s *cur = RomfileRoot; + if (prev) + cur = prev->next; + while (cur) { + if (memcmp(prefix, cur->name, prefixlen) == 0) + return cur; + cur = cur->next; + } + return NULL; +} + +struct romfile_s * +romfile_findprefix(const char *prefix, struct romfile_s *prev) +{ + return __romfile_findprefix(prefix, strlen(prefix), prev); +} + +struct romfile_s * +romfile_find(const char *name) +{ + return __romfile_findprefix(name, strlen(name) + 1, NULL); +} + +// Helper function to find, malloc_tmphigh, and copy a romfile. This +// function adds a trailing zero to the malloc'd copy. +void * +romfile_loadfile(const char *name, int *psize) +{ + struct romfile_s *file = romfile_find(name); + if (!file) + return NULL; + + int filesize = file->size; + if (!filesize) + return NULL; + + char *data = malloc_tmphigh(filesize+1); + if (!data) { + warn_noalloc(); + return NULL; + } + + dprintf(5, "Copying romfile '%s' (len %d)\n", name, filesize); + int ret = file->copy(file, data, filesize); + if (ret < 0) { + free(data); + return NULL; + } + if (psize) + *psize = filesize; + data[filesize] = '\0'; + return data; +} + +// Attempt to load an integer from the given file - return 'defval' +// if unsuccessful. +u64 +romfile_loadint(const char *name, u64 defval) +{ + struct romfile_s *file = romfile_find(name); + if (!file) + return defval; + + int filesize = file->size; + if (!filesize || filesize > sizeof(u64) || (filesize & (filesize-1))) + // Doesn't look like a valid integer. + return defval; + + u64 val = 0; + int ret = file->copy(file, &val, sizeof(val)); + if (ret < 0) + return defval; + return val; +} diff --git a/qemu/roms/seabios/src/romfile.h b/qemu/roms/seabios/src/romfile.h new file mode 100644 index 000000000..c6d62a1dd --- /dev/null +++ b/qemu/roms/seabios/src/romfile.h @@ -0,0 +1,19 @@ +#ifndef __ROMFILE_H +#define __ROMFILE_H + +#include "types.h" // u32 + +// romfile.c +struct romfile_s { + struct romfile_s *next; + char name[128]; + u32 size; + int (*copy)(struct romfile_s *file, void *dest, u32 maxlen); +}; +void romfile_add(struct romfile_s *file); +struct romfile_s *romfile_findprefix(const char *prefix, struct romfile_s *prev); +struct romfile_s *romfile_find(const char *name); +void *romfile_loadfile(const char *name, int *psize); +u64 romfile_loadint(const char *name, u64 defval); + +#endif // romfile.h diff --git a/qemu/roms/seabios/src/romlayout.S b/qemu/roms/seabios/src/romlayout.S new file mode 100644 index 000000000..93b6874e7 --- /dev/null +++ b/qemu/roms/seabios/src/romlayout.S @@ -0,0 +1,659 @@ +// Rom layout and bios assembler to C interface. +// +// Copyright (C) 2008-2012 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "asm-offsets.h" // BREGS_* +#include "config.h" // CONFIG_* +#include "entryfuncs.S" // ENTRY_* +#include "hw/rtc.h" // CMOS_RESET_CODE +#include "x86.h" // CR0_* + + .code16 + + +/**************************************************************** + * 16bit / 32bit call trampolines + ****************************************************************/ + +// Place CPU into 32bit mode from 16bit mode. +// %edx = return location (in 32bit mode) +// Clobbers: ecx, flags, segment registers, cr0, idt/gdt + DECLFUNC transition32 +transition32_nmi_off: + // transition32 when NMI and A20 are already initialized + movl %eax, %ecx + jmp 1f +transition32: + movl %eax, %ecx + + // Disable irqs (and clear direction flag) + cli + cld + + // Disable nmi + movl $CMOS_RESET_CODE|NMI_DISABLE_BIT, %eax + outb %al, $PORT_CMOS_INDEX + inb $PORT_CMOS_DATA, %al + + // enable a20 + inb $PORT_A20, %al + orb $A20_ENABLE_BIT, %al + outb %al, $PORT_A20 + + // Set segment descriptors +1: lidtw %cs:pmode_IDT_info + lgdtw %cs:rombios32_gdt_48 + + // Enable protected mode + movl %cr0, %eax + orl $CR0_PE, %eax + movl %eax, %cr0 + + // start 32bit protected mode code + ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 2f) + + .code32 + // init data segments +2: movl $SEG32_MODE32_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + movl %ecx, %eax + jmpl *%edx + .code16 + +// Place CPU into 16bit mode from 32bit mode. +// %edx = return location (in 16bit mode) +// Clobbers: ecx, flags, segment registers, cr0, idt/gdt + DECLFUNC transition16 + .global transition16big + .code32 +transition16: + movl %eax, %ecx + + // restore data segment limits to 0xffff + movl $SEG32_MODE16_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + +#if CONFIG_DISABLE_A20 + // disable a20 + inb $PORT_A20, %al + andb $~A20_ENABLE_BIT, %al + outb %al, $PORT_A20 +#endif + + // Jump to 16bit mode + ljmpw $SEG32_MODE16_CS, $1f + +transition16big: + movl %eax, %ecx + + movl $SEG32_MODE16BIG_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + ljmpw $SEG32_MODE16BIG_CS, $1f + + .code16 +1: + // Disable protected mode + movl %cr0, %eax + andl $~CR0_PE, %eax + movl %eax, %cr0 + + // far jump to flush CPU queue after transition to real mode + ljmpw $SEG_BIOS, $2f + +2: + // restore IDT to normal real-mode defaults + lidtw %cs:rmode_IDT_info + + // Clear segment registers + xorw %ax, %ax + movw %ax, %fs + movw %ax, %gs + movw %ax, %es + movw %ax, %ds + movw %ax, %ss // Assume stack is in segment 0 + + movl %ecx, %eax + jmpl *%edx + + +/**************************************************************** + * External calling trampolines + ****************************************************************/ + +// Far call a 16bit function from 16bit mode with a specified cpu register state +// %eax = address of struct bregs, %edx = segment of struct bregs +// Clobbers: %e[bc]x, %e[ds]i, flags + DECLFUNC __farcall16 +__farcall16: + // Save %edx/%eax, %ebp + pushl %ebp + pushl %eax + pushl %edx + + // Setup for iretw call + movl %edx, %ds + pushw %cs + pushw $1f // return point + pushw BREGS_flags(%eax) // flags + pushl BREGS_code(%eax) // CS:IP + + // Load calling registers and invoke call + RESTOREBREGS_DSEAX + iretw // XXX - just do a lcalll +1: + // Store flags, es, eax + pushfw + cli + cld + pushw %ds + pushl %eax + movw 0x08(%esp), %ds + movl 0x0c(%esp), %eax + SAVEBREGS_POP_DSEAX + popw BREGS_flags(%eax) + movw %ss, %cx + movw %cx, %ds // Restore %ds == %ss + + // Remove %edx/%eax, restore %ebp + popl %edx + popl %eax + popl %ebp + + retl + +// IRQ trampolines + .macro IRQ_TRAMPOLINE num + DECLFUNC irq_trampoline_0x\num + irq_trampoline_0x\num : + int $0x\num + lretw + .endm + + IRQ_TRAMPOLINE 02 + IRQ_TRAMPOLINE 10 + IRQ_TRAMPOLINE 13 + IRQ_TRAMPOLINE 15 + IRQ_TRAMPOLINE 16 + IRQ_TRAMPOLINE 18 + IRQ_TRAMPOLINE 19 + IRQ_TRAMPOLINE 1c + IRQ_TRAMPOLINE 4a + + +/**************************************************************** + * Misc. entry points. + ****************************************************************/ + +// Entry point for QEMU smi interrupts. + DECLFUNC entry_smi +entry_smi: + // Transition to 32bit mode. + movl $1f + BUILD_BIOS_ADDR, %edx + jmp transition32_nmi_off + .code32 +1: movl $BUILD_SMM_ADDR + 0x8000, %esp + calll _cfunc32flat_handle_smi - BUILD_BIOS_ADDR + rsm + .code16 + +// Entry point for QEMU smp sipi interrupts. + DECLFUNC entry_smp +entry_smp: + // Transition to 32bit mode. + cli + cld + movl $2f + BUILD_BIOS_ADDR, %edx + jmp transition32_nmi_off + .code32 + // Acquire lock and take ownership of shared stack +1: rep ; nop +2: lock btsl $0, SMPLock + jc 1b + movl SMPStack, %esp + // Call handle_smp + calll _cfunc32flat_handle_smp - BUILD_BIOS_ADDR + // Release lock and halt processor. + movl $0, SMPLock +3: hlt + jmp 3b + .code16 + +// Resume (and reboot) entry point - called from entry_post + DECLFUNC entry_resume +entry_resume: + // Disable interrupts + cli + cld + // Use the ExtraStack in low mem. + movl $_zonelow_seg, %eax + movw %ax, %ds + movw %ax, %ss + movl $ExtraStack + BUILD_EXTRA_STACK_SIZE, %esp + // Call handler. + jmp handle_resume + +// PMM entry point + DECLFUNC entry_pmm +entry_pmm: + pushl %esp // Backup %esp, then clear high bits + movzwl %sp, %esp + pushfl // Save registers clobbered by C code + cli + cld + PUSHBREGS + movl %ss, %ecx // Move %ss to %ds + movw %cx, %ds + shll $4, %ecx + movl $_cfunc32flat_handle_pmm, %eax // Setup: call32(handle_pmm, args, -1) + leal PUSHBREGS_size+12(%esp, %ecx), %edx // %edx points to start of args + movl $-1, %ecx + calll call32 + movw %ax, BREGS_eax(%esp) // Modify %ax:%dx to return %eax + shrl $16, %eax + movw %ax, BREGS_edx(%esp) + POPBREGS + popfl + popl %esp + lretw + +// PnP entry points + DECLFUNC entry_pnp_real + .global entry_pnp_prot +entry_pnp_prot: + pushl %esp + jmp 1f +entry_pnp_real: + pushl %esp // Backup %esp, then clear high bits + movzwl %sp, %esp +1: + pushfl // Save registers clobbered by C code + cli + cld + PUSHBREGS + movw %ss, %cx // Move %ss to %ds + movw %cx, %ds + leal PUSHBREGS_size+12(%esp), %eax // %eax points to start of u16 args + calll handle_pnp + movw %ax, BREGS_eax(%esp) // Modify %eax to return %ax + POPBREGS + popfl + popl %esp + lretw + +// APM entry points + DECLFUNC entry_apm16 +entry_apm16: + pushfw // save flags + pushl %eax // dummy + ENTRY_ARG handle_apm + addw $4, %sp // pop dummy + popfw // restore flags + lretw + + DECLFUNC entry_apm32 + .code32 +entry_apm32: + pushfl + pushl %gs + pushl %cs // Move second descriptor after %cs to %gs + addl $16, (%esp) + popl %gs + ENTRY_ARG_ESP _cfunc32seg_handle_apm + popl %gs + popfl + lretl + .code16 + +// PCI-BIOS entry points + DECLFUNC entry_pcibios32 + .code32 +entry_pcibios32: + pushfl + pushl %gs // Backup %gs and set %gs=%ds + pushl %ds + popl %gs + ENTRY_ARG_ESP _cfunc32seg_handle_pcibios + popl %gs + popfl + lretl + .code16 + + DECLFUNC entry_pcibios16 +entry_pcibios16: + ENTRY_ARG handle_pcibios + iretw + +// int 1589 entry point + DECLFUNC entry_1589 +entry_1589: + ENTRY_ARG handle_1589 + iretw + +// BIOS32 support + DECLFUNC entry_bios32 + .code32 +entry_bios32: + pushfl +#if CONFIG_PCIBIOS + // Check for PCI-BIOS request + cmpl $0x49435024, %eax // $PCI + jne 1f + movl $BUILD_BIOS_ADDR, %ebx + movl $BUILD_BIOS_SIZE, %ecx + movl $entry_pcibios32, %edx + xorb %al, %al + jmp 2f +#endif + // Unknown request +1: movb $0x80, %al + // Return to caller +2: popfl + lretl + .code16 + +// 32bit elf entry point + DECLFUNC entry_elf + .code32 +entry_elf: + cli + cld + lidtl (BUILD_BIOS_ADDR + pmode_IDT_info) + lgdtl (BUILD_BIOS_ADDR + rombios32_gdt_48) + movl $SEG32_MODE32_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl $BUILD_STACK_ADDR, %esp + ljmpl $SEG32_MODE32_CS, $_cfunc32flat_handle_post + .code16 + +// UEFI Compatibility Support Module (CSM) entry point + DECLFUNC entry_csm +entry_csm: + // Backup register state + pushfw + cli + cld + pushl %eax // dummy + PUSHBREGS + + // Backup stack location and convert to a "flat pointer" + movl %ss, %eax + movw %ax, BREGS_code+2(%esp) // Store %ss in bregs->code.seg + shll $4, %eax + addl %esp, %eax + + // Change to BUILD_STACK_ADDR stack and call handle_csm(bregs) + ENTRY_INTO32 _cfunc32flat_handle_csm + + DECLFUNC __csm_return + .code32 +__csm_return: + movl $1f, %edx + jmp transition16big + .code16 + + // Switch back to original stack +1: movzwl BREGS_code+2(%eax), %edx + movl %edx, %ecx + shll $4, %ecx + subl %ecx, %eax + movl %edx, %ss + movl %eax, %esp + + // Restore register state and return. + POPBREGS + addw $4, %sp // pop dummy + popfw + lretw + + +/**************************************************************** + * Interrupt entry points + ****************************************************************/ + + // Main entry point for hardware interrupts handled on extra stack + DECLFUNC irqentry_extrastack +irqentry_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movl $_zonelow_seg, %eax + movl %eax, %ds + movl StackPos, %eax + subl $PUSHBREGS_size+8, %eax + SAVEBREGS_POP_DSEAX + popl %ecx + movl %esp, PUSHBREGS_size(%eax) + movw %ss, PUSHBREGS_size+4(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll *%ecx + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+4(%eax), %ss + movl PUSHBREGS_size(%eax), %esp + RESTOREBREGS_DSEAX + iretw + + // Main entry point for software interrupts handled on extra stack + DECLFUNC irqentry_arg_extrastack +irqentry_arg_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movl $_zonelow_seg, %eax + movl %eax, %ds + movl StackPos, %eax + subl $PUSHBREGS_size+16, %eax + SAVEBREGS_POP_DSEAX // Save registers on extra stack + popl %ecx + movl %esp, PUSHBREGS_size+8(%eax) + movw %ss, PUSHBREGS_size+12(%eax) + popl BREGS_code(%eax) + popw BREGS_flags(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll *%ecx + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+12(%eax), %ss + movl PUSHBREGS_size+8(%eax), %esp + popl %edx + popw %dx + pushw BREGS_flags(%eax) + pushl BREGS_code(%eax) + RESTOREBREGS_DSEAX + iretw + + // Main entry point for software interrupts (using caller's stack) + DECLFUNC irqentry_arg +irqentry_arg: + ENTRY_ARG_ST + iretw + + // Helper macros for hardware interrupt declaration + .macro IRQ_ENTRY num + .global entry_\num + entry_\num : + pushl $ handle_\num + jmp irqentry_extrastack + .endm + + .macro DECL_IRQ_ENTRY num + DECLFUNC entry_\num + IRQ_ENTRY \num + .endm + + // Helper macros for software interrupt declaration + .macro IRQ_ENTRY_ARG num + .global entry_\num + entry_\num : + pushl $ handle_\num +#if CONFIG_ENTRY_EXTRASTACK + jmp irqentry_arg_extrastack +#else + jmp irqentry_arg +#endif + .endm + + .macro DECL_IRQ_ENTRY_ARG num + DECLFUNC entry_\num + IRQ_ENTRY_ARG \num + .endm + + // Various entry points (that don't require a fixed location). + DECL_IRQ_ENTRY_ARG 13 + DECL_IRQ_ENTRY 76 + DECL_IRQ_ENTRY 70 + DECL_IRQ_ENTRY 74 + DECL_IRQ_ENTRY 75 + DECL_IRQ_ENTRY hwpic1 + DECL_IRQ_ENTRY hwpic2 + + // int 18/19 are special - they reset stack and call into 32bit mode. + DECLFUNC entry_19 +entry_19: + ENTRY_INTO32 _cfunc32flat_handle_19 + + DECLFUNC entry_18 +entry_18: + ENTRY_INTO32 _cfunc32flat_handle_18 + + +/**************************************************************** + * Fixed position entry points + ****************************************************************/ + + // Specify a location in the fixed part of bios area. + .macro ORG addr + .section .fixedaddr.\addr + .endm + + ORG 0xe05b +entry_post: + cmpl $0, %cs:HaveRunPost // Check for resume/reboot + jnz entry_resume + ENTRY_INTO32 _cfunc32flat_handle_post // Normal entry point + + ORG 0xe2c3 + IRQ_ENTRY 02 + + ORG 0xe3fe + .global entry_13_official +entry_13_official: + jmp entry_13 + + // 0xe401 - OldFDPT in misc.c + + ORG 0xe6f2 + .global entry_19_official +entry_19_official: + jmp entry_19 + + // 0xe6f5 - BIOS_CONFIG_TABLE in misc.c + + // 0xe729 - BaudTable in misc.c + + ORG 0xe739 + IRQ_ENTRY_ARG 14 + + ORG 0xe82e + IRQ_ENTRY_ARG 16 + + ORG 0xe987 + IRQ_ENTRY 09 + + ORG 0xec59 + IRQ_ENTRY_ARG 40 + + ORG 0xef57 + IRQ_ENTRY 0e + + // 0xefc7 - diskette_param_table in misc.c + + ORG 0xefd2 + IRQ_ENTRY_ARG 17 + + ORG 0xf045 +entry_10_0x0f: + // XXX - INT 10 Functions 0-Fh Entry Point + iretw + + ORG 0xf065 + IRQ_ENTRY_ARG 10 + + // 0xf0a4 - VideoParams in misc.c + + ORG 0xf841 + IRQ_ENTRY_ARG 12 + + ORG 0xf84d + IRQ_ENTRY_ARG 11 + + ORG 0xf859 + .global entry_15_official +entry_15_official: + cmpb $0x89, %ah + je entry_1589 // 1589 calls return in protected mode + IRQ_ENTRY_ARG 15 + + // 0xfa6e - vgafont8 in font.c + + ORG 0xfe6e + .global entry_1a_official +entry_1a_official: + cmpb $0xb1, %ah + je entry_pcibios16 // PCIBIOS calls can be in protected mode + IRQ_ENTRY_ARG 1a + + ORG 0xfea5 + IRQ_ENTRY 08 + + // 0xfef3 - InitVectors in misc.c + + ORG 0xff53 + .global entry_iret_official +entry_iret_official: + iretw + + ORG 0xff54 + IRQ_ENTRY_ARG 05 + + ORG 0xfff0 // Power-up Entry Point + .global reset_vector +reset_vector: + ljmpw $SEG_BIOS, $entry_post + + // 0xfff5 - BiosDate in misc.c + + // 0xfffe - BiosModelId in misc.c + + // 0xffff - BiosChecksum in misc.c + + .end diff --git a/qemu/roms/seabios/src/serial.c b/qemu/roms/seabios/src/serial.c new file mode 100644 index 000000000..88349c8a9 --- /dev/null +++ b/qemu/roms/seabios/src/serial.c @@ -0,0 +1,317 @@ +// 16bit code to handle serial and printer services. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "hw/serialio.h" // SEROFF_IER +#include "output.h" // debug_enter +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // serial_setup + + +/**************************************************************** + * COM ports + ****************************************************************/ + +static u16 +detect_serial(u16 port, u8 timeout, u8 count) +{ + if (CONFIG_DEBUG_SERIAL && port == CONFIG_DEBUG_SERIAL_PORT + && !romfile_loadint("etc/advertise-serial-debug-port", 1)) + return 0; + outb(0x02, port+SEROFF_IER); + u8 ier = inb(port+SEROFF_IER); + if (ier != 0x02) + return 0; + u8 iir = inb(port+SEROFF_IIR); + if ((iir & 0x3f) != 0x02) + return 0; + + outb(0x00, port+SEROFF_IER); + SET_BDA(port_com[count], port); + SET_BDA(com_timeout[count], timeout); + return 1; +} + +void +serial_setup(void) +{ + if (! CONFIG_SERIAL) + return; + dprintf(3, "init serial\n"); + + u16 count = 0; + count += detect_serial(PORT_SERIAL1, 0x0a, count); + count += detect_serial(PORT_SERIAL2, 0x0a, count); + count += detect_serial(PORT_SERIAL3, 0x0a, count); + count += detect_serial(PORT_SERIAL4, 0x0a, count); + dprintf(1, "Found %d serial ports\n", count); + + // Equipment word bits 9..11 determing # serial ports + set_equipment_flags(0xe00, count << 9); +} + +static u16 +getComAddr(struct bregs *regs) +{ + if (regs->dx >= 4) { + set_invalid(regs); + return 0; + } + u16 addr = GET_BDA(port_com[regs->dx]); + if (! addr) + set_invalid(regs); + return addr; +} + +// SERIAL - INITIALIZE PORT +static void +handle_1400(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR); + if ((regs->al & 0xE0) == 0) { + outb(0x17, addr+SEROFF_DLL); + outb(0x04, addr+SEROFF_DLH); + } else { + u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5); + outb(val16 & 0xFF, addr+SEROFF_DLL); + outb(val16 >> 8, addr+SEROFF_DLH); + } + outb(regs->al & 0x1F, addr+SEROFF_LCR); + regs->ah = inb(addr+SEROFF_LSR); + regs->al = inb(addr+SEROFF_MSR); + set_success(regs); +} + +// SERIAL - WRITE CHARACTER TO PORT +static void +handle_1401(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx])); + for (;;) { + u8 lsr = inb(addr+SEROFF_LSR); + if ((lsr & 0x60) == 0x60) { + // Success - can write data + outb(regs->al, addr+SEROFF_DATA); + // XXX - reread lsr? + regs->ah = lsr; + break; + } + if (irqtimer_check(end)) { + // Timed out - can't write data. + regs->ah = lsr | 0x80; + break; + } + yield(); + } + set_success(regs); +} + +// SERIAL - READ CHARACTER FROM PORT +static void +handle_1402(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx])); + for (;;) { + u8 lsr = inb(addr+SEROFF_LSR); + if (lsr & 0x01) { + // Success - can read data + regs->al = inb(addr+SEROFF_DATA); + regs->ah = lsr; + break; + } + if (irqtimer_check(end)) { + // Timed out - can't read data. + regs->ah = lsr | 0x80; + break; + } + yield(); + } + set_success(regs); +} + +// SERIAL - GET PORT STATUS +static void +handle_1403(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + regs->ah = inb(addr+SEROFF_LSR); + regs->al = inb(addr+SEROFF_MSR); + set_success(regs); +} + +static void +handle_14XX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +// INT 14h Serial Communications Service Entry Point +void VISIBLE16 +handle_14(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_14); + if (! CONFIG_SERIAL) { + handle_14XX(regs); + return; + } + + switch (regs->ah) { + case 0x00: handle_1400(regs); break; + case 0x01: handle_1401(regs); break; + case 0x02: handle_1402(regs); break; + case 0x03: handle_1403(regs); break; + default: handle_14XX(regs); break; + } +} + + +/**************************************************************** + * LPT ports + ****************************************************************/ + +static u16 +detect_parport(u16 port, u8 timeout, u8 count) +{ + // clear input mode + outb(inb(port+2) & 0xdf, port+2); + + outb(0xaa, port); + if (inb(port) != 0xaa) + // Not present + return 0; + SET_BDA(port_lpt[count], port); + SET_BDA(lpt_timeout[count], timeout); + return 1; +} + +void +lpt_setup(void) +{ + if (! CONFIG_LPT) + return; + dprintf(3, "init lpt\n"); + + u16 count = 0; + count += detect_parport(PORT_LPT1, 0x14, count); + count += detect_parport(PORT_LPT2, 0x14, count); + dprintf(1, "Found %d lpt ports\n", count); + + // Equipment word bits 14..15 determing # parallel ports + set_equipment_flags(0xc000, count << 14); +} + +static u16 +getLptAddr(struct bregs *regs) +{ + if (regs->dx >= 3) { + set_invalid(regs); + return 0; + } + u16 addr = GET_BDA(port_lpt[regs->dx]); + if (! addr) + set_invalid(regs); + return addr; +} + +// INT 17 - PRINTER - WRITE CHARACTER +static void +handle_1700(struct bregs *regs) +{ + u16 addr = getLptAddr(regs); + if (!addr) + return; + + u32 end = irqtimer_calc_ticks(GET_BDA(lpt_timeout[regs->dx])); + + outb(regs->al, addr); + u8 val8 = inb(addr+2); + outb(val8 | 0x01, addr+2); // send strobe + udelay(5); + outb(val8 & ~0x01, addr+2); + + for (;;) { + u8 v = inb(addr+1); + if (!(v & 0x40)) { + // Success + regs->ah = v ^ 0x48; + break; + } + if (irqtimer_check(end)) { + // Timeout + regs->ah = (v ^ 0x48) | 0x01; + break; + } + yield(); + } + + set_success(regs); +} + +// INT 17 - PRINTER - INITIALIZE PORT +static void +handle_1701(struct bregs *regs) +{ + u16 addr = getLptAddr(regs); + if (!addr) + return; + + u8 val8 = inb(addr+2); + outb(val8 & ~0x04, addr+2); // send init + udelay(5); + outb(val8 | 0x04, addr+2); + + regs->ah = inb(addr+1) ^ 0x48; + set_success(regs); +} + +// INT 17 - PRINTER - GET STATUS +static void +handle_1702(struct bregs *regs) +{ + u16 addr = getLptAddr(regs); + if (!addr) + return; + regs->ah = inb(addr+1) ^ 0x48; + set_success(regs); +} + +static void +handle_17XX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +// INT17h : Printer Service Entry Point +void VISIBLE16 +handle_17(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_17); + if (! CONFIG_LPT) { + handle_17XX(regs); + return; + } + + switch (regs->ah) { + case 0x00: handle_1700(regs); break; + case 0x01: handle_1701(regs); break; + case 0x02: handle_1702(regs); break; + default: handle_17XX(regs); break; + } +} diff --git a/qemu/roms/seabios/src/stacks.c b/qemu/roms/seabios/src/stacks.c new file mode 100644 index 000000000..1dbdfe9bb --- /dev/null +++ b/qemu/roms/seabios/src/stacks.c @@ -0,0 +1,827 @@ +// Code for manipulating stack locations. +// +// Copyright (C) 2009-2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // CR0_PE +#include "fw/paravirt.h" // PORT_SMI_CMD +#include "hw/rtc.h" // rtc_use +#include "list.h" // hlist_node +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "stacks.h" // struct mutex_s +#include "util.h" // useRTC + +#define MAIN_STACK_MAX (1024*1024) + + +/**************************************************************** + * 16bit / 32bit calling + ****************************************************************/ + +struct { + u8 method; + u8 cmosindex; + u8 a20; + u16 ss, fs, gs; + struct descloc_s gdt; +} Call32Data VARLOW; + +#define C32_SLOPPY 1 +#define C32_SMM 2 + +int HaveSmmCall32 VARFSEG; + +// Backup state in preparation for call32_smm() +static void +call32_smm_prep(void) +{ + // Backup cmos index register and disable nmi + u8 cmosindex = inb(PORT_CMOS_INDEX); + outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); + SET_LOW(Call32Data.cmosindex, cmosindex); + + // Backup ss + SET_LOW(Call32Data.ss, GET_SEG(SS)); + + SET_LOW(Call32Data.method, C32_SMM); +} + +// Restore state backed up during call32_smm() +static void +call32_smm_post(void) +{ + SET_LOW(Call32Data.method, 0); + SET_LOW(Call32Data.ss, 0); + + // Restore cmos index register + outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); +} + +#define ASM32_SWITCH16 " .pushsection .text.32fseg." UNIQSEC "\n .code16\n" +#define ASM32_BACK32 " .popsection\n .code32\n" +#define ASM16_SWITCH32 " .code32\n" +#define ASM16_BACK16 " .code16gcc\n" + +// Call a SeaBIOS C function in 32bit mode using smm trampoline +static u32 +call32_smm(void *func, u32 eax) +{ + ASSERT16(); + dprintf(9, "call32_smm %p %x\n", func, eax); + call32_smm_prep(); + u32 bkup_esp; + asm volatile( + // Backup esp / set esp to flat stack location + " movl %%esp, %0\n" + " movl %%ss, %%eax\n" + " shll $4, %%eax\n" + " addl %%eax, %%esp\n" + + // Transition to 32bit mode, call func, return to 16bit + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n" + " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + ASM16_SWITCH32 + "1:movl %1, %%eax\n" + " calll *%2\n" + " movl %%eax, %1\n" + + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n" + " movl $2f, %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + // Restore esp + ASM16_BACK16 + "2:movl %0, %%esp\n" + : "=&r" (bkup_esp), "+r" (eax) + : "r" (func) + : "eax", "ecx", "edx", "ebx", "cc", "memory"); + call32_smm_post(); + + dprintf(9, "call32_smm done %p %x\n", func, eax); + return eax; +} + +// 16bit handler code called from call16_smm() +u32 VISIBLE16 +call16_smm_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) +{ + if (!CONFIG_CALL32_SMM) + return eax; + call32_smm_post(); + u32 ret = func(eax, edx); + call32_smm_prep(); + return ret; +} + +static u32 +call16_smm(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (!CONFIG_CALL32_SMM) + return eax; + func -= BUILD_BIOS_ADDR; + dprintf(9, "call16_smm %p %x %x\n", func, eax, edx); + u32 stackoffset = Call32Data.ss << 4; + asm volatile( + // Restore esp + " subl %0, %%esp\n" + + // Transition to 16bit mode, call func, return to 32bit + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n" + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + ASM32_SWITCH16 + "1:movl %1, %%eax\n" + " movl %3, %%ecx\n" + " calll _cfunc16_call16_smm_helper\n" + " movl %%eax, %1\n" + + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n" + " movl $2f, %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + // Set esp to flat stack location + ASM32_BACK32 + "2:addl %0, %%esp\n" + : "+r" (stackoffset), "+r" (eax), "+d" (edx) + : "r" (func) + : "eax", "ecx", "ebx", "cc", "memory"); + return eax; +} + +// Backup state in preparation for call32_sloppy() +static void +call32_sloppy_prep(void) +{ + // Backup cmos index register and disable nmi + u8 cmosindex = inb(PORT_CMOS_INDEX); + outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); + SET_LOW(Call32Data.cmosindex, cmosindex); + + // Enable a20 and backup it's previous state + SET_LOW(Call32Data.a20, set_a20(1)); + + // Backup ss/fs/gs and gdt + SET_LOW(Call32Data.ss, GET_SEG(SS)); + SET_LOW(Call32Data.fs, GET_SEG(FS)); + SET_LOW(Call32Data.gs, GET_SEG(GS)); + struct descloc_s gdt; + sgdt(&gdt); + SET_LOW(Call32Data.gdt.length, gdt.length); + SET_LOW(Call32Data.gdt.addr, gdt.addr); + + SET_LOW(Call32Data.method, C32_SLOPPY); +} + +// Restore state backed up during call32_sloppy() +static void +call32_sloppy_post(void) +{ + SET_LOW(Call32Data.method, 0); + SET_LOW(Call32Data.ss, 0); + + // Restore gdt and fs/gs + struct descloc_s gdt; + gdt.length = GET_LOW(Call32Data.gdt.length); + gdt.addr = GET_LOW(Call32Data.gdt.addr); + lgdt(&gdt); + SET_SEG(FS, GET_LOW(Call32Data.fs)); + SET_SEG(GS, GET_LOW(Call32Data.gs)); + + // Restore a20 + set_a20(GET_LOW(Call32Data.a20)); + + // Restore cmos index register + outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); +} + +// Call a C function in 32bit mode. This clobbers the 16bit segment +// selector registers. +static u32 +call32_sloppy(void *func, u32 eax) +{ + ASSERT16(); + call32_sloppy_prep(); + u32 bkup_ss, bkup_esp; + asm volatile( + // Backup ss/esp / set esp to flat stack location + " movl %%ss, %0\n" + " movl %%esp, %1\n" + " shll $4, %0\n" + " addl %0, %%esp\n" + " shrl $4, %0\n" + + // Transition to 32bit mode, call func, return to 16bit + " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n" + " jmp transition32\n" + ASM16_SWITCH32 + "1:calll *%3\n" + " movl $2f, %%edx\n" + " jmp transition16big\n" + + // Restore ds/ss/esp + ASM16_BACK16 + "2:movl %0, %%ds\n" + " movl %0, %%ss\n" + " movl %1, %%esp\n" + : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax) + : "r" (func) + : "ecx", "edx", "cc", "memory"); + call32_sloppy_post(); + return eax; +} + +// 16bit handler code called from call16_sloppy() +u32 VISIBLE16 +call16_sloppy_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) +{ + call32_sloppy_post(); + u32 ret = func(eax, edx); + call32_sloppy_prep(); + return ret; +} + +// Jump back to 16bit mode while in 32bit mode from call32_sloppy() +static u32 +call16_sloppy(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > MAIN_STACK_MAX) + panic("call16_sloppy with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + u32 stackseg = Call32Data.ss; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16big\n" + // Setup ss/esp and call func + ASM32_SWITCH16 + "1:movl %3, %%ecx\n" + " shll $4, %3\n" + " movw %%cx, %%ss\n" + " subl %3, %%esp\n" + " movw %%cx, %%ds\n" + " movl %2, %%edx\n" + " movl %1, %%ecx\n" + " calll _cfunc16_call16_sloppy_helper\n" + // Return to 32bit and restore esp + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:addl %3, %%esp\n" + : "+a" (eax) + : "r" (func), "r" (edx), "r" (stackseg) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function. +u32 VISIBLE16 +call32(void *func, u32 eax, u32 errret) +{ + ASSERT16(); + if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32)) + return call32_smm(func, eax); + u32 cr0 = getcr0(); + if (cr0 & CR0_PE) + // Called in 16bit protected mode?! + return errret; + return call32_sloppy(func, eax); +} + +// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function. +static u32 +call16(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16 with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16\n" + // Call func + ASM32_SWITCH16 + "1:movl %2, %%edx\n" + " calll *%1\n" + // Return to 32bit + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:\n" + : "+a" (eax) + : "r" (func), "r" (edx) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 16bit SeaBIOS function in "big real" mode. +static u32 +call16big(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16big with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16big\n" + // Call func + ASM32_SWITCH16 + "1:movl %2, %%edx\n" + " calll *%1\n" + // Return to 32bit + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:\n" + : "+a" (eax) + : "r" (func), "r" (edx) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 16bit SeaBIOS function, restoring the mode from last call32(). +static u32 +call16_back(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (CONFIG_CALL32_SMM && Call32Data.method == C32_SMM) + return call16_smm(eax, edx, func); + if (Call32Data.method == C32_SLOPPY) + return call16_sloppy(eax, edx, func); + if (in_post()) + return call16big(eax, edx, func); + return call16(eax, edx, func); +} + + +/**************************************************************** + * Extra 16bit stack + ****************************************************************/ + +// Space for a stack for 16bit code. +u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8); +u8 *StackPos VARLOW; + +// Test if currently on the extra stack +int +on_extra_stack(void) +{ + return MODE16 && GET_SEG(SS) == SEG_LOW && getesp() > (u32)ExtraStack; +} + +// Switch to the extra stack and call a function. +u32 +stack_hop(u32 eax, u32 edx, void *func) +{ + if (on_extra_stack()) + return ((u32 (*)(u32, u32))func)(eax, edx); + ASSERT16(); + u16 stack_seg = SEG_LOW; + u32 bkup_ss, bkup_esp; + asm volatile( + // Backup current %ss/%esp values. + "movw %%ss, %w3\n" + "movl %%esp, %4\n" + // Copy stack seg to %ds/%ss and set %esp + "movw %w6, %%ds\n" + "movw %w6, %%ss\n" + "movl %5, %%esp\n" + "pushl %3\n" + "pushl %4\n" + // Call func + "calll *%2\n" + "popl %4\n" + "popl %3\n" + // Restore segments and stack + "movw %w3, %%ds\n" + "movw %w3, %%ss\n" + "movl %4, %%esp" + : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp) + : "m" (StackPos), "r" (stack_seg) + : "cc", "memory"); + return eax; +} + +// Switch back to original caller's stack and call a function. +u32 +stack_hop_back(u32 eax, u32 edx, void *func) +{ + if (!MODESEGMENT) + return call16_back(eax, edx, func); + if (!MODE16 || !on_extra_stack()) + return ((u32 (*)(u32, u32))func)(eax, edx); + ASSERT16(); + u16 bkup_ss; + u32 bkup_stack_pos, temp; + asm volatile( + // Backup stack_pos and current %ss/%esp + "movl %6, %4\n" + "movw %%ss, %w3\n" + "movl %%esp, %6\n" + // Restore original callers' %ss/%esp + "movl -4(%4), %5\n" + "movl %5, %%ss\n" + "movw %%ds:-8(%4), %%sp\n" + "movl %5, %%ds\n" + // Call func + "calll *%2\n" + // Restore %ss/%esp and stack_pos + "movw %w3, %%ds\n" + "movw %w3, %%ss\n" + "movl %6, %%esp\n" + "movl %4, %6" + : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss) + , "=&r" (bkup_stack_pos), "=&r" (temp), "+m" (StackPos) + : + : "cc", "memory"); + return eax; +} + + +/**************************************************************** + * External 16bit interface calling + ****************************************************************/ + +// Far call 16bit code with a specified register state. +void VISIBLE16 +_farcall16(struct bregs *callregs, u16 callregseg) +{ + if (need_hop_back()) { + extern void _cfunc16__farcall16(void); + stack_hop_back((u32)callregs, callregseg, _cfunc16__farcall16); + return; + } + ASSERT16(); + asm volatile( + "calll __farcall16\n" + : "+a" (callregs), "+m" (*callregs), "+d" (callregseg) + : + : "ebx", "ecx", "esi", "edi", "cc", "memory"); +} + +void +farcall16(struct bregs *callregs) +{ + extern void _cfunc16__farcall16(void); + call16((u32)callregs, 0, _cfunc16__farcall16); +} + +void +farcall16big(struct bregs *callregs) +{ + extern void _cfunc16__farcall16(void); + call16big((u32)callregs, 0, _cfunc16__farcall16); +} + +// Invoke a 16bit software interrupt. +void +__call16_int(struct bregs *callregs, u16 offset) +{ + callregs->code.offset = offset; + if (!MODESEGMENT) { + callregs->code.seg = SEG_BIOS; + _farcall16((void*)callregs - Call32Data.ss * 16, Call32Data.ss); + return; + } + callregs->code.seg = GET_SEG(CS); + _farcall16(callregs, GET_SEG(SS)); +} + +// Reset the machine +void +reset(void) +{ + extern void reset_vector(void) __noreturn; + if (!MODE16) + call16_back(0, 0, reset_vector); + reset_vector(); +} + + +/**************************************************************** + * Threads + ****************************************************************/ + +// Thread info - stored at bottom of each thread stack - don't change +// without also updating the inline assembler below. +struct thread_info { + void *stackpos; + struct hlist_node node; +}; +struct thread_info MainThread VARFSEG = { + NULL, { &MainThread.node, &MainThread.node.next } +}; +#define THREADSTACKSIZE 4096 + +// Check if any threads are running. +static int +have_threads(void) +{ + return (CONFIG_THREADS + && GET_FLATPTR(MainThread.node.next) != &MainThread.node); +} + +// Return the 'struct thread_info' for the currently running thread. +struct thread_info * +getCurThread(void) +{ + u32 esp = getesp(); + if (esp <= MAIN_STACK_MAX) + return &MainThread; + return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE); +} + +static int ThreadControl; + +// Initialize the support for internal threads. +void +thread_init(void) +{ + if (! CONFIG_THREADS) + return; + ThreadControl = romfile_loadint("etc/threads", 1); +} + +// Should hardware initialization threads run during optionrom execution. +int +threads_during_optionroms(void) +{ + return CONFIG_THREADS && ThreadControl == 2 && in_post(); +} + +// Switch to next thread stack. +static void +switch_next(struct thread_info *cur) +{ + struct thread_info *next = container_of( + cur->node.next, struct thread_info, node); + if (cur == next) + // Nothing to do. + return; + asm volatile( + " pushl $1f\n" // store return pc + " pushl %%ebp\n" // backup %ebp + " movl %%esp, (%%eax)\n" // cur->stackpos = %esp + " movl (%%ecx), %%esp\n" // %esp = next->stackpos + " popl %%ebp\n" // restore %ebp + " retl\n" // restore pc + "1:\n" + : "+a"(cur), "+c"(next) + : + : "ebx", "edx", "esi", "edi", "cc", "memory"); +} + +// Last thing called from a thread (called on MainThread stack). +static void +__end_thread(struct thread_info *old) +{ + hlist_del(&old->node); + dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old); + free(old); + if (!have_threads()) + dprintf(1, "All threads complete.\n"); +} + +// Create a new thread and start executing 'func' in it. +void +run_thread(void (*func)(void*), void *data) +{ + ASSERT32FLAT(); + if (! CONFIG_THREADS || ! ThreadControl) + goto fail; + struct thread_info *thread; + thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE); + if (!thread) + goto fail; + + dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread); + thread->stackpos = (void*)thread + THREADSTACKSIZE; + struct thread_info *cur = getCurThread(); + hlist_add_after(&thread->node, &cur->node); + asm volatile( + // Start thread + " pushl $1f\n" // store return pc + " pushl %%ebp\n" // backup %ebp + " movl %%esp, (%%edx)\n" // cur->stackpos = %esp + " movl (%%ebx), %%esp\n" // %esp = thread->stackpos + " calll *%%ecx\n" // Call func + + // End thread + " movl %%ebx, %%eax\n" // %eax = thread + " movl 4(%%ebx), %%ebx\n" // %ebx = thread->node.next + " movl (%5), %%esp\n" // %esp = MainThread.stackpos + " calll %4\n" // call __end_thread(thread) + " movl -4(%%ebx), %%esp\n" // %esp = next->stackpos + " popl %%ebp\n" // restore %ebp + " retl\n" // restore pc + "1:\n" + : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur) + : "m"(*(u8*)__end_thread), "m"(MainThread) + : "esi", "edi", "cc", "memory"); + return; + +fail: + func(data); +} + + +/**************************************************************** + * Thread helpers + ****************************************************************/ + +// Low-level irq enable. +void VISIBLE16 +check_irqs(void) +{ + if (need_hop_back()) { + extern void _cfunc16_check_irqs(void); + stack_hop_back(0, 0, _cfunc16_check_irqs); + return; + } + asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory"); +} + +// Briefly permit irqs to occur. +void +yield(void) +{ + if (MODESEGMENT || !CONFIG_THREADS) { + check_irqs(); + return; + } + struct thread_info *cur = getCurThread(); + if (cur == &MainThread) + // Permit irqs to fire + check_irqs(); + + // Switch to the next thread + switch_next(cur); +} + +void VISIBLE16 +wait_irq(void) +{ + if (need_hop_back()) { + extern void _cfunc16_wait_irq(void); + stack_hop_back(0, 0, _cfunc16_wait_irq); + return; + } + asm volatile("sti ; hlt ; cli ; cld": : :"memory"); +} + +// Wait for next irq to occur. +void +yield_toirq(void) +{ + if (!MODESEGMENT && have_threads()) { + // Threads still active - do a yield instead. + yield(); + return; + } + wait_irq(); +} + +// Wait for all threads (other than the main thread) to complete. +void +wait_threads(void) +{ + ASSERT32FLAT(); + while (have_threads()) + yield(); +} + +void +mutex_lock(struct mutex_s *mutex) +{ + ASSERT32FLAT(); + if (! CONFIG_THREADS) + return; + while (mutex->isLocked) + yield(); + mutex->isLocked = 1; +} + +void +mutex_unlock(struct mutex_s *mutex) +{ + ASSERT32FLAT(); + if (! CONFIG_THREADS) + return; + mutex->isLocked = 0; +} + + +/**************************************************************** + * Thread preemption + ****************************************************************/ + +int CanPreempt VARFSEG; +static u32 PreemptCount; + +// Turn on RTC irqs and arrange for them to check the 32bit threads. +void +start_preempt(void) +{ + if (! threads_during_optionroms()) + return; + CanPreempt = 1; + PreemptCount = 0; + rtc_use(); +} + +// Turn off RTC irqs / stop checking for thread execution. +void +finish_preempt(void) +{ + if (! threads_during_optionroms()) { + yield(); + return; + } + CanPreempt = 0; + rtc_release(); + dprintf(9, "Done preempt - %d checks\n", PreemptCount); + yield(); +} + +// Check if preemption is on, and wait for it to complete if so. +int +wait_preempt(void) +{ + if (MODESEGMENT || !CONFIG_THREADS || !CanPreempt + || getesp() < MAIN_STACK_MAX) + return 0; + while (CanPreempt) + yield(); + return 1; +} + +// Try to execute 32bit threads. +void VISIBLE32INIT +yield_preempt(void) +{ + PreemptCount++; + switch_next(&MainThread); +} + +// 16bit code that checks if threads are pending and executes them if so. +void +check_preempt(void) +{ + extern void _cfunc32flat_yield_preempt(void); + if (CONFIG_THREADS && GET_GLOBAL(CanPreempt) && have_threads()) + call32(_cfunc32flat_yield_preempt, 0, 0); +} + + +/**************************************************************** + * call32 helper + ****************************************************************/ + +struct call32_params_s { + void *func; + u32 eax, edx, ecx; +}; + +u32 VISIBLE32FLAT +call32_params_helper(struct call32_params_s *params) +{ + return ((u32 (*)(u32, u32, u32))params->func)( + params->eax, params->edx, params->ecx); +} + +u32 +call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret) +{ + ASSERT16(); + struct call32_params_s params = {func, eax, edx, ecx}; + extern void _cfunc32flat_call32_params_helper(void); + return call32(_cfunc32flat_call32_params_helper + , (u32)MAKE_FLATPTR(GET_SEG(SS), ¶ms), errret); +} diff --git a/qemu/roms/seabios/src/stacks.h b/qemu/roms/seabios/src/stacks.h new file mode 100644 index 000000000..82c4c3c85 --- /dev/null +++ b/qemu/roms/seabios/src/stacks.h @@ -0,0 +1,53 @@ +// Misc function and variable declarations. +#ifndef __STACKS_H +#define __STACKS_H + +#include "types.h" // u32 + +#define CALL32SMM_CMDID 0xb5 +#define CALL32SMM_ENTERID 0x1234 +#define CALL32SMM_RETURNID 0x5678 + +// stacks.c +extern int HaveSmmCall32; +u32 call32(void *func, u32 eax, u32 errret); +extern u8 ExtraStack[], *StackPos; +u32 stack_hop(u32 eax, u32 edx, void *func); +u32 stack_hop_back(u32 eax, u32 edx, void *func); +int on_extra_stack(void); +struct bregs; +void farcall16(struct bregs *callregs); +void farcall16big(struct bregs *callregs); +void __call16_int(struct bregs *callregs, u16 offset); +#define call16_int(nr, callregs) do { \ + extern void irq_trampoline_ ##nr (); \ + __call16_int((callregs), (u32)&irq_trampoline_ ##nr ); \ + } while (0) +void reset(void); +extern struct thread_info MainThread; +struct thread_info *getCurThread(void); +void yield(void); +void yield_toirq(void); +void thread_init(void); +int threads_during_optionroms(void); +void run_thread(void (*func)(void*), void *data); +void wait_threads(void); +struct mutex_s { u32 isLocked; }; +void mutex_lock(struct mutex_s *mutex); +void mutex_unlock(struct mutex_s *mutex); +void start_preempt(void); +void finish_preempt(void); +int wait_preempt(void); +void check_preempt(void); +u32 call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret); + +// Inline functions + +// Check if a call to stack_hop_back is needed. +static inline int +need_hop_back(void) +{ + return !MODESEGMENT || on_extra_stack(); +} + +#endif // stacks.h diff --git a/qemu/roms/seabios/src/std/LegacyBios.h b/qemu/roms/seabios/src/std/LegacyBios.h new file mode 100644 index 000000000..5170c3786 --- /dev/null +++ b/qemu/roms/seabios/src/std/LegacyBios.h @@ -0,0 +1,985 @@ +/** @file + The EFI Legacy BIOS Protocol is used to abstract legacy Option ROM usage + under EFI and Legacy OS boot. This file also includes all the related + COMPATIBILIY16 structures and defintions. + + Note: The names for EFI_IA32_REGISTER_SET elements were picked to follow + well known naming conventions. + + Thunk is the code that switches from 32-bit protected environment into the 16-bit real-mode + environment. Reverse thunk is the code that does the opposite. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This protocol is defined in Framework for EFI Compatibility Support Module spec + Version 0.97. + +**/ + +#ifndef _EFI_LEGACY_BIOS_H_ +#define _EFI_LEGACY_BIOS_H_ + +/// +/// +/// +#pragma pack(1) + +typedef UINT8 SERIAL_MODE; +typedef UINT8 PARALLEL_MODE; + +#define EFI_COMPATIBILITY16_TABLE_SIGNATURE SIGNATURE_32 ('I', 'F', 'E', '$') + +/// +/// There is a table located within the traditional BIOS in either the 0xF000:xxxx or 0xE000:xxxx +/// physical address range. It is located on a 16-byte boundary and provides the physical address of the +/// entry point for the Compatibility16 functions. These functions provide the platform-specific +/// information that is required by the generic EfiCompatibility code. The functions are invoked via +/// thunking by using EFI_LEGACY_BIOS_PROTOCOL.FarCall86() with the 32-bit physical +/// entry point. +/// +typedef struct { + /// + /// The string "$EFI" denotes the start of the EfiCompatibility table. Byte 0 is "I," byte + /// 1 is "F," byte 2 is "E," and byte 3 is "$" and is normally accessed as a DWORD or UINT32. + /// + UINT32 Signature; + + /// + /// The value required such that byte checksum of TableLength equals zero. + /// + UINT8 TableChecksum; + + /// + /// The length of this table. + /// + UINT8 TableLength; + + /// + /// The major EFI revision for which this table was generated. + /// + UINT8 EfiMajorRevision; + + /// + /// The minor EFI revision for which this table was generated. + /// + UINT8 EfiMinorRevision; + + /// + /// The major revision of this table. + /// + UINT8 TableMajorRevision; + + /// + /// The minor revision of this table. + /// + UINT8 TableMinorRevision; + + /// + /// Reserved for future usage. + /// + UINT16 Reserved; + + /// + /// The segment of the entry point within the traditional BIOS for Compatibility16 functions. + /// + UINT16 Compatibility16CallSegment; + + /// + /// The offset of the entry point within the traditional BIOS for Compatibility16 functions. + /// + UINT16 Compatibility16CallOffset; + + /// + /// The segment of the entry point within the traditional BIOS for EfiCompatibility + /// to invoke the PnP installation check. + /// + UINT16 PnPInstallationCheckSegment; + + /// + /// The Offset of the entry point within the traditional BIOS for EfiCompatibility + /// to invoke the PnP installation check. + /// + UINT16 PnPInstallationCheckOffset; + + /// + /// EFI system resources table. Type EFI_SYSTEM_TABLE is defined in the IntelPlatform + ///Innovation Framework for EFI Driver Execution Environment Core Interface Specification (DXE CIS). + /// + UINT32 EfiSystemTable; + + /// + /// The address of an OEM-provided identifier string. The string is null terminated. + /// + UINT32 OemIdStringPointer; + + /// + /// The 32-bit physical address where ACPI RSD PTR is stored within the traditional + /// BIOS. The remained of the ACPI tables are located at their EFI addresses. The size + /// reserved is the maximum for ACPI 2.0. The EfiCompatibility will fill in the ACPI + /// RSD PTR with either the ACPI 1.0b or 2.0 values. + /// + UINT32 AcpiRsdPtrPointer; + + /// + /// The OEM revision number. Usage is undefined but provided for OEM module usage. + /// + UINT16 OemRevision; + + /// + /// The 32-bit physical address where INT15 E820 data is stored within the traditional + /// BIOS. The EfiCompatibility code will fill in the E820Pointer value and copy the + /// data to the indicated area. + /// + UINT32 E820Pointer; + + /// + /// The length of the E820 data and is filled in by the EfiCompatibility code. + /// + UINT32 E820Length; + + /// + /// The 32-bit physical address where the $PIR table is stored in the traditional BIOS. + /// The EfiCompatibility code will fill in the IrqRoutingTablePointer value and + /// copy the data to the indicated area. + /// + UINT32 IrqRoutingTablePointer; + + /// + /// The length of the $PIR table and is filled in by the EfiCompatibility code. + /// + UINT32 IrqRoutingTableLength; + + /// + /// The 32-bit physical address where the MP table is stored in the traditional BIOS. + /// The EfiCompatibility code will fill in the MpTablePtr value and copy the data + /// to the indicated area. + /// + UINT32 MpTablePtr; + + /// + /// The length of the MP table and is filled in by the EfiCompatibility code. + /// + UINT32 MpTableLength; + + /// + /// The segment of the OEM-specific INT table/code. + /// + UINT16 OemIntSegment; + + /// + /// The offset of the OEM-specific INT table/code. + /// + UINT16 OemIntOffset; + + /// + /// The segment of the OEM-specific 32-bit table/code. + /// + UINT16 Oem32Segment; + + /// + /// The offset of the OEM-specific 32-bit table/code. + /// + UINT16 Oem32Offset; + + /// + /// The segment of the OEM-specific 16-bit table/code. + /// + UINT16 Oem16Segment; + + /// + /// The offset of the OEM-specific 16-bit table/code. + /// + UINT16 Oem16Offset; + + /// + /// The segment of the TPM binary passed to 16-bit CSM. + /// + UINT16 TpmSegment; + + /// + /// The offset of the TPM binary passed to 16-bit CSM. + /// + UINT16 TpmOffset; + + /// + /// A pointer to a string identifying the independent BIOS vendor. + /// + UINT32 IbvPointer; + + /// + /// This field is NULL for all systems not supporting PCI Express. This field is the base + /// value of the start of the PCI Express memory-mapped configuration registers and + /// must be filled in prior to EfiCompatibility code issuing the Compatibility16 function + /// Compatibility16InitializeYourself(). + /// Compatibility16InitializeYourself() is defined in Compatability16 + /// Functions. + /// + UINT32 PciExpressBase; + + /// + /// Maximum PCI bus number assigned. + /// + UINT8 LastPciBus; + + /// + /// Start address of UMB RAM + /// + UINT32 UmaAddress; + + /// + /// Size of UMB RAM + /// + UINT32 UmaSize; + + /// + /// Start address of persistent allocation in high (>1MiB) memory + /// + UINT32 HiPermanentMemoryAddress; + + /// + /// Size of persistent allocation in high (>1MiB) memory + /// + UINT32 HiPermanentMemorySize; +} EFI_COMPATIBILITY16_TABLE; + +/// +/// Functions provided by the CSM binary which communicate between the EfiCompatibility +/// and Compatability16 code. +/// +/// Inconsistent with the specification here: +/// The member's name started with "Compatibility16" [defined in Intel Framework +/// Compatibility Support Module Specification / 0.97 version] +/// has been changed to "Legacy16" since keeping backward compatible. +/// +typedef enum { + /// + /// Causes the Compatibility16 code to do any internal initialization required. + /// Input: + /// AX = Compatibility16InitializeYourself + /// ES:BX = Pointer to EFI_TO_COMPATIBILITY16_INIT_TABLE + /// Return: + /// AX = Return Status codes + /// + Legacy16InitializeYourself = 0x0000, + + /// + /// Causes the Compatibility16 BIOS to perform any drive number translations to match the boot sequence. + /// Input: + /// AX = Compatibility16UpdateBbs + /// ES:BX = Pointer to EFI_TO_COMPATIBILITY16_BOOT_TABLE + /// Return: + /// AX = Returned status codes + /// + Legacy16UpdateBbs = 0x0001, + + /// + /// Allows the Compatibility16 code to perform any final actions before booting. The Compatibility16 + /// code is read/write. + /// Input: + /// AX = Compatibility16PrepareToBoot + /// ES:BX = Pointer to EFI_TO_COMPATIBILITY16_BOOT_TABLE structure + /// Return: + /// AX = Returned status codes + /// + Legacy16PrepareToBoot = 0x0002, + + /// + /// Causes the Compatibility16 BIOS to boot. The Compatibility16 code is Read/Only. + /// Input: + /// AX = Compatibility16Boot + /// Output: + /// AX = Returned status codes + /// + Legacy16Boot = 0x0003, + + /// + /// Allows the Compatibility16 code to get the last device from which a boot was attempted. This is + /// stored in CMOS and is the priority number of the last attempted boot device. + /// Input: + /// AX = Compatibility16RetrieveLastBootDevice + /// Output: + /// AX = Returned status codes + /// BX = Priority number of the boot device. + /// + Legacy16RetrieveLastBootDevice = 0x0004, + + /// + /// Allows the Compatibility16 code rehook INT13, INT18, and/or INT19 after dispatching a legacy OpROM. + /// Input: + /// AX = Compatibility16DispatchOprom + /// ES:BX = Pointer to EFI_DISPATCH_OPROM_TABLE + /// Output: + /// AX = Returned status codes + /// BX = Number of non-BBS-compliant devices found. Equals 0 if BBS compliant. + /// + Legacy16DispatchOprom = 0x0005, + + /// + /// Finds a free area in the 0xFxxxx or 0xExxxx region of the specified length and returns the address + /// of that region. + /// Input: + /// AX = Compatibility16GetTableAddress + /// BX = Allocation region + /// 00 = Allocate from either 0xE0000 or 0xF0000 64 KB blocks. + /// Bit 0 = 1 Allocate from 0xF0000 64 KB block + /// Bit 1 = 1 Allocate from 0xE0000 64 KB block + /// CX = Requested length in bytes. + /// DX = Required address alignment. Bit mapped. First non-zero bit from the right is the alignment. + /// Output: + /// AX = Returned status codes + /// DS:BX = Address of the region + /// + Legacy16GetTableAddress = 0x0006, + + /// + /// Enables the EfiCompatibility module to do any nonstandard processing of keyboard LEDs or state. + /// Input: + /// AX = Compatibility16SetKeyboardLeds + /// CL = LED status. + /// Bit 0 Scroll Lock 0 = Off + /// Bit 1 NumLock + /// Bit 2 Caps Lock + /// Output: + /// AX = Returned status codes + /// + Legacy16SetKeyboardLeds = 0x0007, + + /// + /// Enables the EfiCompatibility module to install an interrupt handler for PCI mass media devices that + /// do not have an OpROM associated with them. An example is SATA. + /// Input: + /// AX = Compatibility16InstallPciHandler + /// ES:BX = Pointer to EFI_LEGACY_INSTALL_PCI_HANDLER structure + /// Output: + /// AX = Returned status codes + /// + Legacy16InstallPciHandler = 0x0008 +} EFI_COMPATIBILITY_FUNCTIONS; + + +/// +/// EFI_DISPATCH_OPROM_TABLE +/// +typedef struct { + UINT16 PnPInstallationCheckSegment; ///< A pointer to the PnpInstallationCheck data structure. + UINT16 PnPInstallationCheckOffset; ///< A pointer to the PnpInstallationCheck data structure. + UINT16 OpromSegment; ///< The segment where the OpROM was placed. Offset is assumed to be 3. + UINT8 PciBus; ///< The PCI bus. + UINT8 PciDeviceFunction; ///< The PCI device * 0x08 | PCI function. + UINT8 NumberBbsEntries; ///< The number of valid BBS table entries upon entry and exit. The IBV code may + ///< increase this number, if BBS-compliant devices also hook INTs in order to force the + ///< OpROM BIOS Setup to be executed. + UINT32 BbsTablePointer; ///< A pointer to the BBS table. + UINT16 RuntimeSegment; ///< The segment where the OpROM can be relocated to. If this value is 0x0000, this + ///< means that the relocation of this run time code is not supported. + ///< Inconsistent with specification here: + ///< The member's name "OpromDestinationSegment" [defined in Intel Framework Compatibility Support Module Specification / 0.97 version] + ///< has been changed to "RuntimeSegment" since keeping backward compatible. + +} EFI_DISPATCH_OPROM_TABLE; + +/// +/// EFI_TO_COMPATIBILITY16_INIT_TABLE +/// +typedef struct { + /// + /// Starting address of memory under 1 MB. The ending address is assumed to be 640 KB or 0x9FFFF. + /// + UINT32 BiosLessThan1MB; + + /// + /// The starting address of the high memory block. + /// + UINT32 HiPmmMemory; + + /// + /// The length of high memory block. + /// + UINT32 HiPmmMemorySizeInBytes; + + /// + /// The segment of the reverse thunk call code. + /// + UINT16 ReverseThunkCallSegment; + + /// + /// The offset of the reverse thunk call code. + /// + UINT16 ReverseThunkCallOffset; + + /// + /// The number of E820 entries copied to the Compatibility16 BIOS. + /// + UINT32 NumberE820Entries; + + /// + /// The amount of usable memory above 1 MB, e.g., E820 type 1 memory. + /// + UINT32 OsMemoryAbove1Mb; + + /// + /// The start of thunk code in main memory. Memory cannot be used by BIOS or PMM. + /// + UINT32 ThunkStart; + + /// + /// The size of the thunk code. + /// + UINT32 ThunkSizeInBytes; + + /// + /// Starting address of memory under 1 MB. + /// + UINT32 LowPmmMemory; + + /// + /// The length of low Memory block. + /// + UINT32 LowPmmMemorySizeInBytes; +} EFI_TO_COMPATIBILITY16_INIT_TABLE; + +/// +/// DEVICE_PRODUCER_SERIAL. +/// +typedef struct { + UINT16 Address; ///< I/O address assigned to the serial port. + UINT8 Irq; ///< IRQ assigned to the serial port. + SERIAL_MODE Mode; ///< Mode of serial port. Values are defined below. +} DEVICE_PRODUCER_SERIAL; + +/// +/// DEVICE_PRODUCER_SERIAL's modes. +///@{ +#define DEVICE_SERIAL_MODE_NORMAL 0x00 +#define DEVICE_SERIAL_MODE_IRDA 0x01 +#define DEVICE_SERIAL_MODE_ASK_IR 0x02 +#define DEVICE_SERIAL_MODE_DUPLEX_HALF 0x00 +#define DEVICE_SERIAL_MODE_DUPLEX_FULL 0x10 +///@) + +/// +/// DEVICE_PRODUCER_PARALLEL. +/// +typedef struct { + UINT16 Address; ///< I/O address assigned to the parallel port. + UINT8 Irq; ///< IRQ assigned to the parallel port. + UINT8 Dma; ///< DMA assigned to the parallel port. + PARALLEL_MODE Mode; ///< Mode of the parallel port. Values are defined below. +} DEVICE_PRODUCER_PARALLEL; + +/// +/// DEVICE_PRODUCER_PARALLEL's modes. +///@{ +#define DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY 0x00 +#define DEVICE_PARALLEL_MODE_MODE_BIDIRECTIONAL 0x01 +#define DEVICE_PARALLEL_MODE_MODE_EPP 0x02 +#define DEVICE_PARALLEL_MODE_MODE_ECP 0x03 +///@} + +/// +/// DEVICE_PRODUCER_FLOPPY +/// +typedef struct { + UINT16 Address; ///< I/O address assigned to the floppy. + UINT8 Irq; ///< IRQ assigned to the floppy. + UINT8 Dma; ///< DMA assigned to the floppy. + UINT8 NumberOfFloppy; ///< Number of floppies in the system. +} DEVICE_PRODUCER_FLOPPY; + +/// +/// LEGACY_DEVICE_FLAGS +/// +typedef struct { + UINT32 A20Kybd : 1; ///< A20 controller by keyboard controller. + UINT32 A20Port90 : 1; ///< A20 controlled by port 0x92. + UINT32 Reserved : 30; ///< Reserved for future usage. +} LEGACY_DEVICE_FLAGS; + +/// +/// DEVICE_PRODUCER_DATA_HEADER +/// +typedef struct { + DEVICE_PRODUCER_SERIAL Serial[4]; ///< Data for serial port x. Type DEVICE_PRODUCER_SERIAL is defined below. + DEVICE_PRODUCER_PARALLEL Parallel[3]; ///< Data for parallel port x. Type DEVICE_PRODUCER_PARALLEL is defined below. + DEVICE_PRODUCER_FLOPPY Floppy; ///< Data for floppy. Type DEVICE_PRODUCER_FLOPPY is defined below. + UINT8 MousePresent; ///< Flag to indicate if mouse is present. + LEGACY_DEVICE_FLAGS Flags; ///< Miscellaneous Boolean state information passed to CSM. +} DEVICE_PRODUCER_DATA_HEADER; + +/// +/// ATAPI_IDENTIFY +/// +typedef struct { + UINT16 Raw[256]; ///< Raw data from the IDE IdentifyDrive command. +} ATAPI_IDENTIFY; + +/// +/// HDD_INFO +/// +typedef struct { + /// + /// Status of IDE device. Values are defined below. There is one HDD_INFO structure + /// per IDE controller. The IdentifyDrive is per drive. Index 0 is master and index + /// 1 is slave. + /// + UINT16 Status; + + /// + /// PCI bus of IDE controller. + /// + UINT32 Bus; + + /// + /// PCI device of IDE controller. + /// + UINT32 Device; + + /// + /// PCI function of IDE controller. + /// + UINT32 Function; + + /// + /// Command ports base address. + /// + UINT16 CommandBaseAddress; + + /// + /// Control ports base address. + /// + UINT16 ControlBaseAddress; + + /// + /// Bus master address. + /// + UINT16 BusMasterAddress; + + UINT8 HddIrq; + + /// + /// Data that identifies the drive data; one per possible attached drive. + /// + ATAPI_IDENTIFY IdentifyDrive[2]; +} HDD_INFO; + +/// +/// HDD_INFO status bits +/// +#define HDD_PRIMARY 0x01 +#define HDD_SECONDARY 0x02 +#define HDD_MASTER_ATAPI_CDROM 0x04 +#define HDD_SLAVE_ATAPI_CDROM 0x08 +#define HDD_MASTER_IDE 0x20 +#define HDD_SLAVE_IDE 0x40 +#define HDD_MASTER_ATAPI_ZIPDISK 0x10 +#define HDD_SLAVE_ATAPI_ZIPDISK 0x80 + +/// +/// BBS_STATUS_FLAGS;\. +/// +typedef struct { + UINT16 OldPosition : 4; ///< Prior priority. + UINT16 Reserved1 : 4; ///< Reserved for future use. + UINT16 Enabled : 1; ///< If 0, ignore this entry. + UINT16 Failed : 1; ///< 0 = Not known if boot failure occurred. + ///< 1 = Boot attempted failed. + + /// + /// State of media present. + /// 00 = No bootable media is present in the device. + /// 01 = Unknown if a bootable media present. + /// 10 = Media is present and appears bootable. + /// 11 = Reserved. + /// + UINT16 MediaPresent : 2; + UINT16 Reserved2 : 4; ///< Reserved for future use. +} BBS_STATUS_FLAGS; + +/// +/// BBS_TABLE, device type values & boot priority values. +/// +typedef struct { + /// + /// The boot priority for this boot device. Values are defined below. + /// + UINT16 BootPriority; + + /// + /// The PCI bus for this boot device. + /// + UINT32 Bus; + + /// + /// The PCI device for this boot device. + /// + UINT32 Device; + + /// + /// The PCI function for the boot device. + /// + UINT32 Function; + + /// + /// The PCI class for this boot device. + /// + UINT8 Class; + + /// + /// The PCI Subclass for this boot device. + /// + UINT8 SubClass; + + /// + /// Segment:offset address of an ASCIIZ description string describing the manufacturer. + /// + UINT16 MfgStringOffset; + + /// + /// Segment:offset address of an ASCIIZ description string describing the manufacturer. + /// + UINT16 MfgStringSegment; + + /// + /// BBS device type. BBS device types are defined below. + /// + UINT16 DeviceType; + + /// + /// Status of this boot device. Type BBS_STATUS_FLAGS is defined below. + /// + BBS_STATUS_FLAGS StatusFlags; + + /// + /// Segment:Offset address of boot loader for IPL devices or install INT13 handler for + /// BCV devices. + /// + UINT16 BootHandlerOffset; + + /// + /// Segment:Offset address of boot loader for IPL devices or install INT13 handler for + /// BCV devices. + /// + UINT16 BootHandlerSegment; + + /// + /// Segment:offset address of an ASCIIZ description string describing this device. + /// + UINT16 DescStringOffset; + + /// + /// Segment:offset address of an ASCIIZ description string describing this device. + /// + UINT16 DescStringSegment; + + /// + /// Reserved. + /// + UINT32 InitPerReserved; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq13Handler; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq18Handler; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq19Handler; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq40Handler; + UINT8 AssignedDriveNumber; + UINT32 AdditionalIrq41Handler; + UINT32 AdditionalIrq46Handler; + UINT32 IBV1; + UINT32 IBV2; +} BBS_TABLE; + +/// +/// BBS device type values +///@{ +#define BBS_FLOPPY 0x01 +#define BBS_HARDDISK 0x02 +#define BBS_CDROM 0x03 +#define BBS_PCMCIA 0x04 +#define BBS_USB 0x05 +#define BBS_EMBED_NETWORK 0x06 +#define BBS_BEV_DEVICE 0x80 +#define BBS_UNKNOWN 0xff +///@} + +/// +/// BBS boot priority values +///@{ +#define BBS_DO_NOT_BOOT_FROM 0xFFFC +#define BBS_LOWEST_PRIORITY 0xFFFD +#define BBS_UNPRIORITIZED_ENTRY 0xFFFE +#define BBS_IGNORE_ENTRY 0xFFFF +///@} + +/// +/// SMM_ATTRIBUTES +/// +typedef struct { + /// + /// Access mechanism used to generate the soft SMI. Defined types are below. The other + /// values are reserved for future usage. + /// + UINT16 Type : 3; + + /// + /// The size of "port" in bits. Defined values are below. + /// + UINT16 PortGranularity : 3; + + /// + /// The size of data in bits. Defined values are below. + /// + UINT16 DataGranularity : 3; + + /// + /// Reserved for future use. + /// + UINT16 Reserved : 7; +} SMM_ATTRIBUTES; + +/// +/// SMM_ATTRIBUTES type values. +///@{ +#define STANDARD_IO 0x00 +#define STANDARD_MEMORY 0x01 +///@} + +/// +/// SMM_ATTRIBUTES port size constants. +///@{ +#define PORT_SIZE_8 0x00 +#define PORT_SIZE_16 0x01 +#define PORT_SIZE_32 0x02 +#define PORT_SIZE_64 0x03 +///@} + +/// +/// SMM_ATTRIBUTES data size constants. +///@{ +#define DATA_SIZE_8 0x00 +#define DATA_SIZE_16 0x01 +#define DATA_SIZE_32 0x02 +#define DATA_SIZE_64 0x03 +///@} + +/// +/// SMM_FUNCTION & relating constants. +/// +typedef struct { + UINT16 Function : 15; + UINT16 Owner : 1; +} SMM_FUNCTION; + +/// +/// SMM_FUNCTION Function constants. +///@{ +#define INT15_D042 0x0000 +#define GET_USB_BOOT_INFO 0x0001 +#define DMI_PNP_50_57 0x0002 +///@} + +/// +/// SMM_FUNCTION Owner constants. +///@{ +#define STANDARD_OWNER 0x0 +#define OEM_OWNER 0x1 +///@} + +/// +/// This structure assumes both port and data sizes are 1. SmmAttribute must be +/// properly to reflect that assumption. +/// +typedef struct { + /// + /// Describes the access mechanism, SmmPort, and SmmData sizes. Type + /// SMM_ATTRIBUTES is defined below. + /// + SMM_ATTRIBUTES SmmAttributes; + + /// + /// Function Soft SMI is to perform. Type SMM_FUNCTION is defined below. + /// + SMM_FUNCTION SmmFunction; + + /// + /// SmmPort size depends upon SmmAttributes and ranges from2 bytes to 16 bytes. + /// + UINT8 SmmPort; + + /// + /// SmmData size depends upon SmmAttributes and ranges from2 bytes to 16 bytes. + /// + UINT8 SmmData; +} SMM_ENTRY; + +/// +/// SMM_TABLE +/// +typedef struct { + UINT16 NumSmmEntries; ///< Number of entries represented by SmmEntry. + SMM_ENTRY SmmEntry; ///< One entry per function. Type SMM_ENTRY is defined below. +} SMM_TABLE; + +/// +/// UDC_ATTRIBUTES +/// +typedef struct { + /// + /// This bit set indicates that the ServiceAreaData is valid. + /// + UINT8 DirectoryServiceValidity : 1; + + /// + /// This bit set indicates to use the Reserve Area Boot Code Address (RACBA) only if + /// DirectoryServiceValidity is 0. + /// + UINT8 RabcaUsedFlag : 1; + + /// + /// This bit set indicates to execute hard disk diagnostics. + /// + UINT8 ExecuteHddDiagnosticsFlag : 1; + + /// + /// Reserved for future use. Set to 0. + /// + UINT8 Reserved : 5; +} UDC_ATTRIBUTES; + +/// +/// UD_TABLE +/// +typedef struct { + /// + /// This field contains the bit-mapped attributes of the PARTIES information. Type + /// UDC_ATTRIBUTES is defined below. + /// + UDC_ATTRIBUTES Attributes; + + /// + /// This field contains the zero-based device on which the selected + /// ServiceDataArea is present. It is 0 for master and 1 for the slave device. + /// + UINT8 DeviceNumber; + + /// + /// This field contains the zero-based index into the BbsTable for the parent device. + /// This index allows the user to reference the parent device information such as PCI + /// bus, device function. + /// + UINT8 BbsTableEntryNumberForParentDevice; + + /// + /// This field contains the zero-based index into the BbsTable for the boot entry. + /// + UINT8 BbsTableEntryNumberForBoot; + + /// + /// This field contains the zero-based index into the BbsTable for the HDD diagnostics entry. + /// + UINT8 BbsTableEntryNumberForHddDiag; + + /// + /// The raw Beer data. + /// + UINT8 BeerData[128]; + + /// + /// The raw data of selected service area. + /// + UINT8 ServiceAreaData[64]; +} UD_TABLE; + +#define EFI_TO_LEGACY_MAJOR_VERSION 0x02 +#define EFI_TO_LEGACY_MINOR_VERSION 0x00 +#define MAX_IDE_CONTROLLER 8 + +/// +/// EFI_TO_COMPATIBILITY16_BOOT_TABLE +/// +typedef struct { + UINT16 MajorVersion; ///< The EfiCompatibility major version number. + UINT16 MinorVersion; ///< The EfiCompatibility minor version number. + UINT32 AcpiTable; ///< The location of the RSDT ACPI table. < 4G range. + UINT32 SmbiosTable; ///< The location of the SMBIOS table in EFI memory. < 4G range. + UINT32 SmbiosTableLength; + // + // Legacy SIO state + // + DEVICE_PRODUCER_DATA_HEADER SioData; ///< Standard traditional device information. + UINT16 DevicePathType; ///< The default boot type. + UINT16 PciIrqMask; ///< Mask of which IRQs have been assigned to PCI. + UINT32 NumberE820Entries; ///< Number of E820 entries. The number can change from the + ///< Compatibility16InitializeYourself() function. + // + // Controller & Drive Identify[2] per controller information + // + HDD_INFO HddInfo[MAX_IDE_CONTROLLER]; ///< Hard disk drive information, including raw Identify Drive data. + UINT32 NumberBbsEntries; ///< Number of entries in the BBS table + UINT32 BbsTable; ///< A pointer to the BBS table. Type BBS_TABLE is defined below. + UINT32 SmmTable; ///< A pointer to the SMM table. Type SMM_TABLE is defined below. + UINT32 OsMemoryAbove1Mb; ///< The amount of usable memory above 1 MB, i.e. E820 type 1 memory. This value can + ///< differ from the value in EFI_TO_COMPATIBILITY16_INIT_TABLE as more + ///< memory may have been discovered. + UINT32 UnconventionalDeviceTable; ///< Information to boot off an unconventional device like a PARTIES partition. Type + ///< UD_TABLE is defined below. +} EFI_TO_COMPATIBILITY16_BOOT_TABLE; + +/// +/// EFI_LEGACY_INSTALL_PCI_HANDLER +/// +typedef struct { + UINT8 PciBus; ///< The PCI bus of the device. + UINT8 PciDeviceFun; ///< The PCI device in bits 7:3 and function in bits 2:0. + UINT8 PciSegment; ///< The PCI segment of the device. + UINT8 PciClass; ///< The PCI class code of the device. + UINT8 PciSubclass; ///< The PCI subclass code of the device. + UINT8 PciInterface; ///< The PCI interface code of the device. + // + // Primary section + // + UINT8 PrimaryIrq; ///< The primary device IRQ. + UINT8 PrimaryReserved; ///< Reserved. + UINT16 PrimaryControl; ///< The primary device control I/O base. + UINT16 PrimaryBase; ///< The primary device I/O base. + UINT16 PrimaryBusMaster; ///< The primary device bus master I/O base. + // + // Secondary Section + // + UINT8 SecondaryIrq; ///< The secondary device IRQ. + UINT8 SecondaryReserved; ///< Reserved. + UINT16 SecondaryControl; ///< The secondary device control I/O base. + UINT16 SecondaryBase; ///< The secondary device I/O base. + UINT16 SecondaryBusMaster; ///< The secondary device bus master I/O base. +} EFI_LEGACY_INSTALL_PCI_HANDLER; + +#endif diff --git a/qemu/roms/seabios/src/std/acpi.h b/qemu/roms/seabios/src/std/acpi.h new file mode 100644 index 000000000..e0d9516ba --- /dev/null +++ b/qemu/roms/seabios/src/std/acpi.h @@ -0,0 +1,297 @@ +#ifndef __ACPI_H +#define __ACPI_H + +#include "types.h" // u32 + +/* + * ACPI 2.0 Generic Address Space definition. + */ +struct acpi_20_generic_address { + u8 address_space_id; + u8 register_bit_width; + u8 register_bit_offset; + u8 reserved; + u64 address; +} PACKED; + +#define RSDP_SIGNATURE 0x2052545020445352LL // "RSD PTR " + +struct rsdp_descriptor { /* Root System Descriptor Pointer */ + u64 signature; /* ACPI signature, contains "RSD PTR " */ + u8 checksum; /* To make sum of struct == 0 */ + u8 oem_id [6]; /* OEM identification */ + u8 revision; /* Must be 0 for 1.0, 2 for 2.0 */ + u32 rsdt_physical_address; /* 32-bit physical address of RSDT */ + u32 length; /* XSDT Length in bytes including hdr */ + u64 xsdt_physical_address; /* 64-bit physical address of XSDT */ + u8 extended_checksum; /* Checksum of entire table */ + u8 reserved [3]; /* Reserved field must be 0 */ +}; + +/* Table structure from Linux kernel (the ACPI tables are under the + BSD license) */ + +#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \ + u32 signature; /* ACPI signature (4 ASCII characters) */ \ + u32 length; /* Length of table, in bytes, including header */ \ + u8 revision; /* ACPI Specification minor version # */ \ + u8 checksum; /* To make sum of entire table == 0 */ \ + u8 oem_id [6]; /* OEM identification */ \ + u8 oem_table_id [8]; /* OEM table identification */ \ + u32 oem_revision; /* OEM revision number */ \ + u8 asl_compiler_id [4]; /* ASL compiler vendor ID */ \ + u32 asl_compiler_revision; /* ASL compiler revision number */ + +/* + * Fixed ACPI Description Table Fixed Feature Flags + */ +#define ACPI_FADT_F_WBINVD (1 << 0) +#define ACPI_FADT_F_WBINVD_FLUSH (1 << 1) +#define ACPI_FADT_F_PROC_C1 (1 << 2) +#define ACPI_FADT_F_P_LVL2_UP (1 << 3) +#define ACPI_FADT_F_PWR_BUTTON (1 << 4) +#define ACPI_FADT_F_SLP_BUTTON (1 << 5) +#define ACPI_FADT_F_FIX_RTC (1 << 6) +#define ACPI_FADT_F_RTC_S4 (1 << 7) +#define ACPI_FADT_F_TMR_VAL_EXT (1 << 8) +#define ACPI_FADT_F_DCK_CAP (1 << 9) +#define ACPI_FADT_F_RESET_REG_SUP (1 << 10) +#define ACPI_FADT_F_SEALED_CASE (1 << 11) +#define ACPI_FADT_F_HEADLESS (1 << 12) +#define ACPI_FADT_F_CPU_SW_SLP (1 << 13) +#define ACPI_FADT_F_PCI_EXP_WAK (1 << 14) +#define ACPI_FADT_F_USE_PLATFORM_CLOCK (1 << 15) +#define ACPI_FADT_F_S4_RTC_STS_VALID (1 << 16) +#define ACPI_FADT_F_REMOTE_POWER_ON_CAPABLE (1 << 17) +#define ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL (1 << 18) +#define ACPI_FADT_F_FORCE_APIC_PHYSICAL_DESTINATION_MODE (1 << 19) +#define ACPI_FADT_F_HW_REDUCED_ACPI (1 << 20) +#define ACPI_FADT_F_LOW_POWER_S0_IDLE_CAPABLE (1 << 21) + +/* + * ACPI 1.0 Fixed ACPI Description Table (FADT) + */ +#define FACP_SIGNATURE 0x50434146 // FACP +struct fadt_descriptor_rev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 firmware_ctrl; /* Physical address of FACS */ + u32 dsdt; /* Physical address of DSDT */ + u8 model; /* System Interrupt Model */ + u8 reserved1; /* Reserved */ + u16 sci_int; /* System vector of SCI interrupt */ + u32 smi_cmd; /* Port address of SMI command port */ + u8 acpi_enable; /* Value to write to smi_cmd to enable ACPI */ + u8 acpi_disable; /* Value to write to smi_cmd to disable ACPI */ + u8 S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */ + u8 reserved2; /* Reserved - must be zero */ + u32 pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */ + u32 pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */ + u32 pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ + u32 pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ + u32 pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ + u32 pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ + u32 gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */ + u32 gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */ + u8 pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */ + u8 pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */ + u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ + u8 pm_tmr_len; /* Byte Length of ports at pm_tm_blk */ + u8 gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ + u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ + u8 gpe1_base; /* Offset in gpe model where gpe1 events start */ + u8 reserved3; /* Reserved */ + u16 plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ + u16 plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ + u16 flush_size; /* Size of area read to flush caches */ + u16 flush_stride; /* Stride used in flushing caches */ + u8 duty_offset; /* Bit location of duty cycle field in p_cnt reg */ + u8 duty_width; /* Bit width of duty cycle field in p_cnt reg */ + u8 day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ + u8 mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ + u8 century; /* Index to century in RTC CMOS RAM */ + u8 reserved4; /* Reserved */ + u8 reserved4a; /* Reserved */ + u8 reserved4b; /* Reserved */ + u32 flags; +} PACKED; + +struct acpi_table_header /* ACPI common table header */ +{ + ACPI_TABLE_HEADER_DEF +} PACKED; + +/* + * ACPI 1.0 Root System Description Table (RSDT) + */ +#define RSDT_SIGNATURE 0x54445352 // RSDT +struct rsdt_descriptor_rev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 table_offset_entry[0]; /* Array of pointers to other */ + /* ACPI tables */ +} PACKED; + +/* + * ACPI 1.0 Firmware ACPI Control Structure (FACS) + */ +#define FACS_SIGNATURE 0x53434146 // FACS +struct facs_descriptor_rev1 +{ + u32 signature; /* ACPI Signature */ + u32 length; /* Length of structure, in bytes */ + u32 hardware_signature; /* Hardware configuration signature */ + u32 firmware_waking_vector; /* ACPI OS waking vector */ + u32 global_lock; /* Global Lock */ + u32 flags; + u8 resverved3 [40]; /* Reserved - must be zero */ +} PACKED; + +/* + * Differentiated System Description Table (DSDT) + */ +#define DSDT_SIGNATURE 0x54445344 // DSDT + +/* + * MADT values and structures + */ + +/* Values for MADT PCATCompat */ + +#define DUAL_PIC 0 +#define MULTIPLE_APIC 1 + +/* Master MADT */ + +#define APIC_SIGNATURE 0x43495041 // APIC +struct multiple_apic_table +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 local_apic_address; /* Physical address of local APIC */ + u32 flags; +} PACKED; + +/* Values for Type in APIC sub-headers */ + +#define APIC_PROCESSOR 0 +#define APIC_IO 1 +#define APIC_XRUPT_OVERRIDE 2 +#define APIC_NMI 3 +#define APIC_LOCAL_NMI 4 +#define APIC_ADDRESS_OVERRIDE 5 +#define APIC_IO_SAPIC 6 +#define APIC_LOCAL_SAPIC 7 +#define APIC_XRUPT_SOURCE 8 +#define APIC_RESERVED 9 /* 9 and greater are reserved */ + +/* + * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) + */ +#define ACPI_SUB_HEADER_DEF /* Common ACPI sub-structure header */\ + u8 type; \ + u8 length; + +/* Sub-structures for MADT */ + +struct madt_processor_apic +{ + ACPI_SUB_HEADER_DEF + u8 processor_id; /* ACPI processor id */ + u8 local_apic_id; /* Processor's local APIC id */ + u32 flags; +} PACKED; + +struct madt_io_apic +{ + ACPI_SUB_HEADER_DEF + u8 io_apic_id; /* I/O APIC ID */ + u8 reserved; /* Reserved - must be zero */ + u32 address; /* APIC physical address */ + u32 interrupt; /* Global system interrupt where INTI + * lines start */ +} PACKED; + +struct madt_intsrcovr { + ACPI_SUB_HEADER_DEF + u8 bus; + u8 source; + u32 gsi; + u16 flags; +} PACKED; + +struct madt_local_nmi { + ACPI_SUB_HEADER_DEF + u8 processor_id; /* ACPI processor id */ + u16 flags; /* MPS INTI flags */ + u8 lint; /* Local APIC LINT# */ +} PACKED; + +/* + * HPET Description Table + */ +#define HPET_SIGNATURE 0x54455048 // HPET +struct acpi_20_hpet { + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 timer_block_id; + struct acpi_20_generic_address addr; + u8 hpet_number; + u16 min_tick; + u8 page_protect; +} PACKED; + +/* + * SRAT (NUMA topology description) table + */ + +#define SRAT_SIGNATURE 0x54415253 // SRAT +struct system_resource_affinity_table +{ + ACPI_TABLE_HEADER_DEF + u32 reserved1; + u32 reserved2[2]; +} PACKED; + +#define SRAT_PROCESSOR 0 +#define SRAT_MEMORY 1 + +struct srat_processor_affinity +{ + ACPI_SUB_HEADER_DEF + u8 proximity_lo; + u8 local_apic_id; + u32 flags; + u8 local_sapic_eid; + u8 proximity_hi[3]; + u32 reserved; +} PACKED; + +struct srat_memory_affinity +{ + ACPI_SUB_HEADER_DEF + u8 proximity[4]; + u16 reserved1; + u64 base_addr; + u64 range_length; + u32 reserved2; + u32 flags; + u32 reserved3[2]; +} PACKED; + +/* PCI fw r3.0 MCFG table. */ +/* Subtable */ +struct acpi_mcfg_allocation { + u64 address; /* Base address, processor-relative */ + u16 pci_segment; /* PCI segment group number */ + u8 start_bus_number; /* Starting PCI Bus number */ + u8 end_bus_number; /* Final PCI Bus number */ + u32 reserved; +} PACKED; + +#define MCFG_SIGNATURE 0x4746434d // MCFG +struct acpi_table_mcfg { + ACPI_TABLE_HEADER_DEF; + u8 reserved[8]; + struct acpi_mcfg_allocation allocation[0]; +} PACKED; + +#endif // acpi.h diff --git a/qemu/roms/seabios/src/std/bda.h b/qemu/roms/seabios/src/std/bda.h new file mode 100644 index 000000000..c321266e2 --- /dev/null +++ b/qemu/roms/seabios/src/std/bda.h @@ -0,0 +1,154 @@ +// BIOS Data Area (and similar) definitions +#ifndef __BDA_H +#define __BDA_H + +#include "disk.h" // struct fdpt_s +#include "types.h" // u8 + + +/**************************************************************** + * Interupt vector table + ****************************************************************/ + +struct rmode_IVT { + struct segoff_s ivec[256]; +}; + + +/**************************************************************** + * Bios Data Area (BDA) + ****************************************************************/ + +struct bios_data_area_s { + // 40:00 + u16 port_com[4]; + u16 port_lpt[3]; + u16 ebda_seg; + // 40:10 + u16 equipment_list_flags; + u8 pad1; + u16 mem_size_kb; + u8 pad2; + u8 ps2_ctrl_flag; + u8 kbd_flag0; + u8 kbd_flag1; + u8 alt_keypad; + u16 kbd_buf_head; + u16 kbd_buf_tail; + // 40:1e + u8 kbd_buf[32]; + u8 floppy_recalibration_status; + u8 floppy_motor_status; + // 40:40 + u8 floppy_motor_counter; + u8 floppy_last_status; + u8 floppy_return_status[7]; + u8 video_mode; + u16 video_cols; + u16 video_pagesize; + u16 video_pagestart; + // 40:50 + u16 cursor_pos[8]; + // 40:60 + u16 cursor_type; + u8 video_page; + u16 crtc_address; + u8 video_msr; + u8 video_pal; + struct segoff_s jump; + u8 other_6b; + u32 timer_counter; + // 40:70 + u8 timer_rollover; + u8 break_flag; + u16 soft_reset_flag; + u8 disk_last_status; + u8 hdcount; + u8 disk_control_byte; + u8 port_disk; + u8 lpt_timeout[4]; + u8 com_timeout[4]; + // 40:80 + u16 kbd_buf_start_offset; + u16 kbd_buf_end_offset; + u8 video_rows; + u16 char_height; + u8 video_ctl; + u8 video_switches; + u8 modeset_ctl; + u8 dcc_index; + u8 floppy_last_data_rate; + u8 disk_status_controller; + u8 disk_error_controller; + u8 disk_interrupt_flag; + u8 floppy_harddisk_info; + // 40:90 + u8 floppy_media_state[4]; + u8 floppy_track[2]; + u8 kbd_flag2; + u8 kbd_led; + struct segoff_s user_wait_complete_flag; + u32 user_wait_timeout; + // 40:A0 + u8 rtc_wait_flag; + u8 other_a1[7]; + struct segoff_s video_savetable; + u8 other_ac[4]; + // 40:B0 + u8 other_b0[5*16]; +} PACKED; + +// BDA floppy_recalibration_status bitdefs +#define FRS_IRQ (1<<7) + +// BDA rtc_wait_flag bitdefs +#define RWS_WAIT_PENDING (1<<0) +#define RWS_WAIT_ELAPSED (1<<7) + +// BDA floppy_media_state bitdefs +#define FMS_DRIVE_STATE_MASK (0x07) +#define FMS_MEDIA_DRIVE_ESTABLISHED (1<<4) +#define FMS_DOUBLE_STEPPING (1<<5) +#define FMS_DATA_RATE_MASK (0xc0) + +// Limit of BDA timer_counter field +#define TICKS_PER_DAY 1573040 + + +/**************************************************************** + * Extended Bios Data Area (EBDA) + ****************************************************************/ + +struct extended_bios_data_area_s { + u8 size; + u8 reserved1[0x21]; + struct segoff_s far_call_pointer; + u8 mouse_flag1; + u8 mouse_flag2; + u8 mouse_data[0x08]; + // 0x30 + u8 other1[0x0d]; + + // 0x3d + struct fdpt_s fdpt[2]; + + // 0x5d + u8 other2[0xC4]; + + // 0x121 - Begin custom storage. +} PACKED; + + +/**************************************************************** + * Bios Config Table + ****************************************************************/ + +struct bios_config_table_s { + u16 size; + u8 model; + u8 submodel; + u8 biosrev; + u8 feature1, feature2, feature3, feature4, feature5; +} PACKED; + +#endif // bda.h diff --git a/qemu/roms/seabios/src/std/disk.h b/qemu/roms/seabios/src/std/disk.h new file mode 100644 index 000000000..639710899 --- /dev/null +++ b/qemu/roms/seabios/src/std/disk.h @@ -0,0 +1,175 @@ +// Standard disk BIOS definitions. +#ifndef __DISK_H +#define __DISK_H + +#include "types.h" // u8 + +#define DISK_RET_SUCCESS 0x00 +#define DISK_RET_EPARAM 0x01 +#define DISK_RET_EADDRNOTFOUND 0x02 +#define DISK_RET_EWRITEPROTECT 0x03 +#define DISK_RET_ECHANGED 0x06 +#define DISK_RET_EBOUNDARY 0x09 +#define DISK_RET_EBADTRACK 0x0c +#define DISK_RET_ECONTROLLER 0x20 +#define DISK_RET_ETIMEOUT 0x80 +#define DISK_RET_ENOTLOCKED 0xb0 +#define DISK_RET_ELOCKED 0xb1 +#define DISK_RET_ENOTREMOVABLE 0xb2 +#define DISK_RET_ETOOMANYLOCKS 0xb4 +#define DISK_RET_EMEDIA 0xC0 +#define DISK_RET_ENOTREADY 0xAA + + +/**************************************************************** + * Interface structs + ****************************************************************/ + +// Bios disk structures. +struct int13ext_s { + u8 size; + u8 reserved; + u16 count; + struct segoff_s data; + u64 lba; +} PACKED; + +// DPTE definition +struct dpte_s { + u16 iobase1; + u16 iobase2; + u8 prefix; + u8 unused; + u8 irq; + u8 blkcount; + u8 dma; + u8 pio; + u16 options; + u16 reserved; + u8 revision; + u8 checksum; +}; + +// Disk Physical Table definition +struct int13dpt_s { + u16 size; + u16 infos; + u32 cylinders; + u32 heads; + u32 spt; + u64 sector_count; + u16 blksize; + struct segoff_s dpte; + u16 key; + u8 dpi_length; + u8 reserved1; + u16 reserved2; + u8 host_bus[4]; + u8 iface_type[8]; + u64 iface_path; + union { + struct { + u64 device_path; + u8 reserved3; + u8 checksum; + } phoenix; + struct { + u64 device_path[2]; + u8 reserved3; + u8 checksum; + } t13; + }; +} PACKED; + +// Floppy info +struct fdpt_s { + u16 cylinders; + u8 heads; + u8 a0h_signature; + u8 phys_sectors; + u16 precompensation; + u8 reserved; + u8 drive_control_byte; + u16 phys_cylinders; + u8 phys_heads; + u16 landing_zone; + u8 sectors; + u8 checksum; +} PACKED; + +// Floppy "Disk Base Table" +struct floppy_dbt_s { + u8 specify1; + u8 specify2; + u8 shutoff_ticks; + u8 bps_code; + u8 sectors; + u8 interblock_len; + u8 data_len; + u8 gap_len; + u8 fill_byte; + u8 settle_time; + u8 startup_time; +} PACKED; + +struct floppy_ext_dbt_s { + struct floppy_dbt_s dbt; + // Extra fields + u8 max_track; + u8 data_rate; + u8 drive_type; +} PACKED; + + +/**************************************************************** + * Master boot record + ****************************************************************/ + +struct packed_chs_s { + u8 heads; + u8 sptcyl; + u8 cyllow; +} PACKED; + +struct partition_s { + u8 status; + struct packed_chs_s first; + u8 type; + struct packed_chs_s last; + u32 lba; + u32 count; +} PACKED; + +struct mbr_s { + u8 code[440]; + // 0x01b8 + u32 diskseg; + // 0x01bc + u16 null; + // 0x01be + struct partition_s partitions[4]; + // 0x01fe + u16 signature; +} PACKED; + +#define MBR_SIGNATURE 0xaa55 + + +/**************************************************************** + * ElTorito CDROM interface + ****************************************************************/ + +struct eltorito_s { + u8 size; + u8 media; + u8 emulated_drive; + u8 controller_index; + u32 ilba; + u16 device_spec; + u16 buffer_segment; + u16 load_segment; + u16 sector_count; + struct packed_chs_s chs; +} PACKED; + +#endif // disk.h diff --git a/qemu/roms/seabios/src/std/mptable.h b/qemu/roms/seabios/src/std/mptable.h new file mode 100644 index 000000000..fa6a22949 --- /dev/null +++ b/qemu/roms/seabios/src/std/mptable.h @@ -0,0 +1,77 @@ +#ifndef __MPTABLE_H +#define __MPTABLE_H + +#include "types.h" // u32 + +#define MPTABLE_SIGNATURE 0x5f504d5f // "_MP_" + +struct mptable_floating_s { + u32 signature; + u32 physaddr; + u8 length; + u8 spec_rev; + u8 checksum; + u8 feature1; + u8 feature2; + u8 reserved[3]; +}; + +#define MPCONFIG_SIGNATURE 0x504d4350 // "PCMP" + +struct mptable_config_s { + u32 signature; + u16 length; + u8 spec; + u8 checksum; + char oemid[8]; + char productid[12]; + u32 oemptr; + u16 oemsize; + u16 entrycount; + u32 lapic; + u16 exttable_length; + u8 exttable_checksum; + u8 reserved; +} PACKED; + +#define MPT_TYPE_CPU 0 +#define MPT_TYPE_BUS 1 +#define MPT_TYPE_IOAPIC 2 +#define MPT_TYPE_INTSRC 3 +#define MPT_TYPE_LOCAL_INT 4 + +struct mpt_cpu { + u8 type; + u8 apicid; + u8 apicver; + u8 cpuflag; + u32 cpusignature; + u32 featureflag; + u32 reserved[2]; +} PACKED; + +struct mpt_bus { + u8 type; + u8 busid; + char bustype[6]; +} PACKED; + +struct mpt_ioapic { + u8 type; + u8 apicid; + u8 apicver; + u8 flags; + u32 apicaddr; +} PACKED; + +struct mpt_intsrc { + u8 type; + u8 irqtype; + u16 irqflag; + u8 srcbus; + u8 srcbusirq; + u8 dstapic; + u8 dstirq; +} PACKED; + +#endif // mptable.h diff --git a/qemu/roms/seabios/src/std/optionrom.h b/qemu/roms/seabios/src/std/optionrom.h new file mode 100644 index 000000000..94ca4ae10 --- /dev/null +++ b/qemu/roms/seabios/src/std/optionrom.h @@ -0,0 +1,59 @@ +#ifndef __OPTIONROMS_H +#define __OPTIONROMS_H + +#include "types.h" // u32 + +#define OPTION_ROM_SIGNATURE 0xaa55 + +struct rom_header { + u16 signature; + u8 size; + u8 initVector[4]; + u8 reserved[17]; + u16 pcioffset; + u16 pnpoffset; +} PACKED; + +#define PCI_ROM_SIGNATURE 0x52494350 // "PCIR" + +struct pci_data { + u32 signature; + u16 vendor; + u16 device; + u16 vitaldata; + u16 dlen; + u8 drevision; + u8 class_lo; + u16 class_hi; + u16 ilen; + u16 irevision; + u8 type; + u8 indicator; + u16 reserved; +} PACKED; + +struct pnp_data { + u32 signature; + u8 revision; + u8 len; + u16 nextoffset; + u8 reserved_08; + u8 checksum; + u32 devid; + u16 manufacturer; + u16 productname; + u8 type_lo; + u16 type_hi; + u8 dev_flags; + u16 bcv; + u16 dv; + u16 bev; + u16 reserved_1c; + u16 staticresource; +} PACKED; + +#define OPTION_ROM_ALIGN 2048 +#define OPTION_ROM_INITVECTOR offsetof(struct rom_header, initVector[0]) +#define PCIROM_CODETYPE_X86 0 + +#endif diff --git a/qemu/roms/seabios/src/std/pirtable.h b/qemu/roms/seabios/src/std/pirtable.h new file mode 100644 index 000000000..9de3a4389 --- /dev/null +++ b/qemu/roms/seabios/src/std/pirtable.h @@ -0,0 +1,35 @@ +#ifndef __PIRTABLE_H +#define __PIRTABLE_H + +#include "types.h" // u32 + +struct link_info { + u8 link; + u16 bitmap; +} PACKED; + +struct pir_slot { + u8 bus; + u8 dev; + struct link_info links[4]; + u8 slot_nr; + u8 reserved; +} PACKED; + +struct pir_header { + u32 signature; + u16 version; + u16 size; + u8 router_bus; + u8 router_devfunc; + u16 exclusive_irqs; + u32 compatible_devid; + u32 miniport_data; + u8 reserved[11]; + u8 checksum; + struct pir_slot slots[0]; +} PACKED; + +#define PIR_SIGNATURE 0x52495024 // $PIR + +#endif // pirtable.h diff --git a/qemu/roms/seabios/src/std/pmm.h b/qemu/roms/seabios/src/std/pmm.h new file mode 100644 index 000000000..80027f38b --- /dev/null +++ b/qemu/roms/seabios/src/std/pmm.h @@ -0,0 +1,19 @@ +#ifndef __PMM_H +#define __PMM_H + +#include "types.h" // u32 + +#define PMM_SIGNATURE 0x4d4d5024 // $PMM + +struct pmmheader { + u32 signature; + u8 version; + u8 length; + u8 checksum; + struct segoff_s entry; + u8 reserved[5]; +} PACKED; + +#define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff + +#endif // pmm.h diff --git a/qemu/roms/seabios/src/std/pnpbios.h b/qemu/roms/seabios/src/std/pnpbios.h new file mode 100644 index 000000000..0871e3ab5 --- /dev/null +++ b/qemu/roms/seabios/src/std/pnpbios.h @@ -0,0 +1,24 @@ +#ifndef __PNPHEADER_H +#define __PNPHEADER_H + +#define PNP_SIGNATURE 0x506e5024 // $PnP + +struct pnpheader { + u32 signature; + u8 version; + u8 length; + u16 control; + u8 checksum; + u32 eventloc; + u16 real_ip; + u16 real_cs; + u16 prot_ip; + u32 prot_base; + u32 oemid; + u16 real_ds; + u32 prot_database; +} PACKED; + +#define FUNCTION_NOT_SUPPORTED 0x82 + +#endif // pnpheader.h diff --git a/qemu/roms/seabios/src/std/smbios.h b/qemu/roms/seabios/src/std/smbios.h new file mode 100644 index 000000000..05137167a --- /dev/null +++ b/qemu/roms/seabios/src/std/smbios.h @@ -0,0 +1,165 @@ +#ifndef __SMBIOS_H +#define __SMBIOS_H + +#include "types.h" // u32 + +/* SMBIOS entry point -- must be written to a 16-bit aligned address + between 0xf0000 and 0xfffff. + */ +struct smbios_entry_point { + char anchor_string[4]; + u8 checksum; + u8 length; + u8 smbios_major_version; + u8 smbios_minor_version; + u16 max_structure_size; + u8 entry_point_revision; + u8 formatted_area[5]; + char intermediate_anchor_string[5]; + u8 intermediate_checksum; + u16 structure_table_length; + u32 structure_table_address; + u16 number_of_structures; + u8 smbios_bcd_revision; +} PACKED; + +/* This goes at the beginning of every SMBIOS structure. */ +struct smbios_structure_header { + u8 type; + u8 length; + u16 handle; +} PACKED; + +/* SMBIOS type 0 - BIOS Information */ +struct smbios_type_0 { + struct smbios_structure_header header; + u8 vendor_str; + u8 bios_version_str; + u16 bios_starting_address_segment; + u8 bios_release_date_str; + u8 bios_rom_size; + u8 bios_characteristics[8]; + u8 bios_characteristics_extension_bytes[2]; + u8 system_bios_major_release; + u8 system_bios_minor_release; + u8 embedded_controller_major_release; + u8 embedded_controller_minor_release; +} PACKED; + +/* SMBIOS type 1 - System Information */ +struct smbios_type_1 { + struct smbios_structure_header header; + u8 manufacturer_str; + u8 product_name_str; + u8 version_str; + u8 serial_number_str; + u8 uuid[16]; + u8 wake_up_type; + u8 sku_number_str; + u8 family_str; +} PACKED; + +/* SMBIOS type 3 - System Enclosure (v2.3) */ +struct smbios_type_3 { + struct smbios_structure_header header; + u8 manufacturer_str; + u8 type; + u8 version_str; + u8 serial_number_str; + u8 asset_tag_number_str; + u8 boot_up_state; + u8 power_supply_state; + u8 thermal_state; + u8 security_status; + u32 oem_defined; + u8 height; + u8 number_of_power_cords; + u8 contained_element_count; + // contained elements follow +} PACKED; + +/* SMBIOS type 4 - Processor Information (v2.0) */ +struct smbios_type_4 { + struct smbios_structure_header header; + u8 socket_designation_str; + u8 processor_type; + u8 processor_family; + u8 processor_manufacturer_str; + u32 processor_id[2]; + u8 processor_version_str; + u8 voltage; + u16 external_clock; + u16 max_speed; + u16 current_speed; + u8 status; + u8 processor_upgrade; + u16 l1_cache_handle; + u16 l2_cache_handle; + u16 l3_cache_handle; +} PACKED; + +/* SMBIOS type 16 - Physical Memory Array + * Associated with one type 17 (Memory Device). + */ +struct smbios_type_16 { + struct smbios_structure_header header; + u8 location; + u8 use; + u8 error_correction; + u32 maximum_capacity; + u16 memory_error_information_handle; + u16 number_of_memory_devices; +} PACKED; + +/* SMBIOS type 17 - Memory Device + * Associated with one type 19 + */ +struct smbios_type_17 { + struct smbios_structure_header header; + u16 physical_memory_array_handle; + u16 memory_error_information_handle; + u16 total_width; + u16 data_width; + u16 size; + u8 form_factor; + u8 device_set; + u8 device_locator_str; + u8 bank_locator_str; + u8 memory_type; + u16 type_detail; +} PACKED; + +/* SMBIOS type 19 - Memory Array Mapped Address */ +struct smbios_type_19 { + struct smbios_structure_header header; + u32 starting_address; + u32 ending_address; + u16 memory_array_handle; + u8 partition_width; +} PACKED; + +/* SMBIOS type 20 - Memory Device Mapped Address */ +struct smbios_type_20 { + struct smbios_structure_header header; + u32 starting_address; + u32 ending_address; + u16 memory_device_handle; + u16 memory_array_mapped_address_handle; + u8 partition_row_position; + u8 interleave_position; + u8 interleaved_data_depth; +} PACKED; + +/* SMBIOS type 32 - System Boot Information */ +struct smbios_type_32 { + struct smbios_structure_header header; + u8 reserved[6]; + u8 boot_status; +} PACKED; + +/* SMBIOS type 127 -- End-of-table */ +struct smbios_type_127 { + struct smbios_structure_header header; +} PACKED; + +#endif // smbios.h diff --git a/qemu/roms/seabios/src/std/vbe.h b/qemu/roms/seabios/src/std/vbe.h new file mode 100644 index 000000000..94b4ad86f --- /dev/null +++ b/qemu/roms/seabios/src/std/vbe.h @@ -0,0 +1,156 @@ +#ifndef __VBE_H +#define __VBE_H + +#include "types.h" // u8 + +#define VESA_SIGNATURE 0x41534556 // VESA +#define VBE2_SIGNATURE 0x32454256 // VBE2 + +struct vbe_info { + u32 signature; + u16 version; + struct segoff_s oem_string; + u32 capabilities; + struct segoff_s video_mode; + u16 total_memory; + u16 oem_revision; + struct segoff_s oem_vendor_string; + struct segoff_s oem_product_string; + struct segoff_s oem_revision_string; + u8 reserved[222]; +} PACKED; + +struct vbe_mode_info { + /* VBE */ + u16 mode_attributes; + u8 winA_attributes; + u8 winB_attributes; + u16 win_granularity; + u16 win_size; + u16 winA_seg; + u16 winB_seg; + struct segoff_s win_func_ptr; + u16 bytes_per_scanline; + /* VBE 1.2 */ + u16 xres; + u16 yres; + u8 xcharsize; + u8 ycharsize; + u8 planes; + u8 bits_per_pixel; + u8 banks; + u8 mem_model; + u8 bank_size; + u8 pages; + u8 reserved0; + /* Direct Color */ + u8 red_size; + u8 red_pos; + u8 green_size; + u8 green_pos; + u8 blue_size; + u8 blue_pos; + u8 alpha_size; + u8 alpha_pos; + u8 directcolor_info; + /* VBE 2.0 */ + u32 phys_base; + u32 reserved1; + u16 reserved2; + /* VBE 3.0 */ + u16 linear_bytes_per_scanline; + u8 bank_pages; + u8 linear_pages; + u8 linear_red_size; + u8 linear_red_pos; + u8 linear_green_size; + u8 linear_green_pos; + u8 linear_blue_size; + u8 linear_blue_pos; + u8 linear_alpha_size; + u8 linear_alpha_pos; + u32 pixclock_max; + u8 reserved[190]; +} PACKED; + +struct vbe_crtc_info { + u16 horiz_total; + u16 horiz_sync_start; + u16 horiz_sync_end; + u16 vert_total; + u16 vert_sync_start; + u16 vert_sync_end; + u8 flags; + u32 pixclock; + u16 refresh_rate; + u8 reserved[40]; +} PACKED; + +/* VBE Return Status Info */ +/* AL */ +#define VBE_RETURN_STATUS_SUPPORTED 0x4F +#define VBE_RETURN_STATUS_UNSUPPORTED 0x00 +/* AH */ +#define VBE_RETURN_STATUS_SUCCESSFULL 0x00 +#define VBE_RETURN_STATUS_FAILED 0x01 +#define VBE_RETURN_STATUS_NOT_SUPPORTED 0x02 +#define VBE_RETURN_STATUS_INVALID 0x03 + +/* VBE Mode Numbers */ + +#define VBE_MODE_VESA_DEFINED 0x0100 +#define VBE_MODE_REFRESH_RATE_USE_CRTC 0x0800 +#define VBE_MODE_LINEAR_FRAME_BUFFER 0x4000 +#define VBE_MODE_PRESERVE_DISPLAY_MEMORY 0x8000 + +#define VBE_VESA_MODE_END_OF_LIST 0xFFFF + +/* Capabilities */ + +#define VBE_CAPABILITY_8BIT_DAC 0x0001 +#define VBE_CAPABILITY_NOT_VGA_COMPATIBLE 0x0002 +#define VBE_CAPABILITY_RAMDAC_USE_BLANK_BIT 0x0004 +#define VBE_CAPABILITY_STEREOSCOPIC_SUPPORT 0x0008 +#define VBE_CAPABILITY_STEREO_VIA_VESA_EVC 0x0010 + +/* Mode Attributes */ + +#define VBE_MODE_ATTRIBUTE_SUPPORTED 0x0001 +#define VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE 0x0002 +#define VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT 0x0004 +#define VBE_MODE_ATTRIBUTE_COLOR_MODE 0x0008 +#define VBE_MODE_ATTRIBUTE_GRAPHICS_MODE 0x0010 +#define VBE_MODE_ATTRIBUTE_NOT_VGA_COMPATIBLE 0x0020 +#define VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW 0x0040 +#define VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE 0x0080 +#define VBE_MODE_ATTRIBUTE_DOUBLE_SCAN_MODE 0x0100 +#define VBE_MODE_ATTRIBUTE_INTERLACE_MODE 0x0200 +#define VBE_MODE_ATTRIBUTE_HARDWARE_TRIPLE_BUFFER 0x0400 +#define VBE_MODE_ATTRIBUTE_HARDWARE_STEREOSCOPIC_DISPLAY 0x0800 +#define VBE_MODE_ATTRIBUTE_DUAL_DISPLAY_START_ADDRESS 0x1000 + +#define VBE_MODE_ATTTRIBUTE_LFB_ONLY ( VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW | VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE ) + +/* Window attributes */ + +#define VBE_WINDOW_ATTRIBUTE_RELOCATABLE 0x01 +#define VBE_WINDOW_ATTRIBUTE_READABLE 0x02 +#define VBE_WINDOW_ATTRIBUTE_WRITEABLE 0x04 + +/* Memory model */ + +#define VBE_MEMORYMODEL_TEXT_MODE 0x00 +#define VBE_MEMORYMODEL_CGA_GRAPHICS 0x01 +#define VBE_MEMORYMODEL_HERCULES_GRAPHICS 0x02 +#define VBE_MEMORYMODEL_PLANAR 0x03 +#define VBE_MEMORYMODEL_PACKED_PIXEL 0x04 +#define VBE_MEMORYMODEL_NON_CHAIN_4_256 0x05 +#define VBE_MEMORYMODEL_DIRECT_COLOR 0x06 +#define VBE_MEMORYMODEL_YUV 0x07 + +/* DirectColorModeInfo */ + +#define VBE_DIRECTCOLOR_COLOR_RAMP_PROGRAMMABLE 0x01 +#define VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE 0x02 + +#endif diff --git a/qemu/roms/seabios/src/std/vga.h b/qemu/roms/seabios/src/std/vga.h new file mode 100644 index 000000000..de9ec7506 --- /dev/null +++ b/qemu/roms/seabios/src/std/vga.h @@ -0,0 +1,63 @@ +#ifndef __VGA_H +#define __VGA_H +// Standard structure definitions for vgabios video tables + +#include "types.h" // u8 + +// standard BIOS Video Parameter Table +struct video_param_s { + u8 twidth; + u8 theightm1; + u8 cheight; + u16 slength; + u8 sequ_regs[4]; + u8 miscreg; + u8 crtc_regs[25]; + u8 actl_regs[20]; + u8 grdc_regs[9]; +} PACKED; + +// Standard Video Save Pointer Table +struct video_save_pointer_s { + struct segoff_s videoparam; + struct segoff_s paramdynamicsave; + struct segoff_s textcharset; + struct segoff_s graphcharset; + struct segoff_s secsavepointer; + u8 reserved[8]; +} PACKED; + +// Data returned by int101B +struct video_func_static { + u32 modes; + u8 reserved_0x04[3]; + u8 scanlines; + u8 cblocks; + u8 active_cblocks; + u16 misc_flags; + u8 reserved_0x0c[2]; + u8 save_flags; + u8 reserved_0x0f; +} PACKED; + +struct video_func_info { + struct segoff_s static_functionality; + u8 bda_0x49[30]; + u8 bda_0x84[3]; + u8 dcc_index; + u8 dcc_alt; + u16 colors; + u8 pages; + u8 scan_lines; + u8 primary_char; + u8 secondar_char; + u8 misc; + u8 non_vga_mode; + u8 reserved_2f[2]; + u8 video_mem; + u8 save_flags; + u8 disp_info; + u8 reserved_34[12]; +} PACKED; + +#endif // vga.h diff --git a/qemu/roms/seabios/src/string.c b/qemu/roms/seabios/src/string.c new file mode 100644 index 000000000..2e4e43746 --- /dev/null +++ b/qemu/roms/seabios/src/string.c @@ -0,0 +1,251 @@ +// String manipulation functions. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "stacks.h" // yield +#include "string.h" // memcpy +#include "farptr.h" // SET_SEG + + +/**************************************************************** + * String ops + ****************************************************************/ + +// Sum the bytes in the specified area. +u8 +checksum_far(u16 buf_seg, void *buf_far, u32 len) +{ + SET_SEG(ES, buf_seg); + u32 i; + u8 sum = 0; + for (i=0; i<len; i++) + sum += GET_VAR(ES, ((u8*)buf_far)[i]); + return sum; +} + +u8 +checksum(void *buf, u32 len) +{ + return checksum_far(GET_SEG(SS), buf, len); +} + +size_t +strlen(const char *s) +{ + if (__builtin_constant_p(s)) + return __builtin_strlen(s); + const char *p = s; + while (*p) + p++; + return p-s; +} + +int +memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n) +{ + while (n--) { + int d = GET_FARVAR(s1seg, *(u8*)s1) - GET_FARVAR(s2seg, *(u8*)s2); + if (d) + return d < 0 ? -1 : 1; + s1++; + s2++; + } + return 0; +} + +// Compare two areas of memory. +int +memcmp(const void *s1, const void *s2, size_t n) +{ + while (n) { + if (*(u8*)s1 != *(u8*)s2) + return *(u8*)s1 < *(u8*)s2 ? -1 : 1; + s1++; + s2++; + n--; + } + return 0; +} + +// Compare two strings. +int +strcmp(const char *s1, const char *s2) +{ + for (;;) { + if (*s1 != *s2) + return *s1 < *s2 ? -1 : 1; + if (! *s1) + return 0; + s1++; + s2++; + } +} + +inline void +memset_far(u16 d_seg, void *d_far, u8 c, size_t len) +{ + SET_SEG(ES, d_seg); + asm volatile( + "rep stosb %%es:(%%di)" + : "+c"(len), "+D"(d_far) + : "a"(c), "m" (__segment_ES) + : "cc", "memory"); +} + +inline void +memset16_far(u16 d_seg, void *d_far, u16 c, size_t len) +{ + len /= 2; + SET_SEG(ES, d_seg); + asm volatile( + "rep stosw %%es:(%%di)" + : "+c"(len), "+D"(d_far) + : "a"(c), "m" (__segment_ES) + : "cc", "memory"); +} + +void * +memset(void *s, int c, size_t n) +{ + while (n) + ((char *)s)[--n] = c; + return s; +} + +void memset_fl(void *ptr, u8 val, size_t size) +{ + if (MODESEGMENT) + memset_far(FLATPTR_TO_SEG(ptr), (void*)(FLATPTR_TO_OFFSET(ptr)), + val, size); + else + memset(ptr, val, size); +} + +inline void +memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len) +{ + SET_SEG(ES, d_seg); + u16 bkup_ds; + asm volatile( + "movw %%ds, %w0\n" + "movw %w4, %%ds\n" + "rep movsb (%%si),%%es:(%%di)\n" + "movw %w0, %%ds" + : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far) + : "r"(s_seg), "m" (__segment_ES) + : "cc", "memory"); +} + +inline void +memcpy_fl(void *d_fl, const void *s_fl, size_t len) +{ + if (MODESEGMENT) + memcpy_far(FLATPTR_TO_SEG(d_fl), (void*)FLATPTR_TO_OFFSET(d_fl) + , FLATPTR_TO_SEG(s_fl), (void*)FLATPTR_TO_OFFSET(s_fl) + , len); + else + memcpy(d_fl, s_fl, len); +} + +void * +#undef memcpy +memcpy(void *d1, const void *s1, size_t len) +#if MODESEGMENT == 0 +#define memcpy __builtin_memcpy +#endif +{ + SET_SEG(ES, GET_SEG(SS)); + void *d = d1; + if (((u32)d1 | (u32)s1 | len) & 3) { + // non-aligned memcpy + asm volatile( + "rep movsb (%%esi),%%es:(%%edi)" + : "+c"(len), "+S"(s1), "+D"(d) + : "m" (__segment_ES) : "cc", "memory"); + return d1; + } + // Common case - use 4-byte copy + len /= 4; + asm volatile( + "rep movsl (%%esi),%%es:(%%edi)" + : "+c"(len), "+S"(s1), "+D"(d) + : "m" (__segment_ES) : "cc", "memory"); + return d1; +} + +// Copy to/from memory mapped IO. IO mem is very slow, so yield +// periodically. +void +iomemcpy(void *d, const void *s, u32 len) +{ + ASSERT32FLAT(); + yield(); + while (len > 3) { + u32 copylen = len; + if (copylen > 2048) + copylen = 2048; + copylen /= 4; + len -= copylen * 4; + asm volatile( + "rep movsl (%%esi),%%es:(%%edi)" + : "+c"(copylen), "+S"(s), "+D"(d) + : : "cc", "memory"); + yield(); + } + if (len) + // Copy any remaining bytes. + memcpy(d, s, len); +} + +void * +memmove(void *d, const void *s, size_t len) +{ + if (s >= d) + return memcpy(d, s, len); + + d += len-1; + s += len-1; + while (len--) { + *(char*)d = *(char*)s; + d--; + s--; + } + + return d; +} + +// Copy a string - truncating it if necessary. +char * +strtcpy(char *dest, const char *src, size_t len) +{ + char *d = dest; + while (--len && *src != '\0') + *d++ = *src++; + *d = '\0'; + return dest; +} + +// locate first occurance of character c in the string s +char * +strchr(const char *s, int c) +{ + for (; *s; s++) + if (*s == c) + return (char*)s; + return NULL; +} + +// Remove any trailing blank characters (spaces, new lines, carriage returns) +char * +nullTrailingSpace(char *buf) +{ + int len = strlen(buf); + char *end = &buf[len-1]; + while (end >= buf && *end <= ' ') + *(end--) = '\0'; + while (*buf && *buf <= ' ') + buf++; + return buf; +} diff --git a/qemu/roms/seabios/src/string.h b/qemu/roms/seabios/src/string.h new file mode 100644 index 000000000..a557d6a44 --- /dev/null +++ b/qemu/roms/seabios/src/string.h @@ -0,0 +1,31 @@ +// String manipulation function defs. +#ifndef __STRING_H +#define __STRING_H + +#include "types.h" // u32 + +// string.c +u8 checksum_far(u16 buf_seg, void *buf_far, u32 len); +u8 checksum(void *buf, u32 len); +size_t strlen(const char *s); +int memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +int strcmp(const char *s1, const char *s2); +inline void memset_far(u16 d_seg, void *d_far, u8 c, size_t len); +inline void memset16_far(u16 d_seg, void *d_far, u16 c, size_t len); +void *memset(void *s, int c, size_t n); +void memset_fl(void *ptr, u8 val, size_t size); +inline void memcpy_far(u16 d_seg, void *d_far + , u16 s_seg, const void *s_far, size_t len); +void memcpy_fl(void *d_fl, const void *s_fl, size_t len); +void *memcpy(void *d1, const void *s1, size_t len); +#if MODESEGMENT == 0 +#define memcpy __builtin_memcpy +#endif +void iomemcpy(void *d, const void *s, u32 len); +void *memmove(void *d, const void *s, size_t len); +char *strtcpy(char *dest, const char *src, size_t len); +char *strchr(const char *s, int c); +char *nullTrailingSpace(char *buf); + +#endif // string.h diff --git a/qemu/roms/seabios/src/system.c b/qemu/roms/seabios/src/system.c new file mode 100644 index 000000000..60a6fce58 --- /dev/null +++ b/qemu/roms/seabios/src/system.c @@ -0,0 +1,357 @@ +// Handler for int 0x15 "system" calls +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "hw/pic.h" // pic_reset +#include "malloc.h" // LegacyRamSize +#include "memmap.h" // E820_RAM +#include "output.h" // debug_enter +#include "string.h" // memcpy_far +#include "util.h" // handle_1553 +#include "x86.h" // set_a20 + +static void +handle_152400(struct bregs *regs) +{ + set_a20(0); + set_code_success(regs); +} + +static void +handle_152401(struct bregs *regs) +{ + set_a20(1); + set_code_success(regs); +} + +static void +handle_152402(struct bregs *regs) +{ + regs->al = get_a20(); + set_code_success(regs); +} + +static void +handle_152403(struct bregs *regs) +{ + regs->bx = 3; + set_code_success(regs); +} + +static void +handle_1524XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +static void +handle_1524(struct bregs *regs) +{ + switch (regs->al) { + case 0x00: handle_152400(regs); break; + case 0x01: handle_152401(regs); break; + case 0x02: handle_152402(regs); break; + case 0x03: handle_152403(regs); break; + default: handle_1524XX(regs); break; + } +} + +// removable media eject +static void +handle_1552(struct bregs *regs) +{ + set_code_success(regs); +} + +static void +handle_1587(struct bregs *regs) +{ + // +++ should probably have descriptor checks + // +++ should have exception handlers + + u8 prev_a20_enable = set_a20(1); // enable A20 line + + // 128K max of transfer on 386+ ??? + // source == destination ??? + + // ES:SI points to descriptor table + // offset use initially comments + // ============================================== + // 00..07 Unused zeros Null descriptor + // 08..0f GDT zeros filled in by BIOS + // 10..17 source ssssssss source of data + // 18..1f dest dddddddd destination of data + // 20..27 CS zeros filled in by BIOS + // 28..2f SS zeros filled in by BIOS + +// check for access rights of source & dest here + + // Initialize GDT descriptor + u64 *gdt_far = (void*)(regs->si + 0); + u16 gdt_seg = regs->es; + u32 loc = (u32)MAKE_FLATPTR(gdt_seg, gdt_far); + SET_FARVAR(gdt_seg, gdt_far[1], GDT_DATA | GDT_LIMIT((6*sizeof(u64))-1) + | GDT_BASE(loc)); + // Initialize CS descriptor + u64 lim = GDT_LIMIT(0x0ffff); + if (in_post()) + lim = GDT_GRANLIMIT(0xffffffff); + SET_FARVAR(gdt_seg, gdt_far[4], GDT_CODE | lim | GDT_BASE(BUILD_BIOS_ADDR)); + // Initialize SS descriptor + loc = (u32)MAKE_FLATPTR(GET_SEG(SS), 0); + SET_FARVAR(gdt_seg, gdt_far[5], GDT_DATA | lim | GDT_BASE(loc)); + + SET_SEG(ES, gdt_seg); + u16 count = regs->cx, si = 0, di = 0; + asm volatile( + // Load new descriptor tables + " lgdtw %%es:(1<<3)(%%eax)\n" + " lidtw %%cs:pmode_IDT_info\n" + + // Enable protected mode + " movl %%cr0, %%eax\n" + " orl $" __stringify(CR0_PE) ", %%eax\n" + " movl %%eax, %%cr0\n" + + // far jump to flush CPU queue after transition to protected mode + " ljmpw $(4<<3), $1f\n" + + // GDT points to valid descriptor table, now load DS, ES + "1:movw $(2<<3), %%ax\n" // 2nd descriptor in table, TI=GDT, RPL=00 + " movw %%ax, %%ds\n" + " movw $(3<<3), %%ax\n" // 3rd descriptor in table, TI=GDT, RPL=00 + " movw %%ax, %%es\n" + + // memcpy CX words using 32bit memcpy if applicable + " testw $1, %%cx\n" + " jnz 3f\n" + " shrw $1, %%cx\n" + " rep movsl %%ds:(%%si), %%es:(%%di)\n" + + // Restore DS and ES segment limits to 0xffff + "2:movw $(5<<3), %%ax\n" // 5th descriptor in table (SS) + " movw %%ax, %%ds\n" + " movw %%ax, %%es\n" + + // Disable protected mode + " movl %%cr0, %%eax\n" + " andl $~" __stringify(CR0_PE) ", %%eax\n" + " movl %%eax, %%cr0\n" + + // far jump to flush CPU queue after transition to real mode + " ljmpw $" __stringify(SEG_BIOS) ", $4f\n" + + // Slower 16bit copy method + "3:rep movsw %%ds:(%%si), %%es:(%%di)\n" + " jmp 2b\n" + + // restore IDT to normal real-mode defaults + "4:lidtw %%cs:rmode_IDT_info\n" + + // Restore %ds (from %ss) + " movw %%ss, %%ax\n" + " movw %%ax, %%ds\n" + : "+a" (gdt_far), "+c"(count), "+m" (__segment_ES) + : "S" (si), "D" (di) + : "cc"); + + set_a20(prev_a20_enable); + + set_code_success(regs); +} + +// Get the amount of extended memory (above 1M) +static void +handle_1588(struct bregs *regs) +{ + u32 rs = GET_GLOBAL(LegacyRamSize); + + // According to Ralf Brown's interrupt the limit should be 15M, + // but real machines mostly return max. 63M. + if (rs > 64*1024*1024) + regs->ax = 63 * 1024; + else + regs->ax = (rs - 1*1024*1024) / 1024; + set_success(regs); +} + +// Switch to protected mode +void VISIBLE16 +handle_1589(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_15); + set_a20(1); + + pic_reset(regs->bl, regs->bh); + + u64 *gdt_far = (void*)(regs->si + 0); + u16 gdt_seg = regs->es; + SET_FARVAR(gdt_seg, gdt_far[7], GDT_CODE | GDT_LIMIT(BUILD_BIOS_SIZE-1) + | GDT_BASE(BUILD_BIOS_ADDR)); + + regs->ds = 3<<3; // 3rd gdt descriptor is %ds + regs->es = 4<<3; // 4th gdt descriptor is %es + regs->code.seg = 6<<3; // 6th gdt descriptor is %cs + + set_code_success(regs); + + asm volatile( + // Load new descriptor tables + " lgdtw %%es:(1<<3)(%%si)\n" + " lidtw %%es:(2<<3)(%%si)\n" + + // Enable protected mode + " movl %%cr0, %%eax\n" + " orl $" __stringify(CR0_PE) ", %%eax\n" + " movl %%eax, %%cr0\n" + + // far jump to flush CPU queue after transition to protected mode + " ljmpw $(7<<3), $1f\n" + + // GDT points to valid descriptor table, now load SS + "1:movw $(5<<3), %%ax\n" + " movw %%ax, %%ds\n" + " movw %%ax, %%ss\n" + : + : "S"(gdt_far), "m" (__segment_ES) + : "eax", "cc"); +} + +// Device busy interrupt. Called by Int 16h when no key available +static void +handle_1590(struct bregs *regs) +{ +} + +// Interrupt complete. Called by Int 16h when key becomes available +static void +handle_1591(struct bregs *regs) +{ +} + +// keyboard intercept +static void +handle_154f(struct bregs *regs) +{ + set_invalid_silent(regs); +} + +static void +handle_15c0(struct bregs *regs) +{ + regs->es = SEG_BIOS; + regs->bx = (u32)&BIOS_CONFIG_TABLE; + set_code_success(regs); +} + +static void +handle_15c1(struct bregs *regs) +{ + regs->es = get_ebda_seg(); + set_success(regs); +} + +static void +handle_15e801(struct bregs *regs) +{ + // my real system sets ax and bx to 0 + // this is confirmed by Ralph Brown list + // but syslinux v1.48 is known to behave + // strangely if ax is set to 0 + // regs.u.r16.ax = 0; + // regs.u.r16.bx = 0; + + u32 rs = GET_GLOBAL(LegacyRamSize); + + // Get the amount of extended memory (above 1M) + if (rs > 16*1024*1024) { + // limit to 15M + regs->cx = 15*1024; + // Get the amount of extended memory above 16M in 64k blocks + regs->dx = (rs - 16*1024*1024) / (64*1024); + } else { + regs->cx = (rs - 1*1024*1024) / 1024; + regs->dx = 0; + } + + // Set configured memory equal to extended memory + regs->ax = regs->cx; + regs->bx = regs->dx; + + set_success(regs); +} + +static void +handle_15e820(struct bregs *regs) +{ + int count = GET_GLOBAL(e820_count); + if (regs->edx != 0x534D4150 || regs->bx >= count + || regs->ecx < sizeof(e820_list[0])) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + + memcpy_far(regs->es, (void*)(regs->di+0) + , get_global_seg(), &e820_list[regs->bx] + , sizeof(e820_list[0])); + if (regs->bx == count-1) + regs->ebx = 0; + else + regs->ebx++; + regs->eax = 0x534D4150; + regs->ecx = sizeof(e820_list[0]); + set_success(regs); +} + +static void +handle_15e8XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +static void +handle_15e8(struct bregs *regs) +{ + switch (regs->al) { + case 0x01: handle_15e801(regs); break; + case 0x20: handle_15e820(regs); break; + default: handle_15e8XX(regs); break; + } +} + +static void +handle_15XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +// INT 15h System Services Entry Point +void VISIBLE16 +handle_15(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_15); + switch (regs->ah) { + case 0x24: handle_1524(regs); break; + case 0x4f: handle_154f(regs); break; + case 0x52: handle_1552(regs); break; + case 0x53: handle_1553(regs); break; + case 0x5f: handle_155f(regs); break; + case 0x7f: handle_157f(regs); break; + case 0x83: handle_1583(regs); break; + case 0x86: handle_1586(regs); break; + case 0x87: handle_1587(regs); break; + case 0x88: handle_1588(regs); break; + case 0x90: handle_1590(regs); break; + case 0x91: handle_1591(regs); break; + case 0xc0: handle_15c0(regs); break; + case 0xc1: handle_15c1(regs); break; + case 0xc2: handle_15c2(regs); break; + case 0xe8: handle_15e8(regs); break; + default: handle_15XX(regs); break; + } +} diff --git a/qemu/roms/seabios/src/types.h b/qemu/roms/seabios/src/types.h new file mode 100644 index 000000000..097372cdb --- /dev/null +++ b/qemu/roms/seabios/src/types.h @@ -0,0 +1,156 @@ +// Basic type definitions for X86 cpus. +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __TYPES_H +#define __TYPES_H + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +typedef unsigned long long u64; +typedef signed long long s64; +typedef u32 size_t; + +union u64_u32_u { + struct { u32 lo, hi; }; + u64 val; +}; + +// Definition for common 16bit segment/offset pointers. +struct segoff_s { + union { + struct { + u16 offset; + u16 seg; + }; + u32 segoff; + }; +}; + +#ifdef MANUAL_NO_JUMP_TABLE +# define default case 775324556: asm(""); default +#endif + +#ifdef WHOLE_PROGRAM +# define __VISIBLE __attribute__((externally_visible)) +#else +# define __VISIBLE +#endif + +#define UNIQSEC __FILE__ "." __stringify(__LINE__) + +#define __noreturn __attribute__((noreturn)) +extern void __force_link_error__only_in_32bit_flat(void) __noreturn; +extern void __force_link_error__only_in_32bit_segmented(void) __noreturn; +extern void __force_link_error__only_in_16bit(void) __noreturn; + +#define __ASM(code) asm(".section .text.asm." UNIQSEC "\n\t" code) + +#if MODE16 == 1 +// Notes a function as externally visible in the 16bit code chunk. +# define VISIBLE16 __VISIBLE +// Notes a function as externally visible in the 32bit flat code chunk. +# define VISIBLE32FLAT +// Notes a 32bit flat function that will only be called during init. +# define VISIBLE32INIT +// Notes a function as externally visible in the 32bit segmented code chunk. +# define VISIBLE32SEG +// Designate a variable as (only) visible to 16bit code. +# define VAR16 __section(".data16." UNIQSEC) +// Designate a variable as (only) visible to 32bit segmented code. +# define VAR32SEG __section(".discard.var32seg." UNIQSEC) +// Designate a variable as visible and located in the e-segment. +# define VARLOW __section(".discard.varlow." UNIQSEC) __VISIBLE __weak +// Designate a variable as visible and located in the f-segment. +# define VARFSEG __section(".discard.varfseg." UNIQSEC) __VISIBLE __weak +// Designate a variable at a specific address in the f-segment. +# define VARFSEGFIXED(addr) __section(".discard.varfixed." UNIQSEC) __VISIBLE __weak +// Verify a variable is only accessable via 32bit "init" functions +# define VARVERIFY32INIT __section(".discard.varinit." UNIQSEC) +// Designate top-level assembler as 16bit only. +# define ASM16(code) __ASM(code) +// Designate top-level assembler as 32bit flat only. +# define ASM32FLAT(code) +// Compile time check for a given mode. +# define ASSERT16() do { } while (0) +# define ASSERT32SEG() __force_link_error__only_in_32bit_segmented() +# define ASSERT32FLAT() __force_link_error__only_in_32bit_flat() +#elif MODESEGMENT == 1 +# define VISIBLE16 +# define VISIBLE32FLAT +# define VISIBLE32INIT +# define VISIBLE32SEG __VISIBLE +# define VAR16 __section(".discard.var16." UNIQSEC) +# define VAR32SEG __section(".data32seg." UNIQSEC) +# define VARLOW __section(".discard.varlow." UNIQSEC) __VISIBLE __weak +# define VARFSEG __section(".discard.varfseg." UNIQSEC) __VISIBLE __weak +# define VARFSEGFIXED(addr) __section(".discard.varfixed." UNIQSEC) __VISIBLE __weak +# define VARVERIFY32INIT __section(".discard.varinit." UNIQSEC) +# define ASM16(code) +# define ASM32FLAT(code) +# define ASSERT16() __force_link_error__only_in_16bit() +# define ASSERT32SEG() do { } while (0) +# define ASSERT32FLAT() __force_link_error__only_in_32bit_flat() +#else +# define VISIBLE16 +# define VISIBLE32FLAT __section(".text.runtime." UNIQSEC) __VISIBLE +# define VISIBLE32INIT __section(".text.init." UNIQSEC) __VISIBLE +# define VISIBLE32SEG +# define VAR16 __section(".discard.var16." UNIQSEC) +# define VAR32SEG __section(".discard.var32seg." UNIQSEC) +# define VARLOW __section(".data.varlow." UNIQSEC) __VISIBLE __weak +# define VARFSEG __section(".data.varfseg." UNIQSEC) __VISIBLE +# define VARFSEGFIXED(addr) __section(".fixedaddr." __stringify(addr)) __VISIBLE __aligned(1) +# define VARVERIFY32INIT __section(".data.varinit." UNIQSEC) +# define ASM16(code) +# define ASM32FLAT(code) __ASM(code) +# define ASSERT16() __force_link_error__only_in_16bit() +# define ASSERT32SEG() __force_link_error__only_in_32bit_segmented() +# define ASSERT32FLAT() do { } while (0) +#endif + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#define DIV_ROUND_CLOSEST(x, divisor)({ \ + typeof(divisor) __divisor = divisor; \ + (((x) + ((__divisor) / 2)) / (__divisor)); \ + }) +#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) +#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) +#define ALIGN_DOWN(x,a) ((x) & ~((typeof(x))(a)-1)) +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#define container_of_or_null(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *___mptr = (ptr); \ + ___mptr ? container_of(___mptr, type, member) : NULL; }) + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define NULL ((void*)0) + +#define __weak __attribute__((weak)) +#define __section(S) __attribute__((section(S))) + +#define PACKED __attribute__((packed)) +#define __aligned(x) __attribute__((aligned(x))) + +#define barrier() __asm__ __volatile__("": : :"memory") + +#define noinline __attribute__((noinline)) +#define __always_inline inline __attribute__((always_inline)) +#define __malloc __attribute__((__malloc__)) +#define __attribute_const __attribute__((__const__)) + +#define __stringify_1(x) #x +#define __stringify(x) __stringify_1(x) + +#endif // types.h diff --git a/qemu/roms/seabios/src/util.h b/qemu/roms/seabios/src/util.h new file mode 100644 index 000000000..09bb8a9f3 --- /dev/null +++ b/qemu/roms/seabios/src/util.h @@ -0,0 +1,237 @@ +// Misc function and variable declarations. +#ifndef __UTIL_H +#define __UTIL_H + +#include "types.h" // u32 + +// apm.c +void apm_shutdown(void); +struct bregs; +void handle_1553(struct bregs *regs); + +// bmp.c +struct bmp_decdata *bmp_alloc(void); +int bmp_decode(struct bmp_decdata *bmp, unsigned char *data, int data_size); +void bmp_get_size(struct bmp_decdata *bmp, int *width, int *height); +int bmp_show(struct bmp_decdata *bmp, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest); + +// boot.c +void boot_init(void); +void boot_add_bev(u16 seg, u16 bev, u16 desc, int prio); +void boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio); +struct drive_s; +void boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio); +void boot_add_hd(struct drive_s *drive_g, const char *desc, int prio); +void boot_add_cd(struct drive_s *drive_g, const char *desc, int prio); +void boot_add_cbfs(void *data, const char *desc, int prio); +void interactive_bootmenu(void); +void bcv_prepboot(void); +struct pci_device; +int bootprio_find_pci_device(struct pci_device *pci); +int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun); +int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave); +int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid); +int bootprio_find_pci_rom(struct pci_device *pci, int instance); +int bootprio_find_named_rom(const char *name, int instance); +struct usbdevice_s; +int bootprio_find_usb(struct usbdevice_s *usbdev, int lun); + +// bootsplash.c +void enable_vga_console(void); +void enable_bootsplash(void); +void disable_bootsplash(void); + +// cdrom.c +extern u8 CDRom_locks[]; +extern struct eltorito_s CDEmu; +extern struct drive_s *cdemu_drive_gf; +struct disk_op_s; +int process_cdemu_op(struct disk_op_s *op); +void cdrom_prepboot(void); +int cdrom_boot(struct drive_s *drive_g); + +// clock.c +void clock_setup(void); +void handle_1583(struct bregs *regs); +u32 irqtimer_calc_ticks(u32 count); +u32 irqtimer_calc(u32 msecs); +int irqtimer_check(u32 end); +void handle_1586(struct bregs *regs); + +// fw/acpi.c +void acpi_setup(void); + +// fw/biostable.c +void copy_pir(void *pos); +void copy_mptable(void *pos); +extern struct pir_header *PirAddr; +void copy_acpi_rsdp(void *pos); +extern struct rsdp_descriptor *RsdpAddr; +extern u32 acpi_pm1a_cnt; +extern u16 acpi_pm_base; +void *find_acpi_rsdp(void); +u32 find_resume_vector(void); +void acpi_reboot(void); +void find_acpi_features(void); +extern struct smbios_entry_point *SMBiosAddr; +void copy_smbios(void *pos); +void display_uuid(void); +void copy_table(void *pos); +void smbios_setup(void); + +// fw/coreboot.c +extern const char *CBvendor, *CBpart; +struct cbfs_file; +void coreboot_debug_putc(char c); +void cbfs_run_payload(struct cbfs_file *file); +void coreboot_platform_setup(void); +void cbfs_payload_setup(void); +void coreboot_preinit(void); +void coreboot_cbfs_init(void); +struct cb_header; +void *find_cb_subtable(struct cb_header *cbh, u32 tag); +struct cb_header *find_cb_table(void); + +// fw/csm.c +int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid); +int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave); +int csm_bootprio_pci(struct pci_device *pci); + +// fw/mptable.c +void mptable_setup(void); + +// fw/mtrr.c +void mtrr_setup(void); + +// fw/pciinit.c +extern const u8 pci_irqs[4]; +void pci_setup(void); +void pci_resume(void); + +// fw/pirtable.c +void pirtable_setup(void); + +// fw/shadow.c +void make_bios_writable(void); +void make_bios_readonly(void); +void qemu_prep_reset(void); + +// fw/smbios.c +void smbios_legacy_setup(void); + +// fw/smm.c +void smm_device_setup(void); +void smm_setup(void); + +// fw/smp.c +extern u32 MaxCountCPUs; +void wrmsr_smp(u32 index, u64 val); +void smp_setup(void); +int apic_id_is_present(u8 apic_id); + +// hw/dma.c +int dma_floppy(u32 addr, int count, int isWrite); +void dma_setup(void); + +// hw/floppy.c +extern struct floppy_ext_dbt_s diskette_param_table2; +void floppy_setup(void); +struct drive_s *init_floppy(int floppyid, int ftype); +int find_floppy_type(u32 size); +int process_floppy_op(struct disk_op_s *op); +void floppy_tick(void); + +// hw/ramdisk.c +void ramdisk_setup(void); +int process_ramdisk_op(struct disk_op_s *op); + +// hw/sdcard.c +int process_sdcard_op(struct disk_op_s *op); +void sdcard_setup(void); + +// hw/timer.c +void timer_setup(void); +void pmtimer_setup(u16 ioport); +u32 timer_calc(u32 msecs); +u32 timer_calc_usec(u32 usecs); +int timer_check(u32 end); +void ndelay(u32 count); +void udelay(u32 count); +void mdelay(u32 count); +void nsleep(u32 count); +void usleep(u32 count); +void msleep(u32 count); +u32 ticks_to_ms(u32 ticks); +u32 ticks_from_ms(u32 ms); +void pit_setup(void); + +// jpeg.c +struct jpeg_decdata *jpeg_alloc(void); +int jpeg_decode(struct jpeg_decdata *jpeg, unsigned char *buf); +void jpeg_get_size(struct jpeg_decdata *jpeg, int *width, int *height); +int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest); + +// kbd.c +void kbd_init(void); +void handle_15c2(struct bregs *regs); +void process_key(u8 key); + +// misc.c +extern struct bios_config_table_s BIOS_CONFIG_TABLE __aligned(1); +extern struct floppy_dbt_s diskette_param_table __aligned(1); +extern u8 BiosChecksum; +int in_post(void); +void mathcp_setup(void); + +// mouse.c +void mouse_init(void); +void process_mouse(u8 data); + +// optionroms.c +struct rom_header; +void callrom(struct rom_header *rom, u16 bdf); +void call_bcv(u16 seg, u16 ip); +int is_pci_vga(struct pci_device *pci); +void optionrom_setup(void); +void vgarom_setup(void); +void s3_resume_vga(void); +extern int ScreenAndDebug; + +// pcibios.c +void handle_1ab1(struct bregs *regs); +void bios32_init(void); + +// pmm.c +void pmm_init(void); +void pmm_prepboot(void); + +// pnpbios.c +u16 get_pnp_offset(void); +void pnp_init(void); + +// post.c +void interface_init(void); +void device_hardware_setup(void); +void prepareboot(void); +void startBoot(void); +void reloc_preinit(void *f, void *arg); + +// resume.c +extern int HaveRunPost; + +// serial.c +void serial_setup(void); +void lpt_setup(void); + +// vgahooks.c +void handle_155f(struct bregs *regs); +void handle_157f(struct bregs *regs); +void vgahook_setup(struct pci_device *pci); + + +// version (auto generated file out/version.c) +extern const char VERSION[]; + +#endif // util.h diff --git a/qemu/roms/seabios/src/vgahooks.c b/qemu/roms/seabios/src/vgahooks.c new file mode 100644 index 000000000..6a4acfeaf --- /dev/null +++ b/qemu/roms/seabios/src/vgahooks.c @@ -0,0 +1,354 @@ +// Hooks for via vgabios calls into main bios. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // set_code_invalid +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pci_find_device +#include "hw/pci_ids.h" // PCI_VENDOR_ID_VIA +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "output.h" // dprintf +#include "string.h" // strcmp +#include "util.h" // handle_155f, handle_157f + +#define VH_VIA 1 +#define VH_INTEL 2 +#define VH_SMI 3 + +int VGAHookHandlerType VARFSEG; + +static void +handle_155fXX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +static void +handle_157fXX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +/**************************************************************** + * Via hooks + ****************************************************************/ + +int ViaFBsize VARFSEG, ViaRamSpeed VARFSEG; + +static void +via_155f01(struct bregs *regs) +{ + regs->eax = 0x5f; + regs->cl = 2; // panel type = 2 = 1024 * 768 + set_success(regs); + dprintf(1, "Warning: VGA panel type is hardcoded\n"); +} + +static void +via_155f02(struct bregs *regs) +{ + regs->eax = 0x5f; + regs->bx = 2; + regs->cx = 0x401; // PAL + crt only + regs->dx = 0; // TV Layout - default + set_success(regs); + dprintf(1, "Warning: VGA TV/CRT output type is hardcoded\n"); +} + +static void +via_155f18(struct bregs *regs) +{ + int fbsize = GET_GLOBAL(ViaFBsize), ramspeed = GET_GLOBAL(ViaRamSpeed); + if (fbsize < 0 || ramspeed < 0) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + regs->eax = 0x5f; + regs->ebx = 0x500 | (ramspeed << 4) | fbsize; + regs->ecx = 0x060; + set_success(regs); +} + +static void +via_155f19(struct bregs *regs) +{ + set_invalid_silent(regs); +} + +static void +via_155f(struct bregs *regs) +{ + switch (regs->al) { + case 0x01: via_155f01(regs); break; + case 0x02: via_155f02(regs); break; + case 0x18: via_155f18(regs); break; + case 0x19: via_155f19(regs); break; + default: handle_155fXX(regs); break; + } +} + +static int +getFBSize(struct pci_device *pci) +{ + /* FB config */ + u8 reg = pci_config_readb(pci->bdf, 0xa1); + + /* GFX disabled ? */ + if (!(reg & 0x80)) + return -1; + + static u8 mem_power[] = {0, 3, 4, 5, 6, 7, 8, 9}; + return mem_power[(reg >> 4) & 0x7]; +} + +static int +getViaRamSpeed(struct pci_device *pci) +{ + return (pci_config_readb(pci->bdf, 0x90) & 0x07) + 3; +} + +static int +getAMDRamSpeed(void) +{ + struct pci_device *pci = pci_find_device(PCI_VENDOR_ID_AMD + , PCI_DEVICE_ID_AMD_K8_NB_MEMCTL); + if (!pci) + return -1; + + /* mem clk 0 = DDR2 400 */ + return (pci_config_readb(pci->bdf, 0x94) & 0x7) + 6; +} + +/* int 0x15 - 5f18 + + ECX = unknown/dont care + EBX[3..0] Frame Buffer Size 2^N MiB + EBX[7..4] Memory speed: + 0: SDR 66Mhz + 1: SDR 100Mhz + 2: SDR 133Mhz + 3: DDR 100Mhz (PC1600 or DDR200) + 4: DDR 133Mhz (PC2100 or DDR266) + 5: DDR 166Mhz (PC2700 or DDR333) + 6: DDR 200Mhz (PC3200 or DDR400) + 7: DDR2 133Mhz (DDR2 533) + 8: DDR2 166Mhz (DDR2 667) + 9: DDR2 200Mhz (DDR2 800) + A: DDR2 233Mhz (DDR2 1066) + B: and above: Unknown + EBX[?..8] Total memory size? + EAX = 0x5f for success +*/ + +#define PCI_DEVICE_ID_VIA_K8M890CE_3 0x3336 +#define PCI_DEVICE_ID_VIA_VX855_MEMCTRL 0x3409 + +static void +via_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_VIA; + + struct pci_device *d = pci_find_device(PCI_VENDOR_ID_VIA + , PCI_DEVICE_ID_VIA_K8M890CE_3); + if (d) { + ViaFBsize = getFBSize(d); + ViaRamSpeed = getAMDRamSpeed(); + return; + } + d = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855_MEMCTRL); + if (d) { + ViaFBsize = getFBSize(d); + ViaRamSpeed = getViaRamSpeed(d); + return; + } + + dprintf(1, "Warning: VGA memory size and speed is hardcoded\n"); + ViaFBsize = 5; // 32M frame buffer + ViaRamSpeed = 4; // MCLK = DDR266 +} + + +/**************************************************************** + * Intel VGA hooks + ****************************************************************/ + +u8 IntelDisplayType VARFSEG, IntelDisplayId VARFSEG; + +static void +intel_155f35(struct bregs *regs) +{ + regs->ax = 0x005f; + regs->cl = GET_GLOBAL(IntelDisplayType); + set_success(regs); +} + +static void +intel_155f40(struct bregs *regs) +{ + regs->ax = 0x005f; + regs->cl = GET_GLOBAL(IntelDisplayId); + set_success(regs); +} + +static void +intel_155f50(struct bregs *regs) +{ + /* Mandatory hook on some Dell laptops */ + regs->ax = 0x005f; + set_success(regs); +} + +static void +intel_155f(struct bregs *regs) +{ + switch (regs->al) { + case 0x35: intel_155f35(regs); break; + case 0x40: intel_155f40(regs); break; + case 0x50: intel_155f50(regs); break; + default: handle_155fXX(regs); break; + } +} + +#define BOOT_DISPLAY_DEFAULT (0) +#define BOOT_DISPLAY_CRT (1 << 0) +#define BOOT_DISPLAY_TV (1 << 1) +#define BOOT_DISPLAY_EFP (1 << 2) +#define BOOT_DISPLAY_LCD (1 << 3) +#define BOOT_DISPLAY_CRT2 (1 << 4) +#define BOOT_DISPLAY_TV2 (1 << 5) +#define BOOT_DISPLAY_EFP2 (1 << 6) +#define BOOT_DISPLAY_LCD2 (1 << 7) + +static void +intel_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_INTEL; + + IntelDisplayType = BOOT_DISPLAY_DEFAULT; + IntelDisplayId = 3; +} + +static void +roda_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_INTEL; + // IntelDisplayType = BOOT_DISPLAY_DEFAULT; + IntelDisplayType = BOOT_DISPLAY_LCD; + // IntelDisplayId = inb(0x60f) & 0x0f; // Correct according to Crete + IntelDisplayId = 3; // Correct according to empirical studies +} + +static void +kontron_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_INTEL; + IntelDisplayType = BOOT_DISPLAY_CRT; + IntelDisplayId = 3; +} + +static void +getac_setup(struct pci_device *pci) +{ +} + +/**************************************************************** + * Silicon Motion hooks + ****************************************************************/ + +u8 SmiBootDisplay VARFSEG; // 1: LCD, 2: CRT, 3: Both */ + +static void +smi_157f02(struct bregs *regs) +{ + /* Boot Display Device Override */ + regs->ax = 0x007f; + regs->bl = GET_GLOBAL(SmiBootDisplay); + set_success(regs); +} + +static void +smi_157f14(struct bregs *regs) +{ + /* ReduceOn support default status */ + regs->ax = 0x007f; + regs->bl = 0x00; + set_success(regs); +} + +static void +smi_157f(struct bregs *regs) +{ + switch (regs->al) { + case 0x02: smi_157f02(regs); break; + case 0x14: smi_157f14(regs); break; + default: handle_157fXX(regs); break; + } +} + +static void +winent_mb6047_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_SMI; + SmiBootDisplay = 0x02; +} + +/**************************************************************** + * Entry and setup + ****************************************************************/ + +// Main 16bit entry point +void +handle_155f(struct bregs *regs) +{ + if (!CONFIG_VGAHOOKS) { + handle_155fXX(regs); + return; + } + + int htype = GET_GLOBAL(VGAHookHandlerType); + switch (htype) { + case VH_VIA: via_155f(regs); break; + case VH_INTEL: intel_155f(regs); break; + default: handle_155fXX(regs); break; + } +} + +// Main 16bit entry point +void +handle_157f(struct bregs *regs) +{ + if (!CONFIG_VGAHOOKS) { + handle_157fXX(regs); + return; + } + + int htype = GET_GLOBAL(VGAHookHandlerType); + switch (htype) { + case VH_SMI: smi_157f(regs); break; + default: handle_157fXX(regs); break; + } +} + +// Setup +void +vgahook_setup(struct pci_device *pci) +{ + if (!CONFIG_VGAHOOKS) + return; + + if (strcmp(CBvendor, "KONTRON") == 0 && strcmp(CBpart, "986LCD-M") == 0) + kontron_setup(pci); + else if (strcmp(CBvendor, "GETAC") == 0 && strcmp(CBpart, "P470") == 0) + getac_setup(pci); + else if (strcmp(CBvendor, "RODA") == 0 && strcmp(CBpart, "RK886EX") == 0) + roda_setup(pci); + else if (strcmp(CBvendor, "Win Enterprise") == 0 && strcmp(CBpart, "MB6047") == 0) + winent_mb6047_setup(pci); + else if (pci->vendor == PCI_VENDOR_ID_VIA) + via_setup(pci); + else if (pci->vendor == PCI_VENDOR_ID_INTEL) + intel_setup(pci); +} diff --git a/qemu/roms/seabios/src/x86.c b/qemu/roms/seabios/src/x86.c new file mode 100644 index 000000000..0fdf86f9c --- /dev/null +++ b/qemu/roms/seabios/src/x86.c @@ -0,0 +1,23 @@ +// X86 utility functions. +// +// Copyright (C) 2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "x86.h" // __cpuid + +void +cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) +{ + // Check for cpu id + u32 origflags = save_flags(); + restore_flags(origflags ^ F_ID); + u32 newflags = save_flags(); + restore_flags(origflags); + + if (((origflags ^ newflags) & F_ID) != F_ID) + // no cpuid + *eax = *ebx = *ecx = *edx = 0; + else + __cpuid(index, eax, ebx, ecx, edx); +} diff --git a/qemu/roms/seabios/src/x86.h b/qemu/roms/seabios/src/x86.h new file mode 100644 index 000000000..7798b1c17 --- /dev/null +++ b/qemu/roms/seabios/src/x86.h @@ -0,0 +1,247 @@ +// Basic x86 asm functions. +#ifndef __X86_H +#define __X86_H + +// CPU flag bitdefs +#define F_CF (1<<0) +#define F_ZF (1<<6) +#define F_IF (1<<9) +#define F_ID (1<<21) + +// CR0 flags +#define CR0_PG (1<<31) // Paging +#define CR0_CD (1<<30) // Cache disable +#define CR0_NW (1<<29) // Not Write-through +#define CR0_PE (1<<0) // Protection enable + +// PORT_A20 bitdefs +#define PORT_A20 0x0092 +#define A20_ENABLE_BIT 0x02 + +#ifndef __ASSEMBLY__ + +#include "types.h" // u32 + +static inline void irq_disable(void) +{ + asm volatile("cli": : :"memory"); +} + +static inline void irq_enable(void) +{ + asm volatile("sti": : :"memory"); +} + +static inline u32 save_flags(void) +{ + u32 flags; + asm volatile("pushfl ; popl %0" : "=rm" (flags)); + return flags; +} + +static inline void restore_flags(u32 flags) +{ + asm volatile("pushl %0 ; popfl" : : "g" (flags) : "memory", "cc"); +} + +static inline void cpu_relax(void) +{ + asm volatile("rep ; nop": : :"memory"); +} + +static inline void nop(void) +{ + asm volatile("nop"); +} + +static inline void hlt(void) +{ + asm volatile("hlt": : :"memory"); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd": : :"memory"); +} + +#define CPUID_TSC (1 << 4) +#define CPUID_MSR (1 << 5) +#define CPUID_APIC (1 << 9) +#define CPUID_MTRR (1 << 12) +static inline void __cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) +{ + asm("cpuid" + : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) + : "0" (index)); +} + +static inline u32 getcr0(void) { + u32 cr0; + asm("movl %%cr0, %0" : "=r"(cr0)); + return cr0; +} +static inline void setcr0(u32 cr0) { + asm("movl %0, %%cr0" : : "r"(cr0)); +} + +static inline u64 rdmsr(u32 index) +{ + u64 ret; + asm ("rdmsr" : "=A"(ret) : "c"(index)); + return ret; +} + +static inline void wrmsr(u32 index, u64 val) +{ + asm volatile ("wrmsr" : : "c"(index), "A"(val)); +} + +static inline u64 rdtscll(void) +{ + u64 val; + asm volatile("rdtsc" : "=A" (val)); + return val; +} + +static inline u32 __ffs(u32 word) +{ + asm("bsf %1,%0" + : "=r" (word) + : "rm" (word)); + return word; +} +static inline u32 __fls(u32 word) +{ + asm("bsr %1,%0" + : "=r" (word) + : "rm" (word)); + return word; +} + +static inline u32 getesp(void) { + u32 esp; + asm("movl %%esp, %0" : "=rm"(esp)); + return esp; +} + +static inline void outb(u8 value, u16 port) { + __asm__ __volatile__("outb %b0, %w1" : : "a"(value), "Nd"(port)); +} +static inline void outw(u16 value, u16 port) { + __asm__ __volatile__("outw %w0, %w1" : : "a"(value), "Nd"(port)); +} +static inline void outl(u32 value, u16 port) { + __asm__ __volatile__("outl %0, %w1" : : "a"(value), "Nd"(port)); +} +static inline u8 inb(u16 port) { + u8 value; + __asm__ __volatile__("inb %w1, %b0" : "=a"(value) : "Nd"(port)); + return value; +} +static inline u16 inw(u16 port) { + u16 value; + __asm__ __volatile__("inw %w1, %w0" : "=a"(value) : "Nd"(port)); + return value; +} +static inline u32 inl(u16 port) { + u32 value; + __asm__ __volatile__("inl %w1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void insb(u16 port, u8 *data, u32 count) { + asm volatile("rep insb (%%dx), %%es:(%%edi)" + : "+c"(count), "+D"(data) : "d"(port) : "memory"); +} +static inline void insw(u16 port, u16 *data, u32 count) { + asm volatile("rep insw (%%dx), %%es:(%%edi)" + : "+c"(count), "+D"(data) : "d"(port) : "memory"); +} +static inline void insl(u16 port, u32 *data, u32 count) { + asm volatile("rep insl (%%dx), %%es:(%%edi)" + : "+c"(count), "+D"(data) : "d"(port) : "memory"); +} +// XXX - outs not limited to es segment +static inline void outsb(u16 port, u8 *data, u32 count) { + asm volatile("rep outsb %%es:(%%esi), (%%dx)" + : "+c"(count), "+S"(data) : "d"(port) : "memory"); +} +static inline void outsw(u16 port, u16 *data, u32 count) { + asm volatile("rep outsw %%es:(%%esi), (%%dx)" + : "+c"(count), "+S"(data) : "d"(port) : "memory"); +} +static inline void outsl(u16 port, u32 *data, u32 count) { + asm volatile("rep outsl %%es:(%%esi), (%%dx)" + : "+c"(count), "+S"(data) : "d"(port) : "memory"); +} + +static inline void writel(void *addr, u32 val) { + barrier(); + *(volatile u32 *)addr = val; +} +static inline void writew(void *addr, u16 val) { + barrier(); + *(volatile u16 *)addr = val; +} +static inline void writeb(void *addr, u8 val) { + barrier(); + *(volatile u8 *)addr = val; +} +static inline u32 readl(const void *addr) { + u32 val = *(volatile const u32 *)addr; + barrier(); + return val; +} +static inline u16 readw(const void *addr) { + u16 val = *(volatile const u16 *)addr; + barrier(); + return val; +} +static inline u8 readb(const void *addr) { + u8 val = *(volatile const u8 *)addr; + barrier(); + return val; +} + +// GDT bits +#define GDT_CODE (0x9bULL << 40) // Code segment - P,R,A bits also set +#define GDT_DATA (0x93ULL << 40) // Data segment - W,A bits also set +#define GDT_B (0x1ULL << 54) // Big flag +#define GDT_G (0x1ULL << 55) // Granularity flag +// GDT bits for segment base +#define GDT_BASE(v) ((((u64)(v) & 0xff000000) << 32) \ + | (((u64)(v) & 0x00ffffff) << 16)) +// GDT bits for segment limit (0-1Meg) +#define GDT_LIMIT(v) ((((u64)(v) & 0x000f0000) << 32) \ + | (((u64)(v) & 0x0000ffff) << 0)) +// GDT bits for segment limit (0-4Gig in 4K chunks) +#define GDT_GRANLIMIT(v) (GDT_G | GDT_LIMIT((v) >> 12)) + +struct descloc_s { + u16 length; + u32 addr; +} PACKED; + +static inline void sgdt(struct descloc_s *desc) { + asm("sgdtl %0" : "=m"(*desc)); +} +static inline void lgdt(struct descloc_s *desc) { + asm("lgdtl %0" : : "m"(*desc) : "memory"); +} + +static inline u8 get_a20(void) { + return (inb(PORT_A20) & A20_ENABLE_BIT) != 0; +} + +static inline u8 set_a20(u8 cond) { + u8 val = inb(PORT_A20); + outb((val & ~A20_ENABLE_BIT) | (cond ? A20_ENABLE_BIT : 0), PORT_A20); + return (val & A20_ENABLE_BIT) != 0; +} + +// x86.c +void cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx); + +#endif // !__ASSEMBLY__ + +#endif // x86.h diff --git a/qemu/roms/seabios/vgasrc/Kconfig b/qemu/roms/seabios/vgasrc/Kconfig new file mode 100644 index 000000000..91d590ae2 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/Kconfig @@ -0,0 +1,162 @@ +# Kconfig SeaBIOS VGA BIOS configuration + +menu "VGA ROM" + choice + prompt "VGA Hardware Type" + default NO_VGABIOS + + config NO_VGABIOS + bool "None" + help + Do not build a VGA BIOS. + + config VGA_STANDARD_VGA + depends on QEMU + bool "QEMU/Bochs Original IBM 256K VGA" + select VGA_STDVGA_PORTS + help + Build basic VGA BIOS support (pre Super-VGA) for use + on emulators. + + config VGA_CIRRUS + depends on QEMU + bool "QEMU/Bochs Cirrus SVGA" + select VGA_STDVGA_PORTS + help + Build support for Cirrus VGA emulation found on QEMU + and Bochs emulators. This is for emulators; it is not + intended for use on real Cirrus hardware. + + config VGA_BOCHS + depends on QEMU + bool "QEMU/Bochs VBE SVGA" + select VGA_STDVGA_PORTS + help + Build support for Bochs DISPI interface (a custom VBE + protocol) found on QEMU and Bochs emulators. + + config VGA_GEODEGX2 + bool "GeodeGX2" + select VGA_STDVGA_PORTS + help + Build support for Geode GX2 vga. + + config VGA_GEODELX + bool "GeodeLX" + select VGA_STDVGA_PORTS + help + Build support for Geode LX vga. + + config VGA_COREBOOT + depends on COREBOOT + bool "coreboot linear framebuffer" + select VGA_EMULATE_TEXT + help + Build support for a vgabios wrapper around video + devices initialized using coreboot native vga init. + + endchoice + + choice + depends on VGA_GEODEGX2 || VGA_GEODELX + prompt "Output Mode" + default VGA_OUTPUT_CRT + + config VGA_OUTPUT_CRT + bool "CRT" + help + Use CRT for output. + + config VGA_OUTPUT_PANEL + bool "Flat Panel" + help + Use flat panel for output. + + config VGA_OUTPUT_CRT_PANEL + bool "CRT and Flat Panel" + help + Use CRT and flat panel for output. + endchoice + + config BUILD_VGABIOS + bool + default !NO_VGABIOS + + config VGA_STDVGA_PORTS + bool + config VGA_EMULATE_TEXT + bool + help + Support emulating text mode features when only a + framebuffer is available. + + config VGA_FIXUP_ASM + depends on BUILD_VGABIOS + bool "Fixup assembler to work with broken emulators" + default y + help + This option will cause the build to attempt to avoid + certain x86 machine instructions that are known to confuse + some emulators. In particular, it works around + deficiencies in the Windows vgabios emulator and the + x86emu vgabios emulator (frequently used in Xorg). + + config VGA_ALLOCATE_EXTRA_STACK + depends on BUILD_VGABIOS + bool "Allocate an internal stack for 16bit interrupt entry point" + default y + help + Attempt to allocate (via BIOS PMM call) an internal stack + for the legacy 16bit 0x10 interrupt entry point. This + reduces the amount of space on the caller's stack that + SeaVGABIOS uses. + + config VGA_EXTRA_STACK_SIZE + int + default 512 + + config VGA_VBE + depends on BUILD_VGABIOS + bool "Video BIOS Extensions (VBE)" + default y + help + Support VBE. + + config VGA_PCI + depends on BUILD_VGABIOS && !VGA_COREBOOT + bool "PCI ROM Headers" + default y + help + Build PCI ROM headers so the vga rom can be extracted from + a PCI device. + + config OVERRIDE_PCI_ID + depends on VGA_PCI + bool "Override PCI Vendor and Device IDs" + help + Specify specific values for the PCI Vendor and Device IDs. + + config VGA_VID + depends on VGA_PCI + hex + prompt "PCI Vendor ID" if OVERRIDE_PCI_ID + default 0x1013 if VGA_CIRRUS + default 0x1234 if VGA_BOCHS + default 0x100b if VGA_GEODEGX2 + default 0x1022 if VGA_GEODELX + default 0x0000 + help + Vendor ID for the PCI ROM + + config VGA_DID + depends on VGA_PCI + hex + prompt "PCI Vendor ID" if OVERRIDE_PCI_ID + default 0x00b8 if VGA_CIRRUS + default 0x1111 if VGA_BOCHS + default 0x0030 if VGA_GEODEGX2 + default 0x2081 if VGA_GEODELX + default 0x0000 + help + Device ID for the PCI ROM +endmenu diff --git a/qemu/roms/seabios/vgasrc/bochsvga.c b/qemu/roms/seabios/vgasrc/bochsvga.c new file mode 100644 index 000000000..aa82fc5a5 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/bochsvga.c @@ -0,0 +1,446 @@ +// Bochs VGA interface to extended "VBE" modes +// +// Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2011 Julian Pidancet <julian.pidancet@citrix.com> +// Copyright (C) 2002 Jeroen Janssen +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bochsvga.h" // bochsvga_set_mode +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "output.h" // dprintf +#include "std/vbe.h" // VBE_CAPABILITY_8BIT_DAC +#include "stdvga.h" // VGAREG_SEQU_ADDRESS +#include "vgabios.h" // struct vbe_modeinfo +#include "x86.h" // outw + + +/**************************************************************** + * Mode tables + ****************************************************************/ + +static struct bochsvga_mode +{ + u16 mode; + struct vgamode_s info; +} bochsvga_modes[] VAR16 = { + /* standard modes */ + { 0x100, { MM_PACKED, 640, 400, 8, 8, 16, SEG_GRAPH } }, + { 0x101, { MM_PACKED, 640, 480, 8, 8, 16, SEG_GRAPH } }, + { 0x102, { MM_PLANAR, 800, 600, 4, 8, 16, SEG_GRAPH } }, + { 0x103, { MM_PACKED, 800, 600, 8, 8, 16, SEG_GRAPH } }, + { 0x104, { MM_PLANAR, 1024, 768, 4, 8, 16, SEG_GRAPH } }, + { 0x105, { MM_PACKED, 1024, 768, 8, 8, 16, SEG_GRAPH } }, + { 0x106, { MM_PLANAR, 1280, 1024, 4, 8, 16, SEG_GRAPH } }, + { 0x107, { MM_PACKED, 1280, 1024, 8, 8, 16, SEG_GRAPH } }, + { 0x10D, { MM_DIRECT, 320, 200, 15, 8, 16, SEG_GRAPH } }, + { 0x10E, { MM_DIRECT, 320, 200, 16, 8, 16, SEG_GRAPH } }, + { 0x10F, { MM_DIRECT, 320, 200, 24, 8, 16, SEG_GRAPH } }, + { 0x110, { MM_DIRECT, 640, 480, 15, 8, 16, SEG_GRAPH } }, + { 0x111, { MM_DIRECT, 640, 480, 16, 8, 16, SEG_GRAPH } }, + { 0x112, { MM_DIRECT, 640, 480, 24, 8, 16, SEG_GRAPH } }, + { 0x113, { MM_DIRECT, 800, 600, 15, 8, 16, SEG_GRAPH } }, + { 0x114, { MM_DIRECT, 800, 600, 16, 8, 16, SEG_GRAPH } }, + { 0x115, { MM_DIRECT, 800, 600, 24, 8, 16, SEG_GRAPH } }, + { 0x116, { MM_DIRECT, 1024, 768, 15, 8, 16, SEG_GRAPH } }, + { 0x117, { MM_DIRECT, 1024, 768, 16, 8, 16, SEG_GRAPH } }, + { 0x118, { MM_DIRECT, 1024, 768, 24, 8, 16, SEG_GRAPH } }, + { 0x119, { MM_DIRECT, 1280, 1024, 15, 8, 16, SEG_GRAPH } }, + { 0x11A, { MM_DIRECT, 1280, 1024, 16, 8, 16, SEG_GRAPH } }, + { 0x11B, { MM_DIRECT, 1280, 1024, 24, 8, 16, SEG_GRAPH } }, + { 0x11C, { MM_PACKED, 1600, 1200, 8, 8, 16, SEG_GRAPH } }, + { 0x11D, { MM_DIRECT, 1600, 1200, 15, 8, 16, SEG_GRAPH } }, + { 0x11E, { MM_DIRECT, 1600, 1200, 16, 8, 16, SEG_GRAPH } }, + { 0x11F, { MM_DIRECT, 1600, 1200, 24, 8, 16, SEG_GRAPH } }, + /* BOCHS modes */ + { 0x140, { MM_DIRECT, 320, 200, 32, 8, 16, SEG_GRAPH } }, + { 0x141, { MM_DIRECT, 640, 400, 32, 8, 16, SEG_GRAPH } }, + { 0x142, { MM_DIRECT, 640, 480, 32, 8, 16, SEG_GRAPH } }, + { 0x143, { MM_DIRECT, 800, 600, 32, 8, 16, SEG_GRAPH } }, + { 0x144, { MM_DIRECT, 1024, 768, 32, 8, 16, SEG_GRAPH } }, + { 0x145, { MM_DIRECT, 1280, 1024, 32, 8, 16, SEG_GRAPH } }, + { 0x146, { MM_PACKED, 320, 200, 8, 8, 16, SEG_GRAPH } }, + { 0x147, { MM_DIRECT, 1600, 1200, 32, 8, 16, SEG_GRAPH } }, + { 0x148, { MM_PACKED, 1152, 864, 8, 8, 16, SEG_GRAPH } }, + { 0x149, { MM_DIRECT, 1152, 864, 15, 8, 16, SEG_GRAPH } }, + { 0x14a, { MM_DIRECT, 1152, 864, 16, 8, 16, SEG_GRAPH } }, + { 0x14b, { MM_DIRECT, 1152, 864, 24, 8, 16, SEG_GRAPH } }, + { 0x14c, { MM_DIRECT, 1152, 864, 32, 8, 16, SEG_GRAPH } }, + { 0x175, { MM_DIRECT, 1280, 768, 16, 8, 16, SEG_GRAPH } }, + { 0x176, { MM_DIRECT, 1280, 768, 24, 8, 16, SEG_GRAPH } }, + { 0x177, { MM_DIRECT, 1280, 768, 32, 8, 16, SEG_GRAPH } }, + { 0x178, { MM_DIRECT, 1280, 800, 16, 8, 16, SEG_GRAPH } }, + { 0x179, { MM_DIRECT, 1280, 800, 24, 8, 16, SEG_GRAPH } }, + { 0x17a, { MM_DIRECT, 1280, 800, 32, 8, 16, SEG_GRAPH } }, + { 0x17b, { MM_DIRECT, 1280, 960, 16, 8, 16, SEG_GRAPH } }, + { 0x17c, { MM_DIRECT, 1280, 960, 24, 8, 16, SEG_GRAPH } }, + { 0x17d, { MM_DIRECT, 1280, 960, 32, 8, 16, SEG_GRAPH } }, + { 0x17e, { MM_DIRECT, 1440, 900, 16, 8, 16, SEG_GRAPH } }, + { 0x17f, { MM_DIRECT, 1440, 900, 24, 8, 16, SEG_GRAPH } }, + { 0x180, { MM_DIRECT, 1440, 900, 32, 8, 16, SEG_GRAPH } }, + { 0x181, { MM_DIRECT, 1400, 1050, 16, 8, 16, SEG_GRAPH } }, + { 0x182, { MM_DIRECT, 1400, 1050, 24, 8, 16, SEG_GRAPH } }, + { 0x183, { MM_DIRECT, 1400, 1050, 32, 8, 16, SEG_GRAPH } }, + { 0x184, { MM_DIRECT, 1680, 1050, 16, 8, 16, SEG_GRAPH } }, + { 0x185, { MM_DIRECT, 1680, 1050, 24, 8, 16, SEG_GRAPH } }, + { 0x186, { MM_DIRECT, 1680, 1050, 32, 8, 16, SEG_GRAPH } }, + { 0x187, { MM_DIRECT, 1920, 1200, 16, 8, 16, SEG_GRAPH } }, + { 0x188, { MM_DIRECT, 1920, 1200, 24, 8, 16, SEG_GRAPH } }, + { 0x189, { MM_DIRECT, 1920, 1200, 32, 8, 16, SEG_GRAPH } }, + { 0x18a, { MM_DIRECT, 2560, 1600, 16, 8, 16, SEG_GRAPH } }, + { 0x18b, { MM_DIRECT, 2560, 1600, 24, 8, 16, SEG_GRAPH } }, + { 0x18c, { MM_DIRECT, 2560, 1600, 32, 8, 16, SEG_GRAPH } }, + { 0x18d, { MM_DIRECT, 1280, 720, 16, 8, 16, SEG_GRAPH } }, + { 0x18e, { MM_DIRECT, 1280, 720, 24, 8, 16, SEG_GRAPH } }, + { 0x18f, { MM_DIRECT, 1280, 720, 32, 8, 16, SEG_GRAPH } }, + { 0x190, { MM_DIRECT, 1920, 1080, 16, 8, 16, SEG_GRAPH } }, + { 0x191, { MM_DIRECT, 1920, 1080, 24, 8, 16, SEG_GRAPH } }, + { 0x192, { MM_DIRECT, 1920, 1080, 32, 8, 16, SEG_GRAPH } }, +}; + +static int dispi_found VAR16 = 0; + +static int is_bochsvga_mode(struct vgamode_s *vmode_g) +{ + return (vmode_g >= &bochsvga_modes[0].info + && vmode_g <= &bochsvga_modes[ARRAY_SIZE(bochsvga_modes)-1].info); +} + +struct vgamode_s *bochsvga_find_mode(int mode) +{ + struct bochsvga_mode *m = bochsvga_modes; + if (GET_GLOBAL(dispi_found)) + for (; m < &bochsvga_modes[ARRAY_SIZE(bochsvga_modes)]; m++) + if (GET_GLOBAL(m->mode) == mode) + return &m->info; + return stdvga_find_mode(mode); +} + +void +bochsvga_list_modes(u16 seg, u16 *dest, u16 *last) +{ + struct bochsvga_mode *m = bochsvga_modes; + if (GET_GLOBAL(dispi_found)) { + for (; m < &bochsvga_modes[ARRAY_SIZE(bochsvga_modes)] && dest<last; m++) { + u16 mode = GET_GLOBAL(m->mode); + if (mode == 0xffff) + continue; + SET_FARVAR(seg, *dest, mode); + dest++; + } + } + stdvga_list_modes(seg, dest, last); +} + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +static inline u16 dispi_read(u16 reg) +{ + outw(reg, VBE_DISPI_IOPORT_INDEX); + return inw(VBE_DISPI_IOPORT_DATA); +} +static inline void dispi_write(u16 reg, u16 val) +{ + outw(reg, VBE_DISPI_IOPORT_INDEX); + outw(val, VBE_DISPI_IOPORT_DATA); +} + +static u8 +bochsvga_dispi_enabled(void) +{ + if (!GET_GLOBAL(dispi_found)) + return 0; + u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); + if (!(en & VBE_DISPI_ENABLED)) + return 0; + return 1; +} + +int +bochsvga_get_window(struct vgamode_s *vmode_g, int window) +{ + if (!bochsvga_dispi_enabled()) + return stdvga_get_window(vmode_g, window); + if (window != 0) + return -1; + return dispi_read(VBE_DISPI_INDEX_BANK); +} + +int +bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val) +{ + if (!bochsvga_dispi_enabled()) + return stdvga_set_window(vmode_g, window, val); + if (window != 0) + return -1; + dispi_write(VBE_DISPI_INDEX_BANK, val); + if (dispi_read(VBE_DISPI_INDEX_BANK) != val) + return -1; + return 0; +} + +int +bochsvga_get_linelength(struct vgamode_s *vmode_g) +{ + if (!bochsvga_dispi_enabled()) + return stdvga_get_linelength(vmode_g); + return dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * vga_bpp(vmode_g) / 8; +} + +int +bochsvga_set_linelength(struct vgamode_s *vmode_g, int val) +{ + stdvga_set_linelength(vmode_g, val); + if (bochsvga_dispi_enabled()) { + int pixels = (val * 8) / vga_bpp(vmode_g); + dispi_write(VBE_DISPI_INDEX_VIRT_WIDTH, pixels); + } + return 0; +} + +int +bochsvga_get_displaystart(struct vgamode_s *vmode_g) +{ + if (!bochsvga_dispi_enabled()) + return stdvga_get_displaystart(vmode_g); + int bpp = vga_bpp(vmode_g); + int linelength = dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * bpp / 8; + int x = dispi_read(VBE_DISPI_INDEX_X_OFFSET); + int y = dispi_read(VBE_DISPI_INDEX_Y_OFFSET); + return x * bpp / 8 + linelength * y; +} + +int +bochsvga_set_displaystart(struct vgamode_s *vmode_g, int val) +{ + stdvga_set_displaystart(vmode_g, val); + if (bochsvga_dispi_enabled()) { + int bpp = vga_bpp(vmode_g); + int linelength = dispi_read(VBE_DISPI_INDEX_VIRT_WIDTH) * bpp / 8; + if (!linelength) + return 0; + dispi_write(VBE_DISPI_INDEX_X_OFFSET, (val % linelength) * 8 / bpp); + dispi_write(VBE_DISPI_INDEX_Y_OFFSET, val / linelength); + } + return 0; +} + +int +bochsvga_get_dacformat(struct vgamode_s *vmode_g) +{ + if (!bochsvga_dispi_enabled()) + return stdvga_get_dacformat(vmode_g); + u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); + return (en & VBE_DISPI_8BIT_DAC) ? 8 : 6; +} + +int +bochsvga_set_dacformat(struct vgamode_s *vmode_g, int val) +{ + if (!bochsvga_dispi_enabled()) + return stdvga_set_dacformat(vmode_g, val); + u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); + if (val == 6) + en &= ~VBE_DISPI_8BIT_DAC; + else if (val == 8) + en |= VBE_DISPI_8BIT_DAC; + else + return -1; + dispi_write(VBE_DISPI_INDEX_ENABLE, en); + return 0; +} + +static int +bochsvga_save_state(u16 seg, u16 *info) +{ + u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); + SET_FARVAR(seg, *info, en); + info++; + if (!(en & VBE_DISPI_ENABLED)) + return 0; + int i; + for (i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) + if (i != VBE_DISPI_INDEX_ENABLE) { + u16 v = dispi_read(i); + SET_FARVAR(seg, *info, v); + info++; + } + return 0; +} + +static int +bochsvga_restore_state(u16 seg, u16 *info) +{ + u16 en = GET_FARVAR(seg, *info); + info++; + if (!(en & VBE_DISPI_ENABLED)) { + dispi_write(VBE_DISPI_INDEX_ENABLE, en); + return 0; + } + int i; + for (i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) + if (i == VBE_DISPI_INDEX_ENABLE) { + dispi_write(i, en); + } else { + dispi_write(i, GET_FARVAR(seg, *info)); + info++; + } + return 0; +} + +int +bochsvga_save_restore(int cmd, u16 seg, void *data) +{ + int ret = stdvga_save_restore(cmd, seg, data); + if (ret < 0 || !(cmd & SR_REGISTERS) || !GET_GLOBAL(dispi_found)) + return ret; + + u16 *info = (data + ret); + if (cmd & SR_SAVE) + bochsvga_save_state(seg, info); + if (cmd & SR_RESTORE) + bochsvga_restore_state(seg, info); + return ret + (VBE_DISPI_INDEX_Y_OFFSET-VBE_DISPI_INDEX_XRES+1)*sizeof(u16); +} + + +/**************************************************************** + * Mode setting + ****************************************************************/ + +int +bochsvga_set_mode(struct vgamode_s *vmode_g, int flags) +{ + if (GET_GLOBAL(dispi_found)) + dispi_write(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); + if (! is_bochsvga_mode(vmode_g)) + return stdvga_set_mode(vmode_g, flags); + if (!GET_GLOBAL(dispi_found)) + return -1; + + u8 memmodel = GET_GLOBAL(vmode_g->memmodel); + if (memmodel == MM_PLANAR) + stdvga_set_mode(stdvga_find_mode(0x6a), 0); + if (memmodel == MM_PACKED && !(flags & MF_NOPALETTE)) + stdvga_set_packed_palette(); + + dispi_write(VBE_DISPI_INDEX_BPP, GET_GLOBAL(vmode_g->depth)); + u16 width = GET_GLOBAL(vmode_g->width); + u16 height = GET_GLOBAL(vmode_g->height); + dispi_write(VBE_DISPI_INDEX_XRES, width); + dispi_write(VBE_DISPI_INDEX_YRES, height); + dispi_write(VBE_DISPI_INDEX_BANK, 0); + u16 bf = ((flags & MF_NOCLEARMEM ? VBE_DISPI_NOCLEARMEM : 0) + | (flags & MF_LINEARFB ? VBE_DISPI_LFB_ENABLED : 0)); + dispi_write(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | bf); + + /* VGA compat setup */ + u16 crtc_addr = VGAREG_VGA_CRTC_ADDRESS; + stdvga_crtc_write(crtc_addr, 0x11, 0x00); + stdvga_crtc_write(crtc_addr, 0x01, width / 8 - 1); + stdvga_set_linelength(vmode_g, width); + stdvga_crtc_write(crtc_addr, 0x12, height - 1); + u8 v = 0; + if ((height - 1) & 0x0100) + v |= 0x02; + if ((height - 1) & 0x0200) + v |= 0x40; + stdvga_crtc_mask(crtc_addr, 0x07, 0x42, v); + + stdvga_crtc_write(crtc_addr, 0x09, 0x00); + stdvga_crtc_mask(crtc_addr, 0x17, 0x00, 0x03); + stdvga_attr_mask(0x10, 0x00, 0x01); + stdvga_grdc_write(0x06, 0x05); + stdvga_sequ_write(0x02, 0x0f); + if (memmodel != MM_PLANAR) { + stdvga_crtc_mask(crtc_addr, 0x14, 0x00, 0x40); + stdvga_attr_mask(0x10, 0x00, 0x40); + stdvga_sequ_mask(0x04, 0x00, 0x08); + stdvga_grdc_mask(0x05, 0x20, 0x40); + } + stdvga_attrindex_write(0x20); + + return 0; +} + + +/**************************************************************** + * Init + ****************************************************************/ + +int +bochsvga_setup(void) +{ + int ret = stdvga_setup(); + if (ret) + return ret; + + /* Sanity checks */ + dispi_write(VBE_DISPI_INDEX_ID, VBE_DISPI_ID0); + if (dispi_read(VBE_DISPI_INDEX_ID) != VBE_DISPI_ID0) { + dprintf(1, "No VBE DISPI interface detected, falling back to stdvga\n"); + return 0; + } + + dispi_write(VBE_DISPI_INDEX_ID, VBE_DISPI_ID5); + SET_VGA(dispi_found, 1); + + if (GET_GLOBAL(HaveRunInit)) + return 0; + + u32 lfb_addr = VBE_DISPI_LFB_PHYSICAL_ADDRESS; + int bdf = GET_GLOBAL(VgaBDF); + if (CONFIG_VGA_PCI && bdf >= 0) { + u16 vendor = pci_config_readw(bdf, PCI_VENDOR_ID); + int barid; + switch (vendor) { + case 0x15ad: /* qemu vmware vga */ + barid = 1; + break; + default: /* stdvga, qxl, virtio */ + barid = 0; + break; + } + u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_0 + barid * 4); + lfb_addr = bar & PCI_BASE_ADDRESS_MEM_MASK; + dprintf(1, "VBE DISPI: bdf %02x:%02x.%x, bar %d\n", pci_bdf_to_bus(bdf) + , pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf), barid); + } + + SET_VGA(VBE_framebuffer, lfb_addr); + u32 totalmem = dispi_read(VBE_DISPI_INDEX_VIDEO_MEMORY_64K) * 64 * 1024; + SET_VGA(VBE_total_memory, totalmem); + SET_VGA(VBE_win_granularity, 64); + SET_VGA(VBE_capabilities, VBE_CAPABILITY_8BIT_DAC); + + dprintf(1, "VBE DISPI: lfb_addr=%x, size %d MB\n", + lfb_addr, totalmem >> 20); + + // Validate modes + u16 en = dispi_read(VBE_DISPI_INDEX_ENABLE); + dispi_write(VBE_DISPI_INDEX_ENABLE, en | VBE_DISPI_GETCAPS); + u16 max_xres = dispi_read(VBE_DISPI_INDEX_XRES); + u16 max_bpp = dispi_read(VBE_DISPI_INDEX_BPP); + dispi_write(VBE_DISPI_INDEX_ENABLE, en); + struct bochsvga_mode *m = bochsvga_modes; + for (; m < &bochsvga_modes[ARRAY_SIZE(bochsvga_modes)]; m++) { + u16 width = GET_GLOBAL(m->info.width); + u16 height = GET_GLOBAL(m->info.height); + u8 depth = GET_GLOBAL(m->info.depth); + u32 mem = (height * DIV_ROUND_UP(width * vga_bpp(&m->info), 8) + * stdvga_vram_ratio(&m->info)); + + if (width > max_xres || depth > max_bpp || mem > totalmem) { + dprintf(1, "Removing mode %x\n", GET_GLOBAL(m->mode)); + SET_VGA(m->mode, 0xffff); + } + } + + return 0; +} diff --git a/qemu/roms/seabios/vgasrc/bochsvga.h b/qemu/roms/seabios/vgasrc/bochsvga.h new file mode 100644 index 000000000..ae5f75db2 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/bochsvga.h @@ -0,0 +1,57 @@ +#ifndef __BOCHSVGA_H +#define __BOCHSVGA_H + +#include "types.h" // u8 + +#define VBE_DISPI_BANK_ADDRESS 0xA0000 +#define VBE_DISPI_BANK_SIZE_KB 64 + +#define VBE_DISPI_MAX_XRES 2560 +#define VBE_DISPI_MAX_YRES 1600 + +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa + +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 + +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + +struct vgamode_s *bochsvga_find_mode(int mode); +void bochsvga_list_modes(u16 seg, u16 *dest, u16 *last); +int bochsvga_get_window(struct vgamode_s *vmode_g, int window); +int bochsvga_set_window(struct vgamode_s *vmode_g, int window, int val); +int bochsvga_get_linelength(struct vgamode_s *vmode_g); +int bochsvga_set_linelength(struct vgamode_s *vmode_g, int val); +int bochsvga_get_displaystart(struct vgamode_s *vmode_g); +int bochsvga_set_displaystart(struct vgamode_s *vmode_g, int val); +int bochsvga_get_dacformat(struct vgamode_s *vmode_g); +int bochsvga_set_dacformat(struct vgamode_s *vmode_g, int val); +int bochsvga_save_restore(int cmd, u16 seg, void *data); +int bochsvga_set_mode(struct vgamode_s *vmode_g, int flags); +int bochsvga_setup(void); + +#endif // bochsvga.h diff --git a/qemu/roms/seabios/vgasrc/cbvga.c b/qemu/roms/seabios/vgasrc/cbvga.c new file mode 100644 index 000000000..1cfb9d377 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/cbvga.c @@ -0,0 +1,193 @@ +// Simple framebuffer vgabios for use with coreboot native vga init. +// +// Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "cbvga.h" // cbvga_setup +#include "output.h" // dprintf +#include "stdvga.h" // SEG_CTEXT +#include "string.h" // memset16_far +#include "util.h" // find_cb_table +#include "vgabios.h" // VGAREG_* + +static int CBmode VAR16; +static struct vgamode_s CBmodeinfo VAR16; +static struct vgamode_s CBemulinfo VAR16; +static u32 CBlinelength VAR16; + +struct vgamode_s *cbvga_find_mode(int mode) +{ + if (mode == GET_GLOBAL(CBmode)) + return &CBmodeinfo; + if (mode == 0x03) + return &CBemulinfo; + return NULL; +} + +void +cbvga_list_modes(u16 seg, u16 *dest, u16 *last) +{ + if (dest<last) { + SET_FARVAR(seg, *dest, GET_GLOBAL(CBmode)); + dest++; + } + SET_FARVAR(seg, *dest, 0xffff); +} + +int +cbvga_get_window(struct vgamode_s *vmode_g, int window) +{ + return -1; +} + +int +cbvga_set_window(struct vgamode_s *vmode_g, int window, int val) +{ + return -1; +} + +int +cbvga_get_linelength(struct vgamode_s *vmode_g) +{ + return GET_GLOBAL(CBlinelength); +} + +int +cbvga_set_linelength(struct vgamode_s *vmode_g, int val) +{ + return -1; +} + +int +cbvga_get_displaystart(struct vgamode_s *vmode_g) +{ + return 0; +} + +int +cbvga_set_displaystart(struct vgamode_s *vmode_g, int val) +{ + return -1; +} + +int +cbvga_get_dacformat(struct vgamode_s *vmode_g) +{ + return -1; +} + +int +cbvga_set_dacformat(struct vgamode_s *vmode_g, int val) +{ + return -1; +} + +int +cbvga_save_restore(int cmd, u16 seg, void *data) +{ + if (cmd & (SR_HARDWARE|SR_DAC|SR_REGISTERS)) + return -1; + return bda_save_restore(cmd, seg, data); +} + +int +cbvga_set_mode(struct vgamode_s *vmode_g, int flags) +{ + u8 emul = vmode_g == &CBemulinfo || GET_GLOBAL(CBmode) == 0x03; + MASK_BDA_EXT(flags, BF_EMULATE_TEXT, emul ? BF_EMULATE_TEXT : 0); + if (!(flags & MF_NOCLEARMEM)) { + if (GET_GLOBAL(CBmodeinfo.memmodel) == MM_TEXT) { + memset16_far(SEG_CTEXT, (void*)0, 0x0720, 80*25*2); + return 0; + } + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = op.y = 0; + op.xlen = GET_GLOBAL(CBmodeinfo.width); + op.ylen = GET_GLOBAL(CBmodeinfo.height); + op.op = GO_MEMSET; + handle_gfx_op(&op); + } + return 0; +} + +#define CB_TAG_FRAMEBUFFER 0x0012 +struct cb_framebuffer { + u32 tag; + u32 size; + + u64 physical_address; + u32 x_resolution; + u32 y_resolution; + u32 bytes_per_line; + u8 bits_per_pixel; + u8 red_mask_pos; + u8 red_mask_size; + u8 green_mask_pos; + u8 green_mask_size; + u8 blue_mask_pos; + u8 blue_mask_size; + u8 reserved_mask_pos; + u8 reserved_mask_size; +}; + +int +cbvga_setup(void) +{ + dprintf(1, "coreboot vga init\n"); + + if (GET_GLOBAL(HaveRunInit)) + return 0; + + struct cb_header *cbh = find_cb_table(); + if (!cbh) { + dprintf(1, "Unable to find coreboot table\n"); + return -1; + } + struct cb_framebuffer *cbfb = find_cb_subtable(cbh, CB_TAG_FRAMEBUFFER); + if (!cbfb) { + // Assume there is an EGA text framebuffer. + dprintf(1, "Did not find coreboot framebuffer - assuming EGA text\n"); + SET_VGA(CBmode, 0x03); + SET_VGA(CBlinelength, 80*2); + SET_VGA(CBmodeinfo.memmodel, MM_TEXT); + SET_VGA(CBmodeinfo.width, 80); + SET_VGA(CBmodeinfo.height, 25); + SET_VGA(CBmodeinfo.depth, 4); + SET_VGA(CBmodeinfo.cwidth, 9); + SET_VGA(CBmodeinfo.cheight, 16); + SET_VGA(CBmodeinfo.sstart, SEG_CTEXT); + return 0; + } + + u64 addr = GET_FARVAR(0, cbfb->physical_address); + u8 bpp = GET_FARVAR(0, cbfb->bits_per_pixel); + u32 xlines = GET_FARVAR(0, cbfb->x_resolution); + u32 ylines = GET_FARVAR(0, cbfb->y_resolution); + u32 linelength = GET_FARVAR(0, cbfb->bytes_per_line); + dprintf(1, "Found FB @ %llx %dx%d with %d bpp (%d stride)\n" + , addr, xlines, ylines, bpp, linelength); + + if (!addr || addr > 0xffffffff + || (bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32)) { + dprintf(1, "Unable to use FB\n"); + return -1; + } + + SET_VGA(CBmode, 0x140); + SET_VGA(VBE_framebuffer, addr); + SET_VGA(VBE_total_memory, linelength * ylines); + SET_VGA(CBlinelength, linelength); + SET_VGA(CBmodeinfo.memmodel, MM_DIRECT); + SET_VGA(CBmodeinfo.width, xlines); + SET_VGA(CBmodeinfo.height, ylines); + SET_VGA(CBmodeinfo.depth, bpp); + SET_VGA(CBmodeinfo.cwidth, 8); + SET_VGA(CBmodeinfo.cheight, 16); + memcpy_far(get_global_seg(), &CBemulinfo + , get_global_seg(), &CBmodeinfo, sizeof(CBemulinfo)); + + return 0; +} diff --git a/qemu/roms/seabios/vgasrc/cbvga.h b/qemu/roms/seabios/vgasrc/cbvga.h new file mode 100644 index 000000000..fb892c85b --- /dev/null +++ b/qemu/roms/seabios/vgasrc/cbvga.h @@ -0,0 +1,20 @@ +#ifndef __CBVGA_H +#define __CBVGA_H + +#include "types.h" // u16 + +struct vgamode_s *cbvga_find_mode(int mode); +void cbvga_list_modes(u16 seg, u16 *dest, u16 *last); +int cbvga_get_window(struct vgamode_s *vmode_g, int window); +int cbvga_set_window(struct vgamode_s *vmode_g, int window, int val); +int cbvga_get_linelength(struct vgamode_s *vmode_g); +int cbvga_set_linelength(struct vgamode_s *vmode_g, int val); +int cbvga_get_displaystart(struct vgamode_s *vmode_g); +int cbvga_set_displaystart(struct vgamode_s *vmode_g, int val); +int cbvga_get_dacformat(struct vgamode_s *vmode_g); +int cbvga_set_dacformat(struct vgamode_s *vmode_g, int val); +int cbvga_save_restore(int cmd, u16 seg, void *data); +int cbvga_set_mode(struct vgamode_s *vmode_g, int flags); +int cbvga_setup(void); + +#endif // cbvga.h diff --git a/qemu/roms/seabios/vgasrc/clext.c b/qemu/roms/seabios/vgasrc/clext.c new file mode 100644 index 000000000..fc5b42f62 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/clext.c @@ -0,0 +1,627 @@ +// QEMU Cirrus CLGD 54xx VGABIOS Extension. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (c) 2004 Makoto Suzuki (suzu) +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "clext.h" // clext_setup +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "output.h" // dprintf +#include "stdvga.h" // VGAREG_SEQU_ADDRESS +#include "string.h" // memset16_far +#include "vgabios.h" // VBE_VENDOR_STRING + + +/**************************************************************** + * Cirrus mode tables + ****************************************************************/ + +/* VGA */ +static u16 cseq_vga[] VAR16 = {0x0007,0xffff}; +static u16 cgraph_vga[] VAR16 = {0x0009,0x000a,0x000b,0xffff}; +static u16 ccrtc_vga[] VAR16 = {0x001a,0x001b,0x001d,0xffff}; + +/* extensions */ +static u16 cgraph_svgacolor[] VAR16 = { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x4005,0x0506,0x0f07,0xff08, + 0x0009,0x000a,0x000b, + 0xffff +}; +/* 640x480x8 */ +static u16 cseq_640x480x8[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, + 0x580b,0x580c,0x580d,0x580e, + 0x0412,0x0013,0x2017, + 0x331b,0x331c,0x331d,0x331e, + 0xffff +}; +static u16 ccrtc_640x480x8[] VAR16 = { + 0x2c11, + 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, + 0x4009,0x000c,0x000d, + 0xea10,0xdf12,0x5013,0x4014,0xdf15,0x0b16,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; +/* 640x480x16 */ +static u16 cseq_640x480x16[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, + 0x580b,0x580c,0x580d,0x580e, + 0x0412,0x0013,0x2017, + 0x331b,0x331c,0x331d,0x331e, + 0xffff +}; +static u16 ccrtc_640x480x16[] VAR16 = { + 0x2c11, + 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, + 0x4009,0x000c,0x000d, + 0xea10,0xdf12,0xa013,0x4014,0xdf15,0x0b16,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; +/* 640x480x24 */ +static u16 cseq_640x480x24[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, + 0x580b,0x580c,0x580d,0x580e, + 0x0412,0x0013,0x2017, + 0x331b,0x331c,0x331d,0x331e, + 0xffff +}; +static u16 ccrtc_640x480x24[] VAR16 = { + 0x2c11, + 0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07, + 0x4009,0x000c,0x000d, + 0xea10,0xdf12,0xf013,0x4014,0xdf15,0x0b16,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; +/* 800x600x8 */ +static u16 cseq_800x600x8[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, + 0x230b,0x230c,0x230d,0x230e, + 0x0412,0x0013,0x2017, + 0x141b,0x141c,0x141d,0x141e, + 0xffff +}; +static u16 ccrtc_800x600x8[] VAR16 = { + 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, + 0x6009,0x000c,0x000d, + 0x7d10,0x5712,0x6413,0x4014,0x5715,0x9816,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; +/* 800x600x16 */ +static u16 cseq_800x600x16[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, + 0x230b,0x230c,0x230d,0x230e, + 0x0412,0x0013,0x2017, + 0x141b,0x141c,0x141d,0x141e, + 0xffff +}; +static u16 ccrtc_800x600x16[] VAR16 = { + 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, + 0x6009,0x000c,0x000d, + 0x7d10,0x5712,0xc813,0x4014,0x5715,0x9816,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; +/* 800x600x24 */ +static u16 cseq_800x600x24[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, + 0x230b,0x230c,0x230d,0x230e, + 0x0412,0x0013,0x2017, + 0x141b,0x141c,0x141d,0x141e, + 0xffff +}; +static u16 ccrtc_800x600x24[] VAR16 = { + 0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007, + 0x6009,0x000c,0x000d, + 0x7d10,0x5712,0x2c13,0x4014,0x5715,0x9816,0xc317,0xff18, + 0x001a,0x321b,0x001d, + 0xffff +}; +/* 1024x768x8 */ +static u16 cseq_1024x768x8[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, + 0x760b,0x760c,0x760d,0x760e, + 0x0412,0x0013,0x2017, + 0x341b,0x341c,0x341d,0x341e, + 0xffff +}; +static u16 ccrtc_1024x768x8[] VAR16 = { + 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, + 0x6009,0x000c,0x000d, + 0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; +/* 1024x768x16 */ +static u16 cseq_1024x768x16[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, + 0x760b,0x760c,0x760d,0x760e, + 0x0412,0x0013,0x2017, + 0x341b,0x341c,0x341d,0x341e, + 0xffff +}; +static u16 ccrtc_1024x768x16[] VAR16 = { + 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, + 0x6009,0x000c,0x000d, + 0x0310,0xff12,0x0013,0x4014,0xff15,0x2416,0xc317,0xff18, + 0x001a,0x321b,0x001d, + 0xffff +}; +/* 1024x768x24 */ +static u16 cseq_1024x768x24[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507, + 0x760b,0x760c,0x760d,0x760e, + 0x0412,0x0013,0x2017, + 0x341b,0x341c,0x341d,0x341e, + 0xffff +}; +static u16 ccrtc_1024x768x24[] VAR16 = { + 0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507, + 0x6009,0x000c,0x000d, + 0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18, + 0x001a,0x321b,0x001d, + 0xffff +}; +/* 1280x1024x8 */ +static u16 cseq_1280x1024x8[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, + 0x760b,0x760c,0x760d,0x760e, + 0x0412,0x0013,0x2017, + 0x341b,0x341c,0x341d,0x341e, + 0xffff +}; +static u16 ccrtc_1280x1024x8[] VAR16 = { + 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, + 0x6009,0x000c,0x000d, + 0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; +/* 1280x1024x16 */ +static u16 cseq_1280x1024x16[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707, + 0x760b,0x760c,0x760d,0x760e, + 0x0412,0x0013,0x2017, + 0x341b,0x341c,0x341d,0x341e, + 0xffff +}; +static u16 ccrtc_1280x1024x16[] VAR16 = { + 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, + 0x6009,0x000c,0x000d, + 0x0310,0xff12,0x4013,0x4014,0xff15,0x2416,0xc317,0xff18, + 0x001a,0x321b,0x001d, + 0xffff +}; + +/* 1600x1200x8 */ +static u16 cseq_1600x1200x8[] VAR16 = { + 0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107, + 0x760b,0x760c,0x760d,0x760e, + 0x0412,0x0013,0x2017, + 0x341b,0x341c,0x341d,0x341e, + 0xffff +}; +static u16 ccrtc_1600x1200x8[] VAR16 = { + 0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707, + 0x6009,0x000c,0x000d, + 0x0310,0xff12,0xc813,0x4014,0xff15,0x2416,0xc317,0xff18, + 0x001a,0x221b,0x001d, + 0xffff +}; + +struct cirrus_mode_s { + u16 mode, vesamode; + struct vgamode_s info; + + u16 hidden_dac; /* 0x3c6 */ + u16 *seq; /* 0x3c4 */ + u16 *graph; /* 0x3ce */ + u16 *crtc; /* 0x3d4 */ +}; + +static struct cirrus_mode_s cirrus_modes[] VAR16 = { + {0x5f,0x101,{MM_PACKED,640,480,8,8,16,SEG_GRAPH},0x00, + cseq_640x480x8,cgraph_svgacolor,ccrtc_640x480x8}, + {0x64,0x111,{MM_DIRECT,640,480,16,8,16,SEG_GRAPH},0xe1, + cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16}, + {0x66,0x110,{MM_DIRECT,640,480,15,8,16,SEG_GRAPH},0xf0, + cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16}, + {0x71,0x112,{MM_DIRECT,640,480,24,8,16,SEG_GRAPH},0xe5, + cseq_640x480x24,cgraph_svgacolor,ccrtc_640x480x24}, + + {0x5c,0x103,{MM_PACKED,800,600,8,8,16,SEG_GRAPH},0x00, + cseq_800x600x8,cgraph_svgacolor,ccrtc_800x600x8}, + {0x65,0x114,{MM_DIRECT,800,600,16,8,16,SEG_GRAPH},0xe1, + cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16}, + {0x67,0x113,{MM_DIRECT,800,600,15,8,16,SEG_GRAPH},0xf0, + cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16}, + + {0x60,0x105,{MM_PACKED,1024,768,8,8,16,SEG_GRAPH},0x00, + cseq_1024x768x8,cgraph_svgacolor,ccrtc_1024x768x8}, + {0x74,0x117,{MM_DIRECT,1024,768,16,8,16,SEG_GRAPH},0xe1, + cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16}, + {0x68,0x116,{MM_DIRECT,1024,768,15,8,16,SEG_GRAPH},0xf0, + cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16}, + + {0x78,0x115,{MM_DIRECT,800,600,24,8,16,SEG_GRAPH},0xe5, + cseq_800x600x24,cgraph_svgacolor,ccrtc_800x600x24}, + {0x79,0x118,{MM_DIRECT,1024,768,24,8,16,SEG_GRAPH},0xe5, + cseq_1024x768x24,cgraph_svgacolor,ccrtc_1024x768x24}, + + {0x6d,0x107,{MM_PACKED,1280,1024,8,8,16,SEG_GRAPH},0x00, + cseq_1280x1024x8,cgraph_svgacolor,ccrtc_1280x1024x8}, + {0x69,0x119,{MM_DIRECT,1280,1024,15,8,16,SEG_GRAPH},0xf0, + cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16}, + {0x75,0x11a,{MM_DIRECT,1280,1024,16,8,16,SEG_GRAPH},0xe1, + cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16}, + + {0x7b,0xffff,{MM_PACKED,1600,1200,8,8,16,SEG_GRAPH},0x00, + cseq_1600x1200x8,cgraph_svgacolor,ccrtc_1600x1200x8}, +}; + +static struct cirrus_mode_s mode_switchback VAR16 = + {0xfe,0xffff,{0xff},0,cseq_vga,cgraph_vga,ccrtc_vga}; + +int +is_cirrus_mode(struct vgamode_s *vmode_g) +{ + return (vmode_g >= &cirrus_modes[0].info + && vmode_g <= &cirrus_modes[ARRAY_SIZE(cirrus_modes)-1].info); +} + +struct vgamode_s * +clext_find_mode(int mode) +{ + struct cirrus_mode_s *table_g = cirrus_modes; + while (table_g < &cirrus_modes[ARRAY_SIZE(cirrus_modes)]) { + if (GET_GLOBAL(table_g->mode) == mode + || GET_GLOBAL(table_g->vesamode) == mode) + return &table_g->info; + table_g++; + } + return stdvga_find_mode(mode); +} + +void +clext_list_modes(u16 seg, u16 *dest, u16 *last) +{ + int i; + for (i=0; i<ARRAY_SIZE(cirrus_modes) && dest<last; i++) { + u16 mode = GET_GLOBAL(cirrus_modes[i].vesamode); + if (mode == 0xffff) + continue; + SET_FARVAR(seg, *dest, mode); + dest++; + } + stdvga_list_modes(seg, dest, last); +} + + +/**************************************************************** + * helper functions + ****************************************************************/ + +int +clext_get_window(struct vgamode_s *vmode_g, int window) +{ + return stdvga_grdc_read(window + 9); +} + +int +clext_set_window(struct vgamode_s *vmode_g, int window, int val) +{ + if (val >= 0x100) + return -1; + stdvga_grdc_write(window + 9, val); + return 0; +} + +int +clext_get_linelength(struct vgamode_s *vmode_g) +{ + u16 crtc_addr = stdvga_get_crtc(); + u8 reg13 = stdvga_crtc_read(crtc_addr, 0x13); + u8 reg1b = stdvga_crtc_read(crtc_addr, 0x1b); + return (((reg1b & 0x10) << 4) + reg13) * 8 / stdvga_vram_ratio(vmode_g); +} + +int +clext_set_linelength(struct vgamode_s *vmode_g, int val) +{ + u16 crtc_addr = stdvga_get_crtc(); + val = DIV_ROUND_UP(val * stdvga_vram_ratio(vmode_g), 8); + stdvga_crtc_write(crtc_addr, 0x13, val); + stdvga_crtc_mask(crtc_addr, 0x1b, 0x10, (val & 0x100) >> 4); + return 0; +} + +int +clext_get_displaystart(struct vgamode_s *vmode_g) +{ + u16 crtc_addr = stdvga_get_crtc(); + u8 b2 = stdvga_crtc_read(crtc_addr, 0x0c); + u8 b1 = stdvga_crtc_read(crtc_addr, 0x0d); + u8 b3 = stdvga_crtc_read(crtc_addr, 0x1b); + u8 b4 = stdvga_crtc_read(crtc_addr, 0x1d); + int val = (b1 | (b2<<8) | ((b3 & 0x01) << 16) | ((b3 & 0x0c) << 15) + | ((b4 & 0x80) << 12)); + return val * 4 / stdvga_vram_ratio(vmode_g); +} + +int +clext_set_displaystart(struct vgamode_s *vmode_g, int val) +{ + u16 crtc_addr = stdvga_get_crtc(); + val = val * stdvga_vram_ratio(vmode_g) / 4; + stdvga_crtc_write(crtc_addr, 0x0d, val); + stdvga_crtc_write(crtc_addr, 0x0c, val >> 8); + stdvga_crtc_mask(crtc_addr, 0x1d, 0x80, (val & 0x0800) >> 4); + stdvga_crtc_mask(crtc_addr, 0x1b, 0x0d + , ((val & 0x0100) >> 8) | ((val & 0x0600) >> 7)); + return 0; +} + +int +clext_save_restore(int cmd, u16 seg, void *data) +{ + if (cmd & SR_REGISTERS) + return -1; + return stdvga_save_restore(cmd, seg, data); +} + + +/**************************************************************** + * Mode setting + ****************************************************************/ + +static void +cirrus_switch_mode_setregs(u16 *data, u16 port) +{ + for (;;) { + u16 val = GET_GLOBAL(*data); + if (val == 0xffff) + return; + outw(val, port); + data++; + } +} + +static void +cirrus_switch_mode(struct cirrus_mode_s *table) +{ + // Unlock cirrus special + stdvga_sequ_write(0x06, 0x12); + cirrus_switch_mode_setregs(GET_GLOBAL(table->seq), VGAREG_SEQU_ADDRESS); + cirrus_switch_mode_setregs(GET_GLOBAL(table->graph), VGAREG_GRDC_ADDRESS); + cirrus_switch_mode_setregs(GET_GLOBAL(table->crtc), stdvga_get_crtc()); + + stdvga_pelmask_write(0x00); + stdvga_pelmask_read(); + stdvga_pelmask_read(); + stdvga_pelmask_read(); + stdvga_pelmask_read(); + stdvga_pelmask_write(GET_GLOBAL(table->hidden_dac)); + stdvga_pelmask_write(0xff); + + u8 memmodel = GET_GLOBAL(table->info.memmodel); + u8 on = 0; + if (memmodel == MM_PLANAR) + on = 0x41; + else if (memmodel != MM_TEXT) + on = 0x01; + stdvga_attr_mask(0x10, 0x01, on); + stdvga_attrindex_write(0x20); +} + +static void +cirrus_enable_16k_granularity(void) +{ + stdvga_grdc_mask(0x0b, 0x00, 0x20); +} + +static void +cirrus_clear_vram(u16 fill) +{ + cirrus_enable_16k_granularity(); + int count = GET_GLOBAL(VBE_total_memory) / (16 * 1024); + int i; + for (i=0; i<count; i++) { + stdvga_grdc_write(0x09, i); + memset16_far(SEG_GRAPH, 0, fill, 16 * 1024); + } + stdvga_grdc_write(0x09, 0x00); +} + +int +clext_set_mode(struct vgamode_s *vmode_g, int flags) +{ + if (!is_cirrus_mode(vmode_g)) { + cirrus_switch_mode(&mode_switchback); + dprintf(1, "cirrus mode switch regular\n"); + return stdvga_set_mode(vmode_g, flags); + } + struct cirrus_mode_s *table_g = container_of( + vmode_g, struct cirrus_mode_s, info); + cirrus_switch_mode(table_g); + if (GET_GLOBAL(vmode_g->memmodel) == MM_PACKED && !(flags & MF_NOPALETTE)) + stdvga_set_packed_palette(); + if (!(flags & MF_LINEARFB)) + cirrus_enable_16k_granularity(); + if (!(flags & MF_NOCLEARMEM)) + // fill with 0xff to keep win 2K happy + cirrus_clear_vram(flags & MF_LEGACY ? 0xffff : 0x0000); + return 0; +} + + +/**************************************************************** + * extbios + ****************************************************************/ + +static void +clext_101280(struct bregs *regs) +{ + u8 v = stdvga_crtc_read(stdvga_get_crtc(), 0x27); + if (v == 0xa0) + // 5430 + regs->ax = 0x0032; + else if (v == 0xb8) + // 5446 + regs->ax = 0x0039; + else + regs->ax = 0x00ff; + regs->bx = 0x00; + return; +} + +static void +clext_101281(struct bregs *regs) +{ + // XXX + regs->ax = 0x0100; +} + +static void +clext_101282(struct bregs *regs) +{ + regs->al = stdvga_crtc_read(stdvga_get_crtc(), 0x27) & 0x03; + regs->ah = 0xAF; +} + +static void +clext_101285(struct bregs *regs) +{ + regs->al = GET_GLOBAL(VBE_total_memory) / (64*1024); +} + +static void +clext_10129a(struct bregs *regs) +{ + regs->ax = 0x4060; + regs->cx = 0x1132; +} + +extern void a0h_callback(void); +ASM16( + // fatal: not implemented yet + "a0h_callback:" + "cli\n" + "hlt\n" + "lretw"); + +static void +clext_1012a0(struct bregs *regs) +{ + struct vgamode_s *table_g = clext_find_mode(regs->al & 0x7f); + regs->ah = (table_g ? 1 : 0); + regs->bx = (u32)a0h_callback; + regs->ds = regs->si = regs->es = regs->di = 0xffff; +} + +static void +clext_1012a1(struct bregs *regs) +{ + regs->bx = 0x0e00; // IBM 8512/8513, color +} + +static void +clext_1012a2(struct bregs *regs) +{ + regs->al = 0x07; // HSync 31.5 - 64.0 kHz +} + +static void +clext_1012ae(struct bregs *regs) +{ + regs->al = 0x01; // High Refresh 75Hz +} + +static void +clext_1012XX(struct bregs *regs) +{ + debug_stub(regs); +} + +void +clext_1012(struct bregs *regs) +{ + switch (regs->bl) { + case 0x80: clext_101280(regs); break; + case 0x81: clext_101281(regs); break; + case 0x82: clext_101282(regs); break; + case 0x85: clext_101285(regs); break; + case 0x9a: clext_10129a(regs); break; + case 0xa0: clext_1012a0(regs); break; + case 0xa1: clext_1012a1(regs); break; + case 0xa2: clext_1012a2(regs); break; + case 0xae: clext_1012ae(regs); break; + default: clext_1012XX(regs); break; + } +} + + +/**************************************************************** + * init + ****************************************************************/ + +static int +cirrus_check(void) +{ + stdvga_sequ_write(0x06, 0x92); + return stdvga_sequ_read(0x06) == 0x12; +} + +static u8 +cirrus_get_memsize(void) +{ + // get DRAM band width + u8 v = stdvga_sequ_read(0x0f); + u8 x = (v >> 3) & 0x03; + if (x == 0x03 && v & 0x80) + // 4MB + return 0x40; + return 0x04 << x; +} + +int +clext_setup(void) +{ + int ret = stdvga_setup(); + if (ret) + return ret; + + dprintf(1, "cirrus init\n"); + if (! cirrus_check()) + return -1; + dprintf(1, "cirrus init 2\n"); + + // memory setup + stdvga_sequ_write(0x0a, stdvga_sequ_read(0x0f) & 0x18); + // set vga mode + stdvga_sequ_write(0x07, 0x00); + // reset bitblt + stdvga_grdc_write(0x31, 0x04); + stdvga_grdc_write(0x31, 0x00); + + if (GET_GLOBAL(HaveRunInit)) + return 0; + + u32 lfb_addr = 0; + int bdf = GET_GLOBAL(VgaBDF); + if (CONFIG_VGA_PCI && bdf >= 0) + lfb_addr = (pci_config_readl(bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK); + SET_VGA(VBE_framebuffer, lfb_addr); + u16 totalmem = cirrus_get_memsize(); + SET_VGA(VBE_total_memory, totalmem * 64 * 1024); + SET_VGA(VBE_win_granularity, 16); + + return 0; +} diff --git a/qemu/roms/seabios/vgasrc/clext.h b/qemu/roms/seabios/vgasrc/clext.h new file mode 100644 index 000000000..cf47a5bf6 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/clext.h @@ -0,0 +1,20 @@ +#ifndef __CLEXT_H +#define __CLEXT_H + +#include "types.h" // u16 + +struct vgamode_s *clext_find_mode(int mode); +void clext_list_modes(u16 seg, u16 *dest, u16 *last); +int clext_get_window(struct vgamode_s *vmode_g, int window); +int clext_set_window(struct vgamode_s *vmode_g, int window, int val); +int clext_get_linelength(struct vgamode_s *vmode_g); +int clext_set_linelength(struct vgamode_s *vmode_g, int val); +int clext_get_displaystart(struct vgamode_s *vmode_g); +int clext_set_displaystart(struct vgamode_s *vmode_g, int val); +int clext_save_restore(int cmd, u16 seg, void *data); +int clext_set_mode(struct vgamode_s *vmode_g, int flags); +struct bregs; +void clext_1012(struct bregs *regs); +int clext_setup(void); + +#endif // clext.h diff --git a/qemu/roms/seabios/vgasrc/geodevga.c b/qemu/roms/seabios/vgasrc/geodevga.c new file mode 100644 index 000000000..f8f61c3a1 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/geodevga.c @@ -0,0 +1,433 @@ +// Geode GX2/LX VGA functions +// +// Copyright (C) 2009 Chris Kindt +// +// Written for Google Summer of Code 2009 for the coreboot project +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "farptr.h" // SET_FARVAR +#include "geodevga.h" // geodevga_setup +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "output.h" // dprintf +#include "stdvga.h" // stdvga_crtc_write +#include "vgabios.h" // VGAREG_* + + +/**************************************************************** +* MSR and High Mem access through VSA Virtual Register +****************************************************************/ + +static u64 geode_msr_read(u32 msrAddr) +{ + union u64_u32_u val; + asm __volatile__ ( + "movw $0x0AC1C, %%dx \n" + "movl $0xFC530007, %%eax \n" + "outl %%eax, %%dx \n" + "addb $2, %%dl \n" + "inw %%dx, %%ax \n" + : "=a" (val.lo), "=d"(val.hi) + : "c"(msrAddr) + : "cc" + ); + + dprintf(4, "%s(0x%08x) = 0x%08x-0x%08x\n" + , __func__, msrAddr, val.hi, val.lo); + return val.val; +} + +static void geode_msr_mask(u32 msrAddr, u64 off, u64 on) +{ + union u64_u32_u uand, uor; + uand.val = ~off; + uor.val = on; + + dprintf(4, "%s(0x%08x, 0x%016llx, 0x%016llx)\n" + , __func__, msrAddr, off, on); + + asm __volatile__ ( + "push %%eax \n" + "movw $0x0AC1C, %%dx \n" + "movl $0xFC530007, %%eax \n" + "outl %%eax, %%dx \n" + "addb $2, %%dl \n" + "pop %%eax \n" + "outw %%ax, %%dx \n" + : + : "c"(msrAddr), "S" (uand.hi), "D" (uand.lo), "b" (uor.hi), "a" (uor.lo) + : "%edx","cc" + ); +} + +static u32 geode_mem_read(u32 addr) +{ + u32 val; + asm __volatile__ ( + "movw $0x0AC1C, %%dx \n" + "movl $0xFC530001, %%eax \n" + "outl %%eax, %%dx \n" + "addb $2, %%dl \n" + "inw %%dx, %%ax \n" + : "=a" (val) + : "b"(addr) + : "cc" + ); + + return val; +} + +static void geode_mem_mask(u32 addr, u32 off, u32 or) +{ + asm __volatile__ ( + "movw $0x0AC1C, %%dx \n" + "movl $0xFC530001, %%eax \n" + "outl %%eax, %%dx \n" + "addb $2, %%dl \n" + "outw %%ax, %%dx \n" + : + : "b"(addr), "S" (~off), "D" (or) + : "%eax","cc" + ); +} + +#define VP_FP_START 0x400 + +static u32 GeodeFB VAR16; +static u32 GeodeDC VAR16; +static u32 GeodeVP VAR16; + +static u32 geode_dc_read(int reg) +{ + u32 val = geode_mem_read(GET_GLOBAL(GeodeDC) + reg); + dprintf(4, "%s(0x%08x) = 0x%08x\n" + , __func__, GET_GLOBAL(GeodeDC) + reg, val); + return val; +} + +static void geode_dc_write(int reg, u32 val) +{ + dprintf(4, "%s(0x%08x, 0x%08x)\n" + , __func__, GET_GLOBAL(GeodeDC) + reg, val); + geode_mem_mask(GET_GLOBAL(GeodeDC) + reg, ~0, val); +} + +static void geode_dc_mask(int reg, u32 off, u32 on) +{ + dprintf(4, "%s(0x%08x, 0x%08x, 0x%08x)\n" + , __func__, GET_GLOBAL(GeodeDC) + reg, off, on); + geode_mem_mask(GET_GLOBAL(GeodeDC) + reg, off, on); +} + +static u32 geode_vp_read(int reg) +{ + u32 val = geode_mem_read(GET_GLOBAL(GeodeVP) + reg); + dprintf(4, "%s(0x%08x) = 0x%08x\n" + , __func__, GET_GLOBAL(GeodeVP) + reg, val); + return val; +} + +static void geode_vp_write(int reg, u32 val) +{ + dprintf(4, "%s(0x%08x, 0x%08x)\n" + , __func__, GET_GLOBAL(GeodeVP) + reg, val); + geode_mem_mask(GET_GLOBAL(GeodeVP) + reg, ~0, val); +} + +static void geode_vp_mask(int reg, u32 off, u32 on) +{ + dprintf(4, "%s(0x%08x, 0x%08x, 0x%08x)\n" + , __func__, GET_GLOBAL(GeodeVP) + reg, off, on); + geode_mem_mask(GET_GLOBAL(GeodeVP) + reg, off, on); +} + +static u32 geode_fp_read(int reg) +{ + u32 val = geode_mem_read(GET_GLOBAL(GeodeVP) + VP_FP_START + reg); + dprintf(4, "%s(0x%08x) = 0x%08x\n" + , __func__, GET_GLOBAL(GeodeVP) + VP_FP_START + reg, val); + return val; +} + +static void geode_fp_write(int reg, u32 val) +{ + dprintf(4, "%s(0x%08x, 0x%08x)\n" + , __func__, GET_GLOBAL(GeodeVP) + VP_FP_START + reg, val); + geode_mem_mask(GET_GLOBAL(GeodeVP) + VP_FP_START + reg, ~0, val); +} + +/**************************************************************** + * Helper functions + ****************************************************************/ + +static int legacyio_check(void) +{ + int ret=0; + u64 val; + + if (CONFIG_VGA_GEODEGX2) + val = geode_msr_read(GLIU0_P2D_BM_4); + else + val = geode_msr_read(MSR_GLIU0_BASE4); + if ((val & 0xffffffff) != 0x0A0fffe0) + ret|=1; + + val = geode_msr_read(GLIU0_IOD_BM_0); + if ((val & 0xffffffff) != 0x3c0ffff0) + ret|=2; + + val = geode_msr_read(GLIU0_IOD_BM_1); + if ((val & 0xffffffff) != 0x3d0ffff0) + ret|=4; + + return ret; +} + +static u32 framebuffer_size(void) +{ + /* We use the P2D_R0 msr to read out the number of pages. + * One page has a size of 4k + * + * Bit Name Description + * 39:20 PMAX Physical Memory Address Max + * 19:0 PMIX Physical Memory Address Min + * + */ + u64 msr = geode_msr_read(GLIU0_P2D_RO); + + u32 pmax = (msr >> 20) & 0x000fffff; + u32 pmin = msr & 0x000fffff; + + u32 val = pmax - pmin; + val += 1; + + /* The page size is 4k */ + return (val << 12); +} + +/**************************************************************** +* Init Functions +****************************************************************/ + +static void geodevga_set_output_mode(void) +{ + u64 msr_addr; + u64 msr; + + /* set output to crt and RGB/YUV */ + if (CONFIG_VGA_GEODEGX2) + msr_addr = VP_MSR_CONFIG_GX2; + else + msr_addr = VP_MSR_CONFIG_LX; + + /* set output mode (RGB/YUV) */ + msr = geode_msr_read(msr_addr); + msr &= ~VP_MSR_CONFIG_FMT; // mask out FMT (bits 5:3) + + if (CONFIG_VGA_OUTPUT_PANEL || CONFIG_VGA_OUTPUT_CRT_PANEL) { + msr |= VP_MSR_CONFIG_FMT_FP; // flat panel + + if (CONFIG_VGA_OUTPUT_CRT_PANEL) { + msr |= VP_MSR_CONFIG_FPC; // simultaneous Flat Panel and CRT + dprintf(1, "output: simultaneous Flat Panel and CRT\n"); + } else { + msr &= ~VP_MSR_CONFIG_FPC; // no simultaneous Flat Panel and CRT + dprintf(1, "ouput: flat panel\n"); + } + } else { + msr |= VP_MSR_CONFIG_FMT_CRT; // CRT only + dprintf(1, "output: CRT\n"); + } + geode_msr_mask(msr_addr, ~msr, msr); +} + +/* Set up the dc (display controller) portion of the geodelx +* The dc provides hardware support for VGA graphics. +*/ +static void dc_setup(void) +{ + dprintf(2, "DC_SETUP\n"); + + geode_dc_write(DC_UNLOCK, DC_LOCK_UNLOCK); + + /* zero memory config */ + geode_dc_write(DC_FB_ST_OFFSET, 0x0); + geode_dc_write(DC_CB_ST_OFFSET, 0x0); + geode_dc_write(DC_CURS_ST_OFFSET, 0x0); + + geode_dc_mask(DC_DISPLAY_CFG, ~DC_CFG_MSK, DC_DISPLAY_CFG_GDEN|DC_DISPLAY_CFG_TRUP); + geode_dc_write(DC_GENERAL_CFG, DC_GENERAL_CFG_VGAE); + + geode_dc_write(DC_UNLOCK, DC_LOCK_LOCK); +} + +/* Setup the vp (video processor) portion of the geodelx +* Under VGA modes the vp was handled by softvg from inside VSA2. +* Without a softvg module, access is only available through a pci bar. +* The High Mem Access virtual register is used to configure the +* pci mmio bar from 16bit friendly io space. +*/ +static void vp_setup(void) +{ + dprintf(2,"VP_SETUP\n"); + + geodevga_set_output_mode(); + + /* Set mmio registers + * there may be some timing issues here, the reads seem + * to slow things down enough work reliably + */ + + u32 reg = geode_vp_read(VP_MISC); + dprintf(1,"VP_SETUP VP_MISC=0x%08x\n",reg); + geode_vp_write(VP_MISC, VP_DCFG_BYP_BOTH); + reg = geode_vp_read(VP_MISC); + dprintf(1,"VP_SETUP VP_MISC=0x%08x\n",reg); + + reg = geode_vp_read(VP_DCFG); + dprintf(1,"VP_SETUP VP_DCFG=0x%08x\n",reg); + geode_vp_mask(VP_DCFG, 0, VP_DCFG_CRT_EN|VP_DCFG_HSYNC_EN|VP_DCFG_VSYNC_EN|VP_DCFG_DAC_BL_EN|VP_DCFG_CRT_SKEW); + reg = geode_vp_read(VP_DCFG); + dprintf(1,"VP_SETUP VP_DCFG=0x%08x\n",reg); + + /* setup flat panel */ + if (CONFIG_VGA_OUTPUT_PANEL || CONFIG_VGA_OUTPUT_CRT_PANEL) { + u64 msr; + + dprintf(1, "Setting up flat panel\n"); + /* write timing register */ + geode_fp_write(FP_PT1, 0x0); + geode_fp_write(FP_PT2, FP_PT2_SCRC); + + /* set pad select for TFT/LVDS */ + msr = VP_MSR_PADSEL_TFT_SEL_HIGH; + msr = msr << 32; + msr |= VP_MSR_PADSEL_TFT_SEL_LOW; + geode_msr_mask(VP_MSR_PADSEL, ~msr, msr); + + /* turn the panel on (if it isn't already) */ + reg = geode_fp_read(FP_PM); + reg |= FP_PM_P; + geode_fp_write(FP_PM, reg); + } +} + +static u8 geode_crtc_01[] VAR16 = { + 0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x14, 0x1f, 0x97, 0xb9, 0xa3, + 0xff }; +static u8 geode_crtc_03[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x28, 0x1f, 0x97, 0xb9, 0xa3, + 0xff }; +static u8 geode_crtc_04[] VAR16 = { + 0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x14, 0x00, 0x97, 0xb9, 0xa2, + 0xff }; +static u8 geode_crtc_05[] VAR16 = { + 0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8e, 0x8f, 0x14, 0x00, 0x97, 0xb9, 0xa2, + 0xff }; +static u8 geode_crtc_06[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x28, 0x00, 0x97, 0xb9, 0xc2, + 0xff }; +static u8 geode_crtc_07[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x28, 0x0f, 0x97, 0xb9, 0xa3, + 0xff }; +static u8 geode_crtc_0d[] VAR16 = { + 0x2d, 0x27, 0x28, 0x90, 0x29, 0x8e, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x14, 0x00, 0x97, 0xb9, 0xe3, + 0xff }; +static u8 geode_crtc_0e[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x28, 0x00, 0x97, 0xb9, 0xe3, + 0xff }; +static u8 geode_crtc_0f[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x65, 0xb9, 0xe3, + 0xff }; +static u8 geode_crtc_11[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0x0b, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe9, 0x8b, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, + 0xff }; +static u8 geode_crtc_13[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x51, 0x9e, 0xbf, 0x1f, + 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x8d, 0x8f, 0x28, 0x40, 0x98, 0xb9, 0xa3, + 0xff }; + +int geodevga_setup(void) +{ + int ret = stdvga_setup(); + if (ret) + return ret; + + dprintf(1,"GEODEVGA_SETUP\n"); + + if ((ret=legacyio_check())) { + dprintf(1,"GEODEVGA_SETUP legacyio_check=0x%x\n",ret); + } + + // Updated timings from geode datasheets, table 6-53 in particular + static u8 *new_crtc[] VAR16 = { + geode_crtc_01, geode_crtc_01, geode_crtc_03, geode_crtc_03, + geode_crtc_04, geode_crtc_05, geode_crtc_06, geode_crtc_07, + 0, 0, 0, 0, 0, + geode_crtc_0d, geode_crtc_0e, geode_crtc_0f, geode_crtc_0f, + geode_crtc_11, geode_crtc_11, geode_crtc_13 }; + int i; + for (i=0; i<ARRAY_SIZE(new_crtc); i++) { + u8 *crtc = GET_GLOBAL(new_crtc[i]); + if (crtc) + stdvga_override_crtc(i, crtc); + } + + if (GET_GLOBAL(VgaBDF) < 0) + // Device should be at 00:01.1 + SET_VGA(VgaBDF, pci_to_bdf(0, 1, 1)); + + // setup geode struct which is used for register access + SET_VGA(GeodeFB, pci_config_readl(GET_GLOBAL(VgaBDF), PCI_BASE_ADDRESS_0)); + SET_VGA(GeodeDC, pci_config_readl(GET_GLOBAL(VgaBDF), PCI_BASE_ADDRESS_2)); + SET_VGA(GeodeVP, pci_config_readl(GET_GLOBAL(VgaBDF), PCI_BASE_ADDRESS_3)); + + dprintf(1, "fb addr: 0x%08x\n", GET_GLOBAL(GeodeFB)); + dprintf(1, "dc addr: 0x%08x\n", GET_GLOBAL(GeodeDC)); + dprintf(1, "vp addr: 0x%08x\n", GET_GLOBAL(GeodeVP)); + + /* setup framebuffer */ + geode_dc_write(DC_UNLOCK, DC_LOCK_UNLOCK); + + /* read fb-bar from pci, then point dc to the fb base */ + u32 fb = GET_GLOBAL(GeodeFB); + if (geode_dc_read(DC_GLIU0_MEM_OFFSET) != fb) + geode_dc_write(DC_GLIU0_MEM_OFFSET, fb); + + geode_dc_write(DC_UNLOCK, DC_LOCK_LOCK); + + u32 fb_size = framebuffer_size(); // in byte + dprintf(1, "%d KB of video memory at 0x%08x\n", fb_size / 1024, fb); + + /* update VBE variables */ + SET_VGA(VBE_framebuffer, fb); + SET_VGA(VBE_total_memory, fb_size / 1024 / 64); // number of 64K blocks + + vp_setup(); + dc_setup(); + + return 0; +} diff --git a/qemu/roms/seabios/vgasrc/geodevga.h b/qemu/roms/seabios/vgasrc/geodevga.h new file mode 100644 index 000000000..61d78084d --- /dev/null +++ b/qemu/roms/seabios/vgasrc/geodevga.h @@ -0,0 +1,89 @@ +// Geode GX2/LX VGA functions +// +// Copyright (C) 2009 Chris Kindt +// +// Writen for Google Summer of Code 2009 for the coreboot project +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#ifndef GEODEVGA_H +#define GEODEVGA_H + +#define VRC_INDEX 0xAC1C // Index register +#define VRC_DATA 0xAC1E // Data register +#define VR_UNLOCK 0xFC53 // Virtual register unlock code + +// Graphics-specific registers: +#define OEM_BAR0 0x50 +#define OEM_BAR1 0x54 +#define OEM_BAR2 0x58 +#define OEM_BAR3 0x5C + +#define DC_LOCK_LOCK 0x00000000 +#define DC_LOCK_UNLOCK 0x00004758 + +/* LX MSRs */ +#define MSR_GLIU0 (1 << 28) +#define MSR_GLIU0_BASE4 (MSR_GLIU0 + 0x23) /* LX */ +#define GLIU0_P2D_BM_4 (MSR_GLIU0 + 0x24) /* GX2 */ +#define GLIU0_P2D_RO (MSR_GLIU0 + 0x29) +#define GLIU0_IOD_BM_0 (MSR_GLIU0 + 0xE0) +#define GLIU0_IOD_BM_1 (MSR_GLIU0 + 0xE1) +#define DC_SPARE 0x80000011 +#define VP_MSR_CONFIG_GX2 0xc0002001 /* GX2 */ +#define VP_MSR_CONFIG_LX 0x48002001 /* LX */ +#define VP_MSR_PADSEL 0x48002011 + +#define VP_MSR_PADSEL_TFT_SEL_LOW 0xDFFFFFFF +#define VP_MSR_PADSEL_TFT_SEL_HIGH 0x0000003F + +/* VP_MSR_CONFIG bits */ +#define VP_MSR_CONFIG_FMT_CRT (0) +#define VP_MSR_CONFIG_FMT_FP (1 << 3) +#define VP_MSR_CONFIG_FPC (1 << 15) +#define VP_MSR_CONFIG_FMT ((1 << 3) | (1 << 4) | (1 << 5)) + + +/* DC REG OFFSET */ +#define DC_UNLOCK 0x0 +#define DC_GENERAL_CFG 0x4 +#define DC_DISPLAY_CFG 0x8 +#define DC_FB_ST_OFFSET 0x10 +#define DC_CB_ST_OFFSET 0x14 +#define DC_CURS_ST_OFFSET 0x18 +#define DC_GLIU0_MEM_OFFSET 0x84 + +/* VP REG OFFSET */ +#define VP_VCFG 0x0 +#define VP_DCFG 0x8 +#define VP_MISC 0x50 + +/* FP REG OFFSET */ +#define FP_PT1 0x00 +#define FP_PT2 0x08 +#define FP_PM 0x10 + + +/* DC bits */ +#define DC_GENERAL_CFG_VGAE (1 << 7) +#define DC_DISPLAY_CFG_GDEN (1 << 3) +#define DC_DISPLAY_CFG_TRUP (1 << 6) + +/* VP bits */ +#define VP_DCFG_CRT_EN (1 << 0) +#define VP_DCFG_HSYNC_EN (1 << 1) +#define VP_DCFG_VSYNC_EN (1 << 2) +#define VP_DCFG_DAC_BL_EN (1 << 3) +#define VP_DCFG_CRT_SKEW (1 << 16) +#define VP_DCFG_BYP_BOTH (1 << 0) + +/* FP bits */ +#define FP_PM_P (1 << 24) /* panel power ctl */ +#define FP_PT2_SCRC (1 << 27) /* panel shift clock retrace activity ctl */ + +/* Mask */ +#define DC_CFG_MSK 0xf000a6 + +int geodevga_setup(); + +#endif diff --git a/qemu/roms/seabios/vgasrc/stdvga.c b/qemu/roms/seabios/vgasrc/stdvga.c new file mode 100644 index 000000000..00a0fc57c --- /dev/null +++ b/qemu/roms/seabios/vgasrc/stdvga.c @@ -0,0 +1,479 @@ +// Standard VGA driver code +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "farptr.h" // SET_FARVAR +#include "stdvga.h" // stdvga_setup +#include "string.h" // memset_far +#include "vgabios.h" // struct vgamode_s +#include "x86.h" // outb + + +/**************************************************************** + * Attribute control + ****************************************************************/ + +void +stdvga_set_border_color(u8 color) +{ + u8 v1 = color & 0x0f; + if (v1 & 0x08) + v1 += 0x08; + stdvga_attr_write(0x00, v1); + + int i; + for (i = 1; i < 4; i++) + stdvga_attr_mask(i, 0x10, color & 0x10); +} + +void +stdvga_set_overscan_border_color(u8 color) +{ + stdvga_attr_write(0x11, color); +} + +u8 +stdvga_get_overscan_border_color(void) +{ + return stdvga_attr_read(0x11); +} + +void +stdvga_set_palette(u8 palid) +{ + int i; + for (i = 1; i < 4; i++) + stdvga_attr_mask(i, 0x01, palid & 0x01); +} + +void +stdvga_set_all_palette_reg(u16 seg, u8 *data_far) +{ + int i; + for (i = 0; i < 0x10; i++) { + stdvga_attr_write(i, GET_FARVAR(seg, *data_far)); + data_far++; + } + stdvga_attr_write(0x11, GET_FARVAR(seg, *data_far)); +} + +void +stdvga_get_all_palette_reg(u16 seg, u8 *data_far) +{ + int i; + for (i = 0; i < 0x10; i++) { + SET_FARVAR(seg, *data_far, stdvga_attr_read(i)); + data_far++; + } + SET_FARVAR(seg, *data_far, stdvga_attr_read(0x11)); +} + +void +stdvga_toggle_intensity(u8 flag) +{ + stdvga_attr_mask(0x10, 0x08, (flag & 0x01) << 3); +} + +void +stdvga_select_video_dac_color_page(u8 flag, u8 data) +{ + if (!(flag & 0x01)) { + // select paging mode + stdvga_attr_mask(0x10, 0x80, data << 7); + return; + } + // select page + u8 val = stdvga_attr_read(0x10); + if (!(val & 0x80)) + data <<= 2; + data &= 0x0f; + stdvga_attr_write(0x14, data); +} + +void +stdvga_read_video_dac_state(u8 *pmode, u8 *curpage) +{ + u8 val1 = stdvga_attr_read(0x10) >> 7; + u8 val2 = stdvga_attr_read(0x14) & 0x0f; + if (!(val1 & 0x01)) + val2 >>= 2; + *pmode = val1; + *curpage = val2; +} + + +/**************************************************************** + * DAC control + ****************************************************************/ + +void +stdvga_perform_gray_scale_summing(u16 start, u16 count) +{ + stdvga_attrindex_write(0x00); + int i; + for (i = start; i < start+count; i++) { + u8 rgb[3]; + stdvga_dac_read(GET_SEG(SS), rgb, i, 1); + + // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue ) + u16 intensity = ((77 * rgb[0] + 151 * rgb[1] + 28 * rgb[2]) + 0x80) >> 8; + if (intensity > 0x3f) + intensity = 0x3f; + rgb[0] = rgb[1] = rgb[2] = intensity; + + stdvga_dac_write(GET_SEG(SS), rgb, i, 1); + } + stdvga_attrindex_write(0x20); +} + + +/**************************************************************** + * Memory control + ****************************************************************/ + +void +stdvga_set_text_block_specifier(u8 spec) +{ + stdvga_sequ_write(0x03, spec); +} + +// Enable reads and writes to the given "plane" when in planar4 mode. +void +stdvga_planar4_plane(int plane) +{ + if (plane < 0) { + // Return to default mode (read plane0, write all planes) + stdvga_sequ_write(0x02, 0x0f); + stdvga_grdc_write(0x04, 0); + } else { + stdvga_sequ_write(0x02, 1<<plane); + stdvga_grdc_write(0x04, plane); + } +} + + +/**************************************************************** + * Font loading + ****************************************************************/ + +static void +get_font_access(void) +{ + stdvga_sequ_write(0x00, 0x01); + stdvga_sequ_write(0x02, 0x04); + stdvga_sequ_write(0x04, 0x07); + stdvga_sequ_write(0x00, 0x03); + stdvga_grdc_write(0x04, 0x02); + stdvga_grdc_write(0x05, 0x00); + stdvga_grdc_write(0x06, 0x04); +} + +static void +release_font_access(void) +{ + stdvga_sequ_write(0x00, 0x01); + stdvga_sequ_write(0x02, 0x03); + stdvga_sequ_write(0x04, 0x03); + stdvga_sequ_write(0x00, 0x03); + u16 v = (stdvga_misc_read() & 0x01) ? 0x0e : 0x0a; + stdvga_grdc_write(0x06, v); + stdvga_grdc_write(0x04, 0x00); + stdvga_grdc_write(0x05, 0x10); +} + +void +stdvga_load_font(u16 seg, void *src_far, u16 count + , u16 start, u8 destflags, u8 fontsize) +{ + get_font_access(); + u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11); + void *dest_far = (void*)(blockaddr + start*32); + u16 i; + for (i = 0; i < count; i++) + memcpy_far(SEG_GRAPH, dest_far + i*32 + , seg, src_far + i*fontsize, fontsize); + release_font_access(); +} + + +/**************************************************************** + * CRTC registers + ****************************************************************/ + +u16 +stdvga_get_crtc(void) +{ + if (stdvga_misc_read() & 1) + return VGAREG_VGA_CRTC_ADDRESS; + return VGAREG_MDA_CRTC_ADDRESS; +} + +// Ratio between system visible framebuffer ram and the actual videoram used. +int +stdvga_vram_ratio(struct vgamode_s *vmode_g) +{ + switch (GET_GLOBAL(vmode_g->memmodel)) { + case MM_TEXT: + return 2; + case MM_CGA: + return 4 / GET_GLOBAL(vmode_g->depth); + case MM_PLANAR: + return 4; + default: + return 1; + } +} + +void +stdvga_set_cursor_shape(u16 cursor_type) +{ + u16 crtc_addr = stdvga_get_crtc(); + stdvga_crtc_write(crtc_addr, 0x0a, cursor_type >> 8); + stdvga_crtc_write(crtc_addr, 0x0b, cursor_type); +} + +void +stdvga_set_cursor_pos(int address) +{ + u16 crtc_addr = stdvga_get_crtc(); + address /= 2; // Assume we're in text mode. + stdvga_crtc_write(crtc_addr, 0x0e, address >> 8); + stdvga_crtc_write(crtc_addr, 0x0f, address); +} + +void +stdvga_set_scan_lines(u8 lines) +{ + stdvga_crtc_mask(stdvga_get_crtc(), 0x09, 0x1f, lines - 1); +} + +// Get vertical display end +u16 +stdvga_get_vde(void) +{ + u16 crtc_addr = stdvga_get_crtc(); + u16 vde = stdvga_crtc_read(crtc_addr, 0x12); + u8 ovl = stdvga_crtc_read(crtc_addr, 0x07); + vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1); + return vde; +} + +int +stdvga_get_window(struct vgamode_s *vmode_g, int window) +{ + return -1; +} + +int +stdvga_set_window(struct vgamode_s *vmode_g, int window, int val) +{ + return -1; +} + +int +stdvga_get_linelength(struct vgamode_s *vmode_g) +{ + u8 val = stdvga_crtc_read(stdvga_get_crtc(), 0x13); + return val * 8 / stdvga_vram_ratio(vmode_g); +} + +int +stdvga_set_linelength(struct vgamode_s *vmode_g, int val) +{ + val = DIV_ROUND_UP(val * stdvga_vram_ratio(vmode_g), 8); + stdvga_crtc_write(stdvga_get_crtc(), 0x13, val); + return 0; +} + +int +stdvga_get_displaystart(struct vgamode_s *vmode_g) +{ + u16 crtc_addr = stdvga_get_crtc(); + int addr = (stdvga_crtc_read(crtc_addr, 0x0c) << 8 + | stdvga_crtc_read(crtc_addr, 0x0d)); + return addr * 4 / stdvga_vram_ratio(vmode_g); +} + +int +stdvga_set_displaystart(struct vgamode_s *vmode_g, int val) +{ + u16 crtc_addr = stdvga_get_crtc(); + val = val * stdvga_vram_ratio(vmode_g) / 4; + stdvga_crtc_write(crtc_addr, 0x0c, val >> 8); + stdvga_crtc_write(crtc_addr, 0x0d, val); + return 0; +} + +int +stdvga_get_dacformat(struct vgamode_s *vmode_g) +{ + return -1; +} + +int +stdvga_set_dacformat(struct vgamode_s *vmode_g, int val) +{ + return -1; +} + + +/**************************************************************** + * Save/Restore state + ****************************************************************/ + +struct saveVideoHardware { + u8 sequ_index; + u8 crtc_index; + u8 grdc_index; + u8 actl_index; + u8 feature; + u8 sequ_regs[4]; + u8 sequ0; + u8 crtc_regs[25]; + u8 actl_regs[20]; + u8 grdc_regs[9]; + u16 crtc_addr; + u8 plane_latch[4]; +} PACKED; + +static void +stdvga_save_hw_state(u16 seg, struct saveVideoHardware *info) +{ + u16 crtc_addr = stdvga_get_crtc(); + SET_FARVAR(seg, info->sequ_index, inb(VGAREG_SEQU_ADDRESS)); + SET_FARVAR(seg, info->crtc_index, inb(crtc_addr)); + SET_FARVAR(seg, info->grdc_index, inb(VGAREG_GRDC_ADDRESS)); + SET_FARVAR(seg, info->actl_index, stdvga_attrindex_read()); + SET_FARVAR(seg, info->feature, inb(VGAREG_READ_FEATURE_CTL)); + + int i; + for (i=0; i<4; i++) + SET_FARVAR(seg, info->sequ_regs[i], stdvga_sequ_read(i+1)); + SET_FARVAR(seg, info->sequ0, stdvga_sequ_read(0)); + + for (i=0; i<25; i++) + SET_FARVAR(seg, info->crtc_regs[i], stdvga_crtc_read(crtc_addr, i)); + + for (i=0; i<20; i++) + SET_FARVAR(seg, info->actl_regs[i], stdvga_attr_read(i)); + + for (i=0; i<9; i++) + SET_FARVAR(seg, info->grdc_regs[i], stdvga_grdc_read(i)); + + SET_FARVAR(seg, info->crtc_addr, crtc_addr); + + /* XXX: read plane latches */ + for (i=0; i<4; i++) + SET_FARVAR(seg, info->plane_latch[i], 0); +} + +static void +stdvga_restore_hw_state(u16 seg, struct saveVideoHardware *info) +{ + int i; + for (i=0; i<4; i++) + stdvga_sequ_write(i+1, GET_FARVAR(seg, info->sequ_regs[i])); + stdvga_sequ_write(0x00, GET_FARVAR(seg, info->sequ0)); + + // Disable CRTC write protection + u16 crtc_addr = GET_FARVAR(seg, info->crtc_addr); + stdvga_crtc_write(crtc_addr, 0x11, 0x00); + // Set CRTC regs + for (i=0; i<25; i++) + if (i != 0x11) + stdvga_crtc_write(crtc_addr, i, GET_FARVAR(seg, info->crtc_regs[i])); + // select crtc base address + stdvga_misc_mask(0x01, crtc_addr == VGAREG_VGA_CRTC_ADDRESS ? 0x01 : 0x00); + + // enable write protection if needed + stdvga_crtc_write(crtc_addr, 0x11, GET_FARVAR(seg, info->crtc_regs[0x11])); + + // Set Attribute Ctl + for (i=0; i<20; i++) + stdvga_attr_write(i, GET_FARVAR(seg, info->actl_regs[i])); + stdvga_attrindex_write(GET_FARVAR(seg, info->actl_index)); + + for (i=0; i<9; i++) + stdvga_grdc_write(i, GET_FARVAR(seg, info->grdc_regs[i])); + + outb(GET_FARVAR(seg, info->sequ_index), VGAREG_SEQU_ADDRESS); + outb(GET_FARVAR(seg, info->crtc_index), crtc_addr); + outb(GET_FARVAR(seg, info->grdc_index), VGAREG_GRDC_ADDRESS); + outb(GET_FARVAR(seg, info->feature), crtc_addr - 0x4 + 0xa); +} + +struct saveDACcolors { + u8 rwmode; + u8 peladdr; + u8 pelmask; + u8 dac[768]; + u8 color_select; +} PACKED; + +static void +stdvga_save_dac_state(u16 seg, struct saveDACcolors *info) +{ + /* XXX: check this */ + SET_FARVAR(seg, info->rwmode, inb(VGAREG_DAC_STATE)); + SET_FARVAR(seg, info->peladdr, inb(VGAREG_DAC_WRITE_ADDRESS)); + SET_FARVAR(seg, info->pelmask, stdvga_pelmask_read()); + stdvga_dac_read(seg, info->dac, 0, 256); + SET_FARVAR(seg, info->color_select, 0); +} + +static void +stdvga_restore_dac_state(u16 seg, struct saveDACcolors *info) +{ + stdvga_pelmask_write(GET_FARVAR(seg, info->pelmask)); + stdvga_dac_write(seg, info->dac, 0, 256); + outb(GET_FARVAR(seg, info->peladdr), VGAREG_DAC_WRITE_ADDRESS); +} + +int +stdvga_save_restore(int cmd, u16 seg, void *data) +{ + void *pos = data; + if (cmd & SR_HARDWARE) { + if (cmd & SR_SAVE) + stdvga_save_hw_state(seg, pos); + if (cmd & SR_RESTORE) + stdvga_restore_hw_state(seg, pos); + pos += sizeof(struct saveVideoHardware); + } + pos += bda_save_restore(cmd, seg, pos); + if (cmd & SR_DAC) { + if (cmd & SR_SAVE) + stdvga_save_dac_state(seg, pos); + if (cmd & SR_RESTORE) + stdvga_restore_dac_state(seg, pos); + pos += sizeof(struct saveDACcolors); + } + return pos - data; +} + + +/**************************************************************** + * Misc + ****************************************************************/ + +void +stdvga_enable_video_addressing(u8 disable) +{ + u8 v = (disable & 1) ? 0x00 : 0x02; + stdvga_misc_mask(0x02, v); +} + +int +stdvga_setup(void) +{ + // switch to color mode and enable CPU access 480 lines + stdvga_misc_write(0xc3); + // more than 64k 3C4/04 + stdvga_sequ_write(0x04, 0x02); + + return 0; +} diff --git a/qemu/roms/seabios/vgasrc/stdvga.h b/qemu/roms/seabios/vgasrc/stdvga.h new file mode 100644 index 000000000..39753b4de --- /dev/null +++ b/qemu/roms/seabios/vgasrc/stdvga.h @@ -0,0 +1,111 @@ +#ifndef __STDVGA_H +#define __STDVGA_H + +#include "types.h" // u8 + +// VGA registers +#define VGAREG_ACTL_ADDRESS 0x3c0 +#define VGAREG_ACTL_WRITE_DATA 0x3c0 +#define VGAREG_ACTL_READ_DATA 0x3c1 + +#define VGAREG_INPUT_STATUS 0x3c2 +#define VGAREG_WRITE_MISC_OUTPUT 0x3c2 +#define VGAREG_VIDEO_ENABLE 0x3c3 +#define VGAREG_SEQU_ADDRESS 0x3c4 +#define VGAREG_SEQU_DATA 0x3c5 + +#define VGAREG_PEL_MASK 0x3c6 +#define VGAREG_DAC_STATE 0x3c7 +#define VGAREG_DAC_READ_ADDRESS 0x3c7 +#define VGAREG_DAC_WRITE_ADDRESS 0x3c8 +#define VGAREG_DAC_DATA 0x3c9 + +#define VGAREG_READ_FEATURE_CTL 0x3ca +#define VGAREG_READ_MISC_OUTPUT 0x3cc + +#define VGAREG_GRDC_ADDRESS 0x3ce +#define VGAREG_GRDC_DATA 0x3cf + +#define VGAREG_MDA_CRTC_ADDRESS 0x3b4 +#define VGAREG_MDA_CRTC_DATA 0x3b5 +#define VGAREG_VGA_CRTC_ADDRESS 0x3d4 +#define VGAREG_VGA_CRTC_DATA 0x3d5 + +#define VGAREG_MDA_WRITE_FEATURE_CTL 0x3ba +#define VGAREG_VGA_WRITE_FEATURE_CTL 0x3da +#define VGAREG_ACTL_RESET 0x3da + +#define VGAREG_MDA_MODECTL 0x3b8 +#define VGAREG_CGA_MODECTL 0x3d8 +#define VGAREG_CGA_PALETTE 0x3d9 + +/* Video memory */ +#define SEG_GRAPH 0xA000 +#define SEG_CTEXT 0xB800 +#define SEG_MTEXT 0xB000 + +// stdvgamodes.c +struct vgamode_s *stdvga_find_mode(int mode); +void stdvga_list_modes(u16 seg, u16 *dest, u16 *last); +void stdvga_build_video_param(void); +void stdvga_override_crtc(int mode, u8 *crtc); +int stdvga_set_mode(struct vgamode_s *vmode_g, int flags); +void stdvga_set_packed_palette(void); + +// stdvgaio.c +u8 stdvga_pelmask_read(void); +void stdvga_pelmask_write(u8 val); +u8 stdvga_misc_read(void); +void stdvga_misc_write(u8 value); +void stdvga_misc_mask(u8 off, u8 on); +u8 stdvga_sequ_read(u8 index); +void stdvga_sequ_write(u8 index, u8 value); +void stdvga_sequ_mask(u8 index, u8 off, u8 on); +u8 stdvga_grdc_read(u8 index); +void stdvga_grdc_write(u8 index, u8 value); +void stdvga_grdc_mask(u8 index, u8 off, u8 on); +u8 stdvga_crtc_read(u16 crtc_addr, u8 index); +void stdvga_crtc_write(u16 crtc_addr, u8 index, u8 value); +void stdvga_crtc_mask(u16 crtc_addr, u8 index, u8 off, u8 on); +u8 stdvga_attr_read(u8 index); +void stdvga_attr_write(u8 index, u8 value); +void stdvga_attr_mask(u8 index, u8 off, u8 on); +u8 stdvga_attrindex_read(void); +void stdvga_attrindex_write(u8 value); +void stdvga_dac_read(u16 seg, u8 *data_far, u8 start, int count); +void stdvga_dac_write(u16 seg, u8 *data_far, u8 start, int count); + +// stdvga.c +void stdvga_set_border_color(u8 color); +void stdvga_set_overscan_border_color(u8 color); +u8 stdvga_get_overscan_border_color(void); +void stdvga_set_palette(u8 palid); +void stdvga_set_all_palette_reg(u16 seg, u8 *data_far); +void stdvga_get_all_palette_reg(u16 seg, u8 *data_far); +void stdvga_toggle_intensity(u8 flag); +void stdvga_select_video_dac_color_page(u8 flag, u8 data); +void stdvga_read_video_dac_state(u8 *pmode, u8 *curpage); +void stdvga_perform_gray_scale_summing(u16 start, u16 count); +void stdvga_set_text_block_specifier(u8 spec); +void stdvga_planar4_plane(int plane); +void stdvga_load_font(u16 seg, void *src_far, u16 count + , u16 start, u8 destflags, u8 fontsize); +u16 stdvga_get_crtc(void); +int stdvga_vram_ratio(struct vgamode_s *vmode_g); +void stdvga_set_cursor_shape(u16 cursor_type); +void stdvga_set_cursor_pos(int address); +void stdvga_set_scan_lines(u8 lines); +u16 stdvga_get_vde(void); +int stdvga_get_window(struct vgamode_s *vmode_g, int window); +int stdvga_set_window(struct vgamode_s *vmode_g, int window, int val); +int stdvga_get_linelength(struct vgamode_s *vmode_g); +int stdvga_set_linelength(struct vgamode_s *vmode_g, int val); +int stdvga_get_displaystart(struct vgamode_s *vmode_g); +int stdvga_set_displaystart(struct vgamode_s *vmode_g, int val); +int stdvga_get_dacformat(struct vgamode_s *vmode_g); +int stdvga_set_dacformat(struct vgamode_s *vmode_g, int val); +int stdvga_save_restore(int cmd, u16 seg, void *data); +void stdvga_enable_video_addressing(u8 disable); +int stdvga_setup(void); + +#endif // stdvga.h diff --git a/qemu/roms/seabios/vgasrc/stdvgaio.c b/qemu/roms/seabios/vgasrc/stdvgaio.c new file mode 100644 index 000000000..d6138c22b --- /dev/null +++ b/qemu/roms/seabios/vgasrc/stdvgaio.c @@ -0,0 +1,185 @@ +// Standard VGA IO port access +// +// Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "farptr.h" // GET_FARVAR +#include "stdvga.h" // stdvga_pelmask_read +#include "x86.h" // inb + +u8 +stdvga_pelmask_read(void) +{ + return inb(VGAREG_PEL_MASK); +} + +void +stdvga_pelmask_write(u8 value) +{ + outb(value, VGAREG_PEL_MASK); +} + + +u8 +stdvga_misc_read(void) +{ + return inb(VGAREG_READ_MISC_OUTPUT); +} + +void +stdvga_misc_write(u8 value) +{ + outb(value, VGAREG_WRITE_MISC_OUTPUT); +} + +void +stdvga_misc_mask(u8 off, u8 on) +{ + stdvga_misc_write((stdvga_misc_read() & ~off) | on); +} + + +u8 +stdvga_sequ_read(u8 index) +{ + outb(index, VGAREG_SEQU_ADDRESS); + return inb(VGAREG_SEQU_DATA); +} + +void +stdvga_sequ_write(u8 index, u8 value) +{ + outw((value<<8) | index, VGAREG_SEQU_ADDRESS); +} + +void +stdvga_sequ_mask(u8 index, u8 off, u8 on) +{ + outb(index, VGAREG_SEQU_ADDRESS); + u8 v = inb(VGAREG_SEQU_DATA); + outb((v & ~off) | on, VGAREG_SEQU_DATA); +} + + +u8 +stdvga_grdc_read(u8 index) +{ + outb(index, VGAREG_GRDC_ADDRESS); + return inb(VGAREG_GRDC_DATA); +} + +void +stdvga_grdc_write(u8 index, u8 value) +{ + outw((value<<8) | index, VGAREG_GRDC_ADDRESS); +} + +void +stdvga_grdc_mask(u8 index, u8 off, u8 on) +{ + outb(index, VGAREG_GRDC_ADDRESS); + u8 v = inb(VGAREG_GRDC_DATA); + outb((v & ~off) | on, VGAREG_GRDC_DATA); +} + + +u8 +stdvga_crtc_read(u16 crtc_addr, u8 index) +{ + outb(index, crtc_addr); + return inb(crtc_addr + 1); +} + +void +stdvga_crtc_write(u16 crtc_addr, u8 index, u8 value) +{ + outw((value<<8) | index, crtc_addr); +} + +void +stdvga_crtc_mask(u16 crtc_addr, u8 index, u8 off, u8 on) +{ + outb(index, crtc_addr); + u8 v = inb(crtc_addr + 1); + outb((v & ~off) | on, crtc_addr + 1); +} + + +u8 +stdvga_attr_read(u8 index) +{ + inb(VGAREG_ACTL_RESET); + u8 orig = inb(VGAREG_ACTL_ADDRESS); + outb(index, VGAREG_ACTL_ADDRESS); + u8 v = inb(VGAREG_ACTL_READ_DATA); + inb(VGAREG_ACTL_RESET); + outb(orig, VGAREG_ACTL_ADDRESS); + return v; +} + +void +stdvga_attr_write(u8 index, u8 value) +{ + inb(VGAREG_ACTL_RESET); + u8 orig = inb(VGAREG_ACTL_ADDRESS); + outb(index, VGAREG_ACTL_ADDRESS); + outb(value, VGAREG_ACTL_WRITE_DATA); + outb(orig, VGAREG_ACTL_ADDRESS); +} + +void +stdvga_attr_mask(u8 index, u8 off, u8 on) +{ + inb(VGAREG_ACTL_RESET); + u8 orig = inb(VGAREG_ACTL_ADDRESS); + outb(index, VGAREG_ACTL_ADDRESS); + u8 v = inb(VGAREG_ACTL_READ_DATA); + outb((v & ~off) | on, VGAREG_ACTL_WRITE_DATA); + outb(orig, VGAREG_ACTL_ADDRESS); +} + +u8 +stdvga_attrindex_read(void) +{ + inb(VGAREG_ACTL_RESET); + return inb(VGAREG_ACTL_ADDRESS); +} + +void +stdvga_attrindex_write(u8 value) +{ + inb(VGAREG_ACTL_RESET); + outb(value, VGAREG_ACTL_ADDRESS); +} + + +void +stdvga_dac_read(u16 seg, u8 *data_far, u8 start, int count) +{ + outb(start, VGAREG_DAC_READ_ADDRESS); + while (count) { + SET_FARVAR(seg, *data_far, inb(VGAREG_DAC_DATA)); + data_far++; + SET_FARVAR(seg, *data_far, inb(VGAREG_DAC_DATA)); + data_far++; + SET_FARVAR(seg, *data_far, inb(VGAREG_DAC_DATA)); + data_far++; + count--; + } +} + +void +stdvga_dac_write(u16 seg, u8 *data_far, u8 start, int count) +{ + outb(start, VGAREG_DAC_WRITE_ADDRESS); + while (count) { + outb(GET_FARVAR(seg, *data_far), VGAREG_DAC_DATA); + data_far++; + outb(GET_FARVAR(seg, *data_far), VGAREG_DAC_DATA); + data_far++; + outb(GET_FARVAR(seg, *data_far), VGAREG_DAC_DATA); + data_far++; + count--; + } +} diff --git a/qemu/roms/seabios/vgasrc/stdvgamodes.c b/qemu/roms/seabios/vgasrc/stdvgamodes.c new file mode 100644 index 000000000..c553514fa --- /dev/null +++ b/qemu/roms/seabios/vgasrc/stdvgamodes.c @@ -0,0 +1,523 @@ +// Standard VGA mode information. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "output.h" // warn_internalerror +#include "stdvga.h" // stdvga_find_mode +#include "string.h" // memcpy_far +#include "vgabios.h" // video_param_table + + +/**************************************************************** + * Video mode register definitions + ****************************************************************/ + +/* Mono */ +static u8 palette0[] VAR16 = { + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f +}; + +static u8 palette1[] VAR16 = { + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, + 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, + 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, + 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, + 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, + 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, + 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, + 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, + 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f +}; + +static u8 palette2[] VAR16 = { + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, + 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x2a,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x15, 0x00,0x00,0x3f, 0x00,0x2a,0x15, 0x00,0x2a,0x3f, + 0x2a,0x00,0x15, 0x2a,0x00,0x3f, 0x2a,0x2a,0x15, 0x2a,0x2a,0x3f, + 0x00,0x15,0x00, 0x00,0x15,0x2a, 0x00,0x3f,0x00, 0x00,0x3f,0x2a, + 0x2a,0x15,0x00, 0x2a,0x15,0x2a, 0x2a,0x3f,0x00, 0x2a,0x3f,0x2a, + 0x00,0x15,0x15, 0x00,0x15,0x3f, 0x00,0x3f,0x15, 0x00,0x3f,0x3f, + 0x2a,0x15,0x15, 0x2a,0x15,0x3f, 0x2a,0x3f,0x15, 0x2a,0x3f,0x3f, + 0x15,0x00,0x00, 0x15,0x00,0x2a, 0x15,0x2a,0x00, 0x15,0x2a,0x2a, + 0x3f,0x00,0x00, 0x3f,0x00,0x2a, 0x3f,0x2a,0x00, 0x3f,0x2a,0x2a, + 0x15,0x00,0x15, 0x15,0x00,0x3f, 0x15,0x2a,0x15, 0x15,0x2a,0x3f, + 0x3f,0x00,0x15, 0x3f,0x00,0x3f, 0x3f,0x2a,0x15, 0x3f,0x2a,0x3f, + 0x15,0x15,0x00, 0x15,0x15,0x2a, 0x15,0x3f,0x00, 0x15,0x3f,0x2a, + 0x3f,0x15,0x00, 0x3f,0x15,0x2a, 0x3f,0x3f,0x00, 0x3f,0x3f,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, + 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f +}; + +static u8 palette3[] VAR16 = { + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, + 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, + 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x05,0x05,0x05, 0x08,0x08,0x08, 0x0b,0x0b,0x0b, + 0x0e,0x0e,0x0e, 0x11,0x11,0x11, 0x14,0x14,0x14, 0x18,0x18,0x18, + 0x1c,0x1c,0x1c, 0x20,0x20,0x20, 0x24,0x24,0x24, 0x28,0x28,0x28, + 0x2d,0x2d,0x2d, 0x32,0x32,0x32, 0x38,0x38,0x38, 0x3f,0x3f,0x3f, + 0x00,0x00,0x3f, 0x10,0x00,0x3f, 0x1f,0x00,0x3f, 0x2f,0x00,0x3f, + 0x3f,0x00,0x3f, 0x3f,0x00,0x2f, 0x3f,0x00,0x1f, 0x3f,0x00,0x10, + 0x3f,0x00,0x00, 0x3f,0x10,0x00, 0x3f,0x1f,0x00, 0x3f,0x2f,0x00, + 0x3f,0x3f,0x00, 0x2f,0x3f,0x00, 0x1f,0x3f,0x00, 0x10,0x3f,0x00, + 0x00,0x3f,0x00, 0x00,0x3f,0x10, 0x00,0x3f,0x1f, 0x00,0x3f,0x2f, + 0x00,0x3f,0x3f, 0x00,0x2f,0x3f, 0x00,0x1f,0x3f, 0x00,0x10,0x3f, + 0x1f,0x1f,0x3f, 0x27,0x1f,0x3f, 0x2f,0x1f,0x3f, 0x37,0x1f,0x3f, + 0x3f,0x1f,0x3f, 0x3f,0x1f,0x37, 0x3f,0x1f,0x2f, 0x3f,0x1f,0x27, + + 0x3f,0x1f,0x1f, 0x3f,0x27,0x1f, 0x3f,0x2f,0x1f, 0x3f,0x37,0x1f, + 0x3f,0x3f,0x1f, 0x37,0x3f,0x1f, 0x2f,0x3f,0x1f, 0x27,0x3f,0x1f, + 0x1f,0x3f,0x1f, 0x1f,0x3f,0x27, 0x1f,0x3f,0x2f, 0x1f,0x3f,0x37, + 0x1f,0x3f,0x3f, 0x1f,0x37,0x3f, 0x1f,0x2f,0x3f, 0x1f,0x27,0x3f, + 0x2d,0x2d,0x3f, 0x31,0x2d,0x3f, 0x36,0x2d,0x3f, 0x3a,0x2d,0x3f, + 0x3f,0x2d,0x3f, 0x3f,0x2d,0x3a, 0x3f,0x2d,0x36, 0x3f,0x2d,0x31, + 0x3f,0x2d,0x2d, 0x3f,0x31,0x2d, 0x3f,0x36,0x2d, 0x3f,0x3a,0x2d, + 0x3f,0x3f,0x2d, 0x3a,0x3f,0x2d, 0x36,0x3f,0x2d, 0x31,0x3f,0x2d, + 0x2d,0x3f,0x2d, 0x2d,0x3f,0x31, 0x2d,0x3f,0x36, 0x2d,0x3f,0x3a, + 0x2d,0x3f,0x3f, 0x2d,0x3a,0x3f, 0x2d,0x36,0x3f, 0x2d,0x31,0x3f, + 0x00,0x00,0x1c, 0x07,0x00,0x1c, 0x0e,0x00,0x1c, 0x15,0x00,0x1c, + 0x1c,0x00,0x1c, 0x1c,0x00,0x15, 0x1c,0x00,0x0e, 0x1c,0x00,0x07, + 0x1c,0x00,0x00, 0x1c,0x07,0x00, 0x1c,0x0e,0x00, 0x1c,0x15,0x00, + 0x1c,0x1c,0x00, 0x15,0x1c,0x00, 0x0e,0x1c,0x00, 0x07,0x1c,0x00, + 0x00,0x1c,0x00, 0x00,0x1c,0x07, 0x00,0x1c,0x0e, 0x00,0x1c,0x15, + 0x00,0x1c,0x1c, 0x00,0x15,0x1c, 0x00,0x0e,0x1c, 0x00,0x07,0x1c, + + 0x0e,0x0e,0x1c, 0x11,0x0e,0x1c, 0x15,0x0e,0x1c, 0x18,0x0e,0x1c, + 0x1c,0x0e,0x1c, 0x1c,0x0e,0x18, 0x1c,0x0e,0x15, 0x1c,0x0e,0x11, + 0x1c,0x0e,0x0e, 0x1c,0x11,0x0e, 0x1c,0x15,0x0e, 0x1c,0x18,0x0e, + 0x1c,0x1c,0x0e, 0x18,0x1c,0x0e, 0x15,0x1c,0x0e, 0x11,0x1c,0x0e, + 0x0e,0x1c,0x0e, 0x0e,0x1c,0x11, 0x0e,0x1c,0x15, 0x0e,0x1c,0x18, + 0x0e,0x1c,0x1c, 0x0e,0x18,0x1c, 0x0e,0x15,0x1c, 0x0e,0x11,0x1c, + 0x14,0x14,0x1c, 0x16,0x14,0x1c, 0x18,0x14,0x1c, 0x1a,0x14,0x1c, + 0x1c,0x14,0x1c, 0x1c,0x14,0x1a, 0x1c,0x14,0x18, 0x1c,0x14,0x16, + 0x1c,0x14,0x14, 0x1c,0x16,0x14, 0x1c,0x18,0x14, 0x1c,0x1a,0x14, + 0x1c,0x1c,0x14, 0x1a,0x1c,0x14, 0x18,0x1c,0x14, 0x16,0x1c,0x14, + 0x14,0x1c,0x14, 0x14,0x1c,0x16, 0x14,0x1c,0x18, 0x14,0x1c,0x1a, + 0x14,0x1c,0x1c, 0x14,0x1a,0x1c, 0x14,0x18,0x1c, 0x14,0x16,0x1c, + 0x00,0x00,0x10, 0x04,0x00,0x10, 0x08,0x00,0x10, 0x0c,0x00,0x10, + 0x10,0x00,0x10, 0x10,0x00,0x0c, 0x10,0x00,0x08, 0x10,0x00,0x04, + 0x10,0x00,0x00, 0x10,0x04,0x00, 0x10,0x08,0x00, 0x10,0x0c,0x00, + 0x10,0x10,0x00, 0x0c,0x10,0x00, 0x08,0x10,0x00, 0x04,0x10,0x00, + + 0x00,0x10,0x00, 0x00,0x10,0x04, 0x00,0x10,0x08, 0x00,0x10,0x0c, + 0x00,0x10,0x10, 0x00,0x0c,0x10, 0x00,0x08,0x10, 0x00,0x04,0x10, + 0x08,0x08,0x10, 0x0a,0x08,0x10, 0x0c,0x08,0x10, 0x0e,0x08,0x10, + 0x10,0x08,0x10, 0x10,0x08,0x0e, 0x10,0x08,0x0c, 0x10,0x08,0x0a, + 0x10,0x08,0x08, 0x10,0x0a,0x08, 0x10,0x0c,0x08, 0x10,0x0e,0x08, + 0x10,0x10,0x08, 0x0e,0x10,0x08, 0x0c,0x10,0x08, 0x0a,0x10,0x08, + 0x08,0x10,0x08, 0x08,0x10,0x0a, 0x08,0x10,0x0c, 0x08,0x10,0x0e, + 0x08,0x10,0x10, 0x08,0x0e,0x10, 0x08,0x0c,0x10, 0x08,0x0a,0x10, + 0x0b,0x0b,0x10, 0x0c,0x0b,0x10, 0x0d,0x0b,0x10, 0x0f,0x0b,0x10, + 0x10,0x0b,0x10, 0x10,0x0b,0x0f, 0x10,0x0b,0x0d, 0x10,0x0b,0x0c, + 0x10,0x0b,0x0b, 0x10,0x0c,0x0b, 0x10,0x0d,0x0b, 0x10,0x0f,0x0b, + 0x10,0x10,0x0b, 0x0f,0x10,0x0b, 0x0d,0x10,0x0b, 0x0c,0x10,0x0b, + 0x0b,0x10,0x0b, 0x0b,0x10,0x0c, 0x0b,0x10,0x0d, 0x0b,0x10,0x0f, + 0x0b,0x10,0x10, 0x0b,0x0f,0x10, 0x0b,0x0d,0x10, 0x0b,0x0c,0x10, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00 +}; + +static u8 sequ_01[] VAR16 = { 0x08, 0x03, 0x00, 0x02 }; +static u8 crtc_01[] VAR16 = { + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3, + 0xff }; +static u8 actl_01[] VAR16 = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x0c, 0x00, 0x0f, 0x08 }; +static u8 grdc_01[] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff }; +static u8 sequ_03[] VAR16 = { 0x00, 0x03, 0x00, 0x02 }; +static u8 crtc_03[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3, + 0xff }; +static u8 sequ_04[] VAR16 = { 0x09, 0x03, 0x00, 0x02 }; +static u8 crtc_04[] VAR16 = { + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, + 0xff }; +static u8 actl_04[] VAR16 = { + 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x03, 0x00 }; +static u8 grdc_04[] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff }; +static u8 sequ_06[] VAR16 = { 0x01, 0x01, 0x00, 0x06 }; +static u8 crtc_06[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2, + 0xff }; +static u8 actl_06[] VAR16 = { + 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x01, 0x00, 0x01, 0x00 }; +static u8 grdc_06[] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0f, 0xff }; +static u8 crtc_07[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, + 0xff }; +static u8 actl_07[] VAR16 = { + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x0e, 0x00, 0x0f, 0x08 }; +static u8 grdc_07[] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff }; +static u8 sequ_0d[] VAR16 = { 0x09, 0x0f, 0x00, 0x06 }; +static u8 crtc_0d[] VAR16 = { + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3, + 0xff }; +static u8 actl_0d[] VAR16 = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x0f, 0x00 }; +static u8 grdc_0d[] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff }; +static u8 sequ_0e[] VAR16 = { 0x01, 0x0f, 0x00, 0x06 }; +static u8 crtc_0e[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3, + 0xff }; +static u8 crtc_0f[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, + 0xff }; +static u8 actl_0f[] VAR16 = { + 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00 }; +static u8 actl_10[] VAR16 = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00 }; +static u8 crtc_11[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, + 0xff }; +static u8 actl_11[] VAR16 = { + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x01, 0x00, 0x0f, 0x00 }; +static u8 sequ_13[] VAR16 = { 0x01, 0x0f, 0x00, 0x0e }; +static u8 crtc_13[] VAR16 = { + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3, + 0xff }; +static u8 actl_13[] VAR16 = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x41, 0x00, 0x0f, 0x00 }; +static u8 grdc_13[] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff }; +static u8 crtc_6A[] VAR16 = { + 0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x8d, 0x57, 0x32, 0x00, 0x57, 0x73, 0xe3, + 0xff }; + +#define PAL(x) x, sizeof(x) + +struct stdvga_mode_s { + u16 mode; + struct vgamode_s info; + + u8 pelmask; + u8 *dac; + u16 dacsize; + u8 *sequ_regs; + u8 miscreg; + u8 *crtc_regs; + u8 *actl_regs; + u8 *grdc_regs; +}; + +static struct stdvga_mode_s vga_modes[] VAR16 = { + //mode { model tx ty bpp cw ch sstart } + // pelm dac sequ misc crtc actl grdc + {0x00, { MM_TEXT, 40, 25, 4, 9, 16, SEG_CTEXT } + , 0xFF, PAL(palette2), sequ_01, 0x67, crtc_01, actl_01, grdc_01}, + {0x01, { MM_TEXT, 40, 25, 4, 9, 16, SEG_CTEXT } + , 0xFF, PAL(palette2), sequ_01, 0x67, crtc_01, actl_01, grdc_01}, + {0x02, { MM_TEXT, 80, 25, 4, 9, 16, SEG_CTEXT } + , 0xFF, PAL(palette2), sequ_03, 0x67, crtc_03, actl_01, grdc_01}, + {0x03, { MM_TEXT, 80, 25, 4, 9, 16, SEG_CTEXT } + , 0xFF, PAL(palette2), sequ_03, 0x67, crtc_03, actl_01, grdc_01}, + {0x04, { MM_CGA, 320, 200, 2, 8, 8, SEG_CTEXT } + , 0xFF, PAL(palette1), sequ_04, 0x63, crtc_04, actl_04, grdc_04}, + {0x05, { MM_CGA, 320, 200, 2, 8, 8, SEG_CTEXT } + , 0xFF, PAL(palette1), sequ_04, 0x63, crtc_04, actl_04, grdc_04}, + {0x06, { MM_CGA, 640, 200, 1, 8, 8, SEG_CTEXT } + , 0xFF, PAL(palette1), sequ_06, 0x63, crtc_06, actl_06, grdc_06}, + {0x07, { MM_TEXT, 80, 25, 4, 9, 16, SEG_MTEXT } + , 0xFF, PAL(palette0), sequ_03, 0x66, crtc_07, actl_07, grdc_07}, + {0x0D, { MM_PLANAR, 320, 200, 4, 8, 8, SEG_GRAPH } + , 0xFF, PAL(palette1), sequ_0d, 0x63, crtc_0d, actl_0d, grdc_0d}, + {0x0E, { MM_PLANAR, 640, 200, 4, 8, 8, SEG_GRAPH } + , 0xFF, PAL(palette1), sequ_0e, 0x63, crtc_0e, actl_0d, grdc_0d}, + {0x0F, { MM_PLANAR, 640, 350, 1, 8, 14, SEG_GRAPH } + , 0xFF, PAL(palette0), sequ_0e, 0xa3, crtc_0f, actl_0f, grdc_0d}, + {0x10, { MM_PLANAR, 640, 350, 4, 8, 14, SEG_GRAPH } + , 0xFF, PAL(palette2), sequ_0e, 0xa3, crtc_0f, actl_10, grdc_0d}, + {0x11, { MM_PLANAR, 640, 480, 1, 8, 16, SEG_GRAPH } + , 0xFF, PAL(palette2), sequ_0e, 0xe3, crtc_11, actl_11, grdc_0d}, + {0x12, { MM_PLANAR, 640, 480, 4, 8, 16, SEG_GRAPH } + , 0xFF, PAL(palette2), sequ_0e, 0xe3, crtc_11, actl_10, grdc_0d}, + {0x13, { MM_PACKED, 320, 200, 8, 8, 8, SEG_GRAPH } + , 0xFF, PAL(palette3), sequ_13, 0x63, crtc_13, actl_13, grdc_13}, + {0x6A, { MM_PLANAR, 800, 600, 4, 8, 16, SEG_GRAPH } + , 0xFF, PAL(palette2), sequ_0e, 0xe3, crtc_6A, actl_10, grdc_0d}, +}; + + +/**************************************************************** + * Mode functions + ****************************************************************/ + +static int +is_stdvga_mode(struct vgamode_s *vmode_g) +{ + return (vmode_g >= &vga_modes[0].info + && vmode_g <= &vga_modes[ARRAY_SIZE(vga_modes)-1].info); +} + +struct vgamode_s * +stdvga_find_mode(int mode) +{ + int i; + for (i = 0; i < ARRAY_SIZE(vga_modes); i++) { + struct stdvga_mode_s *stdmode_g = &vga_modes[i]; + if (GET_GLOBAL(stdmode_g->mode) == mode) + return &stdmode_g->info; + } + return NULL; +} + +void +stdvga_list_modes(u16 seg, u16 *dest, u16 *last) +{ + int i; + for (i = 0; i < ARRAY_SIZE(vga_modes) && dest < last; i++) { + struct stdvga_mode_s *stdmode_g = &vga_modes[i]; + u16 mode = GET_GLOBAL(stdmode_g->mode); + if (mode == 0xffff) + continue; + SET_FARVAR(seg, *dest, mode); + dest++; + } + + SET_FARVAR(seg, *dest, 0xffff); +} + +void +stdvga_build_video_param(void) +{ + static u8 parammodes[] VAR16 = { + 0, 0, 0, 0, 0x04, 0x05, 0x06, 0x07, + 0, 0, 0, 0, 0, 0x0d, 0x0e, 0, + 0, 0x0f, 0x10, 0, 0, 0, 0, 0x01, + 0x03, 0x07, 0x11, 0x12, 0x13 + }; + + int i; + for (i=0; i<ARRAY_SIZE(parammodes); i++) { + int mode = GET_GLOBAL(parammodes[i]); + if (! mode) + continue; + struct video_param_s *vparam_g = &video_param_table[i]; + struct vgamode_s *vmode_g = stdvga_find_mode(mode); + if (!vmode_g) + continue; + int width = GET_GLOBAL(vmode_g->width); + int height = GET_GLOBAL(vmode_g->height); + u8 memmodel = GET_GLOBAL(vmode_g->memmodel); + int cheight = GET_GLOBAL(vmode_g->cheight); + if (memmodel == MM_TEXT) { + SET_VGA(vparam_g->twidth, width); + SET_VGA(vparam_g->theightm1, height-1); + } else { + int cwidth = GET_GLOBAL(vmode_g->cwidth); + SET_VGA(vparam_g->twidth, width / cwidth); + SET_VGA(vparam_g->theightm1, (height / cheight) - 1); + } + SET_VGA(vparam_g->cheight, cheight); + SET_VGA(vparam_g->slength, calc_page_size(memmodel, width, height)); + struct stdvga_mode_s *stdmode_g = container_of( + vmode_g, struct stdvga_mode_s, info); + memcpy_far(get_global_seg(), vparam_g->sequ_regs + , get_global_seg(), GET_GLOBAL(stdmode_g->sequ_regs) + , ARRAY_SIZE(vparam_g->sequ_regs)); + SET_VGA(vparam_g->miscreg, GET_GLOBAL(stdmode_g->miscreg)); + memcpy_far(get_global_seg(), vparam_g->crtc_regs + , get_global_seg(), GET_GLOBAL(stdmode_g->crtc_regs) + , ARRAY_SIZE(vparam_g->crtc_regs)); + memcpy_far(get_global_seg(), vparam_g->actl_regs + , get_global_seg(), GET_GLOBAL(stdmode_g->actl_regs) + , ARRAY_SIZE(vparam_g->actl_regs)); + memcpy_far(get_global_seg(), vparam_g->grdc_regs + , get_global_seg(), GET_GLOBAL(stdmode_g->grdc_regs) + , ARRAY_SIZE(vparam_g->grdc_regs)); + } + + // Fill available legacy modes in video_func_static table + u32 modes = 0; + for (i = 0; i < ARRAY_SIZE(vga_modes); i++) { + u16 mode = vga_modes[i].mode; + if (mode <= 0x13) + modes |= 1<<i; + } + SET_VGA(static_functionality.modes, modes); +} + +void +stdvga_override_crtc(int mode, u8 *crtc) +{ + struct vgamode_s *vmode_g = stdvga_find_mode(mode); + if (!vmode_g) + return; + struct stdvga_mode_s *stdmode_g = container_of( + vmode_g, struct stdvga_mode_s, info); + SET_VGA(stdmode_g->crtc_regs, crtc); +} + +static void +clear_screen(struct vgamode_s *vmode_g) +{ + switch (GET_GLOBAL(vmode_g->memmodel)) { + case MM_TEXT: + memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024); + break; + case MM_CGA: + memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024); + break; + default: + // XXX - old code gets/sets/restores sequ register 2 to 0xf - + // but it should always be 0xf anyway. + memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024); + } +} + +int +stdvga_set_mode(struct vgamode_s *vmode_g, int flags) +{ + if (! is_stdvga_mode(vmode_g)) { + warn_internalerror(); + return -1; + } + struct stdvga_mode_s *stdmode_g = container_of( + vmode_g, struct stdvga_mode_s, info); + + // if palette loading (bit 3 of modeset ctl = 0) + if (!(flags & MF_NOPALETTE)) { // Set the PEL mask + stdvga_pelmask_write(GET_GLOBAL(stdmode_g->pelmask)); + + // From which palette + u8 *palette_g = GET_GLOBAL(stdmode_g->dac); + u16 palsize = GET_GLOBAL(stdmode_g->dacsize) / 3; + + // Always 256*3 values + stdvga_dac_write(get_global_seg(), palette_g, 0, palsize); + int i; + for (i = palsize; i < 0x0100; i++) { + static u8 rgb[3] VAR16; + stdvga_dac_write(get_global_seg(), rgb, i, 1); + } + + if (flags & MF_GRAYSUM) + stdvga_perform_gray_scale_summing(0x00, 0x100); + } + + // Set Attribute Ctl + u8 *regs = GET_GLOBAL(stdmode_g->actl_regs); + int i; + for (i = 0; i <= 0x13; i++) + stdvga_attr_write(i, GET_GLOBAL(regs[i])); + stdvga_attr_write(0x14, 0x00); + + // Set Sequencer Ctl + stdvga_sequ_write(0x00, 0x03); + regs = GET_GLOBAL(stdmode_g->sequ_regs); + for (i = 1; i <= 4; i++) + stdvga_sequ_write(i, GET_GLOBAL(regs[i - 1])); + + // Set Grafx Ctl + regs = GET_GLOBAL(stdmode_g->grdc_regs); + for (i = 0; i <= 8; i++) + stdvga_grdc_write(i, GET_GLOBAL(regs[i])); + + // Set CRTC address VGA or MDA + u8 miscreg = GET_GLOBAL(stdmode_g->miscreg); + u16 crtc_addr = VGAREG_VGA_CRTC_ADDRESS; + if (!(miscreg & 1)) + crtc_addr = VGAREG_MDA_CRTC_ADDRESS; + + // Disable CRTC write protection + stdvga_crtc_write(crtc_addr, 0x11, 0x00); + // Set CRTC regs + regs = GET_GLOBAL(stdmode_g->crtc_regs); + for (i = 0; i <= 0x18; i++) + stdvga_crtc_write(crtc_addr, i, GET_GLOBAL(regs[i])); + + // Set the misc register + stdvga_misc_write(miscreg); + + // Enable video + stdvga_attrindex_write(0x20); + + // Clear screen + if (!(flags & MF_NOCLEARMEM)) + clear_screen(vmode_g); + + // Write the fonts in memory + u8 memmodel = GET_GLOBAL(vmode_g->memmodel); + if (memmodel == MM_TEXT) + stdvga_load_font(get_global_seg(), vgafont16, 0x100, 0, 0, 16); + + return 0; +} + +// Load the standard palette associated with 8bpp packed pixel vga modes. +void +stdvga_set_packed_palette(void) +{ + stdvga_dac_write(get_global_seg(), palette3, 0, sizeof(palette3) / 3); +} diff --git a/qemu/roms/seabios/vgasrc/vbe.c b/qemu/roms/seabios/vgasrc/vbe.c new file mode 100644 index 000000000..af3d0ccb8 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vbe.c @@ -0,0 +1,426 @@ +// Video Bios Extensions handlers +// +// Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2011 Julian Pidancet <julian.pidancet@citrix.com> +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "std/vbe.h" // struct vbe_info +#include "string.h" // memset_far +#include "vgabios.h" // handle_104f +#include "vgahw.h" // vgahw_set_mode + +u32 VBE_total_memory VAR16 = 256 * 1024; +u32 VBE_capabilities VAR16; +u32 VBE_framebuffer VAR16; +u16 VBE_win_granularity VAR16; + +static void +vbe_104f00(struct bregs *regs) +{ + u16 seg = regs->es; + struct vbe_info *info = (void*)(regs->di+0); + + if (GET_FARVAR(seg, info->signature) == VBE2_SIGNATURE) { + dprintf(4, "Get VBE Controller: VBE2 Signature found\n"); + } else if (GET_FARVAR(seg, info->signature) == VESA_SIGNATURE) { + dprintf(4, "Get VBE Controller: VESA Signature found\n"); + } else { + dprintf(4, "Get VBE Controller: Invalid Signature\n"); + } + + memset_far(seg, info, 0, sizeof(*info)); + + SET_FARVAR(seg, info->signature, VESA_SIGNATURE); + + SET_FARVAR(seg, info->version, 0x0300); + + SET_FARVAR(seg, info->oem_string, + SEGOFF(get_global_seg(), (u32)VBE_OEM_STRING)); + SET_FARVAR(seg, info->capabilities, GET_GLOBAL(VBE_capabilities)); + + /* We generate our mode list in the reserved field of the info block */ + u16 *destmode = (void*)info->reserved; + SET_FARVAR(seg, info->video_mode, SEGOFF(seg, (u32)destmode)); + + /* Total memory (in 64k blocks) */ + SET_FARVAR(seg, info->total_memory + , GET_GLOBAL(VBE_total_memory) / (64*1024)); + + SET_FARVAR(seg, info->oem_vendor_string, + SEGOFF(get_global_seg(), (u32)VBE_VENDOR_STRING)); + SET_FARVAR(seg, info->oem_product_string, + SEGOFF(get_global_seg(), (u32)VBE_PRODUCT_STRING)); + SET_FARVAR(seg, info->oem_revision_string, + SEGOFF(get_global_seg(), (u32)VBE_REVISION_STRING)); + + /* Fill list of modes */ + u16 *last = (void*)&info->reserved[sizeof(info->reserved)]; + vgahw_list_modes(seg, destmode, last - 1); + + regs->ax = 0x004f; +} + +static void +vbe_104f01(struct bregs *regs) +{ + u16 seg = regs->es; + struct vbe_mode_info *info = (void*)(regs->di+0); + u16 mode = regs->cx; + + dprintf(1, "VBE mode info request: %x\n", mode); + + struct vgamode_s *vmode_g = vgahw_find_mode(mode & ~MF_VBEFLAGS); + if (! vmode_g) { + dprintf(1, "VBE mode %x not found\n", mode); + regs->ax = 0x014f; + return; + } + + memset_far(seg, info, 0, sizeof(*info)); + + // Basic information about video controller. + u32 win_granularity = GET_GLOBAL(VBE_win_granularity); + SET_FARVAR(seg, info->winA_attributes, + (win_granularity ? VBE_WINDOW_ATTRIBUTE_RELOCATABLE : 0) | + VBE_WINDOW_ATTRIBUTE_READABLE | + VBE_WINDOW_ATTRIBUTE_WRITEABLE); + SET_FARVAR(seg, info->winB_attributes, 0); + SET_FARVAR(seg, info->win_granularity, win_granularity); + SET_FARVAR(seg, info->win_size, 64); /* Bank size 64K */ + SET_FARVAR(seg, info->winA_seg, GET_GLOBAL(vmode_g->sstart)); + SET_FARVAR(seg, info->winB_seg, 0x0); + extern void entry_104f05(void); + SET_FARVAR(seg, info->win_func_ptr + , SEGOFF(get_global_seg(), (u32)entry_104f05)); + // Basic information about mode. + int width = GET_GLOBAL(vmode_g->width); + int height = GET_GLOBAL(vmode_g->height); + int linesize = DIV_ROUND_UP(width * vga_bpp(vmode_g), 8); + SET_FARVAR(seg, info->bytes_per_scanline, linesize); + SET_FARVAR(seg, info->xres, width); + SET_FARVAR(seg, info->yres, height); + SET_FARVAR(seg, info->xcharsize, GET_GLOBAL(vmode_g->cwidth)); + SET_FARVAR(seg, info->ycharsize, GET_GLOBAL(vmode_g->cheight)); + int depth = GET_GLOBAL(vmode_g->depth); + SET_FARVAR(seg, info->bits_per_pixel, depth); + u8 memmodel = GET_GLOBAL(vmode_g->memmodel); + SET_FARVAR(seg, info->mem_model, memmodel); + SET_FARVAR(seg, info->reserved0, 1); + + // Mode specific info. + u16 mode_attr = VBE_MODE_ATTRIBUTE_SUPPORTED | + VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | + VBE_MODE_ATTRIBUTE_COLOR_MODE | + VBE_MODE_ATTRIBUTE_GRAPHICS_MODE | + VBE_MODE_ATTRIBUTE_NOT_VGA_COMPATIBLE; + u32 framebuffer = 0; + int planes = 1, banks = 1; + u32 pages = GET_GLOBAL(VBE_total_memory) / ALIGN(height * linesize, 64*1024); + switch (memmodel) { + case MM_TEXT: + mode_attr &= ~VBE_MODE_ATTRIBUTE_GRAPHICS_MODE; + mode_attr |= VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT; + if (GET_GLOBAL(vmode_g->sstart) == SEG_MTEXT) + mode_attr &= ~VBE_MODE_ATTRIBUTE_COLOR_MODE; + pages = 1; + break; + case MM_CGA: + pages = 1; + banks = 2; + SET_FARVAR(seg, info->bank_size, 8); + break; + case MM_PLANAR: + planes = 4; + pages /= 4; + break; + default: + framebuffer = GET_GLOBAL(VBE_framebuffer); + if (framebuffer) + mode_attr |= VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE; + break; + } + if (pages > 128) + pages = 128; + if (pages < 2) + pages++; + SET_FARVAR(seg, info->mode_attributes, mode_attr); + SET_FARVAR(seg, info->planes, planes); + SET_FARVAR(seg, info->pages, pages - 1); + SET_FARVAR(seg, info->banks, banks); + + // Pixel color breakdown + u8 r_size, r_pos, g_size, g_pos, b_size, b_pos, a_size, a_pos; + switch (depth) { + case 15: r_size = 5; r_pos = 10; g_size = 5; g_pos = 5; + b_size = 5; b_pos = 0; a_size = 1; a_pos = 15; break; + case 16: r_size = 5; r_pos = 11; g_size = 6; g_pos = 5; + b_size = 5; b_pos = 0; a_size = 0; a_pos = 0; break; + case 24: r_size = 8; r_pos = 16; g_size = 8; g_pos = 8; + b_size = 8; b_pos = 0; a_size = 0; a_pos = 0; break; + case 32: r_size = 8; r_pos = 16; g_size = 8; g_pos = 8; + b_size = 8; b_pos = 0; a_size = 8; a_pos = 24; + SET_FARVAR(seg, info->directcolor_info, + VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE); + break; + default: r_size = 0; r_pos = 0; g_size = 0; g_pos = 0; + b_size = 0; b_pos = 0; a_size = 0; a_pos = 0; break; + } + SET_FARVAR(seg, info->red_size, r_size); + SET_FARVAR(seg, info->red_pos, r_pos); + SET_FARVAR(seg, info->green_size, g_size); + SET_FARVAR(seg, info->green_pos, g_pos); + SET_FARVAR(seg, info->blue_size, b_size); + SET_FARVAR(seg, info->blue_pos, b_pos); + SET_FARVAR(seg, info->alpha_size, a_size); + SET_FARVAR(seg, info->alpha_pos, a_pos); + + // Linear framebuffer info. + if (framebuffer) { + SET_FARVAR(seg, info->phys_base, framebuffer); + + SET_FARVAR(seg, info->reserved1, 0); + SET_FARVAR(seg, info->reserved2, 0); + SET_FARVAR(seg, info->linear_bytes_per_scanline, linesize); + SET_FARVAR(seg, info->linear_pages, 0); + SET_FARVAR(seg, info->linear_red_size, r_size); + SET_FARVAR(seg, info->linear_red_pos, r_pos); + SET_FARVAR(seg, info->linear_green_size, g_size); + SET_FARVAR(seg, info->linear_green_pos, g_pos); + SET_FARVAR(seg, info->linear_blue_size, b_size); + SET_FARVAR(seg, info->linear_blue_pos, b_pos); + SET_FARVAR(seg, info->linear_alpha_size, a_size); + SET_FARVAR(seg, info->linear_alpha_pos, a_pos); + } + + regs->ax = 0x004f; +} + +static void +vbe_104f02(struct bregs *regs) +{ + dprintf(1, "VBE mode set: %x\n", regs->bx); + + int mode = regs->bx & ~MF_VBEFLAGS; + int flags = regs->bx & MF_VBEFLAGS; + int ret = vga_set_mode(mode, flags); + + regs->ah = ret; + regs->al = 0x4f; +} + +static void +vbe_104f03(struct bregs *regs) +{ + regs->bx = GET_BDA_EXT(vbe_mode); + dprintf(1, "VBE current mode=%x\n", regs->bx); + regs->ax = 0x004f; +} + +static void +vbe_104f04(struct bregs *regs) +{ + u16 seg = regs->es; + void *data = (void*)(regs->bx+0); + u16 states = regs->cx; + u8 cmd = regs->dl; + if (states & ~0x0f || cmd > 2) + goto fail; + int ret = vgahw_save_restore(states | (cmd<<8), seg, data); + if (ret < 0) + goto fail; + if (cmd == 0) + regs->bx = ret / 64; + regs->ax = 0x004f; + return; +fail: + regs->ax = 0x014f; +} + +void VISIBLE16 +vbe_104f05(struct bregs *regs) +{ + if (regs->bh > 1 || regs->bl > 1) + goto fail; + if (GET_BDA_EXT(vbe_mode) & MF_LINEARFB) { + regs->ah = VBE_RETURN_STATUS_INVALID; + return; + } + struct vgamode_s *vmode_g = get_current_mode(); + if (! vmode_g) + goto fail; + if (regs->bh) { + int ret = vgahw_get_window(vmode_g, regs->bl); + if (ret < 0) + goto fail; + regs->dx = ret; + regs->ax = 0x004f; + return; + } + int ret = vgahw_set_window(vmode_g, regs->bl, regs->dx); + if (ret) + goto fail; + regs->ax = 0x004f; + return; +fail: + regs->ax = 0x014f; +} + +static void +vbe_104f06(struct bregs *regs) +{ + if (regs->bl > 0x02) + goto fail; + struct vgamode_s *vmode_g = get_current_mode(); + if (! vmode_g) + goto fail; + int bpp = vga_bpp(vmode_g); + + if (regs->bl == 0x00) { + int ret = vgahw_set_linelength(vmode_g, DIV_ROUND_UP(regs->cx * bpp, 8)); + if (ret) + goto fail; + } else if (regs->bl == 0x02) { + int ret = vgahw_set_linelength(vmode_g, regs->cx); + if (ret) + goto fail; + } + int linelength = vgahw_get_linelength(vmode_g); + if (linelength < 0) + goto fail; + + regs->bx = linelength; + regs->cx = (linelength * 8) / bpp; + regs->dx = GET_GLOBAL(VBE_total_memory) / linelength; + regs->ax = 0x004f; + return; +fail: + regs->ax = 0x014f; +} + +static void +vbe_104f07(struct bregs *regs) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (! vmode_g) + goto fail; + int bpp = vga_bpp(vmode_g); + int linelength = vgahw_get_linelength(vmode_g); + if (linelength < 0) + goto fail; + + int ret; + switch (regs->bl) { + case 0x80: + case 0x00: + ret = vgahw_set_displaystart( + vmode_g, DIV_ROUND_UP(regs->cx * bpp, 8) + linelength * regs->dx); + if (ret) + goto fail; + break; + case 0x01: + ret = vgahw_get_displaystart(vmode_g); + if (ret < 0) + goto fail; + regs->dx = ret / linelength; + regs->cx = (ret % linelength) * 8 / bpp; + break; + default: + goto fail; + } + regs->ax = 0x004f; + return; +fail: + regs->ax = 0x014f; +} + +static void +vbe_104f08(struct bregs *regs) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (! vmode_g) + goto fail; + u8 memmodel = GET_GLOBAL(vmode_g->memmodel); + if (memmodel == MM_DIRECT || memmodel == MM_YUV) { + regs->ax = 0x034f; + return; + } + if (regs->bl > 1) + goto fail; + if (regs->bl == 0) { + int ret = vgahw_set_dacformat(vmode_g, regs->bh); + if (ret < 0) + goto fail; + } + int ret = vgahw_get_dacformat(vmode_g); + if (ret < 0) + goto fail; + regs->bh = ret; + regs->ax = 0x004f; + return; +fail: + regs->ax = 0x014f; +} + +static void +vbe_104f0a(struct bregs *regs) +{ + debug_stub(regs); + regs->ax = 0x0100; +} + +static void +vbe_104f10(struct bregs *regs) +{ + switch (regs->bl) { + case 0x00: + regs->bx = 0x0f30; + break; + case 0x01: + MASK_BDA_EXT(flags, BF_PM_MASK, regs->bh & BF_PM_MASK); + break; + case 0x02: + regs->bh = GET_BDA_EXT(flags) & BF_PM_MASK; + break; + default: + regs->ax = 0x014f; + return; + } + regs->ax = 0x004f; +} + +static void +vbe_104fXX(struct bregs *regs) +{ + debug_stub(regs); + regs->ax = 0x0100; +} + +void noinline +handle_104f(struct bregs *regs) +{ + if (!CONFIG_VGA_VBE) { + vbe_104fXX(regs); + return; + } + + switch (regs->al) { + case 0x00: vbe_104f00(regs); break; + case 0x01: vbe_104f01(regs); break; + case 0x02: vbe_104f02(regs); break; + case 0x03: vbe_104f03(regs); break; + case 0x04: vbe_104f04(regs); break; + case 0x05: vbe_104f05(regs); break; + case 0x06: vbe_104f06(regs); break; + case 0x07: vbe_104f07(regs); break; + case 0x08: vbe_104f08(regs); break; + case 0x0a: vbe_104f0a(regs); break; + case 0x10: vbe_104f10(regs); break; + default: vbe_104fXX(regs); break; + } +} diff --git a/qemu/roms/seabios/vgasrc/vgabios.c b/qemu/roms/seabios/vgasrc/vgabios.c new file mode 100644 index 000000000..4aa50e1c1 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgabios.c @@ -0,0 +1,1176 @@ +// VGA bios implementation +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "bregs.h" // struct bregs +#include "clext.h" // clext_1012 +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "std/vbe.h" // VBE_RETURN_STATUS_FAILED +#include "stdvga.h" // stdvga_set_cursor_shape +#include "string.h" // memset_far +#include "vgabios.h" // calc_page_size +#include "vgahw.h" // vgahw_set_mode + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Return the bits per pixel in system memory for a given mode. +int +vga_bpp(struct vgamode_s *vmode_g) +{ + switch (GET_GLOBAL(vmode_g->memmodel)) { + case MM_TEXT: + return 16; + case MM_PLANAR: + return 1; + } + u8 depth = GET_GLOBAL(vmode_g->depth); + if (depth > 8) + return ALIGN(depth, 8); + return depth; +} + +u16 +calc_page_size(u8 memmodel, u16 width, u16 height) +{ + switch (memmodel) { + case MM_TEXT: + return ALIGN(width * height * 2, 2*1024); + case MM_CGA: + return 16*1024; + default: + return ALIGN(width * height / 8, 8*1024); + } +} + +// Determine cursor shape (taking into account possible cursor scaling) +u16 +get_cursor_shape(void) +{ + u16 cursor_type = GET_BDA(cursor_type); + u8 emulate_cursor = (GET_BDA(video_ctl) & 1) == 0; + if (!emulate_cursor) + return cursor_type; + u8 start = (cursor_type >> 8) & 0x3f; + u8 end = cursor_type & 0x1f; + u16 cheight = GET_BDA(char_height); + if (cheight <= 8 || end >= 8 || start >= 0x20) + return cursor_type; + if (end != (start + 1)) + start = ((start + 1) * cheight / 8) - 1; + else + start = ((end + 1) * cheight / 8) - 2; + end = ((end + 1) * cheight / 8) - 1; + return (start << 8) | end; +} + +static void +set_cursor_shape(u16 cursor_type) +{ + vgafb_set_swcursor(0); + SET_BDA(cursor_type, cursor_type); + if (CONFIG_VGA_STDVGA_PORTS) + stdvga_set_cursor_shape(get_cursor_shape()); +} + +static void +set_cursor_pos(struct cursorpos cp) +{ + u8 page = cp.page, x = cp.x, y = cp.y; + + // Should not happen... + if (page > 7) + return; + + vgafb_set_swcursor(0); + + // Bios cursor pos + SET_BDA(cursor_pos[page], (y << 8) | x); + + if (!CONFIG_VGA_STDVGA_PORTS) + return; + + // Set the hardware cursor + u8 current = GET_BDA(video_page); + if (cp.page != current) + return; + + // Calculate the memory address + stdvga_set_cursor_pos((int)text_address(cp)); +} + +struct cursorpos +get_cursor_pos(u8 page) +{ + if (page == 0xff) + // special case - use current page + page = GET_BDA(video_page); + if (page > 7) { + struct cursorpos cp = { 0, 0, 0xfe }; + return cp; + } + u16 xy = GET_BDA(cursor_pos[page]); + struct cursorpos cp = {xy, xy>>8, page}; + return cp; +} + +static void +set_active_page(u8 page) +{ + if (page > 7) + return; + + // Get the mode + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + + vgafb_set_swcursor(0); + + // Calculate memory address of start of page + struct cursorpos cp = {0, 0, page}; + int address = (int)text_address(cp); + vgahw_set_displaystart(vmode_g, address); + + // And change the BIOS page + SET_BDA(video_pagestart, address); + SET_BDA(video_page, page); + + dprintf(1, "Set active page %02x address %04x\n", page, address); + + // Display the cursor, now the page is active + set_cursor_pos(get_cursor_pos(page)); +} + +static void +set_scan_lines(u8 lines) +{ + stdvga_set_scan_lines(lines); + SET_BDA(char_height, lines); + u16 vde = stdvga_get_vde(); + u8 rows = vde / lines; + SET_BDA(video_rows, rows - 1); + u16 cols = GET_BDA(video_cols); + SET_BDA(video_pagesize, calc_page_size(MM_TEXT, cols, rows)); + if (lines == 8) + set_cursor_shape(0x0607); + else + set_cursor_shape(((lines - 3) << 8) | (lines - 2)); +} + + +/**************************************************************** + * Character writing + ****************************************************************/ + +// Write a character to the screen and calculate new cursor position. +static void +write_char(struct cursorpos *pcp, struct carattr ca) +{ + vgafb_write_char(*pcp, ca); + pcp->x++; + // Do we need to wrap ? + if (pcp->x == GET_BDA(video_cols)) { + pcp->x = 0; + pcp->y++; + } +} + +// Write a character to the screen at a given position. Implement +// special characters and scroll the screen if necessary. +static void +write_teletype(struct cursorpos *pcp, struct carattr ca) +{ + switch (ca.car) { + case 7: + //FIXME should beep + break; + case 8: + if (pcp->x > 0) + pcp->x--; + break; + case '\r': + pcp->x = 0; + break; + case '\n': + pcp->y++; + break; + default: + write_char(pcp, ca); + break; + } + + // Do we need to scroll ? + u16 nbrows = GET_BDA(video_rows); + if (pcp->y > nbrows) { + pcp->y--; + + struct cursorpos dest = {0, 0, pcp->page}; + struct cursorpos src = {0, 1, pcp->page}; + struct cursorpos size = {GET_BDA(video_cols), nbrows}; + vgafb_move_chars(dest, src, size); + + struct cursorpos clr = {0, nbrows, pcp->page}; + struct carattr attr = {' ', 0, 0}; + struct cursorpos clrsize = {GET_BDA(video_cols), 1}; + vgafb_clear_chars(clr, attr, clrsize); + } +} + + +/**************************************************************** + * Save and restore bda state + ****************************************************************/ + +struct saveBDAstate { + u8 bda_0x49[28]; + u8 bda_0x84[6]; + u16 vbe_mode; + struct segoff_s font0; + struct segoff_s font1; +}; + +int +bda_save_restore(int cmd, u16 seg, void *data) +{ + if (!(cmd & SR_BDA)) + return 0; + struct saveBDAstate *info = data; + if (cmd & SR_SAVE) { + memcpy_far(seg, info->bda_0x49, SEG_BDA, (void*)0x49 + , sizeof(info->bda_0x49)); + memcpy_far(seg, info->bda_0x84, SEG_BDA, (void*)0x84 + , sizeof(info->bda_0x84)); + SET_FARVAR(seg, info->vbe_mode, GET_BDA_EXT(vbe_mode)); + SET_FARVAR(seg, info->font0, GET_IVT(0x1f)); + SET_FARVAR(seg, info->font1, GET_IVT(0x43)); + } + if (cmd & SR_RESTORE) { + memcpy_far(SEG_BDA, (void*)0x49, seg, info->bda_0x49 + , sizeof(info->bda_0x49)); + memcpy_far(SEG_BDA, (void*)0x84, seg, info->bda_0x84 + , sizeof(info->bda_0x84)); + u16 vbe_mode = GET_FARVAR(seg, info->vbe_mode); + SET_BDA_EXT(vbe_mode, vbe_mode); + struct vgamode_s *vmode_g = vgahw_find_mode(vbe_mode & ~MF_VBEFLAGS); + SET_BDA_EXT(vgamode_offset, (u32)vmode_g); + SET_IVT(0x1f, GET_FARVAR(seg, info->font0)); + SET_IVT(0x43, GET_FARVAR(seg, info->font1)); + } + return sizeof(*info); +} + + +/**************************************************************** + * Mode setting + ****************************************************************/ + +struct vgamode_s * +get_current_mode(void) +{ + return (void*)(GET_BDA_EXT(vgamode_offset)+0); +} + +// Setup BDA after a mode switch. +int +vga_set_mode(int mode, int flags) +{ + dprintf(1, "set VGA mode %x\n", mode); + struct vgamode_s *vmode_g = vgahw_find_mode(mode); + if (!vmode_g) + return VBE_RETURN_STATUS_FAILED; + + vgafb_set_swcursor(0); + + int ret = vgahw_set_mode(vmode_g, flags); + if (ret) + return ret; + + // Set the BIOS mem + int width = GET_GLOBAL(vmode_g->width); + int height = GET_GLOBAL(vmode_g->height); + u8 memmodel = GET_GLOBAL(vmode_g->memmodel); + int cheight = GET_GLOBAL(vmode_g->cheight); + if (mode < 0x100) + SET_BDA(video_mode, mode); + else + SET_BDA(video_mode, 0xff); + SET_BDA_EXT(vbe_mode, mode | (flags & MF_VBEFLAGS)); + SET_BDA_EXT(vgamode_offset, (u32)vmode_g); + if (memmodel == MM_TEXT) { + SET_BDA(video_cols, width); + SET_BDA(video_rows, height-1); + SET_BDA(cursor_type, 0x0607); + } else { + int cwidth = GET_GLOBAL(vmode_g->cwidth); + SET_BDA(video_cols, width / cwidth); + SET_BDA(video_rows, (height / cheight) - 1); + SET_BDA(cursor_type, vga_emulate_text() ? 0x0607 : 0x0000); + } + SET_BDA(video_pagesize, calc_page_size(memmodel, width, height)); + SET_BDA(crtc_address, CONFIG_VGA_STDVGA_PORTS ? stdvga_get_crtc() : 0); + SET_BDA(char_height, cheight); + SET_BDA(video_ctl, 0x60 | (flags & MF_NOCLEARMEM ? 0x80 : 0x00)); + SET_BDA(video_switches, 0xF9); + SET_BDA(modeset_ctl, GET_BDA(modeset_ctl) & 0x7f); + int i; + for (i=0; i<8; i++) + SET_BDA(cursor_pos[i], 0x0000); + SET_BDA(video_pagestart, 0x0000); + SET_BDA(video_page, 0x00); + + // Set the ints 0x1F and 0x43 + SET_IVT(0x1f, SEGOFF(get_global_seg(), (u32)&vgafont8[128 * 8])); + + switch (cheight) { + case 8: + SET_IVT(0x43, SEGOFF(get_global_seg(), (u32)vgafont8)); + break; + case 14: + SET_IVT(0x43, SEGOFF(get_global_seg(), (u32)vgafont14)); + break; + case 16: + SET_IVT(0x43, SEGOFF(get_global_seg(), (u32)vgafont16)); + break; + } + + return 0; +} + + +/**************************************************************** + * VGA int 10 handler + ****************************************************************/ + +static void +handle_1000(struct bregs *regs) +{ + int mode = regs->al & 0x7f; + + // Set regs->al + if (mode > 7) + regs->al = 0x20; + else if (mode == 6) + regs->al = 0x3f; + else + regs->al = 0x30; + + int flags = MF_LEGACY | (GET_BDA(modeset_ctl) & (MF_NOPALETTE|MF_GRAYSUM)); + if (regs->al & 0x80) + flags |= MF_NOCLEARMEM; + + vga_set_mode(mode, flags); +} + +static void +handle_1001(struct bregs *regs) +{ + set_cursor_shape(regs->cx); +} + +static void +handle_1002(struct bregs *regs) +{ + struct cursorpos cp = {regs->dl, regs->dh, regs->bh}; + set_cursor_pos(cp); +} + +static void +handle_1003(struct bregs *regs) +{ + regs->cx = GET_BDA(cursor_type); + struct cursorpos cp = get_cursor_pos(regs->bh); + regs->dl = cp.x; + regs->dh = cp.y; +} + +// Read light pen pos (unimplemented) +static void +handle_1004(struct bregs *regs) +{ + debug_stub(regs); + regs->ax = regs->bx = regs->cx = regs->dx = 0; +} + +static void +handle_1005(struct bregs *regs) +{ + set_active_page(regs->al); +} + +static void +verify_scroll(struct bregs *regs, int dir) +{ + u8 ulx = regs->cl, uly = regs->ch, lrx = regs->dl, lry = regs->dh; + u16 nbrows = GET_BDA(video_rows) + 1; + if (lry >= nbrows) + lry = nbrows - 1; + u16 nbcols = GET_BDA(video_cols); + if (lrx >= nbcols) + lrx = nbcols - 1; + int wincols = lrx - ulx + 1, winrows = lry - uly + 1; + if (wincols <= 0 || winrows <= 0) + return; + + u8 page = GET_BDA(video_page); + int clearlines = regs->al, movelines = winrows - clearlines; + if (!clearlines || movelines <= 0) { + // Clear whole area. + struct cursorpos clr = {ulx, uly, page}; + struct carattr attr = {' ', regs->bh, 1}; + struct cursorpos clrsize = {wincols, winrows}; + vgafb_clear_chars(clr, attr, clrsize); + return; + } + + if (dir > 0) { + // Normal scroll + struct cursorpos dest = {ulx, uly, page}; + struct cursorpos src = {ulx, uly + clearlines, page}; + struct cursorpos size = {wincols, movelines}; + vgafb_move_chars(dest, src, size); + + struct cursorpos clr = {ulx, uly + movelines, page}; + struct carattr attr = {' ', regs->bh, 1}; + struct cursorpos clrsize = {wincols, clearlines}; + vgafb_clear_chars(clr, attr, clrsize); + } else { + // Scroll down + struct cursorpos dest = {ulx, uly + clearlines, page}; + struct cursorpos src = {ulx, uly, page}; + struct cursorpos size = {wincols, movelines}; + vgafb_move_chars(dest, src, size); + + struct cursorpos clr = {ulx, uly, page}; + struct carattr attr = {' ', regs->bh, 1}; + struct cursorpos clrsize = {wincols, clearlines}; + vgafb_clear_chars(clr, attr, clrsize); + } +} + +static void +handle_1006(struct bregs *regs) +{ + verify_scroll(regs, 1); +} + +static void +handle_1007(struct bregs *regs) +{ + verify_scroll(regs, -1); +} + +static void +handle_1008(struct bregs *regs) +{ + struct carattr ca = vgafb_read_char(get_cursor_pos(regs->bh)); + regs->al = ca.car; + regs->ah = ca.attr; +} + +static void noinline +handle_1009(struct bregs *regs) +{ + struct carattr ca = {regs->al, regs->bl, 1}; + struct cursorpos cp = get_cursor_pos(regs->bh); + int count = regs->cx; + while (count--) + write_char(&cp, ca); +} + +static void noinline +handle_100a(struct bregs *regs) +{ + struct carattr ca = {regs->al, regs->bl, 0}; + struct cursorpos cp = get_cursor_pos(regs->bh); + int count = regs->cx; + while (count--) + write_char(&cp, ca); +} + + +static void +handle_100b00(struct bregs *regs) +{ + stdvga_set_border_color(regs->bl); +} + +static void +handle_100b01(struct bregs *regs) +{ + stdvga_set_palette(regs->bl); +} + +static void +handle_100bXX(struct bregs *regs) +{ + debug_stub(regs); +} + +static void +handle_100b(struct bregs *regs) +{ + if (!CONFIG_VGA_STDVGA_PORTS) { + handle_100bXX(regs); + return; + } + switch (regs->bh) { + case 0x00: handle_100b00(regs); break; + case 0x01: handle_100b01(regs); break; + default: handle_100bXX(regs); break; + } +} + + +static void +handle_100c(struct bregs *regs) +{ + // XXX - page (regs->bh) is unused + vgafb_write_pixel(regs->al, regs->cx, regs->dx); +} + +static void +handle_100d(struct bregs *regs) +{ + // XXX - page (regs->bh) is unused + regs->al = vgafb_read_pixel(regs->cx, regs->dx); +} + +static void noinline +handle_100e(struct bregs *regs) +{ + // Ralf Brown Interrupt list is WRONG on bh(page) + // We do output only on the current page ! + struct carattr ca = {regs->al, regs->bl, 0}; + struct cursorpos cp = get_cursor_pos(0xff); + write_teletype(&cp, ca); + set_cursor_pos(cp); +} + +static void +handle_100f(struct bregs *regs) +{ + regs->bh = GET_BDA(video_page); + regs->al = GET_BDA(video_mode) | (GET_BDA(video_ctl) & 0x80); + regs->ah = GET_BDA(video_cols); +} + + +static void +handle_101000(struct bregs *regs) +{ + if (regs->bl > 0x14) + return; + stdvga_attr_write(regs->bl, regs->bh); +} + +static void +handle_101001(struct bregs *regs) +{ + stdvga_set_overscan_border_color(regs->bh); +} + +static void +handle_101002(struct bregs *regs) +{ + stdvga_set_all_palette_reg(regs->es, (u8*)(regs->dx + 0)); +} + +static void +handle_101003(struct bregs *regs) +{ + stdvga_toggle_intensity(regs->bl); +} + +static void +handle_101007(struct bregs *regs) +{ + if (regs->bl > 0x14) + return; + regs->bh = stdvga_attr_read(regs->bl); +} + +static void +handle_101008(struct bregs *regs) +{ + regs->bh = stdvga_get_overscan_border_color(); +} + +static void +handle_101009(struct bregs *regs) +{ + stdvga_get_all_palette_reg(regs->es, (u8*)(regs->dx + 0)); +} + +static void noinline +handle_101010(struct bregs *regs) +{ + u8 rgb[3] = {regs->dh, regs->ch, regs->cl}; + stdvga_dac_write(GET_SEG(SS), rgb, regs->bx, 1); +} + +static void +handle_101012(struct bregs *regs) +{ + stdvga_dac_write(regs->es, (u8*)(regs->dx + 0), regs->bx, regs->cx); +} + +static void +handle_101013(struct bregs *regs) +{ + stdvga_select_video_dac_color_page(regs->bl, regs->bh); +} + +static void noinline +handle_101015(struct bregs *regs) +{ + u8 rgb[3]; + stdvga_dac_read(GET_SEG(SS), rgb, regs->bx, 1); + regs->dh = rgb[0]; + regs->ch = rgb[1]; + regs->cl = rgb[2]; +} + +static void +handle_101017(struct bregs *regs) +{ + stdvga_dac_read(regs->es, (u8*)(regs->dx + 0), regs->bx, regs->cx); +} + +static void +handle_101018(struct bregs *regs) +{ + stdvga_pelmask_write(regs->bl); +} + +static void +handle_101019(struct bregs *regs) +{ + regs->bl = stdvga_pelmask_read(); +} + +static void +handle_10101a(struct bregs *regs) +{ + stdvga_read_video_dac_state(®s->bl, ®s->bh); +} + +static void +handle_10101b(struct bregs *regs) +{ + stdvga_perform_gray_scale_summing(regs->bx, regs->cx); +} + +static void +handle_1010XX(struct bregs *regs) +{ + debug_stub(regs); +} + +static void +handle_1010(struct bregs *regs) +{ + if (!CONFIG_VGA_STDVGA_PORTS) { + handle_1010XX(regs); + return; + } + switch (regs->al) { + case 0x00: handle_101000(regs); break; + case 0x01: handle_101001(regs); break; + case 0x02: handle_101002(regs); break; + case 0x03: handle_101003(regs); break; + case 0x07: handle_101007(regs); break; + case 0x08: handle_101008(regs); break; + case 0x09: handle_101009(regs); break; + case 0x10: handle_101010(regs); break; + case 0x12: handle_101012(regs); break; + case 0x13: handle_101013(regs); break; + case 0x15: handle_101015(regs); break; + case 0x17: handle_101017(regs); break; + case 0x18: handle_101018(regs); break; + case 0x19: handle_101019(regs); break; + case 0x1a: handle_10101a(regs); break; + case 0x1b: handle_10101b(regs); break; + default: handle_1010XX(regs); break; + } +} + + +static void +handle_101100(struct bregs *regs) +{ + stdvga_load_font(regs->es, (void*)(regs->bp+0), regs->cx + , regs->dx, regs->bl, regs->bh); +} + +static void +handle_101101(struct bregs *regs) +{ + stdvga_load_font(get_global_seg(), vgafont14, 0x100, 0, regs->bl, 14); +} + +static void +handle_101102(struct bregs *regs) +{ + stdvga_load_font(get_global_seg(), vgafont8, 0x100, 0, regs->bl, 8); +} + +static void +handle_101103(struct bregs *regs) +{ + stdvga_set_text_block_specifier(regs->bl); +} + +static void +handle_101104(struct bregs *regs) +{ + stdvga_load_font(get_global_seg(), vgafont16, 0x100, 0, regs->bl, 16); +} + +static void +handle_101110(struct bregs *regs) +{ + stdvga_load_font(regs->es, (void*)(regs->bp+0), regs->cx + , regs->dx, regs->bl, regs->bh); + set_scan_lines(regs->bh); +} + +static void +handle_101111(struct bregs *regs) +{ + stdvga_load_font(get_global_seg(), vgafont14, 0x100, 0, regs->bl, 14); + set_scan_lines(14); +} + +static void +handle_101112(struct bregs *regs) +{ + stdvga_load_font(get_global_seg(), vgafont8, 0x100, 0, regs->bl, 8); + set_scan_lines(8); +} + +static void +handle_101114(struct bregs *regs) +{ + stdvga_load_font(get_global_seg(), vgafont16, 0x100, 0, regs->bl, 16); + set_scan_lines(16); +} + +static void +handle_101120(struct bregs *regs) +{ + SET_IVT(0x1f, SEGOFF(regs->es, regs->bp)); +} + +void +load_gfx_font(u16 seg, u16 off, u8 height, u8 bl, u8 dl) +{ + u8 rows; + + SET_IVT(0x43, SEGOFF(seg, off)); + switch(bl) { + case 0: + rows = dl; + break; + case 1: + rows = 14; + break; + case 3: + rows = 43; + break; + case 2: + default: + rows = 25; + break; + } + SET_BDA(video_rows, rows - 1); + SET_BDA(char_height, height); +} + +static void +handle_101121(struct bregs *regs) +{ + load_gfx_font(regs->es, regs->bp, regs->cx, regs->bl, regs->dl); +} + +static void +handle_101122(struct bregs *regs) +{ + load_gfx_font(get_global_seg(), (u32)vgafont14, 14, regs->bl, regs->dl); +} + +static void +handle_101123(struct bregs *regs) +{ + load_gfx_font(get_global_seg(), (u32)vgafont8, 8, regs->bl, regs->dl); +} + +static void +handle_101124(struct bregs *regs) +{ + load_gfx_font(get_global_seg(), (u32)vgafont16, 16, regs->bl, regs->dl); +} + +static void +handle_101130(struct bregs *regs) +{ + switch (regs->bh) { + case 0x00: { + struct segoff_s so = GET_IVT(0x1f); + regs->es = so.seg; + regs->bp = so.offset; + break; + } + case 0x01: { + struct segoff_s so = GET_IVT(0x43); + regs->es = so.seg; + regs->bp = so.offset; + break; + } + case 0x02: + regs->es = get_global_seg(); + regs->bp = (u32)vgafont14; + break; + case 0x03: + regs->es = get_global_seg(); + regs->bp = (u32)vgafont8; + break; + case 0x04: + regs->es = get_global_seg(); + regs->bp = (u32)vgafont8 + 128 * 8; + break; + case 0x05: + regs->es = get_global_seg(); + regs->bp = (u32)vgafont14alt; + break; + case 0x06: + regs->es = get_global_seg(); + regs->bp = (u32)vgafont16; + break; + case 0x07: + regs->es = get_global_seg(); + regs->bp = (u32)vgafont16alt; + break; + default: + dprintf(1, "Get font info BH(%02x) was discarded\n", regs->bh); + return; + } + // Set byte/char of on screen font + regs->cx = GET_BDA(char_height) & 0xff; + + // Set Highest char row + regs->dl = GET_BDA(video_rows); +} + +static void +handle_1011XX(struct bregs *regs) +{ + debug_stub(regs); +} + +static void +handle_1011(struct bregs *regs) +{ + if (CONFIG_VGA_STDVGA_PORTS) { + switch (regs->al) { + case 0x00: handle_101100(regs); return; + case 0x01: handle_101101(regs); return; + case 0x02: handle_101102(regs); return; + case 0x03: handle_101103(regs); return; + case 0x04: handle_101104(regs); return; + case 0x10: handle_101110(regs); return; + case 0x11: handle_101111(regs); return; + case 0x12: handle_101112(regs); return; + case 0x14: handle_101114(regs); return; + } + } + switch (regs->al) { + case 0x30: handle_101130(regs); break; + case 0x20: handle_101120(regs); break; + case 0x21: handle_101121(regs); break; + case 0x22: handle_101122(regs); break; + case 0x23: handle_101123(regs); break; + case 0x24: handle_101124(regs); break; + default: handle_1011XX(regs); break; + } +} + + +static void +handle_101210(struct bregs *regs) +{ + u16 crtc_addr = GET_BDA(crtc_address); + if (crtc_addr == VGAREG_MDA_CRTC_ADDRESS) + regs->bx = 0x0103; + else + regs->bx = 0x0003; + regs->cx = GET_BDA(video_switches) & 0x0f; +} + +static void +handle_101230(struct bregs *regs) +{ + u8 mctl = GET_BDA(modeset_ctl); + u8 vswt = GET_BDA(video_switches); + switch (regs->al) { + case 0x00: + // 200 lines + mctl = (mctl & ~0x10) | 0x80; + vswt = (vswt & ~0x0f) | 0x08; + break; + case 0x01: + // 350 lines + mctl &= ~0x90; + vswt = (vswt & ~0x0f) | 0x09; + break; + case 0x02: + // 400 lines + mctl = (mctl & ~0x80) | 0x10; + vswt = (vswt & ~0x0f) | 0x09; + break; + default: + dprintf(1, "Select vert res (%02x) was discarded\n", regs->al); + break; + } + SET_BDA(modeset_ctl, mctl); + SET_BDA(video_switches, vswt); + regs->al = 0x12; +} + +static void +handle_101231(struct bregs *regs) +{ + u8 v = (regs->al & 0x01) << 3; + u8 mctl = GET_BDA(video_ctl) & ~0x08; + SET_BDA(video_ctl, mctl | v); + regs->al = 0x12; +} + +static void +handle_101232(struct bregs *regs) +{ + if (CONFIG_VGA_STDVGA_PORTS) { + stdvga_enable_video_addressing(regs->al); + regs->al = 0x12; + } +} + +static void +handle_101233(struct bregs *regs) +{ + u8 v = ((regs->al << 1) & 0x02) ^ 0x02; + u8 v2 = GET_BDA(modeset_ctl) & ~0x02; + SET_BDA(modeset_ctl, v | v2); + regs->al = 0x12; +} + +static void +handle_101234(struct bregs *regs) +{ + SET_BDA(video_ctl, (GET_BDA(video_ctl) & ~0x01) | (regs->al & 0x01)); + regs->al = 0x12; +} + +static void +handle_101235(struct bregs *regs) +{ + debug_stub(regs); + regs->al = 0x12; +} + +static void +handle_101236(struct bregs *regs) +{ + debug_stub(regs); + regs->al = 0x12; +} + +static void +handle_1012XX(struct bregs *regs) +{ + debug_stub(regs); +} + +static void +handle_1012(struct bregs *regs) +{ + if (CONFIG_VGA_CIRRUS && regs->bl >= 0x80) { + clext_1012(regs); + return; + } + + switch (regs->bl) { + case 0x10: handle_101210(regs); break; + case 0x30: handle_101230(regs); break; + case 0x31: handle_101231(regs); break; + case 0x32: handle_101232(regs); break; + case 0x33: handle_101233(regs); break; + case 0x34: handle_101234(regs); break; + case 0x35: handle_101235(regs); break; + case 0x36: handle_101236(regs); break; + default: handle_1012XX(regs); break; + } +} + + +// Write string +static void noinline +handle_1013(struct bregs *regs) +{ + struct cursorpos cp; + if (regs->dh == 0xff) + // if row=0xff special case : use current cursor position + cp = get_cursor_pos(regs->bh); + else + cp = (struct cursorpos) {regs->dl, regs->dh, regs->bh}; + + u16 count = regs->cx; + u8 *offset_far = (void*)(regs->bp + 0); + u8 attr = regs->bl; + while (count--) { + u8 car = GET_FARVAR(regs->es, *offset_far); + offset_far++; + if (regs->al & 2) { + attr = GET_FARVAR(regs->es, *offset_far); + offset_far++; + } + + struct carattr ca = {car, attr, 1}; + write_teletype(&cp, ca); + } + + if (regs->al & 1) + set_cursor_pos(cp); +} + + +static void +handle_101a00(struct bregs *regs) +{ + regs->bx = GET_BDA(dcc_index); + regs->al = 0x1a; +} + +static void +handle_101a01(struct bregs *regs) +{ + SET_BDA(dcc_index, regs->bl); + dprintf(1, "Alternate Display code (%02x) was discarded\n", regs->bh); + regs->al = 0x1a; +} + +static void +handle_101aXX(struct bregs *regs) +{ + debug_stub(regs); +} + +static void +handle_101a(struct bregs *regs) +{ + switch (regs->al) { + case 0x00: handle_101a00(regs); break; + case 0x01: handle_101a01(regs); break; + default: handle_101aXX(regs); break; + } +} + + +struct video_func_static static_functionality VAR16 = { + .modes = 0x00, // Filled in by stdvga_build_video_param() + .scanlines = 0x07, // 200, 350, 400 scan lines + .cblocks = 0x02, // mamimum number of visible charsets in text mode + .active_cblocks = 0x08, // total number of charset blocks in text mode + .misc_flags = 0x0ce7, +}; + +static void +handle_101b(struct bregs *regs) +{ + u16 seg = regs->es; + struct video_func_info *info = (void*)(regs->di+0); + memset_far(seg, info, 0, sizeof(*info)); + // Address of static functionality table + SET_FARVAR(seg, info->static_functionality + , SEGOFF(get_global_seg(), (u32)&static_functionality)); + + // Hard coded copy from BIOS area. Should it be cleaner ? + memcpy_far(seg, info->bda_0x49, SEG_BDA, (void*)0x49 + , sizeof(info->bda_0x49)); + memcpy_far(seg, info->bda_0x84, SEG_BDA, (void*)0x84 + , sizeof(info->bda_0x84)); + + SET_FARVAR(seg, info->dcc_index, GET_BDA(dcc_index)); + SET_FARVAR(seg, info->colors, 16); + SET_FARVAR(seg, info->pages, 8); + SET_FARVAR(seg, info->scan_lines, 2); + SET_FARVAR(seg, info->video_mem, 3); + regs->al = 0x1B; +} + + +static void +handle_101c(struct bregs *regs) +{ + u16 seg = regs->es; + void *data = (void*)(regs->bx+0); + u16 states = regs->cx; + u8 cmd = regs->al; + if (states & ~0x07 || cmd > 2) + goto fail; + int ret = vgahw_save_restore(states | (cmd<<8), seg, data); + if (ret < 0) + goto fail; + if (cmd == 0) + regs->bx = ret / 64; + regs->al = 0x1c; +fail: + return; +} + +static void +handle_10XX(struct bregs *regs) +{ + debug_stub(regs); +} + +// INT 10h Video Support Service Entry Point +void VISIBLE16 +handle_10(struct bregs *regs) +{ + debug_enter(regs, DEBUG_VGA_10); + switch (regs->ah) { + case 0x00: handle_1000(regs); break; + case 0x01: handle_1001(regs); break; + case 0x02: handle_1002(regs); break; + case 0x03: handle_1003(regs); break; + case 0x04: handle_1004(regs); break; + case 0x05: handle_1005(regs); break; + case 0x06: handle_1006(regs); break; + case 0x07: handle_1007(regs); break; + case 0x08: handle_1008(regs); break; + case 0x09: handle_1009(regs); break; + case 0x0a: handle_100a(regs); break; + case 0x0b: handle_100b(regs); break; + case 0x0c: handle_100c(regs); break; + case 0x0d: handle_100d(regs); break; + case 0x0e: handle_100e(regs); break; + case 0x0f: handle_100f(regs); break; + case 0x10: handle_1010(regs); break; + case 0x11: handle_1011(regs); break; + case 0x12: handle_1012(regs); break; + case 0x13: handle_1013(regs); break; + case 0x1a: handle_101a(regs); break; + case 0x1b: handle_101b(regs); break; + case 0x1c: handle_101c(regs); break; + case 0x4f: handle_104f(regs); break; + default: handle_10XX(regs); break; + } +} diff --git a/qemu/roms/seabios/vgasrc/vgabios.h b/qemu/roms/seabios/vgasrc/vgabios.h new file mode 100644 index 000000000..fd796f2e6 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgabios.h @@ -0,0 +1,149 @@ +#ifndef __VGABIOS_H +#define __VGABIOS_H + +#include "config.h" // CONFIG_VGA_EMULATE_TEXT +#include "types.h" // u8 +#include "farptr.h" // struct segoff_s +#include "std/vga.h" // struct video_param_s + +// Save/Restore flags +#define SR_HARDWARE 0x0001 +#define SR_BDA 0x0002 +#define SR_DAC 0x0004 +#define SR_REGISTERS 0x0008 +#define SR_SAVE 0x0100 +#define SR_RESTORE 0x0200 + +// Mode flags +#define MF_LEGACY 0x0001 +#define MF_GRAYSUM 0x0002 +#define MF_NOPALETTE 0x0008 +#define MF_CUSTOMCRTC 0x0800 +#define MF_LINEARFB 0x4000 +#define MF_NOCLEARMEM 0x8000 +#define MF_VBEFLAGS 0xfe00 + +// Memory model types +#define MM_TEXT 0x00 +#define MM_CGA 0x01 +#define MM_HERCULES 0x02 +#define MM_PLANAR 0x03 +#define MM_PACKED 0x04 +#define MM_NON_CHAIN_4_256 0x05 +#define MM_DIRECT 0x06 +#define MM_YUV 0x07 + +struct vgamode_s { + u8 memmodel; + u16 width; + u16 height; + u8 depth; + u8 cwidth; + u8 cheight; + u16 sstart; +}; + +// Graphics pixel operations. +struct gfx_op { + struct vgamode_s *vmode_g; + u32 linelength; + u32 displaystart; + + u8 op; + u16 x, y; + + u8 pixels[8]; + u16 xlen, ylen; + u16 srcy; +}; + +#define GO_READ8 1 +#define GO_WRITE8 2 +#define GO_MEMSET 3 +#define GO_MEMMOVE 4 + +// Custom internal storage in BDA +#define VGA_CUSTOM_BDA 0xb9 + +struct vga_bda_s { + u8 flags; + u16 vbe_mode; + u16 vgamode_offset; +} PACKED; + +#define BF_PM_MASK 0x0f +#define BF_EMULATE_TEXT 0x10 +#define BF_SWCURSOR 0x20 + +#define GET_BDA_EXT(var) \ + GET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var) +#define SET_BDA_EXT(var, val) \ + SET_FARVAR(SEG_BDA, ((struct vga_bda_s *)VGA_CUSTOM_BDA)->var, (val)) +#define MASK_BDA_EXT(var, off, on) \ + SET_BDA_EXT(var, (GET_BDA_EXT(var) & ~(off)) | (on)) + +static inline int vga_emulate_text(void) { + return CONFIG_VGA_EMULATE_TEXT && GET_BDA_EXT(flags) & BF_EMULATE_TEXT; +} + +// Debug settings +#define DEBUG_VGA_POST 1 +#define DEBUG_VGA_10 3 + +// vgafonts.c +extern u8 vgafont8[]; +extern u8 vgafont14[]; +extern u8 vgafont16[]; +extern u8 vgafont14alt[]; +extern u8 vgafont16alt[]; + +// vgainit.c +extern struct video_save_pointer_s video_save_pointer_table; +extern struct video_param_s video_param_table[29]; + +// vgabios.c +extern int VgaBDF; +extern int HaveRunInit; +#define SET_VGA(var, val) SET_FARVAR(get_global_seg(), (var), (val)) +struct carattr { + u8 car, attr, use_attr, pad; +}; +struct cursorpos { + u8 x, y, page, pad; +}; +int vga_bpp(struct vgamode_s *vmode_g); +u16 calc_page_size(u8 memmodel, u16 width, u16 height); +u16 get_cursor_shape(void); +struct cursorpos get_cursor_pos(u8 page); +int bda_save_restore(int cmd, u16 seg, void *data); +struct vgamode_s *get_current_mode(void); +int vga_set_mode(int mode, int flags); +extern struct video_func_static static_functionality; + +// vgafb.c +void init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g); +void handle_gfx_op(struct gfx_op *op); +void *text_address(struct cursorpos cp); +void vgafb_move_chars(struct cursorpos dest + , struct cursorpos src, struct cursorpos movesize); +void vgafb_clear_chars(struct cursorpos dest + , struct carattr ca, struct cursorpos movesize); +void vgafb_write_char(struct cursorpos cp, struct carattr ca); +struct carattr vgafb_read_char(struct cursorpos cp); +void vgafb_write_pixel(u8 color, u16 x, u16 y); +u8 vgafb_read_pixel(u16 x, u16 y); +void vgafb_set_swcursor(int enable); + +// vbe.c +extern u32 VBE_total_memory; +extern u32 VBE_capabilities; +extern u32 VBE_framebuffer; +extern u16 VBE_win_granularity; +#define VBE_OEM_STRING "SeaBIOS VBE(C) 2011" +#define VBE_VENDOR_STRING "SeaBIOS Developers" +#define VBE_PRODUCT_STRING "SeaBIOS VBE Adapter" +#define VBE_REVISION_STRING "Rev. 1" +struct bregs; +void handle_104f(struct bregs *regs); + +#endif // vgabios.h diff --git a/qemu/roms/seabios/vgasrc/vgaentry.S b/qemu/roms/seabios/vgasrc/vgaentry.S new file mode 100644 index 000000000..d9ebdb93c --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgaentry.S @@ -0,0 +1,147 @@ +// Rom layout and bios assembler to C interface. +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "asm-offsets.h" // BREGS_* +#include "config.h" // CONFIG_* +#include "entryfuncs.S" // ENTRY_* + + +/**************************************************************** + * Rom Header + ****************************************************************/ + + .section .rom.header + .code16 + .global _rom_header, _rom_header_size, _rom_header_checksum +_rom_header: + .word 0xaa55 +_rom_header_size: + .byte 0 +_rom_header_entry: + jmp _optionrom_entry +_rom_header_checksum: + .byte 0 +_rom_header_other: + .space 17 +_rom_header_pcidata: +#if CONFIG_VGA_PCI == 1 + .word rom_pci_data +#else + .word 0 +#endif +_rom_header_pnpdata: + .word 0 +_rom_header_other2: + .word 0 +_rom_header_signature: + .asciz "IBM" + + +/**************************************************************** + * Entry points + ****************************************************************/ + + // This macro implements a call while avoiding instructions + // that old versions of x86emu have problems with. + .macro VGA_CALLL cfunc +#if CONFIG_VGA_FIXUP_ASM + pushw %ax + callw \cfunc +#else + calll \cfunc +#endif + .endm + + // This macro is the same as ENTRY_ARG except VGA_CALLL is used. + .macro ENTRY_ARG_VGA cfunc + cli + cld + PUSHBREGS + movw %ss, %ax // Move %ss to %ds + movw %ax, %ds + movl %esp, %ebx // Backup %esp, then zero high bits + movzwl %sp, %esp + movl %esp, %eax // First arg is pointer to struct bregs + VGA_CALLL \cfunc + movl %ebx, %esp // Restore %esp (including high bits) + POPBREGS + .endm + + DECLFUNC entry_104f05 +entry_104f05: + ENTRY_ARG_VGA vbe_104f05 + lretw + + DECLFUNC _optionrom_entry +_optionrom_entry: + ENTRY_ARG_VGA vga_post + lretw + + DECLFUNC entry_10 +entry_10: + ENTRY_ARG_VGA handle_10 + iretw + + // Entry point using extra stack + DECLFUNC entry_10_extrastack +entry_10_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movw %cs:ExtraStackSeg, %ds + movl $(CONFIG_VGA_EXTRA_STACK_SIZE-PUSHBREGS_size-16), %eax + SAVEBREGS_POP_DSEAX // Save registers on extra stack + movl %esp, PUSHBREGS_size+8(%eax) + movw %ss, PUSHBREGS_size+12(%eax) + popl BREGS_code(%eax) + popw BREGS_flags(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + VGA_CALLL handle_10 + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+12(%eax), %ss + movl PUSHBREGS_size+8(%eax), %esp + popl %edx + popw %dx + pushw BREGS_flags(%eax) + pushl BREGS_code(%eax) + RESTOREBREGS_DSEAX + iretw + + // Timer irq handling + DECLFUNC entry_timer_hook +entry_timer_hook: + ENTRY handle_timer_hook + ljmpw *%cs:Timer_Hook_Resume + + // Timer irq handling on extra stack + DECLFUNC entry_timer_hook_extrastack +entry_timer_hook_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movw %cs:ExtraStackSeg, %ds + movl $(CONFIG_VGA_EXTRA_STACK_SIZE-PUSHBREGS_size-8), %eax + SAVEBREGS_POP_DSEAX + movl %esp, PUSHBREGS_size(%eax) + movw %ss, PUSHBREGS_size+4(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll handle_timer_hook + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+4(%eax), %ss + movl PUSHBREGS_size(%eax), %esp + RESTOREBREGS_DSEAX + ljmpw *%cs:Timer_Hook_Resume diff --git a/qemu/roms/seabios/vgasrc/vgafb.c b/qemu/roms/seabios/vgasrc/vgafb.c new file mode 100644 index 000000000..1a94fcf70 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgafb.c @@ -0,0 +1,702 @@ +// Code for manipulating VGA framebuffers. +// +// Copyright (C) 2009-2014 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "byteorder.h" // cpu_to_be16 +#include "output.h" // dprintf +#include "stdvga.h" // stdvga_planar4_plane +#include "string.h" // memset_far +#include "vgabios.h" // vgafb_scroll +#include "vgahw.h" // vgahw_get_linelength + +static inline void +memmove_stride(u16 seg, void *dst, void *src, int copylen, int stride, int lines) +{ + if (src < dst) { + dst += stride * (lines - 1); + src += stride * (lines - 1); + stride = -stride; + } + for (; lines; lines--, dst+=stride, src+=stride) + memcpy_far(seg, dst, seg, src, copylen); +} + +static inline void +memset_stride(u16 seg, void *dst, u8 val, int setlen, int stride, int lines) +{ + for (; lines; lines--, dst+=stride) + memset_far(seg, dst, val, setlen); +} + +static inline void +memset16_stride(u16 seg, void *dst, u16 val, int setlen, int stride, int lines) +{ + for (; lines; lines--, dst+=stride) + memset16_far(seg, dst, val, setlen); +} + + +/**************************************************************** + * Basic stdvga graphic manipulation + ****************************************************************/ + +static void +gfx_planar(struct gfx_op *op) +{ + if (!CONFIG_VGA_STDVGA_PORTS) + return; + void *dest_far = (void*)(op->y * op->linelength + op->x / 8); + int plane; + switch (op->op) { + default: + case GO_READ8: + memset(op->pixels, 0, sizeof(op->pixels)); + for (plane = 0; plane < 4; plane++) { + stdvga_planar4_plane(plane); + u8 data = GET_FARVAR(SEG_GRAPH, *(u8*)dest_far); + int pixel; + for (pixel=0; pixel<8; pixel++) + op->pixels[pixel] |= ((data>>(7-pixel)) & 1) << plane; + } + break; + case GO_WRITE8: + for (plane = 0; plane<4; plane++) { + stdvga_planar4_plane(plane); + u8 data = 0; + int pixel; + for (pixel=0; pixel<8; pixel++) + data |= ((op->pixels[pixel]>>plane) & 1) << (7-pixel); + SET_FARVAR(SEG_GRAPH, *(u8*)dest_far, data); + } + break; + case GO_MEMSET: + for (plane = 0; plane < 4; plane++) { + stdvga_planar4_plane(plane); + u8 data = (op->pixels[0] & (1<<plane)) ? 0xff : 0x00; + memset_stride(SEG_GRAPH, dest_far, data + , op->xlen / 8, op->linelength, op->ylen); + } + break; + case GO_MEMMOVE: ; + void *src_far = (void*)(op->srcy * op->linelength + op->x / 8); + for (plane = 0; plane < 4; plane++) { + stdvga_planar4_plane(plane); + memmove_stride(SEG_GRAPH, dest_far, src_far + , op->xlen / 8, op->linelength, op->ylen); + } + break; + } + stdvga_planar4_plane(-1); +} + +static void +gfx_cga(struct gfx_op *op) +{ + int bpp = GET_GLOBAL(op->vmode_g->depth); + void *dest_far = (void*)(op->y / 2 * op->linelength + op->x / 8 * bpp); + switch (op->op) { + default: + case GO_READ8: + if (op->y & 1) + dest_far += 0x2000; + if (bpp == 1) { + u8 data = GET_FARVAR(SEG_CTEXT, *(u8*)dest_far); + int pixel; + for (pixel=0; pixel<8; pixel++) + op->pixels[pixel] = (data >> (7-pixel)) & 1; + } else { + u16 data = GET_FARVAR(SEG_CTEXT, *(u16*)dest_far); + data = be16_to_cpu(data); + int pixel; + for (pixel=0; pixel<8; pixel++) + op->pixels[pixel] = (data >> ((7-pixel)*2)) & 3; + } + break; + case GO_WRITE8: + if (op->y & 1) + dest_far += 0x2000; + if (bpp == 1) { + u8 data = 0; + int pixel; + for (pixel=0; pixel<8; pixel++) + data |= (op->pixels[pixel] & 1) << (7-pixel); + SET_FARVAR(SEG_CTEXT, *(u8*)dest_far, data); + } else { + u16 data = 0; + int pixel; + for (pixel=0; pixel<8; pixel++) + data |= (op->pixels[pixel] & 3) << ((7-pixel) * 2); + data = cpu_to_be16(data); + SET_FARVAR(SEG_CTEXT, *(u16*)dest_far, data); + } + break; + case GO_MEMSET: ; + u8 data = op->pixels[0]; + if (bpp == 1) + data = (data&1) | ((data&1)<<1); + data &= 3; + data |= (data<<2) | (data<<4) | (data<<6); + memset_stride(SEG_CTEXT, dest_far, data + , op->xlen / 8 * bpp, op->linelength, op->ylen / 2); + memset_stride(SEG_CTEXT, dest_far + 0x2000, data + , op->xlen / 8 * bpp, op->linelength, op->ylen / 2); + break; + case GO_MEMMOVE: ; + void *src_far = (void*)(op->srcy / 2 * op->linelength + op->x / 8 * bpp); + memmove_stride(SEG_CTEXT, dest_far, src_far + , op->xlen / 8 * bpp, op->linelength, op->ylen / 2); + memmove_stride(SEG_CTEXT, dest_far + 0x2000, src_far + 0x2000 + , op->xlen / 8 * bpp, op->linelength, op->ylen / 2); + break; + } +} + +static void +gfx_packed(struct gfx_op *op) +{ + void *dest_far = (void*)(op->y * op->linelength + op->x); + switch (op->op) { + default: + case GO_READ8: + memcpy_far(GET_SEG(SS), op->pixels, SEG_GRAPH, dest_far, 8); + break; + case GO_WRITE8: + memcpy_far(SEG_GRAPH, dest_far, GET_SEG(SS), op->pixels, 8); + break; + case GO_MEMSET: + memset_stride(SEG_GRAPH, dest_far, op->pixels[0] + , op->xlen, op->linelength, op->ylen); + break; + case GO_MEMMOVE: ; + void *src_far = (void*)(op->srcy * op->linelength + op->x); + memmove_stride(SEG_GRAPH, dest_far, src_far + , op->xlen, op->linelength, op->ylen); + break; + } +} + + +/**************************************************************** + * Direct framebuffers in high mem + ****************************************************************/ + +// Use int 1587 call to copy memory to/from the framebuffer. +static void +memcpy_high(void *dest, void *src, u32 len) +{ + u64 gdt[6]; + gdt[2] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)src); + gdt[3] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)dest); + + // Call int 1587 to copy data. + len/=2; + u32 flags; + u32 eax = 0x8700; + u32 si = (u32)&gdt; + SET_SEG(ES, GET_SEG(SS)); + asm volatile( + "stc\n" + "int $0x15\n" + "cli\n" + "cld\n" + "pushfl\n" + "popl %0\n" + : "=r" (flags), "+a" (eax), "+S" (si), "+c" (len) + : : "cc", "memory"); +} + +static void +memmove_stride_high(void *dst, void *src, int copylen, int stride, int lines) +{ + if (src < dst) { + dst += stride * (lines - 1); + src += stride * (lines - 1); + stride = -stride; + } + for (; lines; lines--, dst+=stride, src+=stride) + memcpy_high(dst, src, copylen); +} + +// Map a CGA color to a "direct" mode rgb value. +static u32 +get_color(int depth, u8 attr) +{ + int rbits, gbits, bbits; + switch (depth) { + case 15: rbits=5; gbits=5; bbits=5; break; + case 16: rbits=5; gbits=6; bbits=5; break; + default: + case 24: rbits=8; gbits=8; bbits=8; break; + } + int h = (attr&8) ? 1 : 0; + int r = (attr&4) ? 2 : 0, g = (attr&2) ? 2 : 0, b = (attr&1) ? 2 : 0; + if ((attr & 0xf) == 6) + g = 1; + int rv = DIV_ROUND_CLOSEST(((1<<rbits) - 1) * (r + h), 3); + int gv = DIV_ROUND_CLOSEST(((1<<gbits) - 1) * (g + h), 3); + int bv = DIV_ROUND_CLOSEST(((1<<bbits) - 1) * (b + h), 3); + return (rv << (gbits+bbits)) + (gv << bbits) + bv; +} + +// Find the closest attribute for a given framebuffer color +static u8 +reverse_color(int depth, u32 color) +{ + int rbits, gbits, bbits; + switch (depth) { + case 15: rbits=5; gbits=5; bbits=5; break; + case 16: rbits=5; gbits=6; bbits=5; break; + default: + case 24: rbits=8; gbits=8; bbits=8; break; + } + int rv = (color >> (gbits+bbits)) & ((1<<rbits)-1); + int gv = (color >> bbits) & ((1<<gbits)-1); + int bv = color & ((1<<bbits)-1); + int r = DIV_ROUND_CLOSEST(rv * 3, (1<<rbits) - 1); + int g = DIV_ROUND_CLOSEST(gv * 3, (1<<gbits) - 1); + int b = DIV_ROUND_CLOSEST(bv * 3, (1<<bbits) - 1); + int h = r && g && b && (r != 2 || g != 2 || b != 2); + return (h ? 8 : 0) | ((r-h) ? 4 : 0) | ((g-h) ? 2 : 0) | ((b-h) ? 1 : 0); +} + +static void +gfx_direct(struct gfx_op *op) +{ + void *fb = (void*)GET_GLOBAL(VBE_framebuffer); + if (!fb) + return; + int depth = GET_GLOBAL(op->vmode_g->depth); + int bypp = DIV_ROUND_UP(depth, 8); + void *dest_far = (fb + op->displaystart + op->y * op->linelength + + op->x * bypp); + switch (op->op) { + default: + case GO_READ8: { + u8 data[64]; + memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8); + int i; + for (i=0; i<8; i++) + op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]); + break; + } + case GO_WRITE8: { + u8 data[64]; + int i; + for (i=0; i<8; i++) + *(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]); + memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8); + break; + } + case GO_MEMSET: { + u32 color = get_color(depth, op->pixels[0]); + u8 data[64]; + int i; + for (i=0; i<8; i++) + *(u32*)&data[i*bypp] = color; + memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8); + memcpy_high(dest_far + bypp * 8, dest_far, op->xlen * bypp - bypp * 8); + for (i=1; i < op->ylen; i++) + memcpy_high(dest_far + op->linelength * i + , dest_far, op->xlen * bypp); + break; + } + case GO_MEMMOVE: ; + void *src_far = (fb + op->displaystart + op->srcy * op->linelength + + op->x * bypp); + memmove_stride_high(dest_far, src_far + , op->xlen * bypp, op->linelength, op->ylen); + break; + } +} + + +/**************************************************************** + * Gfx interface + ****************************************************************/ + +// Prepare a struct gfx_op for use. +void +init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g) +{ + memset(op, 0, sizeof(*op)); + op->vmode_g = vmode_g; + op->linelength = vgahw_get_linelength(vmode_g); + op->displaystart = vgahw_get_displaystart(vmode_g); +} + +// Issue a graphics operation. +void +handle_gfx_op(struct gfx_op *op) +{ + switch (GET_GLOBAL(op->vmode_g->memmodel)) { + case MM_PLANAR: + gfx_planar(op); + break; + case MM_CGA: + gfx_cga(op); + break; + case MM_PACKED: + gfx_packed(op); + break; + case MM_DIRECT: + gfx_direct(op); + break; + default: + break; + } +} + +// Move characters when in graphics mode. +static void +gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest + , struct cursorpos src, struct cursorpos movesize) +{ + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = dest.x * 8; + op.xlen = movesize.x * 8; + int cheight = GET_BDA(char_height); + op.y = dest.y * cheight; + op.ylen = movesize.y * cheight; + op.srcy = src.y * cheight; + op.op = GO_MEMMOVE; + handle_gfx_op(&op); +} + +// Clear area of screen in graphics mode. +static void +gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest + , struct carattr ca, struct cursorpos clearsize) +{ + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = dest.x * 8; + op.xlen = clearsize.x * 8; + int cheight = GET_BDA(char_height); + op.y = dest.y * cheight; + op.ylen = clearsize.y * cheight; + op.pixels[0] = ca.attr; + if (vga_emulate_text()) + op.pixels[0] = ca.attr >> 4; + op.op = GO_MEMSET; + handle_gfx_op(&op); +} + +// Return the font for a given character +struct segoff_s +get_font_data(u8 c) +{ + int char_height = GET_BDA(char_height); + struct segoff_s font; + if (char_height == 8 && c >= 128) { + font = GET_IVT(0x1f); + c -= 128; + } else { + font = GET_IVT(0x43); + } + font.offset += c * char_height; + return font; +} + +// Write a character to the screen in graphics mode. +static void +gfx_write_char(struct vgamode_s *vmode_g + , struct cursorpos cp, struct carattr ca) +{ + if (cp.x >= GET_BDA(video_cols)) + return; + + struct segoff_s font = get_font_data(ca.car); + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = cp.x * 8; + int cheight = GET_BDA(char_height); + op.y = cp.y * cheight; + u8 fgattr = ca.attr, bgattr = 0x00; + int usexor = 0; + if (vga_emulate_text()) { + if (ca.use_attr) { + bgattr = fgattr >> 4; + fgattr = fgattr & 0x0f; + } else { + // Read bottom right pixel of the cell to guess bg color + op.op = GO_READ8; + op.y += cheight-1; + handle_gfx_op(&op); + op.y -= cheight-1; + bgattr = op.pixels[7]; + fgattr = bgattr ^ 0x7; + } + } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) { + usexor = 1; + fgattr &= 0x7f; + } + int i; + for (i = 0; i < cheight; i++, op.y++) { + u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i)); + if (usexor) { + op.op = GO_READ8; + handle_gfx_op(&op); + int j; + for (j = 0; j < 8; j++) + op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00; + } else { + int j; + for (j = 0; j < 8; j++) + op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr; + } + op.op = GO_WRITE8; + handle_gfx_op(&op); + } +} + +// Read a character from the screen in graphics mode. +static struct carattr +gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp) +{ + u8 lines[16]; + int cheight = GET_BDA(char_height); + if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines)) + goto fail; + + // Read cell from screen + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.op = GO_READ8; + op.x = cp.x * 8; + op.y = cp.y * cheight; + int car = 0; + u8 fgattr = 0x00, bgattr = 0x00; + if (vga_emulate_text()) { + // Read bottom right pixel of the cell to guess bg color + op.y += cheight-1; + handle_gfx_op(&op); + op.y -= cheight-1; + bgattr = op.pixels[7]; + fgattr = bgattr ^ 0x7; + // Report space character for blank cells (skip null character check) + car = 1; + } + u8 i, j; + for (i=0; i<cheight; i++, op.y++) { + u8 line = 0; + handle_gfx_op(&op); + for (j=0; j<8; j++) + if (op.pixels[j] != bgattr) { + line |= 0x80 >> j; + fgattr = op.pixels[j]; + } + lines[i] = line; + } + + // Determine font + for (; car<256; car++) { + struct segoff_s font = get_font_data(car); + if (memcmp_far(GET_SEG(SS), lines + , font.seg, (void*)(font.offset+0), cheight) == 0) + return (struct carattr){car, fgattr | (bgattr << 4), 0}; + } +fail: + return (struct carattr){0, 0, 0}; +} + +// Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell +void +gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp) +{ + u16 cursor_type = get_cursor_shape(); + u8 start = cursor_type >> 8, end = cursor_type & 0xff; + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = cp.x * 8; + int cheight = GET_BDA(char_height); + op.y = cp.y * cheight + start; + + int i; + for (i = start; i < cheight && i <= end; i++, op.y++) { + op.op = GO_READ8; + handle_gfx_op(&op); + int j; + for (j = 0; j < 8; j++) + op.pixels[j] ^= 0x07; + op.op = GO_WRITE8; + handle_gfx_op(&op); + } +} + +// Set the pixel at the given position. +void +vgafb_write_pixel(u8 color, u16 x, u16 y) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + vgafb_set_swcursor(0); + + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = ALIGN_DOWN(x, 8); + op.y = y; + op.op = GO_READ8; + handle_gfx_op(&op); + + int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8; + if (usexor) + op.pixels[x & 0x07] ^= color & 0x7f; + else + op.pixels[x & 0x07] = color; + op.op = GO_WRITE8; + handle_gfx_op(&op); +} + +// Return the pixel at the given position. +u8 +vgafb_read_pixel(u16 x, u16 y) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return 0; + vgafb_set_swcursor(0); + + struct gfx_op op; + init_gfx_op(&op, vmode_g); + op.x = ALIGN_DOWN(x, 8); + op.y = y; + op.op = GO_READ8; + handle_gfx_op(&op); + + return op.pixels[x & 0x07]; +} + + +/**************************************************************** + * Text ops + ****************************************************************/ + +// Return the fb offset for the given character address when in text mode. +void * +text_address(struct cursorpos cp) +{ + int stride = GET_BDA(video_cols) * 2; + u32 pageoffset = GET_BDA(video_pagesize) * cp.page; + return (void*)pageoffset + cp.y * stride + cp.x * 2; +} + +// Move characters on screen. +void +vgafb_move_chars(struct cursorpos dest + , struct cursorpos src, struct cursorpos movesize) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + vgafb_set_swcursor(0); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_move_chars(vmode_g, dest, src, movesize); + return; + } + + int stride = GET_BDA(video_cols) * 2; + memmove_stride(GET_GLOBAL(vmode_g->sstart) + , text_address(dest), text_address(src) + , movesize.x * 2, stride, movesize.y); +} + +// Clear area of screen. +void +vgafb_clear_chars(struct cursorpos dest + , struct carattr ca, struct cursorpos clearsize) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + vgafb_set_swcursor(0); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_clear_chars(vmode_g, dest, ca, clearsize); + return; + } + + int stride = GET_BDA(video_cols) * 2; + u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car; + memset16_stride(GET_GLOBAL(vmode_g->sstart), text_address(dest), attr + , clearsize.x * 2, stride, clearsize.y); +} + +// Write a character to the screen. +void +vgafb_write_char(struct cursorpos cp, struct carattr ca) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + vgafb_set_swcursor(0); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_write_char(vmode_g, cp, ca); + return; + } + + void *dest_far = text_address(cp); + if (ca.use_attr) { + u16 dummy = (ca.attr << 8) | ca.car; + SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)dest_far, dummy); + } else { + SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, ca.car); + } +} + +// Return the character at the given position on the screen. +struct carattr +vgafb_read_char(struct cursorpos cp) +{ + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return (struct carattr){0, 0, 0}; + vgafb_set_swcursor(0); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) + return gfx_read_char(vmode_g, cp); + + u16 *dest_far = text_address(cp); + u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far); + return (struct carattr){v, v>>8, 0}; +} + +// Draw/undraw a cursor on the screen +void +vgafb_set_swcursor(int enable) +{ + if (!vga_emulate_text()) + return; + u8 flags = GET_BDA_EXT(flags); + if (!!(flags & BF_SWCURSOR) == enable) + // Already in requested mode. + return; + struct vgamode_s *vmode_g = get_current_mode(); + if (!vmode_g) + return; + struct cursorpos cp = get_cursor_pos(0xff); + if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows) + || GET_BDA(cursor_type) >= 0x2000) + // Cursor not visible + return; + + SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0)); + + if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) { + gfx_set_swcursor(vmode_g, enable, cp); + return; + } + + // In text mode, swap foreground and background attributes for cursor + void *dest_far = text_address(cp) + 1; + u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far); + attr = (attr >> 4) | (attr << 4); + SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr); +} diff --git a/qemu/roms/seabios/vgasrc/vgafonts.c b/qemu/roms/seabios/vgasrc/vgafonts.c new file mode 100644 index 000000000..47a6437f2 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgafonts.c @@ -0,0 +1,785 @@ +#include "vgabios.h" // vgafont8 + +/* + * These fonts come from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip + * The package is (c) by Joseph Gil + * The individual fonts are public domain + */ +u8 vgafont8[256 * 8] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, + 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, + 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, + 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, + 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, + 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, + 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, + 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, + 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, + 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, + 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, + 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, + 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, + 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, + 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, + 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, + 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, + 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, + 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, + 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, + 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, + 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, + 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, + 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, + 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, + 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, + 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, + 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, + 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, + 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, + 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, + 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +u8 vgafont14[256 * 14] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c, 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, + 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0x40, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +u8 vgafont16[256 * 16] VAR16 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +u8 vgafont14alt[1] VAR16; +u8 vgafont16alt[1] VAR16; diff --git a/qemu/roms/seabios/vgasrc/vgahw.h b/qemu/roms/seabios/vgasrc/vgahw.h new file mode 100644 index 000000000..39f818ab5 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgahw.h @@ -0,0 +1,143 @@ +#ifndef __VGAHW_H +#define __VGAHW_H + +#include "types.h" // u8 +#include "config.h" // CONFIG_* + +#include "cbvga.h" // cbvga_setup +#include "clext.h" // clext_set_mode +#include "bochsvga.h" // bochsvga_set_mode +#include "stdvga.h" // stdvga_set_mode +#include "geodevga.h" // geodevga_setup + +static inline struct vgamode_s *vgahw_find_mode(int mode) { + if (CONFIG_VGA_CIRRUS) + return clext_find_mode(mode); + if (CONFIG_VGA_BOCHS) + return bochsvga_find_mode(mode); + if (CONFIG_VGA_COREBOOT) + return cbvga_find_mode(mode); + return stdvga_find_mode(mode); +} + +static inline int vgahw_set_mode(struct vgamode_s *vmode_g, int flags) { + if (CONFIG_VGA_CIRRUS) + return clext_set_mode(vmode_g, flags); + if (CONFIG_VGA_BOCHS) + return bochsvga_set_mode(vmode_g, flags); + if (CONFIG_VGA_COREBOOT) + return cbvga_set_mode(vmode_g, flags); + return stdvga_set_mode(vmode_g, flags); +} + +static inline void vgahw_list_modes(u16 seg, u16 *dest, u16 *last) { + if (CONFIG_VGA_CIRRUS) + clext_list_modes(seg, dest, last); + else if (CONFIG_VGA_BOCHS) + bochsvga_list_modes(seg, dest, last); + else if (CONFIG_VGA_COREBOOT) + cbvga_list_modes(seg, dest, last); + else + stdvga_list_modes(seg, dest, last); +} + +static inline int vgahw_setup(void) { + if (CONFIG_VGA_CIRRUS) + return clext_setup(); + if (CONFIG_VGA_BOCHS) + return bochsvga_setup(); + if (CONFIG_VGA_GEODEGX2 || CONFIG_VGA_GEODELX) + return geodevga_setup(); + if (CONFIG_VGA_COREBOOT) + return cbvga_setup(); + return stdvga_setup(); +} + +static inline int vgahw_get_window(struct vgamode_s *vmode_g, int window) { + if (CONFIG_VGA_CIRRUS) + return clext_get_window(vmode_g, window); + if (CONFIG_VGA_BOCHS) + return bochsvga_get_window(vmode_g, window); + if (CONFIG_VGA_COREBOOT) + return cbvga_get_window(vmode_g, window); + return stdvga_get_window(vmode_g, window); +} + +static inline int vgahw_set_window(struct vgamode_s *vmode_g, int window + , int val) { + if (CONFIG_VGA_CIRRUS) + return clext_set_window(vmode_g, window, val); + if (CONFIG_VGA_BOCHS) + return bochsvga_set_window(vmode_g, window, val); + if (CONFIG_VGA_COREBOOT) + return cbvga_set_window(vmode_g, window, val); + return stdvga_set_window(vmode_g, window, val); +} + +static inline int vgahw_get_linelength(struct vgamode_s *vmode_g) { + if (CONFIG_VGA_CIRRUS) + return clext_get_linelength(vmode_g); + if (CONFIG_VGA_BOCHS) + return bochsvga_get_linelength(vmode_g); + if (CONFIG_VGA_COREBOOT) + return cbvga_get_linelength(vmode_g); + return stdvga_get_linelength(vmode_g); +} + +static inline int vgahw_set_linelength(struct vgamode_s *vmode_g, int val) { + if (CONFIG_VGA_CIRRUS) + return clext_set_linelength(vmode_g, val); + if (CONFIG_VGA_BOCHS) + return bochsvga_set_linelength(vmode_g, val); + if (CONFIG_VGA_COREBOOT) + return cbvga_set_linelength(vmode_g, val); + return stdvga_set_linelength(vmode_g, val); +} + +static inline int vgahw_get_displaystart(struct vgamode_s *vmode_g) { + if (CONFIG_VGA_CIRRUS) + return clext_get_displaystart(vmode_g); + if (CONFIG_VGA_BOCHS) + return bochsvga_get_displaystart(vmode_g); + if (CONFIG_VGA_COREBOOT) + return cbvga_get_displaystart(vmode_g); + return stdvga_get_displaystart(vmode_g); +} + +static inline int vgahw_set_displaystart(struct vgamode_s *vmode_g, int val) { + if (CONFIG_VGA_CIRRUS) + return clext_set_displaystart(vmode_g, val); + if (CONFIG_VGA_BOCHS) + return bochsvga_set_displaystart(vmode_g, val); + if (CONFIG_VGA_COREBOOT) + return cbvga_set_displaystart(vmode_g, val); + return stdvga_set_displaystart(vmode_g, val); +} + +static inline int vgahw_get_dacformat(struct vgamode_s *vmode_g) { + if (CONFIG_VGA_BOCHS) + return bochsvga_get_dacformat(vmode_g); + if (CONFIG_VGA_COREBOOT) + return cbvga_get_dacformat(vmode_g); + return stdvga_get_dacformat(vmode_g); +} + +static inline int vgahw_set_dacformat(struct vgamode_s *vmode_g, int val) { + if (CONFIG_VGA_BOCHS) + return bochsvga_set_dacformat(vmode_g, val); + if (CONFIG_VGA_COREBOOT) + return cbvga_set_dacformat(vmode_g, val); + return stdvga_set_dacformat(vmode_g, val); +} + +static inline int vgahw_save_restore(int cmd, u16 seg, void *data) { + if (CONFIG_VGA_CIRRUS) + return clext_save_restore(cmd, seg, data); + if (CONFIG_VGA_BOCHS) + return bochsvga_save_restore(cmd, seg, data); + if (CONFIG_VGA_COREBOOT) + return cbvga_save_restore(cmd, seg, data); + return stdvga_save_restore(cmd, seg, data); +} + +#endif // vgahw.h diff --git a/qemu/roms/seabios/vgasrc/vgainit.c b/qemu/roms/seabios/vgasrc/vgainit.c new file mode 100644 index 000000000..8d1226182 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgainit.c @@ -0,0 +1,195 @@ +// Main VGA bios initialization +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "hw/pci.h" // pci_config_readw +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "hw/serialio.h" // serial_debug_preinit +#include "output.h" // dprintf +#include "std/optionrom.h" // struct pci_data +#include "std/pmm.h" // struct pmmheader +#include "string.h" // checksum_far +#include "util.h" // VERSION +#include "vgabios.h" // video_save_pointer_table +#include "vgahw.h" // vgahw_setup + +struct video_save_pointer_s video_save_pointer_table VAR16; + +struct video_param_s video_param_table[29] VAR16; + +// Type of emulator platform - for dprintf with certain compile options. +int PlatformRunningOn VAR16; + + +/**************************************************************** + * PCI Data + ****************************************************************/ + +struct pci_data rom_pci_data VAR16 VISIBLE16 = { + .signature = PCI_ROM_SIGNATURE, + .vendor = CONFIG_VGA_VID, + .device = CONFIG_VGA_DID, + .dlen = 0x18, + .class_hi = 0x300, + .irevision = 1, + .type = PCIROM_CODETYPE_X86, + .indicator = 0x80, +}; + + +/**************************************************************** + * PMM call and extra stack setup + ****************************************************************/ + +u16 ExtraStackSeg VAR16 VISIBLE16; + +static void +allocate_extra_stack(void) +{ + if (!CONFIG_VGA_ALLOCATE_EXTRA_STACK) + return; + u32 pmmscan; + for (pmmscan=0; pmmscan < BUILD_BIOS_SIZE; pmmscan+=16) { + struct pmmheader *pmm = (void*)pmmscan; + if (GET_FARVAR(SEG_BIOS, pmm->signature) != PMM_SIGNATURE) + continue; + if (checksum_far(SEG_BIOS, pmm, GET_FARVAR(SEG_BIOS, pmm->length))) + continue; + struct segoff_s entry = GET_FARVAR(SEG_BIOS, pmm->entry); + dprintf(1, "Attempting to allocate VGA stack via pmm call to %04x:%04x\n" + , entry.seg, entry.offset); + u16 res1, res2; + asm volatile( + "pushl %0\n" + "pushw $(8|1)\n" // Permanent low memory request + "pushl $0xffffffff\n" // Anonymous handle + "pushl $" __stringify(CONFIG_VGA_EXTRA_STACK_SIZE/16) "\n" + "pushw $0x00\n" // PMM allocation request + "lcallw *12(%%esp)\n" + "addl $16, %%esp\n" + "cli\n" + "cld\n" + : "+r" (entry.segoff), "=a" (res1), "=d" (res2) : : "cc", "memory"); + u32 res = res1 | (res2 << 16); + if (!res || res == PMM_FUNCTION_NOT_SUPPORTED) + return; + dprintf(1, "VGA stack allocated at %x\n", res); + SET_VGA(ExtraStackSeg, res >> 4); + extern void entry_10_extrastack(void); + SET_IVT(0x10, SEGOFF(get_global_seg(), (u32)entry_10_extrastack)); + return; + } +} + + +/**************************************************************** + * Timer hook + ****************************************************************/ + +struct segoff_s Timer_Hook_Resume VAR16 VISIBLE16; + +void VISIBLE16 +handle_timer_hook(void) +{ + if (!vga_emulate_text()) + return; + vgafb_set_swcursor(GET_BDA(timer_counter) % 18 < 9); +} + +static void +hook_timer_irq(void) +{ + if (!CONFIG_VGA_EMULATE_TEXT) + return; + extern void entry_timer_hook(void); + extern void entry_timer_hook_extrastack(void); + struct segoff_s oldirq = GET_IVT(0x08); + struct segoff_s newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook); + if (CONFIG_VGA_ALLOCATE_EXTRA_STACK && GET_GLOBAL(ExtraStackSeg)) + newirq = SEGOFF(get_global_seg(), (u32)entry_timer_hook_extrastack); + dprintf(1, "Hooking hardware timer irq (old=%x new=%x)\n" + , oldirq.segoff, newirq.segoff); + SET_VGA(Timer_Hook_Resume, oldirq); + SET_IVT(0x08, newirq); +} + + +/**************************************************************** + * VGA post + ****************************************************************/ + +static void +init_bios_area(void) +{ + // init detected hardware BIOS Area + // set 80x25 color (not clear from RBIL but usual) + set_equipment_flags(0x30, 0x20); + + // Set the basic modeset options + SET_BDA(modeset_ctl, 0x51); + + SET_BDA(dcc_index, CONFIG_VGA_STDVGA_PORTS ? 0x08 : 0xff); + SET_BDA(video_savetable + , SEGOFF(get_global_seg(), (u32)&video_save_pointer_table)); + + // FIXME + SET_BDA(video_msr, 0x00); // Unavailable on vanilla vga, but... + SET_BDA(video_pal, 0x00); // Unavailable on vanilla vga, but... +} + +int VgaBDF VAR16 = -1; +int HaveRunInit VAR16; + +void VISIBLE16 +vga_post(struct bregs *regs) +{ + serial_debug_preinit(); + dprintf(1, "Start SeaVGABIOS (version %s)\n", VERSION); + debug_enter(regs, DEBUG_VGA_POST); + + if (CONFIG_VGA_PCI && !GET_GLOBAL(HaveRunInit)) { + u16 bdf = regs->ax; + if ((pci_config_readw(bdf, PCI_VENDOR_ID) + == GET_GLOBAL(rom_pci_data.vendor)) + && (pci_config_readw(bdf, PCI_DEVICE_ID) + == GET_GLOBAL(rom_pci_data.device))) + SET_VGA(VgaBDF, bdf); + } + + int ret = vgahw_setup(); + if (ret) { + dprintf(1, "Failed to initialize VGA hardware. Exiting.\n"); + return; + } + + if (GET_GLOBAL(HaveRunInit)) + return; + + init_bios_area(); + + SET_VGA(video_save_pointer_table.videoparam + , SEGOFF(get_global_seg(), (u32)video_param_table)); + if (CONFIG_VGA_STDVGA_PORTS) + stdvga_build_video_param(); + + extern void entry_10(void); + SET_IVT(0x10, SEGOFF(get_global_seg(), (u32)entry_10)); + + allocate_extra_stack(); + + hook_timer_irq(); + + SET_VGA(HaveRunInit, 1); + + // Fixup checksum + extern u8 _rom_header_size, _rom_header_checksum; + SET_VGA(_rom_header_checksum, 0); + u8 sum = -checksum_far(get_global_seg(), 0, + GET_GLOBAL(_rom_header_size) * 512); + SET_VGA(_rom_header_checksum, sum); +} diff --git a/qemu/roms/seabios/vgasrc/vgalayout.lds.S b/qemu/roms/seabios/vgasrc/vgalayout.lds.S new file mode 100644 index 000000000..533734d85 --- /dev/null +++ b/qemu/roms/seabios/vgasrc/vgalayout.lds.S @@ -0,0 +1,23 @@ +// Linker definitions for an option rom +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH("i386") +ENTRY(_optionrom_entry) +SECTIONS +{ + .text 0 : { + KEEP(*(.rom.header)) + *(.text.*) + _rodata = . ; + *(.rodata*) + *(.data16.*) + } + + // Discard regular data sections to force a link error if + // 16bit code attempts to access data not marked with VAR16. + /DISCARD/ : { *(.text*) *(.rodata*) *(.data*) *(.bss*) *(COMMON) } +} |