commit e0bd090b2d4a8ba9814442e0469c65bad135b1f2 Author: ultraji Date: Fri Oct 25 21:54:25 2019 +0800 2019.10.25 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2736720 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +.vscode + +linux-0.12/**/*.o +linux-0.12/**/*.a +linux-0.12/Image +linux-0.12/System.map +linux-0.12/boot/bootsect +linux-0.12/boot/bootsect.s +linux-0.12/boot/setup +linux-0.12/boot/setup.s +linux-0.12/kernel/chr_drv/keyboard.s +linux-0.12/tools/build +linux-0.12/tools/system +linux-0.12/tools/kernel + +oslab/bochsout.txt +oslab/Image +oslab/hdc.img + +src/setup/bochs-2.6.9/* +src/setup/bochs-2.6.9.tar.gz \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100755 index 0000000..b732f09 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# linux-0.12 源码学习 + +*参考《Linux内核完全剖析 --基于0.12内核》* + +1. linux-0.12目录为修改过的源代码,其中加入了**中文注释**,修改部分代码使其能在现在的环境下**编译**,并且支持**GDB调试**。 + +2. oslab为实验目录,切到oslab目录下,运行该目录下的`run.sh`脚本即可运行linux0.12操作系统。 + +![实验截图](https://lychee.ultraji.xyz/uploads/big/f2c32a415408eba43066af5de70c6271.jpg) + +### 一、环境搭建 + +#### 1.1 方式一 + +可以选择已创建好的docker镜像作为实验环境(人生苦短,我用容器)。linux用户可以通过挂载将本地项目目录挂载到容器中,windows或mac用户可以在容器内重新git clone一份(因为不区分文件名大小写会导致挂载出错)。 + +1. docker方式 + + ```shell + # 1. 从docker hub中拉取镜像 + docker pull ultraji/ubuntu-xfce-novnc + # 2. 运行容器 + docker run -t -i -p 6080:6080 -v ${本地项目路径}:${容器内项目路径} ultraji/ubuntu-xfce-novnc + ``` + + 或docker-compose方式 + + ```shell + # 1. 切到项目docker目录下 + cd linux-0.12/src/docker + # 2.修改docker-compose.yaml中的项目挂载路径 + ... + # 3.启动容器 + docker-compose up -d + ``` + +2. 通过浏览器输入```http://localhost:6080```访问容器内的桌面系统了。 + + - vnc登陆密码: 123456 + - 默认用户: ubuntu + - 用户密码: 123456 + +#### 1.2 方式二 + +**ubuntu(64bit,>=14.04)** 的用户也可以使用`src/code`目录下的一键环境搭建脚本[setup.sh](src/code/setup.sh)。 + +### 二、如何使用 + +该项目的oslab为实验目录,切到oslab目录下,运行该目录下的`run.sh`脚本即可运行linux0.12操作系统。 + +- `./run.sh -m`  编译生成新的Image镜像 +- `./run.sh -g`  运行bochs模拟器,与gdb联调 diff --git a/linux-0.12/Makefile b/linux-0.12/Makefile new file mode 100644 index 0000000..515465e --- /dev/null +++ b/linux-0.12/Makefile @@ -0,0 +1,157 @@ +include Rules.make + +# +# if you want the ram-disk device, define this to be the size in blocks. +# +RAMDISK = #-DRAMDISK=512 + +# -0: 使用16bit代码段  +# -a: 开启与GNU as,ld部分兼容性选项 +AS86 = $(QUIET_CC)as86 -0 -a + +# -0: 产生具有16bit魔数的头结构 +LD86 = $(QUIET_LINK)ld86 -0 + +# -s(去除): 输出文件中省略所有的符号信息 +# -x: 删除所有局部符号 +# -M: 用于输出符号表 +# -e startup_32(新增): 指定入口 +# -Ttext 0(新增): 使`startup_32`标号对应的地址为`0x0` +LDFLAGS = -M -x -Ttext 0 -e startup_32 + +CC += $(RAMDISK) + +# -g: 生成调试信息 +# -Wall: 打印警告 +# -O: 对代码进行优化 +# -fstrength-reduce: 优化循环语句 +# -fomit-frame-pointer: 对无需帧指针的函数不要把帧指针保留在寄存器中 +# -fcombine-regs(去除): 不再被gcc支持 +# -mstring-insns(去除): Linus本人增加的选项(gcc中没有) +# -fno-builtin(新增): 阻止gcc会把没有参数的printf优化成puts +CFLAGS = -g -Wall -O -fstrength-reduce -fomit-frame-pointer -fno-builtin + +# -Iinclude: 通过-I指定在该项目的include文件夹中搜索 +CPP += -Iinclude + +# +# ROOT_DEV specifies the default root-device when making the image. +# This can be either FLOPPY, /dev/xxxx or empty, in which case the +# default of /dev/hd6 is used by 'build'. +# +ROOT_DEV= #/dev/hd6 +SWAP_DEV= #/dev/hd2 + +ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o +DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a +MATH =kernel/math/math.a +LIBS =lib/lib.a + +.c.s: + $(CC) $(CFLAGS) \ + -nostdinc -Iinclude -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -nostdinc -Iinclude -c -o $*.o $< + +all: Image + +Image: boot/bootsect boot/setup tools/system tools/build + @cp -f tools/system system.tmp + @strip system.tmp + @objcopy -O binary -R .note -R .comment system.tmp tools/kernel + tools/build boot/bootsect boot/setup tools/kernel $(ROOT_DEV) $(SWAP_DEV) > Image + @rm system.tmp + @rm tools/kernel -f + @sync + +# 上面的修改就可以保证开启gcc调试后,System文件不变大,build也就不会失败了(build对system大小有 +# 限制)。原先规则为下面两句 +# tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) $(SWAP_DEV) > Image +# sync + +disk: Image + dd bs=8192 if=Image of=/dev/PS0 + +tools/build: tools/build.c + $(CC) $(CFLAGS) \ + -o tools/build tools/build.c + +boot/head.o: boot/head.s + +tools/system: boot/head.o init/main.o \ + $(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS) + $(LD) $(LDFLAGS) boot/head.o init/main.o \ + $(ARCHIVES) \ + $(DRIVERS) \ + $(MATH) \ + $(LIBS) \ + -o tools/system > System.map + +kernel/math/math.a: + @(cd kernel/math; make) + +kernel/blk_drv/blk_drv.a: + @(cd kernel/blk_drv; make) + +kernel/chr_drv/chr_drv.a: + @(cd kernel/chr_drv; make) + +kernel/kernel.o: + @(cd kernel; make) + +mm/mm.o: + @(cd mm; make) + +fs/fs.o: + @(cd fs; make) + +lib/lib.a: + @(cd lib; make) + +boot/setup: boot/setup.s + $(AS86) -o boot/setup.o boot/setup.s + $(LD86) -s -o boot/setup boot/setup.o + +boot/setup.s: boot/setup.S include/linux/config.h + $(CPP) -traditional boot/setup.S -o boot/setup.s + +boot/bootsect.s: boot/bootsect.S include/linux/config.h + $(CPP) -traditional boot/bootsect.S -o boot/bootsect.s + +boot/bootsect: boot/bootsect.s + $(AS86) -o boot/bootsect.o boot/bootsect.s + $(LD86) -s -o boot/bootsect boot/bootsect.o + +clean: + rm -f Image System.map tmp_make core boot/bootsect boot/setup \ + boot/bootsect.s boot/setup.s + rm -f init/*.o tools/system tools/build boot/*.o + (cd mm;make clean) + (cd fs;make clean) + (cd kernel;make clean) + (cd lib;make clean) + +backup: clean + (cd .. ; tar cf - linux | compress - > backup.Z) + sync + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + (cd fs; make dep) + (cd kernel; make dep) + (cd mm; make dep) + +### Dependencies: +init/main.o : init/main.c include/unistd.h include/sys/stat.h \ + include/sys/types.h include/sys/time.h include/time.h include/sys/times.h \ + include/sys/utsname.h include/sys/param.h include/sys/resource.h \ + include/utime.h include/linux/tty.h include/termios.h include/linux/sched.h \ + include/linux/head.h include/linux/fs.h include/linux/mm.h \ + include/linux/kernel.h include/signal.h include/asm/system.h \ + include/asm/io.h include/stddef.h include/stdarg.h include/fcntl.h \ + include/string.h diff --git a/linux-0.12/Rules.make b/linux-0.12/Rules.make new file mode 100644 index 0000000..5a1f77c --- /dev/null +++ b/linux-0.12/Rules.make @@ -0,0 +1,17 @@ +# 使make编译过程更清晰 + +CCCOLOR = "\033[34m" +LINKCOLOR = "\033[34;1m" +SRCCOLOR = "\033[33m" +BINCOLOR = "\033[37;1m" +MAKECOLOR = "\033[32;1m" +ENDCOLOR = "\033[0m" + +QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2; +QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; + +AS = $(QUIET_CC)as --32 # --32: 64位系统需要加 +LD = $(QUIET_LINK)ld -m elf_i386 # -m elf_i386: 64位系统需要加 +AR = $(QUIET_LINK)ar +CC = $(QUIET_CC)gcc-3.4 -m32 # -m32: 64位系统需要加 +CPP = $(QUIET_CC)cpp -nostdinc # -nostdinc: 不要去搜索标准头文件目录,即不使用标准头文件 \ No newline at end of file diff --git a/linux-0.12/boot/bootsect.S b/linux-0.12/boot/bootsect.S new file mode 100755 index 0000000..6e1789c --- /dev/null +++ b/linux-0.12/boot/bootsect.S @@ -0,0 +1,482 @@ +! SYS_SIZE is the number of clicks (16 bytes) to be loaded. +! 0x3000 is 0x30000 bytes = 196kB, more than enough for current +! versions of linux +; SYS_SIZE是要加载的系统模块长度的节数(每节有16b)。0x3000节就等 +; 于0x30000bytes=192KB,对于当前的版本空间已足够了。 + +; 该头文件里定义了内核用到的一些常数符号和Linus自己使用的默认硬盘默认参数块。 +#include + +SYSSIZE = DEF_SYSSIZE ; 系统模块大小为0x3000节 + +! +! bootsect.s (C) 1991 Linus Torvalds +! modified by Drew Eckhardt +! +! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves +! iself out of the way to address 0x90000, and jumps there. +! +! It then loads 'setup' directly after itself (0x90200), and the system +! at 0x10000, using BIOS interrupts. +! +! NOTE! currently system is at most 8*65536 bytes long. This should be no +! problem, even in the future. I want to keep it simple. This 512 kB +! kernel size should be enough, especially as this doesn't contain the +! buffer cache as in minix +! +! The loader has been made as simple as possible, and continuos +! read errors will result in a unbreakable loop. Reboot by hand. It +! loads pretty fast by getting whole sectors at a time whenever possible. + +; 以下是前面这些文字的翻译: +; bootsect.s (C) 1991 Linus Torvalds 版权所有 +; Drew Eckhardt 修改 +; +; bootsect.s被BIOS启动子程序加载至0x7c00处,并将自己移到了地址0x90000处,并跳转至那里。 +; +; 它然后使用BIOS中断将setup直接加载到自己的后面(0x90200),并将system加载到地址0x10000处。 +; +; 注意! 目前的内核系统最大长度限制为512KB字节,即使是在将来这也应该没有问题的。我想让它保持简 +; 单明了。这样512KB的最大内核长度应该足够了,尤其是这里没有像MINIX中一样包含缓冲区。 +; +; 加载程序已经做得够简单了,而且持续的读操作出错将导致死循环。就只能手工重启。 +; 只要能够一次读取所有的扇区,加载过程可以做的很快的。 + +.globl begtext, begdata, begbss, endtext, enddata, endbss +.text +begtext: +.data +begdata: +.bss +begbss: +.text + +SETUPLEN = 4 ! nr of setup-sectors + ; setup 占用的磁盘扇区数 +BOOTSEG = 0x07c0 ! original address of boot-sector + ; bootsect 代码所在的原地址(被BIOS子程序加载至此处) +INITSEG = DEF_INITSEG ! we move boot here - out of the way + ; bootsect将要移动到的目的段位置,为了避开系统模块占用处 +SETUPSEG = DEF_SETUPSEG ! setup starts here + ; setup程序代码的段位置 +SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536). + ; system模块将被加载到0x10000 +ENDSEG = SYSSEG + SYSSIZE ! where to stop loading + ; 停止加载的段地址 + +! ROOT_DEV & SWAP_DEV are now written by "build". +; 根文件系统设备号ROOT_DEV和交换设备号SWAP_DEV现在由tools目录下的build程序写入。 + +ROOT_DEV = 0 +SWAP_DEV = 0 + +entry start ; 告知链接程序,程序从start标号处开始执行 +start: +;;;;; 1. 将自身(bootsect)从0x7c00移动到0x90000处,共256字(512字节) ;;;;;;;;;;;;;;;;;; + mov ax,#BOOTSEG + mov ds,ax + mov ax,#INITSEG + mov es,ax + mov cx,#256 + sub si,si ; 源地址 ds:si = 0x07c0:0x0000 + sub di,di ; 目标地址 es:di = 0x9000:0x0000 + rep + movw ; 此处结束后,代码已经成功移动到0x90000 + jmpi go,INITSEG ; 段间跳转(Jump Intersegment),跳转到INITSEG:go(段地址:段内偏移)处。 + +;;;;; 从go处开始,CPU在已移动到0x90000处继续执行 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; 以下几行代码设置了几个段寄存器,包括栈寄存器ss和sp。 +go: mov ax,cs + mov dx,#0xfef4 ! arbitrary value >>512 - disk parm size + ; 栈指针要远大于512字节偏移(即0x90200)处都可以;一般setup程序大概占用4个 + ; 扇区,所以sp要大于(0x90200+0x200*4+堆栈大小)。 + ; 这里sp被设置成了0x9ff00-12(参数表长度),即sp = 0xfef4。 + mov ds,ax + mov es,ax + ;push ax ; 临时保存段值(0x9000),供后面使用。 + + mov ss,ax ! put stack at 0x9ff00 - 12. + mov sp,dx +/* + * Many BIOS's default disk parameter tables will not + * recognize multi-sector reads beyond the maximum sector number + * specified in the default diskette parameter tables - this may + * mean 7 sectors in some cases. + * + * Since single sector reads are slow and out of the question, + * we must take care of this by creating new parameter tables + * (for the first disk) in RAM. We will set the maximum sector + * count to 18 - the most we will encounter on an HD 1.44. + * + * High doesn't hurt. Low does. + * + * Segments are as follows: ds=es=ss=cs - INITSEG, + * fs = 0, gs = parameter table segment + */ +/* + * 对于多扇区读操作所读的扇区数超过默认磁盘参数表中指定的最大扇区数时,很多BIOS + * 将不能进行正确识别。在某些情况下是7个扇区。 + * + * 由于单扇区读操作太慢,不予考虑。我们必须通过在内存中重新创建新的参数表(为第1个驱动器) + * 来解决这个问题。我们将把其中最大扇区数设置为18,即在1.44MB磁盘上会碰到的最大值。 + * + * 数值大不会出问题,但太小就不行了。 + * + * 段寄存器将被设置成:ds = es = ss = cs 都为INITSEG(0x9000), + * fs = 0, gs = 参数表所在段值。 + */ + +;;;;; 修改软驱参数表 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; BIOS中断0x1e的中断向量值是软驱参数表地址。该向量值位于内存0x1e*4=0x78处。这段代码首先从内 +; 存0x0000:0x0078处复制原软驱参数表到0x9000:0xfef4处,然后修改表中的每磁道最大扇区数为18。 + + push #0 + pop fs + mov bx, #0x78 ; fs:bx is parameter table address + + seg fs ; seg fs只影响接下来的一条语句,表示下一条语句的操作数在fs所指的段中 + lgs si,(bx) ! gs:si is source ; 将fs:bx赋值给gs:si 0x0000:0x0078 + mov di,dx ! es:di is destination ; 0x9000:0xfef4 + mov cx,#6 ! copy 12 bytes + cld + + rep + seg gs + movw + + mov di,dx + movb 4(di),*18 ! patch sector count ;修改新表的最大扇区数为18 + + seg fs ; 让中断向量0x1e的值指向新表 + mov (bx),di + seg fs + mov 2(bx),es + + mov ax,cs ; pop ax ! ax = 0x9000 + mov fs,ax + mov gs,ax + + xor ah,ah ! reset FDC ; 复位软盘控制器,让其采用新参数。 + xor dl,dl ! dl = 0 ; 第1个软驱 + int 0x13 + +! load the setup-sectors directly after the bootblock. +! Note that 'es' is already set up. +; 在bootsect程序块后紧跟着加载setup模块的代码数据。 +; 在移动代码时,es的值已被设置好。 + +;;;;; 2. 加载setup模块到0x90200开始处,共读4个扇区 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; 如果读出错,显示磁盘上出错扇区位置,则复位驱动器,并重试,没有退路。 +load_setup: + xor dx, dx ! drive 0, head 0 + mov cx,#0x0002 ! sector 2, track 0 + mov bx,#0x0200 ! address = 512, in INITSEG + mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors + int 0x13 ! read it + jnc ok_load_setup ! ok - continue ;jnc - jump not cf + + push ax ! dump error code + call print_nl + mov bp, sp + call print_hex + pop ax + + xor dl, dl ! reset FDC ;复位磁盘控制器,重试。 + xor ah, ah + int 0x13 + j load_setup ; j - jmp + +ok_load_setup: + +! Get disk drive parameters, specifically nr of sectors/track +; 这段代码取磁盘驱动器的参数,特别是每道的扇区数量,并保存在位置sectors处。 + xor dl,dl + mov ah,#0x08 ! AH=8 is get drive parameters + int 0x13 + xor ch,ch + seg cs + mov sectors,cx ; 保存每磁道扇区数。 + mov ax,#INITSEG + mov es,ax ; 上面取磁盘参数中断改了es的值,这里需要改回来 + +! Print some inane message +; 在显示一些信息('Loading\r\n',共9个字符)。 + mov ah,#0x03 ! read cursor pos + xor bh,bh + int 0x10 + + mov cx,#9 + mov bx,#0x0007 ! page 0, attribute 7 (normal) + mov bp,#msg1 + mov ax,#0x1301 ! write string, move cursor + int 0x10 +! ok, we've written the message, now + +! we want to load the system (at 0x10000) +;;;;; 3. 将system模块加载到0x10000(64K)处 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ax,#SYSSEG + mov es,ax ! segment of 0x010000 + call read_it ; 读磁盘上system模块 + call kill_motor ; 关闭驱动器马达 + call print_nl + +! After that we check which root-device to use. If the device is +! defined (!= 0), nothing is done and the given device is used. +! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending +! on the number of sectors that the BIOS reports currently. +; 此后,我们检查要使用哪个根文件系统设备。如果已经指定了设备(!=0)就直接使用给定 +; 的设备。否则就需要根据BIOS报告的每磁道扇区数来确定到底使用/dev/PS0(2,28) +; 还是/dev/at0(2,8)。 +; 在Linux中,软驱的主设备号是2,次设备号 = type << 2 + nr, +; type 软驱的类型(2->1.2M或7->1.44M等)。 +; nr 0-3分别对应软驱A、B、C或D; + + seg cs + mov ax,root_dev + or ax,ax + jne root_defined + seg cs ; 取出sectors的值(每磁道扇区数) + mov bx,sectors + mov ax,#0x0208 ! /dev/PS0 - 1.2Mb + cmp bx,#15 ; sectors=15则说明是1.2MB的软驱 + je root_defined + mov ax,#0x021c ! /dev/PS0 - 1.44Mb + cmp bx,#18 ; sectors=18则说明是1.44MB的软驱 + je root_defined +undef_root: + jmp undef_root +root_defined: + seg cs + mov root_dev,ax + +! after that (everyting loaded), we jump to +! the setup-routine loaded directly after +! the bootblock: + +;;;;; 4. 到此,所有程序都加载完毕,我们就跳转到setup程序去 ;;;;;;;;;;;;;;;;;;;;;;;;; + jmpi 0,SETUPSEG + +;;;;; bootsect.S程序到此就结束了 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; 下面是几个子程序: +; read_it 用于读取磁盘上的system模块 +; kill_motor 用于关闭软驱电动机 +; 还有一些屏幕显示子程序 + +! This routine loads the system at address 0x10000, making sure +! no 64kB boundaries are crossed. We try to load it as fast as +! possible, loading whole tracks whenever we can. +! +! in: es - starting address segment (normally 0x1000) +! +; 该子程序将系统模块system加载到内存地址0x10000处,并确定没有跨越64KB的内存边界。 +sread: .word 1+SETUPLEN ! sectors read of current track + ; 当前磁道的已读扇区数(bootsect + setup) +head: .word 0 ! current head ;当前磁头号 +track: .word 0 ! current track ;当前磁道号 + +read_it: +; 首先测试输入的段值。必须位于内存地址64KB边界处,否则进入死循环。 + mov ax,es + test ax,#0x0fff +die: + jne die ! es must be at 64kB boundary ; es值必须位于64KB地址边界。 + xor bx,bx ! bx is starting address within segment +rp_read: + mov ax,es + cmp ax,#ENDSEG ! have we loaded all yet? ; 是否已经加载了全部数据? + jb ok1_read + ret +ok1_read: + ; 计算和验证当前磁道需要读取的扇区数,放在ax寄存器中。 + seg cs + mov ax,sectors + sub ax,sread + mov cx,ax + shl cx,#9 + add cx,bx + jnc ok2_read + je ok2_read + xor ax,ax + sub ax,bx + shr ax,#9 +ok2_read: + call read_track ; 读当前磁道上指定扇区和需读扇区数的数据 + mov cx,ax + add ax,sread + seg cs + cmp ax,sectors + jne ok3_read + mov ax,#1 + sub ax,head + jne ok4_read + inc track +ok4_read: + mov head,ax + xor ax,ax +ok3_read: + mov sread,ax + shl cx,#9 + add bx,cx + jnc rp_read + mov ax,es + add ah,#0x10 + mov es,ax + xor bx,bx + jmp rp_read + +; 读当前磁道上指定开始扇区和需读扇区数的数据到es:bx开始处。 +read_track: + pusha ; push all + pusha + mov ax, #0xe2e ! loading... message 2e = . + mov bx, #7 + int 0x10 + popa + ; 开始进行磁道扇区读操作 + mov dx,track + mov cx,sread + inc cx + mov ch,dl + mov dx,head + mov dh,dl + and dx,#0x0100 + mov ah,#2 + + push dx ! save for error dump + push cx ; 保留出错情况 + push bx + push ax + + int 0x13 + jc bad_rt + add sp, #8 ; 若没有出错,丢弃为出错情况保存的信息 + popa + ret + +; 读磁盘操作出错 +bad_rt: + push ax ! save error code + call print_all ! ah = error, al = read + + xor ah,ah + xor dl,dl + int 0x13 + + add sp, #10 + popa + jmp read_track + +/* + * print_all is for debugging purposes. + * It will print out all of the registers. The assumption is that this is + * called from a routine, with a stack frame like + * dx + * cx + * bx + * ax + * error + * ret <- sp + * +*/ +; print_all 用于调试目的,前提是从一个子程序中调用。栈帧结构如上所示 +print_all: + mov cx, #5 ! error code + 4 registers + mov bp, sp ; 保存当前栈指针sp. + +print_loop: + push cx ! save count left + call print_nl ! nl for readability + jae no_reg ! see if register name is needed + + ; 下面几行用于显示寄存器号,例如:"AX:", 0x45 - E + mov ax, #0xe05 + 0x41 - 1 ; ah = 功能号(0x0e);al = 字符(0x05 + 0x41 - 1) + sub al, cl + int 0x10 + + mov al, #0x58 ! X + int 0x10 + + mov al, #0x3a ! : + int 0x10 +; 显示寄存器bp所指栈中的内容 +no_reg: + add bp, #2 ! next register + call print_hex ! print it + pop cx + loop print_loop + ret + +; 显示回车换行 +print_nl: + mov ax, #0xe0d ! CR + int 0x10 + mov al, #0xa ! LF + int 0x10 + ret + +/* + * print_hex is for debugging purposes, and prints the word + * pointed to by ss:bp in hexadecmial. +*/ +/* 子程序print_hex用于调试目的.它使用十六进制在屏幕上显示出ss:bp指向的字 */ +print_hex: + mov cx, #4 ! 4 hex digits + mov dx, (bp) ! load word into dx + +; 先显示高字节,因此需要把dx中值左旋4位,此时高4位在dx的低4位中 +print_digit: + rol dx, #4 ! rotate so that lowest 4 bits are used ;左旋4位 + mov ah, #0xe + mov al, dl ! mask off so we have only next nibble + and al, #0xf ; 只取低四位显示 + add al, #0x30 ! convert to 0 based digit, '0' + cmp al, #0x39 ! check for overflow ; 大于9的处理,转换成A-F + jbe good_digit + add al, #0x41 - 0x30 - 0xa ! 'A' - '0' - 0xa + +good_digit: + int 0x10 + loop print_digit + ret + +/* + * This procedure turns off the floppy drive motor, so + * that we enter the kernel in a known state, and + * don't have to worry about it later. ' + */ +; 这个子程序用于关闭软驱的马达,这样我们进入内核后它处于已知状态,以后也就无须担心它了。 +kill_motor: + push dx + mov dx,#0x3f2 + xor al, al + outb + pop dx + ret + +sectors: + .word 0 + +msg1: + .byte 13,10 + .ascii "Loading" + +.org 506 +; swap_dev在第506开始的2个字节中,root_dev在第508开始的2个字节中 +swap_dev: + .word SWAP_DEV +root_dev: + .word ROOT_DEV + +; 下面2个字节是有效引导扇区的标志(必须位于引导扇区的最后两个字节中) +boot_flag: + .word 0xAA55 + +.text +endtext: +.data +enddata: +.bss +endbss: diff --git a/linux-0.12/boot/head.s b/linux-0.12/boot/head.s new file mode 100755 index 0000000..9d67a84 --- /dev/null +++ b/linux-0.12/boot/head.s @@ -0,0 +1,356 @@ +/* + * linux/boot/head.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * head.s contains the 32-bit startup code. + * + * NOTE!!! Startup happens at absolute address 0x00000000, which is also where + * the page directory will exist. The startup code will be overwritten by + * the page directory. + */ +/* + * head.s含有32位启动代码。 + * + * 注意!!! 32位启动代码是从绝对地址0x00000000开始的,这里也同样是保存页目录的地方,因此这里 + * 的启动代码将在之后被页目录覆盖掉。 + * + */ +.text +.globl idt, gdt, pg_dir, tmp_floppy_area +/***** 页目录表(0x00000000)将会存放在这里 *****/ +pg_dir: + +# head.s主要做了四件事: +# 1. 将系统堆栈放置在stack_start指向的数据区(之后,该栈就被用作任务0和任务1共同使用的用户栈) +# 2. 重新加载了新的中断描述符表和全局段描述符表 +# 3. 初始化页目录表和4个内核专属的页表 +# 4. 通过ret跳转到init/main.c中的main运行 +.globl startup_32 +startup_32: + movl $0x10, %eax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + lss stack_start, %esp # 设置系统堆栈 + + call setup_idt # 设置中断描述符表 + call setup_gdt # 设置全局描述符表 + + # 因为修改了gdt(段描述符中的段限长8MB改成了16MB),所以需要重新装载所有的段寄存器。CS代码段寄存器 + # 已经在setup_gdt中重新加载过了。 + + movl $0x10, %eax # reload all the segment registers + mov %ax, %ds # after changing gdt. CS was already + mov %ax, %es # reloaded in 'setup_gdt' + mov %ax, %fs + mov %ax, %gs + lss stack_start, %esp + + # 下面代码用于测试A20地址线是否已经开启 + # 采用的方法是向内存地址0x000000处写入任意一个数值,然后看内存地址0x100000(1M)处是否也是这个数值。 + # 如果一直相同的话(表示地址A20线没有选通),就一直比较下去,即死循环。 + xorl %eax, %eax +1: incl %eax # check that A20 really IS enabled + movl %eax, 0x000000 # loop forever if it isn't + cmpl %eax, 0x100000 + je 1b +/* + * NOTE! 486 should set bit 16, to check for write-protect in supervisor + * mode. Then it would be unnecessary with the "verify_area()"-calls. + * 486 users probably want to set the NE (#5) bit also, so as to use + * int 16 for math errors. + */ +/* + * 注意!在下面这段程序中,486应该将位16置位,以检查在超级用户模式下的写保护,此后"verify_area()" + * 调用就不需要了。486的用户通常也会想将NE(#5)置位,以便对数学协处理器的出错使用int 16。 + * + */ + # 上面原注释中提到的486CPU中CR0控制器的位16是写保护标志WP,用于禁止超级用户级的程序向一般用户只读 + # 页面中进行写操作。该标志主要用于操作系统在创建新进程时实现写时复制方法。 + + # 下面这段程序用于检查数学协处理器芯片是否存在 + # 方法是修改控制寄存器CR0,在假设存在协处理器的情况下执行一个协处理器指令,如果出错的话则说明协处理器 + # 芯片不存在,需要设置CR0中的协处理器仿真位EM(位2),并复位协处理器存在标志MP(位1)。 + movl %cr0, %eax # check math chip + andl $0x80000011, %eax # Save PG,PE,ET + /* "orl $0x10020,%eax" here for 486 might be good */ + orl $2, %eax # set MP + movl %eax, %cr0 + call check_x87 + jmp after_page_tables + +/* + * We depend on ET to be correct. This checks for 287/387. + */ +/* + * 我们依赖于ET标志的正确性来检测287/387存在与否. + * + */ +# fninit向协处理器发出初始化命令,它会把协处理器置于一个末受以前操作影响的已和状态,设置其控制字为默认值, +# 清除状态字和所有浮点栈式寄存器。非等待形式的这条指令(fninit)还会让协处理器终止执行当前正在执行的任何 +# 先前的算术操作。 +# fstsw指令取协处理器的状态字。 +# 如果系统中存在协处理器的话,那么在执行了fninit指令后其状态字低字节肯定为0。 + +check_x87: + fninit + fstsw %ax + cmpb $0, %al # 初始化状态字应该为0,否则说明协处理器不存在 + je 1f /* no coprocessor: have to set bits */ + movl %cr0, %eax + xorl $6, %eax /* reset MP, set EM */ + movl %eax, %cr0 + ret + +.align 4 # 按4字节方式对齐内存地址, 为了提高32位CPU访问内存中代码或数据的速度和效率 + # 两个字节值是80287协处理器指令fsetpm的机器码。其作用是把80287设置为保护模式。 + # 80387无需该指令,并且将会把该指令看作是空操作 +1: .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ # 287协处理器码 + ret + +/* + * setup_idt + * + * sets up a idt with 256 entries pointing to + * ignore_int, interrupt gates. It then loads + * idt. Everything that wants to install itself + * in the idt-table may do so themselves. Interrupts + * are enabled elsewhere, when we can be relatively + * sure everything is ok. This routine will be over- + * written by the page tables. + */ +/* + * setup_idt + * + * 将中断描述符表idt设置成具有256个项,并都指向ignore_int中断门。然后加载中断描述符表寄 + * 存器(lidt指令)。真正实用的中断门以后再安装。当我们在其他地方认为一切都正常时再开启中断。 + */ + +setup_idt: + lea ignore_int, %edx + movl $0x00080000, %eax # 将选择符0x0008置入eax的高16位中 + movw %dx, %ax /* selector = 0x0008 = cs */ + movw $0x8E00, %dx /* interrupt gate - dpl=0, present */ + # 此时edx含有门描述符高4字节的值 + + lea idt, %edi # idt是中断描述符表的地址 + mov $256, %ecx +rp_sidt: + movl %eax, (%edi) # 将哑中断门描述符存入表中 + movl %edx, 4(%edi) + addl $8, %edi # edi指向表中下一项 + dec %ecx + jne rp_sidt + lidt idt_descr # 加载中断描述符表寄存器值 + ret + +/* + * setup_gdt + * + * This routines sets up a new gdt and loads it. + * Only two entries are currently built, the same + * ones that were built in init.s. The routine + * is VERY complicated at two whole lines, so this + * rather long comment is certainly needed :-). + * This routine will beoverwritten by the page tables. + */ + +# 加载全局描述符表寄存器(全局描述符表内容已设置好) +setup_gdt: + lgdt gdt_descr + ret + +/* + * I put the kernel page tables right after the page directory, + * using 4 of them to span 16 Mb of physical memory. People with + * more than 16MB will have to expand this. + */ +/* + * Linus将内核的内存页表直接放在页目录之后,使用了4个表来寻址16MB的物理内存。如果你有 + * 多于16MB的内存,就需要在这里进行扩充修改。 + */ + + # 每个页表长为4KB字节(1页内存页面) +.org 0x1000 # 从偏移0x1000处开始存放第1个页表 +pg0: + +.org 0x2000 +pg1: + +.org 0x3000 +pg2: + +.org 0x4000 +pg3: + +.org 0x5000 +/* + * tmp_floppy_area is used by the floppy-driver when DMA cannot + * reach to a buffer-block. It needs to be aligned, so that it isn't + * on a 64kB border. + */ +/* + * 当DMA不能访问缓冲块时,下面的tmp_floppy_area内存块就可供软盘 + * 驱动程序使用。其地址需要对齐调整,这样就不会跨越64KB边界。 + */ + +tmp_floppy_area: + .fill 1024,1,0 + +/***** 为跳转到init/main.c中的main()函数作准备工作 *****/ +# 前面3个入栈0值应该分别表示envp,argv指针和argc的值(main()没有用到) +# pushl $L6 压入返回地址 +# pushl $main 压入main函数的入口地址 +# 当head.s最后执行ret指令时就会弹出main()的地址 +after_page_tables: + pushl $0 # These are the parameters to main :-) + pushl $0 # 这些是调用main程序的参数(指init/main.c). + pushl $0 + pushl $L6 # return address for main, if it decides to. + pushl $main + jmp setup_paging # 跳转至setup_paging +L6: + jmp L6 # main should never return here, but + # just in case, we know what happens. + # main程序绝对不应该返回到这里,不过为了以防万一,所以 + # 添加了该语句。这样我们就知道发生什么问题了。 + +/* This is the default interrupt "handler" :-) */ +/* 下面是默认的中断"向量句柄" */ +int_msg: + .asciz "Unknown interrupt\n\r" + +.align 4 +ignore_int: + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + + movl $0x10, %eax # 设置段选择符(使ds,es,fs指向gdt表中的数据段) + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + pushl $int_msg + call printk # 该函数在kernel/printk.c中 + popl %eax + + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret # 中断返回 + +/* + * Setup_paging + * + * This routine sets up paging by setting the page bit + * in cr0. The page tables are set up, identity-mapping + * the first 16MB. The pager assumes that no illegal + * addresses are produced (ie >4Mb on a 4Mb machine). + * + * NOTE! Although all physical memory should be identity + * mapped by this routine, only the kernel page functions + * use the >1Mb addresses directly. All "normal" functions + * use just the lower 1Mb, or the local data space, which + * will be mapped to some other place - mm keeps track of + * that. + * + * For those with more memory than 16 Mb - tough luck. I've + * not got it, why should you :-) The source is here. Change + * it. (Seriously - it shouldn't be too difficult. Mostly + * change some constants etc. I left it at 16Mb, as my machine + * even cannot be extended past that (ok, but it was cheap :-) + * I've tried to show which constants to change by having + * some kind of marker at them (search for "16Mb"), but I + * won't guarantee that's all :-( ) + */ +/* + * 这个子程序通过设置控制寄存器cr0的标志(PG位31)来启动对内存的分页处理功能,并设置各个页表项 + * 的内容,以恒等映射前16MB的物理内存。分页器假定不会产生非法的地址映射(也即在只有4MB的机器上 + * 设置出大于4MB的内存地址) + * + * 注意!尽管所有的物理地址都应该由这个子程序进行恒等映射,但只有内核页面管理函数能直接使用>1MB + * 的地址。所有"普通"函数仅使用低于1MB的地址空间,或者是使用局部数据空间,该地址空间将被映射到 + * 其他一些地方去--mm(内存管理程序)会管理这些事的. + * + */ + # 上面英文注释第2段的含义是指在机器物理内存中大于1MB的内存空间主要被用于主内存区。主内存区空间 + # 由mm模块管理,它涉及页面映射操作。内核中所有其它函数就是这里指的"普通"函数。 + + +# 初始化页目录表前4项和4个页表 +.align 4 +setup_paging: + movl $1024 * 5, %ecx /* 5 pages - pg_dir+4 page tables */ + xorl %eax, %eax + xorl %edi, %edi /* pg_dir is at 0x000 */ + # 页目录从0x0000地址开始 + cld;rep;stosl # eax内容存到es:edi所指内存位置处,且edi增4. + + # 设置页目录表中的前4个页目录项 + # 例如第1个页目录项: + # 页表所在地址 = 0x00001007 & 0xfffff000 = 0x1000 + # 页表属性标志 = 0x00001007 & 0x00000fff = 0x07 表示该页存在,用户可读写. + movl $pg0 + 7, pg_dir /* set present bit/user r/w */ + movl $pg1 + 7, pg_dir + 4 /* --------- " " --------- */ + movl $pg2 + 7, pg_dir + 8 /* --------- " " --------- */ + movl $pg3 + 7, pg_dir + 12 /* --------- " " --------- */ + + # 设置4个页表中所有项的内容(共4096项),从最后一个页表的最后一项开始按倒退顺序填写 + movl $pg3 + 4092, %edi # edi->最后一页的最后一项. + movl $0xfff007, %eax /* 16Mb - 4096 + 7 (r/w user,p) */ + std # 方向位置位,edi值递减(4字节) +1: stosl /* fill pages backwards - more efficient :-) */ + subl $0x1000, %eax # 每填好一项,物理地址值减0x1000。 + jge 1b # 如果小于0则说明全填写好了 + cld + # 设置页目录表基地址寄存器cr3(保存页目录表的物理地址) + xorl %eax, %eax /* pg_dir is at 0x0000 */ + movl %eax, %cr3 /* cr3 - page directory start */ + # 设置启动使用分页处理(cr0的PG标志,位31) + movl %cr0, %eax + orl $0x80000000, %eax # 添上PG标志 + movl %eax, %cr0 /* set paging (PG) bit */ + ret /* this also flushes prefetch-queue */ + +# 在改变分页处理标志后要求使用转移指令刷新预取指令队列,这里用的是返回指令ret。 +# 该返回指令ret的另一个作用并跳转到/init/main.c程序去运行。 + +.align 4 + +# 前2字节是描述符表的限长,后4字节是描述符表在线性地址空间中的32位基地址。 +.word 0 +idt_descr: + .word 256 * 8 - 1 # idt contains 256 entries + .long idt + +.align 4 # 这个对齐貌似多余 + +.word 0 +gdt_descr: + .word 256 * 8 - 1 # so does gdt (not that that's any + .long gdt # magic number, but it works for me :^) + +.align 8 +# 中断描述符表(空表) +idt: .fill 256, 8, 0 # idt is uninitialized + +# 全局描述符表 +# 前4项分别是空项(不用)、代码段描述符、数据段描述符、系统调用段描述符(没有使用) +# 同时还预留了252项的空间,用于放置所创建任务的局部描述符(LDT)和对应的任务状态段TSS的描述符 +# (0-nul, 1-cs, 2-ds, 3-syscall, 4-TSS0, 5-LDT0, 6-TSS1, 7-LDT1, 8-TSS2 etc...) +gdt: + .quad 0x0000000000000000 /* NULL descriptor */ + .quad 0x00c09a0000000fff /* 16Mb */ # 0x08,内核代码段,长度16MB + .quad 0x00c0920000000fff /* 16Mb */ # 0x10,内核数据段,长度16MB + .quad 0x0000000000000000 /* TEMPORARY - don't use */ + .fill 252, 8, 0 /* space for LDT's and TSS's etc */ diff --git a/linux-0.12/boot/setup.S b/linux-0.12/boot/setup.S new file mode 100644 index 0000000..7191af4 --- /dev/null +++ b/linux-0.12/boot/setup.S @@ -0,0 +1,703 @@ +! +! setup.s (C) 1991 Linus Torvalds +! +! setup.s is responsible for getting the system data from the BIOS, +! and putting them into the appropriate places in system memory. +! both setup.s and system has been loaded by the bootblock. +! +! This code asks the bios for memory/disk/other parameters, and +! puts them in a "safe" place: 0x90000-0x901FF, ie where the +! boot-block used to be. It is then up to the protected mode +! system to read them from there before the area is overwritten +! for buffer-blocks. +! +; setup.s负责从BIOS中获取系统数据,并将这些数据放到系统内存的适当地方。 +; 此时setup.s和system已经由bootsect引导块加载到内存中。 +; +; 这段代码询问bios有关内存/磁盘/其它参数,并将这些参数放到一个“安全的”地 +; 方:0x90000 - 0x901FF,也即原来bootsect代码块曾经在的地方,然后在被缓冲 +; 块覆盖掉之前由保护模式的system读取。 +! + +! NOTE! These had better be the same as in bootsect.s! +; 以下这些参数最好和bootsect.s中的相同! +#include +; config.h中定义了DEF_INITSEG = 0x9000,DEF_SYSSEG = 0x1000,DEF_SETUPSEG = 0x9020。 +INITSEG = DEF_INITSEG ! we move boot here - out of the way +SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536). +SETUPSEG = DEF_SETUPSEG ! this is the current segment + +.globl begtext, begdata, begbss, endtext, enddata, endbss +.text +begtext: +.data +begdata: +.bss +begbss: +.text + +entry start +start: + +! ok, the read went well so we get current cursor position and save it for +! posterity. +; ok,整个读磁盘过程都正常,现在将光标位置保存以备今后使用。 + + mov ax,#INITSEG ! this is done in bootsect already, but... + ; 这已经在bootsect程序中设置过ds,但是... + mov ds,ax + +;;;;; 1.获取系统初始化所需要的参数 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 内存地址 ; 字节 ; 内容 ; +; 0x90000 ; 2 ; 光标位置 ; +; 0x90002 ; 2 ; 扩展内存大小 ; +; 0x90004 ; 2 ; 显示页面 ; +; 0x90006 ; 1 ; 显示模式 ; +; 0x90007 ; 1 ; 字符列数 ; +; 0x90008 ; 2 ; ?? ; +; 0x9000A ; 1 ; 安装的显示内存 ; +; 0x9000B ; 1 ; 显示状态(彩色/单色) ; +; 0x9000C ; 2 ; 显示卡特性参数 ; +; 0x9000E ; 1 ; 屏幕当前行值 ; +; 0x9000F ; 1 ; 屏幕当前列值 ; +; ... ; +; 0x90080 ; 16; 第1个硬盘的参数表 ; +; 0x90090 ; 16; 第2个硬盘的参数表 ; +; 0x901FC ; 2 ; 根文件系统所在的设备号(bootsec.s中设置); + +! Get memory size (extended mem, kB) +;;; 取从0x100000(1M)处开始的扩展内存大小(KB) + mov ah,#0x88 + int 0x15 + mov [2],ax + +! check for EGA/VGA and some config parameters +;;; 检查EGA/VGA和一些配置参数 + mov ah,#0x12 + mov bl,#0x10 + int 0x10 + mov [8],ax + mov [10],bx + mov [12],cx + ; 检测屏幕当前行列值。若显示卡是VGA卡,则请求用户选择显示行列值 + mov ax,#0x5019 ; 预设行列值(ah = 80列,al = 25行) + cmp bl,#0x10 + je novga + call chsvga +novga: + mov [14],ax +;;; 获取光标位置 + mov ah,#0x03 ! read cursor pos + xor bh,bh + int 0x10 ! save it in known place, con_init fetches + mov [0],dx ! it from 0x90000. + +! Get video-card data: +;;; 取显示卡当前显示模式 + mov ah,#0x0f + int 0x10 + mov [4],bx ! bh = display page + mov [6],ax ! al = video mode, ah = window width + +;;; 复制两个硬盘的参数表: +! Get hd0 data + mov ax,#0x0000 + mov ds,ax + lds si,[4*0x41] ; 取中断向量0x41的值,即hd0参数表的地址--ds:si + mov ax,#INITSEG + mov es,ax + mov di,#0x0080 ; 传输目的地址: 0x9000:0x0080 --es:di + mov cx,#0x10 + rep + movsb + +! Get hd1 data + mov ax,#0x0000 + mov ds,ax + lds si,[4*0x46] ; 取中断向量0x46的值,即hd1参数表的地址--ds:si + mov ax,#INITSEG + mov es,ax + mov di,#0x0090 ; 传输目的地址: 0x9000:0x0090 --es:di + mov cx,#0x10 + rep + movsb + +! Check that there IS a hd1 :-) +;;; 检查系统是否存在第2个硬盘,如果不存在则第2个表清零。 + + mov ax,#0x01500 + mov dl,#0x81 + int 0x13 + jc no_disk1 + cmp ah,#3 + je is_disk1 ; 是硬盘吗?(类型 = 3?). +no_disk1: + mov ax,#INITSEG + mov es,ax + mov di,#0x0090 + mov cx,#0x10 + mov ax,#0x00 + rep + stosb +is_disk1: + +! now we want to move to protected mode ... +; 这里开始,我们将要开始进入保护模式 + + cli ! no interrupts allowed ! ; 这里开始不允许任何中断 + +! first we move the system to it's rightful place +;;;;; 2. 把整个system模块移动到0x00000位置 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + mov ax,#0x0000 + cld ! 'direction' = 0, movs moves forward +do_move: + mov es,ax ! destination segment ; es:di是目的地址(初始为0x0:0x0) + add ax,#0x1000 + cmp ax,#0x9000 ; 已经把最后一段(从0x8000段开始的64KB)代码移动完. + jz end_move ; 判断是否移动完成 + mov ds,ax ! source segment + sub di,di + sub si,si + mov cx,#0x8000 ; 移动0x8000个字 + rep + movsw + jmp do_move + +! then we load the segment descriptors +;;;;; 3. 此后,我们加载段描述符表(临时gdt,ldt表) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +end_move: + mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-) + mov ds,ax + lidt idt_48 ! load idt with 0,0 ; 加载IDT寄存器 + lgdt gdt_48 ! load gdt with whatever appropriate ; 加载GDT寄存器 + +! that was painless, now we enable A20 +;;;;; 为了能访问1MB以上的内存,需要开启A20地址线 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + call empty_8042 + mov al,#0xD1 ! command write ; 0xD1 - 写数据到P2口 + out #0x64,al ; 位1用于A20线的选通 + call empty_8042 + mov al,#0xDF ! A20 on + out #0x60,al + call empty_8042 + +! well, that went ok, I hope. Now we have to reprogram the interrupts :-( +! we put them right after the intel-reserved hardware interrupts, at +! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really +! messed this up with the original PC, and they haven't been able to +! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, +! which is used for the internal hardware interrupts as well. We just +! have to reprogram the 8259's, and it isn't fun. +; 希望以上一切正常。现在我们必须重新对中断进行编程 :-( +; 我们将它们放在正好处于intel保留的硬件中断后面,在int 0x20 - 0x2F。 +; 在那里它们不会引起冲突。不幸的是IBM在原PC机中搞糟了,以后也没有纠正过来。 +; PC机的BIOS将中断放在了0x08-0x0f,这些中断也被用于内部硬件中断。 +; 所以我们就必须重新对8259中断控制器进行编程,这一点都没意思。 + +;;;;; 以下是重新对8259A芯片进行编程,不是需要理解的重点,不做具体介绍 ;;;;;;;;;;;;;;;;; + mov al,#0x11 ! initialization sequence + out #0x20,al ! send it to 8259A-1 + .word 0x00eb,0x00eb ! jmp $+2, jmp $+2 ; $ 表示当前指令的地址, + out #0xA0,al ! and to 8259A-2 + .word 0x00eb,0x00eb + mov al,#0x20 ! start of hardware int's (0x20) + out #0x21,al + .word 0x00eb,0x00eb + mov al,#0x28 ! start of hardware int's 2 (0x28) + out #0xA1,al + .word 0x00eb,0x00eb + mov al,#0x04 ! 8259-1 is master + out #0x21,al + .word 0x00eb,0x00eb + mov al,#0x02 ! 8259-2 is slave + out #0xA1,al + .word 0x00eb,0x00eb + mov al,#0x01 ! 8086 mode for both + out #0x21,al + .word 0x00eb,0x00eb + out #0xA1,al + .word 0x00eb,0x00eb + mov al,#0xFF ! mask off all interrupts for now + out #0x21,al + .word 0x00eb,0x00eb + out #0xA1,al + +! well, that certainly wasn't fun :-(. Hopefully it works, and we don't +! need no steenking BIOS anyway (except for the initial loading :-). +! The BIOS-routine wants lots of unnecessary data, and it's less +! "interesting" anyway. This is how REAL programmers do it. +! +! Well, now's the time to actually move into protected mode. To make +! things as simple as possible, we do no register set-up or anything, +! we let the gnu-compiled 32-bit programs do that. We just jump to +! absolute address 0x00000, in 32-bit protected mode. +; 哼,上面这段当然没劲:-(,希望这样能工作,而且我们也不再需要乏味的BIOS了(除了 +; 初始的加载:-)。BIOS子程序要求很多不必要的数据,而且它一点都没趣。那是“真正”的 +; 程序员所做的事。 +; 进入保护模式时,我们只需要简单地跳转到绝对地址0x00000处就好。 +; 这里设置进入32位保护模式运行。首先加载机器状态字(lmsw - Load Machine Status +; Word),也称控制寄存器CR0,将其位0置1将导致CPU工作在保护模式。 +; 注:在Intel公司的手册上建议80386或以上CPU应该使用指令“mov cr0,ax”切换到保护模式。 + +;;;;; 4. 开启保护模式 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + mov ax,#0x0001 ! protected mode (PE) bit + lmsw ax ! This is it! ; 将CR0的位0置1开启保护模式 + jmpi 0,8 ! jmp offset 0 of segment 8 (cs) + ; 偏移地址是0,因为system模块已被移动到0x00000处了。 + ; 段值8则是保护模式下的段选择符。 + ; 段选择符长度为16位(2字节): + ; 位0-1 - 表示请求的特权级0-3 ,linux0.12只用到两级:0级(系统级)和3级(用户级) + ; 位2 - 用于选择全局描述符表(0)还是局部描述符表(1) + ; 位3-15 - 描述符表项的索引 + ; 段选择符8(0000,0000,0000,1000)表示请求特权级0、使用全局描述符表gdt中的第1项(该项指出代码 + ; 的基地址是0),因此这里的跳转指令就会去执行system中的代码。 + +;;;;;;;;;;;;;;;; setup程序到这就结束了 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +! This routine checks that the keyboard command queue is empty +! No timeout is used - if this hangs there is something wrong with +! the machine, and we probably couldn't proceed anyway. ' +empty_8042: + .word 0x00eb,0x00eb + in al,#0x64 ! 8042 status port ; 读AT键盘控制器状态寄存器 + test al,#2 ! is input buffer full? ; 测试位1,输入缓冲器满? + jnz empty_8042 ! yes - loop + ret + +! Routine trying to recognize type of SVGA-board present (if any) +! and if it recognize one gives the choices of resolution it offers. +! If one is found the resolution chosen is given by al,ah (rows,cols). + +;;;;; 以下代码牵涉到众多显卡端口信息,比较复杂。但跟内核运行关系不大,所以可以跳过不看 ;;; +chsvga: cld + push ds + push cs + pop ds + mov ax,#0xc000 + mov es,ax + lea si,msg1 + call prtstr +nokey: + in al,#0x60 + cmp al,#0x82 + jb nokey + cmp al,#0xe0 + ja nokey + cmp al,#0x9c + je svga + mov ax,#0x5019 + pop ds + ret +svga: lea si,idati ! Check ATI 'clues' + mov di,#0x31 + mov cx,#0x09 + repe + cmpsb + jne noati + lea si,dscati + lea di,moati + lea cx,selmod + jmp cx +noati: mov ax,#0x200f ! Check Ahead 'clues' + mov dx,#0x3ce + out dx,ax + inc dx + in al,dx + cmp al,#0x20 + je isahed + cmp al,#0x21 + jne noahed +isahed: lea si,dscahead + lea di,moahead + lea cx,selmod + jmp cx +noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues' + in al,dx + or al,#0x10 + out dx,al + mov dx,#0x104 + in al,dx + mov bl,al + mov dx,#0x3c3 + in al,dx + and al,#0xef + out dx,al + cmp bl,[idcandt] + jne nocant + lea si,dsccandt + lea di,mocandt + lea cx,selmod + jmp cx +nocant: mov dx,#0x3d4 ! Check Cirrus 'clues' + mov al,#0x0c + out dx,al + inc dx + in al,dx + mov bl,al + xor al,al + out dx,al + dec dx + mov al,#0x1f + out dx,al + inc dx + in al,dx + mov bh,al + xor ah,ah + shl al,#4 + mov cx,ax + mov al,bh + shr al,#4 + add cx,ax + shl cx,#8 + add cx,#6 + mov ax,cx + mov dx,#0x3c4 + out dx,ax + inc dx + in al,dx + and al,al + jnz nocirr + mov al,bh + out dx,al + in al,dx + cmp al,#0x01 + jne nocirr + call rst3d4 + lea si,dsccirrus + lea di,mocirrus + lea cx,selmod + jmp cx +rst3d4: mov dx,#0x3d4 + mov al,bl + xor ah,ah + shl ax,#8 + add ax,#0x0c + out dx,ax + ret +nocirr: call rst3d4 ! Check Everex 'clues' + mov ax,#0x7000 + xor bx,bx + int 0x10 + cmp al,#0x70 + jne noevrx + shr dx,#4 + cmp dx,#0x678 + je istrid + cmp dx,#0x236 + je istrid + lea si,dsceverex + lea di,moeverex + lea cx,selmod + jmp cx +istrid: lea cx,ev2tri + jmp cx +noevrx: lea si,idgenoa ! Check Genoa 'clues' + xor ax,ax + seg es + mov al,[0x37] + mov di,ax + mov cx,#0x04 + dec si + dec di +l1: inc si + inc di + mov al,(si) + seg es + and al,(di) + cmp al,(si) + loope l1 + cmp cx,#0x00 + jne nogen + lea si,dscgenoa + lea di,mogenoa + lea cx,selmod + jmp cx +nogen: lea si,idparadise ! Check Paradise 'clues' + mov di,#0x7d + mov cx,#0x04 + repe + cmpsb + jne nopara + lea si,dscparadise + lea di,moparadise + lea cx,selmod + jmp cx +nopara: mov dx,#0x3c4 ! Check Trident 'clues' + mov al,#0x0e + out dx,al + inc dx + in al,dx + xchg ah,al + mov al,#0x00 + out dx,al + in al,dx + xchg al,ah + mov bl,al ! Strange thing ... in the book this wasn't ' + and bl,#0x02 ! necessary but it worked on my card which + jz setb2 ! is a trident. Without it the screen goes + and al,#0xfd ! blurred ... + jmp clrb2 ! +setb2: or al,#0x02 ! +clrb2: out dx,al + and ah,#0x0f + cmp ah,#0x02 + jne notrid +ev2tri: lea si,dsctrident + lea di,motrident + lea cx,selmod + jmp cx +notrid: mov dx,#0x3cd ! Check Tseng 'clues' + in al,dx ! Could things be this simple ! :-) + mov bl,al + mov al,#0x55 + out dx,al + in al,dx + mov ah,al + mov al,bl + out dx,al + cmp ah,#0x55 + jne notsen + lea si,dsctseng + lea di,motseng + lea cx,selmod + jmp cx +notsen: mov dx,#0x3cc ! Check Video7 'clues' + in al,dx + mov dx,#0x3b4 + and al,#0x01 + jz even7 + mov dx,#0x3d4 +even7: mov al,#0x0c + out dx,al + inc dx + in al,dx + mov bl,al + mov al,#0x55 + out dx,al + in al,dx + dec dx + mov al,#0x1f + out dx,al + inc dx + in al,dx + mov bh,al + dec dx + mov al,#0x0c + out dx,al + inc dx + mov al,bl + out dx,al + mov al,#0x55 + xor al,#0xea + cmp al,bh + jne novid7 + lea si,dscvideo7 + lea di,movideo7 +selmod: push si + lea si,msg2 + call prtstr + xor cx,cx + mov cl,(di) + pop si + push si + push cx +tbl: pop bx + push bx + mov al,bl + sub al,cl + call dprnt + call spcing + lodsw + xchg al,ah + call dprnt + xchg ah,al + push ax + mov al,#0x78 + call prnt1 + pop ax + call dprnt + call docr + loop tbl + pop cx + call docr + lea si,msg3 + call prtstr + pop si + add cl,#0x80 +nonum: in al,#0x60 ! Quick and dirty... + cmp al,#0x82 + jb nonum + cmp al,#0x8b + je zero + cmp al,cl + ja nonum + jmp nozero +zero: sub al,#0x0a +nozero: sub al,#0x80 + dec al + xor ah,ah + add di,ax + inc di + push ax + mov al,(di) + int 0x10 + pop ax + shl ax,#1 + add si,ax + lodsw + pop ds + ret +; 若都不是上面检测地显示卡,我们就只好采用默认地80*25的标准行列值。 +novid7: pop ds ! Here could be code to support standard 80x50,80x30 + mov ax,#0x5019 + ret + +! Routine that 'tabs' to next col. + +spcing: mov al,#0x2e + call prnt1 + mov al,#0x20 + call prnt1 + mov al,#0x20 + call prnt1 + mov al,#0x20 + call prnt1 + mov al,#0x20 + call prnt1 + ret + +! Routine to print asciiz-string at DS:SI + +prtstr: lodsb + and al,al + jz fin + call prnt1 + jmp prtstr +fin: ret + +! Routine to print a decimal value on screen, the value to be +! printed is put in al (i.e 0-255). + +dprnt: push ax + push cx + mov ah,#0x00 + mov cl,#0x0a + idiv cl + cmp al,#0x09 + jbe lt100 + call dprnt + jmp skip10 +lt100: add al,#0x30 + call prnt1 +skip10: mov al,ah + add al,#0x30 + call prnt1 + pop cx + pop ax + ret + +! Part of above routine, this one just prints ascii al + +prnt1: push ax + push cx + mov bh,#0x00 + mov cx,#0x01 + mov ah,#0x0e + int 0x10 + pop cx + pop ax + ret + +! Prints + + +docr: push ax + push cx + mov bh,#0x00 + mov ah,#0x0e + mov al,#0x0a + mov cx,#0x01 + int 0x10 + mov al,#0x0d + int 0x10 + pop cx + pop ax + ret +;;;;; 跟显示卡相关代码至此结束 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;; 全局描述符表(临时,每个描述符项长8个字节) +gdt: + .word 0,0,0,0 ! dummy ;第1个描述符,不用 + + ; 在GDT表的偏移量是0x08。它是内核代码段选择符的值。 + .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) + .word 0x0000 ! base address=0 + .word 0x9A00 ! code read/exec ; 代码段为只读,可执行 + .word 0x00C0 ! granularity=4096, 386 ; 颗粒度4K,32位 + + .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb) + .word 0x0000 ! base address=0 + .word 0x9200 ! data read/write ; 数据段为可读可写 + .word 0x00C0 ! granularity=4096, 386 ; 颗粒度4K,32位 + +; 加载中断描述符表寄存器指令lidt要求的6字节操作数。 +; 注:CPU要求在进入保护模式之前需设置idt表,因此这里先设置一个长度为0的空表。 +idt_48: + .word 0 ! idt limit=0 ; idt的限长 + .word 0,0 ! idt base=0L ; idt表在线性地址空间中的32位基地址 + +; 加载全局描述符表寄存器指令lgdt要求的6字节操作数。 +gdt_48: + .word 0x800 ! gdt limit=2048, 256 GDT entries + ; 表限长2k + .word 512+gdt,0x9 ! gdt base = 0X9xxxx + ; (线性地址空间)基地址:0x90200 + gdt + +;;;;; 以下跟显示卡相关(可以不看) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +msg1: .ascii "Press to see SVGA-modes available or any other key to continue." + db 0x0d, 0x0a, 0x0a, 0x00 +msg2: .ascii "Mode: COLSxROWS:" + db 0x0d, 0x0a, 0x0a, 0x00 +msg3: .ascii "Choose mode by pressing the corresponding number." + db 0x0d, 0x0a, 0x00 + +idati: .ascii "761295520" +idcandt: .byte 0xa5 +idgenoa: .byte 0x77, 0x00, 0x66, 0x99 +idparadise: .ascii "VGA=" + +! Manufacturer: Numofmodes: Mode: +; 厂家: 模式数量: 模式列表: +moati: .byte 0x02, 0x23, 0x33 +moahead: .byte 0x05, 0x22, 0x23, 0x24, 0x2f, 0x34 +mocandt: .byte 0x02, 0x60, 0x61 +mocirrus: .byte 0x04, 0x1f, 0x20, 0x22, 0x31 +moeverex: .byte 0x0a, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40 +mogenoa: .byte 0x0a, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78 +moparadise: .byte 0x02, 0x55, 0x54 +motrident: .byte 0x07, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a +motseng: .byte 0x05, 0x26, 0x2a, 0x23, 0x24, 0x22 +movideo7: .byte 0x06, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45 + +! msb = Cols lsb = Rows: +; 高字节 = 列数 低字节 = 行数 +dscati: .word 0x8419, 0x842c +dscahead: .word 0x842c, 0x8419, 0x841c, 0xa032, 0x5042 +dsccandt: .word 0x8419, 0x8432 +dsccirrus: .word 0x8419, 0x842c, 0x841e, 0x6425 +dsceverex: .word 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e +dscgenoa: .word 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b +dscparadise: .word 0x8419, 0x842b +dsctrident: .word 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c +dsctseng: .word 0x503c, 0x6428, 0x8419, 0x841c, 0x842c +dscvideo7: .word 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c +;;;;;; 显示卡相关 到这结束 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +.text +endtext: +.data +enddata: +.bss +endbss: diff --git a/linux-0.12/fs/Makefile b/linux-0.12/fs/Makefile new file mode 100644 index 0000000..8e541fd --- /dev/null +++ b/linux-0.12/fs/Makefile @@ -0,0 +1,127 @@ +include ../Rules.make + +CFLAGS = -g -Wall -fstrength-reduce -fomit-frame-pointer \ + -fno-defer-pop -nostdinc -fno-builtin -I../include +CPP += -E -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< + +OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \ + block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \ + bitmap.o fcntl.o ioctl.o truncate.o select.o + +fs.o: $(OBJS) + $(LD) -r -o fs.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h +block_dev.o : block_dev.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/asm/segment.h ../include/asm/system.h +buffer.o : buffer.c ../include/stdarg.h ../include/linux/config.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/system.h \ + ../include/asm/io.h +char_dev.o : char_dev.c ../include/errno.h ../include/sys/types.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/asm/segment.h ../include/asm/io.h +exec.o : exec.c ../include/signal.h ../include/sys/types.h \ + ../include/errno.h ../include/string.h ../include/sys/stat.h \ + ../include/a.out.h ../include/linux/fs.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/asm/segment.h +fcntl.o : fcntl.c ../include/string.h ../include/errno.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/segment.h \ + ../include/fcntl.h ../include/sys/stat.h +file_dev.o : file_dev.c ../include/errno.h ../include/fcntl.h \ + ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/segment.h +file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h +inode.o : inode.c ../include/string.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/system.h +ioctl.o : ioctl.c ../include/string.h ../include/errno.h \ + ../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ + ../include/sys/time.h ../include/time.h ../include/sys/resource.h +namei.o : namei.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ + ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ + ../include/asm/segment.h ../include/string.h ../include/fcntl.h \ + ../include/errno.h ../include/const.h ../include/sys/stat.h +open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.h \ + ../include/sys/types.h ../include/utime.h ../include/sys/stat.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/linux/tty.h ../include/termios.h \ + ../include/asm/segment.h +pipe.o : pipe.c ../include/signal.h ../include/sys/types.h \ + ../include/errno.h ../include/termios.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/segment.h +read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.h \ + ../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/segment.h +select.o : select.c ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \ + ../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h \ + ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/segment.h \ + ../include/asm/system.h ../include/sys/stat.h ../include/string.h \ + ../include/const.h ../include/errno.h +stat.o : stat.c ../include/errno.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/asm/segment.h +super.o : super.c ../include/linux/config.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/asm/system.h ../include/errno.h \ + ../include/sys/stat.h +truncate.o : truncate.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ + ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ + ../include/sys/stat.h diff --git a/linux-0.12/fs/bitmap.c b/linux-0.12/fs/bitmap.c new file mode 100755 index 0000000..71fb4fd --- /dev/null +++ b/linux-0.12/fs/bitmap.c @@ -0,0 +1,268 @@ +/* + * linux/fs/bitmap.c + * + * (C) 1991 Linus Torvalds + */ + +/* bitmap.c contains the code that handles the inode and block bitmaps */ +/* bitmap.c程序含有处理i节点和磁盘块位图的代码 */ +#include +#include +#include + +/* 将指定地址(addr)处的一块1024字节内存清零 */ +#define clear_block(addr) \ + __asm__( \ + "cld\n\t" \ + "rep\n\t" \ + "stosl" \ + ::"a" (0), "c" (BLOCK_SIZE / 4), "D" ((long) (addr))) + +/** + * 把指定地址开始的第nr个位偏移处的比特位置位(nr可大于32!) + * btsl指令用于测试并设置位(Bit Test and Set)。把基地址(%3)和位偏移值(%2)所指定的位值先保存到进 + * 位标志CF中,然后设置该位为1。指令setb用于根据进位标志CF设置操作数(%al)。如果CF=1,则%al=1, + * 否则%al=0。 + * @param[in] nr 位偏移 + * @param[in] addr 指定地址的基地址 + * @retval 返回addr+nr处比特位的原位值 + */ +#define set_bit(nr, addr) ({ \ + register int res; \ + __asm__ __volatile__("btsl %2, %3\n\tsetb %%al" \ + :"=a" (res) \ + :"0" (0),"r" (nr),"m" (*(addr))); \ + res;}) + +/** + * 复位指定地址开始的第nr位偏移处的位,返回原位值的反码 + * @param[in] nr 位偏移 + * @param[in] addr 指定地址的基地址 + * @retval 返回addr+nr处比特位的原位值的反码 + */ +#define clear_bit(nr, addr) ({ \ + register int res; \ + __asm__ __volatile__("btrl %2, %3\n\tsetnb %%al" \ + :"=a" (res) \ + :"0" (0), "r" (nr), "m" (*(addr))); \ + res;}) + +/** + * 从addr开始寻找第1个0值位 + * 在addr指定地址开始的位图中寻找第1个是0的位,并将其距离addr的位偏移值返回。addr是缓冲块数据区 + * 的地址,扫描寻找的范围是1024字节(8192位)。 + * @param[in] addr 指定地址 + * @retval 返回第一个0值位距离addr的位偏移值 + */ +#define find_first_zero(addr) ({ \ + int __res; \ + __asm__( \ + "cld\n" \ + "1:\tlodsl\n\t" \ + "notl %%eax\n\t" \ + "bsfl %%eax, %%edx\n\t" \ + "je 2f\n\t" \ + "addl %%edx, %%ecx\n\t" \ + "jmp 3f\n" \ + "2:\taddl $32, %%ecx\n\t" \ + "cmpl $8192, %%ecx\n\t" \ + "jl 1b\n" \ + "3:" \ + :"=c" (__res) \ + :"c" (0), "S" (addr)); \ + __res;}) + +/** + * 释放设备dev上数据区中的逻辑块block + * @param[in] dev 设备号 + * @param[in] block 逻辑块号(盘块号) + * @retval 成功返回1,失败返回0 + */ +int free_block(int dev, int block) +{ + struct super_block * sb; + struct buffer_head * bh; + + if (!(sb = get_super(dev))) { + panic("trying to free block on nonexistent device"); + } + if (block < sb->s_firstdatazone || block >= sb->s_nzones) { + panic("trying to free block not in datazone"); + } + /* 然后从hash表中寻找该块数据 */ + bh = get_hash_table(dev, block); + if (bh) { + if (bh->b_count > 1) { /* 引用次数大于1,该块还有人用,则调用brelse()后退出 */ + brelse(bh); + return 0; + } + bh->b_dirt = 0; + bh->b_uptodate = 0; + if (bh->b_count) { /* 若此时b_count为1, 则调用brelse()释放之 */ + brelse(bh); + } + } + /* 接着复位block在逻辑块位图中的位(置0) */ + block -= sb->s_firstdatazone - 1 ; + if (clear_bit(block & 8191, sb->s_zmap[block/8192]->b_data)) { /* 1个缓冲块有1024B,即8192bits */ + printk("block (%04x:%d) ", dev, block + sb->s_firstdatazone - 1); + printk("free_block: bit already cleared\n"); + } + /* 最后置相应逻辑块位图所在缓冲区已修改标志 */ + sb->s_zmap[block/8192]->b_dirt = 1; + return 1; +} + +/** + * 向设备dev申请一个逻辑块 + * @param[in] dev 设备号 + * @retval 成功返回逻辑块号,失败返回0。 + */ +int new_block(int dev) +{ + struct buffer_head * bh; + struct super_block * sb; + int i, j; + + if (!(sb = get_super(dev))) { + panic("trying to get new block from nonexistant device"); + } + /* 扫描文件系统的8块逻辑块位图,寻找首个0值位,以寻找空闲逻辑块,获取设置该逻辑块的块号 */ + j = 8192; + for (i = 0 ; i < 8 ; i++) { + if ((bh = sb->s_zmap[i])) { + if ((j = find_first_zero(bh->b_data)) < 8192) { + break; + } + } + } + /* 然后如果全部扫描完8块逻辑块位图的所有位还没有找到0值位或者位图所在的缓冲块指针无效 + (bn = NULL)则表示当前没有空闲逻辑块 */ + if (i >= 8 || !bh || j >= 8192) { + return 0; + } + /* 设置找到的新逻辑块j对应逻辑块位图中的位,若对应位已经置位,则出错停机 */ + if (set_bit(j, bh->b_data)) { + panic("new_block: bit already set"); + } + bh->b_dirt = 1; + /* 计算该块在逻辑块位图中位偏移值,偏移值大于该设备上的总逻辑块数,则出错 */ + j += i * 8192 + sb->s_firstdatazone - 1; + if (j >= sb->s_nzones) { + return 0; + } + /* 在高速缓冲区中为该设备上指定的逻辑块号取得一个缓冲块,并返回缓冲块头指针 */ + if (!(bh = getblk(dev, j))) { + panic("new_block: cannot get block"); + } + /* 因为新取出的逻辑块其引用次数一定为1,若不是1,说明内核有问题。*/ + if (bh->b_count != 1) { + panic("new block: count is != 1"); + } + /* 将新逻辑块清零,并设置其已更新标志和已修改标志。然后释放对应缓冲块,返回逻辑块号 */ + clear_block(bh->b_data); + bh->b_uptodate = 1; + bh->b_dirt = 1; + brelse(bh); + return j; +} + +// 下面两个函数与上面逻辑块操作类似,只是对象换成了i节点 + +/** + * 释放指定的i节点 + * @param[in] inode 指向要释放的i节点的指针 + * @retval void + */ +void free_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + + if (!inode) { + return; + } + /* i节点上的设备号字段为0,说明该节点没有使用 */ + if (!inode->i_dev) { + memset(inode, 0, sizeof(*inode)); + return; + } + /* 如果此i节点还有其他程序引用,则不释放,说明内核有问题,停机 */ + if (inode->i_count > 1) { + printk("trying to free inode with count=%d\n", inode->i_count); + panic("free_inode"); + } + /* 如果文件连接数不为0,则表示还有其他文件目录项在使用该节点,因此也不应释放,而应该放回等 */ + if (inode->i_nlinks) { + panic("trying to free inode with links"); + } + if (!(sb = get_super(inode->i_dev))) { + panic("trying to free inode on nonexistent device"); + } + if (inode->i_num < 1 || inode->i_num > sb->s_ninodes) { + panic("trying to free inode 0 or nonexistant inode"); + } + /* 找到inode所在的逻辑块,其中i_num>>13即i_num/8192 */ + if (!(bh = sb->s_imap[inode->i_num >> 13])) { + panic("nonexistent imap in superblock"); + } + /* 现在我们复位i节点对应的节点位图中的位 */ + if (clear_bit(inode->i_num & 8191, bh->b_data)) { + printk("free_inode: bit already cleared.\n\r"); + } + /* 置i节点位图所在缓冲区已修改标志,并清空该i节点结构所占内存区 */ + bh->b_dirt = 1; + memset(inode, 0, sizeof(*inode)); +} + +/** + * 为设备dev建立一个新i节点 + * @param[in] dev 设备号 + * @retval 成功返回新i节点的指针,失败返回NULL + */ +struct m_inode * new_inode(int dev) +{ + struct m_inode * inode; + struct super_block * sb; + struct buffer_head * bh; + int i, j; + + /* 首先从内存i节点表(inode_table)中获取一个空闲i节点项,并读取指定设备的超级块结构。*/ + if (!(inode = get_empty_inode())) { + return NULL; + } + if (!(sb = get_super(dev))) { + panic("new_inode with unknown device"); + } + /*扫描超级块中8块i节点位图,寻找第1个0位(空闲节点),获取并设置该i节点的节点号。*/ + j = 8192; + for (i = 0 ; i < 8 ; i++) { + if ((bh = sb->s_imap[i])) { + if ((j = find_first_zero(bh->b_data)) < 8192) { + break; + } + } + } + /* 如果全部扫描完还没找到空闲i节点或者位图所在的缓冲块无效(bh = NULL),则放回先前申请的i节 + 点表中的i节点,并返回空指针退出 */ + if (!bh || j >= 8192 || j + i * 8192 > sb->s_ninodes) { + iput(inode); + return NULL; + } + /* 现在已经找到了还未使用的i节点号j。于是置位i节点j对应的i节点位图相应比特位。然后置i节点位 + 图所在缓冲块已修改标志 */ + if (set_bit(j, bh->b_data)) { + panic("new_inode: bit already set"); + } + bh->b_dirt = 1; + /* 初始化该i节点结构 */ + inode->i_count = 1; + inode->i_nlinks = 1; + inode->i_dev = dev; + inode->i_uid = current->euid; + inode->i_gid = current->egid; + inode->i_dirt = 1; + inode->i_num = j + i * 8192; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + return inode; +} diff --git a/linux-0.12/fs/block_dev.c b/linux-0.12/fs/block_dev.c new file mode 100755 index 0000000..4591a07 --- /dev/null +++ b/linux-0.12/fs/block_dev.c @@ -0,0 +1,119 @@ +/* + * linux/fs/block_dev.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +#include +#include +#include +#include + +extern int *blk_size[]; + +/** + * 数据块写函数 + * 向指定设备(实际先写到高速缓冲中)从给定偏移处写入指定长度数据 + * @param[in] dev 设备号 + * @param[in] pos 设备文件中偏移量指针 + * @param[in] buf 用户空间中缓冲区地址 + * @param[in] count 要写的字节数 + * @retval 成功返回已写入字节数,若没有写入任何字节或出错则返回出错号 + */ +int block_write(int dev, long * pos, char * buf, int count) +{ + int block = *pos >> BLOCK_SIZE_BITS; + int offset = *pos & (BLOCK_SIZE - 1); + int chars; + int written = 0; + int size; + struct buffer_head * bh; + register char * p; + + if (blk_size[MAJOR(dev)]) { + size = blk_size[MAJOR(dev)][MINOR(dev)]; + } else { + size = 0x7fffffff; /* 没有对设备指定长度,就使用默认长度2G个块 */ + } + while (count > 0) { + if (block >= size) { + return written ? written : -EIO; + } + chars = BLOCK_SIZE - offset; /* 本块可写入的字节数 */ + if (chars > count) { + chars = count; + } + if (chars == BLOCK_SIZE) { + bh = getblk(dev, block); + } else { + bh = breada(dev, block, block+1, block+2, -1); + } + block ++; + if (!bh) { + return written ? written : -EIO; + } + p = offset + bh->b_data; + offset = 0; + *pos += chars; + written += chars; + count -= chars; + while (chars-- > 0) { + *(p++) = get_fs_byte(buf++); + } + bh->b_dirt = 1; + brelse(bh); + } + return written; +} + + +/** + * 数据块读函数 + * 从指定设备和位置处读入指定长度数据到用户缓冲区中 + * @param[in] dev 设备号 + * @param[in] pos 设备文件中领衔量指针 + * @param[in] buf 用户空间中缓冲区地址 + * @param[in] count 要传送的字节数 + * @retval 返回已读入字节数。若没有读入任何字节或出错,则返回出错号 + */ +int block_read(int dev, unsigned long * pos, char * buf, int count) +{ + int block = *pos >> BLOCK_SIZE_BITS; + int offset = *pos & (BLOCK_SIZE-1); + int chars; + int size; + int read = 0; + struct buffer_head * bh; + register char * p; + + if (blk_size[MAJOR(dev)]) { + size = blk_size[MAJOR(dev)][MINOR(dev)]; + } else { + size = 0x7fffffff; + } + while (count>0) { + if (block >= size) { + return read ? read : -EIO; + } + chars = BLOCK_SIZE - offset; + if (chars > count) { + chars = count; + } + if (!(bh = breada(dev, block, block+1, block+2, -1))) { + return read ? read : -EIO; + } + block++; + p = offset + bh->b_data; + offset = 0; + *pos += chars; + read += chars; + count -= chars; + while (chars-->0) { + put_fs_byte(*(p++), buf++); + } + brelse(bh); + } + return read; +} diff --git a/linux-0.12/fs/buffer.c b/linux-0.12/fs/buffer.c new file mode 100755 index 0000000..a07b447 --- /dev/null +++ b/linux-0.12/fs/buffer.c @@ -0,0 +1,648 @@ +/* + * linux/fs/buffer.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 'buffer.c' implements the buffer-cache functions. Race-conditions have + * been avoided by NEVER letting a interrupt change a buffer (except for the + * data, of course), but instead letting the caller do it. NOTE! As interrupts + * can wake up a caller, some cli-sti sequences are needed to check for + * sleep-on-calls. These should be extremely quick, though (I hope). + */ +/* + * 'buffer.c'用于实现缓冲区高速缓存功能。通过不让中断处理过程改变缓冲区,而是让调用者来执行,避 + * 免了竞争条件(当然除改变数据外)。注意!由于中断可以唤醒一个调用者,因此就需要开关中断指令 + * (cli-sti)序列来检测由于调用而睡眠。但需要非常快(我希望是这样)。 + */ + + +/* + * NOTE! There is one discordant note here: checking floppies for + * disk change. This is where it fits best, I think, as it should + * invalidate changed floppy-disk-caches. + */ +/* + * 注意!有一个程序应不属于这里:检测软盘是否更换。但我想这里是放置该程序最好的地方了,因为它需 + * 要使已更换软盘缓冲失效。 + */ + +#include + +#include +#include +#include +#include +#include +#include + +// buffer_wait变量是等待空闲缓冲块而睡眠的任务队列头指针。它与缓冲块头部结构中b_wait指针的作用 +// 不同。当任务申请一个缓冲块而正好遇到系统缺乏可用空闲缓冲块时,当前任务就会被添加到buffer_wait +// 睡眠等待队列中。而b_wait则是专门供等待指定缓冲块(即b_wait对应的缓冲块)的任务使用的等待队列头 +// 指针。 +// +// NR_BUFFERS其值即是变量名nr_buffers,并且在include/linux/fs.h文件声明为全局变量。利用大写名 +// 称来隐含地表示nr_buffers是一个在初始化之后不再改变的"常量"。它将在buffer_init()中被设置。 + +/* end是由编译时的连接程序ld生成,表明内核代码的末端 */ +extern int end; + +/* 高速缓冲区开始于内核代码末端位置 */ +struct buffer_head *start_buffer = (struct buffer_head *) &end; + +/* 缓冲区Hash表数组 */ +struct buffer_head *hash_table[NR_HASH]; + +/* 空闲缓冲块链表头指针 */ +static struct buffer_head *free_list; + +/* 等待空闲缓冲块而睡眠的任务队列 */ +static struct task_struct *buffer_wait = NULL; + +/* 系统所含缓冲个数 */ +int NR_BUFFERS = 0; + + +// wait_on_buffer中,虽然是在关闭中断(cli)之后去睡眠的,但这样做并不会影响在其他进程上下文中响应 +// 中断。因为每个进程都在自己的TSS段中保存了标志寄存器EFLAGS的值,所在在进程切换时CPU中当前 +// EFLAGS的值也随之改变。 +// +// 使用sleep_on()进入睡眠状态的进程需要用wake_up()明确地唤醒。 + +/** + * 等待指定缓冲块解锁 + * 如果指定的缓冲块bh已经上锁就让进程不可中断地睡眠在该缓冲块的等待队列b_wait中。在缓冲块解锁时, + * 其等待队列上的所有进程将被唤醒。 + * @param[in] bh 指定缓冲块头指针 + * @retval void + */ +static inline void wait_on_buffer(struct buffer_head * bh) +{ + cli(); + while (bh->b_lock) { /* 如果已被上锁则进程进入睡眠,等待其解锁 */ + sleep_on(&bh->b_wait); + } + sti(); +} + +/** + * 设备数据同步 + * 将内存高速缓冲区中的数据同步到设备中 + * @param[in] void + * @retval 0 + */ +int sys_sync(void) +{ + int i; + struct buffer_head *bh; + + sync_inodes(); /* write out inodes into buffers */ + /* 将修改过的i节点写入缓冲区 */ + bh = start_buffer; + for (i = 0; i < NR_BUFFERS; i++, bh++) { + wait_on_buffer(bh); /* 等待缓冲区解锁 */ + if (bh->b_dirt) { + ll_rw_block(WRITE, bh); /* 产生写设备块请求 */ + } + } + return 0; +} + +/** + * 对指定设备进行数据同步 + * 该函数首先搜索高速缓冲区中所有缓冲块。对于指定设备dev的缓冲块,若其数据已被修改过就写入盘中 + * (同步操作)。然后把内存中i节点数据写入高速缓冲中。之后再指定设备dev执行一次与上述相同的写盘操 + * 作。 + * @param[in] dev 设备号 + * @retval 0 + */ +int sync_dev(int dev) +{ + int i; + struct buffer_head *bh; + + /* 这里采用两遍同步操作是为了提高内核执行效率。第一遍缓冲区同步操作可以让内核中许多“脏块” + 变干净,使得inode的同步操作能够高效执行。*/ + bh = start_buffer; + for (i = 0; i < NR_BUFFERS; i++, bh++) { + if (bh->b_dev != dev) { + continue; + } + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_dirt) { + ll_rw_block(WRITE, bh); + } + } + sync_inodes(); + bh = start_buffer; + for (i = 0; i < NR_BUFFERS; i++, bh++) { + if (bh->b_dev != dev) { + continue; + } + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_dirt) { + ll_rw_block(WRITE, bh); + } + } + return 0; +} + +/** + * 使指定设备在高速缓冲区中的数据无效 + * 扫描高速缓冲中所有的缓冲块。对指定设备的缓冲块复位其有效(更新)标志和修改标志。 + * @param[in] dev 设备号 + * @retval void + */ +void inline invalidate_buffers(int dev) +{ + int i; + struct buffer_head *bh; + + bh = start_buffer; + for (i = 0; i < NR_BUFFERS; i++, bh++) { + if (bh->b_dev != dev){ + continue; + } + wait_on_buffer(bh); + /* 由于进程执行过睡眠等待,所以需要再判断一下缓冲区是否是指定设备 */ + if (bh->b_dev == dev) { + bh->b_uptodate = bh->b_dirt = 0; + } + } +} + +/* + * This routine checks whether a floppy has been changed, and + * invalidates all buffer-cache-entries in that case. This + * is a relatively slow routine, so we have to try to minimize using + * it. Thus it is called only upon a 'mount' or 'open'. This + * is the best way of combining speed and utility, I think. + * People changing diskettes in the middle of an operation deserve + * to loose :-) + * + * NOTE! Although currently this is only for floppies, the idea is + * that any additional removable block-device will use this routine, + * and that mount/open needn't know that floppies/whatever are + * special. + */ +/* + * 该子程序检查一个软盘是否已被更换,如果已经更换就使高速缓冲中与该软驱对应的所有缓冲区无效。该 + * 子程序相对来说较慢,所以我们要尽量少使用它。所以仅在执行'mount'或'open'时才调用它。我想这是 + * 将程度与实用性相结合的最好方法。若在操作过程中更换软盘,就会导致数据的丢失。这是咎由自取。 + * + * 注意!尽管目前该子程序仅用于软盘,以后任何可移动介质的块设备都有将使用该程序,mount/open操作 + * 不需要知道是软盘还是其他什么特殊介质。 + */ + +/** + * 检查磁盘是否更换 + * 检查磁盘是否更换,如果已更换就使用对应调整缓冲区无效 + * @param[in] dev 设备号 + * @retval void + */ +void check_disk_change(int dev) +{ + int i; + /* 首先检测一下是否为软盘设备。因为当时仅支持软盘可移动介质 */ + if (MAJOR(dev) != 2) { + return; + } + /* 测试软盘是否已更换 */ + if (!floppy_change(dev & 0x03)) { + return; + } + /* 软盘已更换,释放该设备的超级块 */ + for (i = 0; i < NR_SUPER; i++) { + if (super_block[i].s_dev == dev) { + put_super(super_block[i].s_dev); + } + } + /* 同时释放对应设备的i节点位图和逻辑位图所占的高速缓冲区 */ + invalidate_inodes(dev); + invalidate_buffers(dev); +} + +// hash队列是双向链表结构,空闲缓冲块队列是双向循环链表结构。 +// +// hash表的主要作用是减少查找比较元素所花费的时间。通过在元素的存储位置与关键字之间建立一个对应 +// 关系(hash函数),我们就可以直接通过函数计算立刻查询到指定的元素。建立函数的方法有多种,这里, +// Linux0.12主要采用了关键字除余数法。因为我们寻找的缓冲块有两个条件,即设备号dev和缓冲块号 +// block,因此设计的hash函数肯定需要包含这两个关键值。这里两个关键字的异或操作只是计算关键值的 +// 一种方法。再对关键值进行MOD运算就可以保证函数计算得到的值都处于函数数组项范围内。 +#define _hashfn(dev, block) (((unsigned)(dev ^ block)) % NR_HASH) +#define hash(dev, block) hash_table[_hashfn(dev, block)] + +/** + * 从hash队列和空闲缓冲队列中移走缓冲块。 + * @param[in] bh 要移除的缓冲区头指针 + * @retval void + */ +static inline void remove_from_queues(struct buffer_head * bh) +{ + /* remove from hash-queue */ + /* 从hash队列中移除缓冲块 */ + if (bh->b_next) { + bh->b_next->b_prev = bh->b_prev; + } + if (bh->b_prev) { + bh->b_prev->b_next = bh->b_next; + } + /* 如果该缓冲区是该队列的第一个块,则让hash表的对应项指向本队列中的下一个缓冲区 */ + if (hash(bh->b_dev,bh->b_blocknr) == bh) { + hash(bh->b_dev,bh->b_blocknr) = bh->b_next; + } + /* remove from free list */ + /* 从空闲缓冲块表中移除缓冲块 */ + if (!(bh->b_prev_free) || !(bh->b_next_free)) { + panic("Free block list corrupted"); + } + bh->b_prev_free->b_next_free = bh->b_next_free; + bh->b_next_free->b_prev_free = bh->b_prev_free; + /* 如果空闲链表头指向本缓冲区,则让其指向下一缓冲区 */ + if (free_list == bh) { + free_list = bh->b_next_free; + } +} + +/** + * 将缓冲块插入空闲链表尾部,同时放入hash队列中 + * @param[in] bh 要插入的缓冲区头指针 + * @retval void + */ +static inline void insert_into_queues(struct buffer_head * bh) +{ + /* put at end of free list */ + /* 放在空闲链表末尾处 */ + bh->b_next_free = free_list; + bh->b_prev_free = free_list->b_prev_free; + free_list->b_prev_free->b_next_free = bh; + free_list->b_prev_free = bh; + /* put the buffer in new hash-queue if it has a device */ + /* 如果该缓冲块对应一个设备,则将其插入新hash队列中 */ + bh->b_prev = NULL; + bh->b_next = NULL; + if (!bh->b_dev) { + return; + } + bh->b_next = hash(bh->b_dev,bh->b_blocknr); + hash(bh->b_dev,bh->b_blocknr) = bh; + /* bug修复!第一次hash()会返回NULL,需要判断一下 */ + if (bh->b_next) { + bh->b_next->b_prev = bh; + } +} + +/** + * 在hash表查找指定缓冲块 + * @param[in] dev 设备号 + * @param[in] block 块号 + * @retval 如果找到则返回缓冲区块的指针,否则返回NULL + */ +static struct buffer_head * find_buffer(int dev, int block) +{ + struct buffer_head * tmp; + + for (tmp = hash(dev, block); tmp != NULL; tmp = tmp->b_next) { + if (tmp->b_dev == dev && tmp->b_blocknr == block) { + return tmp; + } + } + return NULL; +} + +/* + * Why like this, I hear you say... The reason is race-conditions. + * As we don't lock buffers (unless we are readint them, that is), + * something might happen to it while we sleep (ie a read-error + * will force it bad). This shouldn't really happen currently, but + * the code is ready. + */ +/* + * 代码为什么会是这样子的?我听见你问...原因是竞争条件。由于我们没有对缓冲块上锁(除非我们正在读 + * 取它们的数据),那么当我们(进程)睡眠时缓冲块可能发生一些问题(例如一个读错误将导致该缓冲块出错)。 + * 目前这种情况实际上是不会发生的,但处理的代码已经准备好了。 + */ + +/** + * 利用hash表在高速缓冲区中寻找指定的缓冲块。 + * @param[in] dev 设备号 + * @param[in] block 块号 + * @retval 如果找到则返回上锁后的缓冲区块的指针,否则返回NULL + */ +struct buffer_head * get_hash_table(int dev, int block) +{ + struct buffer_head * bh; + + for (;;) { + if (!(bh = find_buffer(dev, block))) { + return NULL; + } + bh->b_count ++; + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_blocknr == block) { + return bh; + } + bh->b_count--; + #if 0 + // Q: 上面为什么不是这样? + // A: bh->b_count先自增,会告诉系统,这个块还要用,别释放。 + wait_on_buffer(bh); + if (bh->b_dev == dev && bh->b_blocknr == block) { + bh->b_count ++; + return bh; + } + #endif + } +} + +/* + * Ok, this is getblk, and it isn't very clear, again to hinder + * race-conditions. Most of the code is seldom used, (ie repeating), + * so it should be much more efficient than it looks. + * + * The algoritm is changed: hopefully better, and an elusive bug removed. + */ +/* + * OK,下面是getblk函数,该函数的逻辑并不是很清晰,同样也是因为要考虑竞争条件问题。其中大部分代 + * 码很少用到(例如重复操作语句),因此它应该比看上去的样子有效得多。 + * + * 算法已经作了改变:希望能更好,而且一个难以琢磨的错误已经去除。 + */ +#define BADNESS(bh) (((bh)->b_dirt << 1) + (bh)->b_lock) + +/** + * 取高速缓冲中指定的缓冲块 + * 检查指定(设备号和块号)的缓冲区是否已经在高速缓冲中。如果指定块已经在高速缓冲中,则返回对应缓 + * 冲区头指针退出;如果不在,就需要在高速中中设置一个对应设备号和块号的新项。返回相应缓冲区头指 + * 针 + * @note 在这里,每次进程执行过wait_on_buffer()睡眠等待,唤醒后需要重新判断等待的缓冲 + * 块是否符合条件。 + * @param[in] dev 设备号 + * @param[in] block 块号 + * @retval 对应缓冲区头指针 + */ +struct buffer_head * getblk(int dev, int block) +{ + struct buffer_head *tmp, *bh; + +repeat: + if ((bh = get_hash_table(dev, block))) { + return bh; + } + tmp = free_list; + do { + if (tmp->b_count) { + continue; + } + if (!bh || BADNESS(tmp) < BADNESS(bh)) { + bh = tmp; + if (!BADNESS(tmp)) { + break; + } + } + /* and repeat until we find something good */ + /* 重复操作直到找到适合的缓冲块 */ + } while ((tmp = tmp->b_next_free) != free_list); + if (!bh) { + sleep_on(&buffer_wait); + goto repeat; + } + wait_on_buffer(bh); + if (bh->b_count) { + goto repeat; + } + while (bh->b_dirt) { + sync_dev(bh->b_dev); + wait_on_buffer(bh); + if (bh->b_count) { + goto repeat; + } + } + /* NOTE!! While we slept waiting for this block, somebody else might */ + /* already have added "this" block to the cache. check it */ + /* 注意!当进程为了等待该缓冲块而睡眠时,其他进程可能已经将该缓冲块加入进高速缓冲中,所以我 + 们也要对此进行检查。 */ + if (find_buffer(dev, block)) { + goto repeat; + } + /* OK, FINALLY we know that this buffer is the only one of it's kind, */ + /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */ + /* OK,最终我们知道该缓冲块是指定参数的唯一一块,而且目前还没有被占用(b_count=0),也未被上 + 锁(b_lock=0),并且是干净的(未被修改的) */ + bh->b_count = 1; + bh->b_dirt = 0; + bh->b_uptodate = 0; + /* 从hash队列和空闲块链表中移出该缓冲头,让该缓冲区用于指定块。然后根据此新设备号和块号重新 + 插入空闲链表和hash队列新位置处,并最终返回缓冲头指针。*/ + remove_from_queues(bh); + bh->b_dev = dev; + bh->b_blocknr = block; + insert_into_queues(bh); + return bh; +} + +/** + * 释放指定缓冲块 + * 等待该缓冲块解锁。然后引用计数递减1,并明确地唤醒等待空闲缓冲块的进程。 + * @param[in] buf 指定缓冲块 + * @retval void + */ +void brelse(struct buffer_head * buf) +{ + if (!buf) { + return; + } + wait_on_buffer(buf); + if (!(buf->b_count--)) { + panic("Trying to free free buffer"); + } + wake_up(&buffer_wait); +} + +/* + * bread() reads a specified block and returns the buffer that contains + * it. It returns NULL if the block was unreadable. + */ + /* + * 从设备上读取指定的数据块并返回含有数据的缓冲区。如果指定的块不存在则返回NULL。 + */ + +/** + * 从设备上读取指定数据块到高速缓冲区 + * @param[in] dev 设备号 + * @param[in] block 块号 + * @retval 缓冲块头指针,失败返回NULL + */ +struct buffer_head * bread(int dev, int block) +{ + struct buffer_head * bh; + + if (!(bh = getblk(dev, block))) { + panic("bread: getblk returned NULL\n"); + } + if (bh->b_uptodate) { + return bh; + } + ll_rw_block(READ, bh); + wait_on_buffer(bh); + if (bh->b_uptodate) { + return bh; + } + brelse(bh); + return NULL; +} + +/** + * 复制内存块 + * 从from地址复制一块(1024B)数据到to地址 + */ +#define COPYBLK(from, to) \ +__asm__( \ + "cld\n\t" \ + "rep\n\t" \ + "movsl\n\t" \ + : \ + :"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \ + ) + +/* + * bread_page reads four buffers into memory at the desired address. It's + * a function of its own, as there is some speed to be got by reading them + * all at the same time, not waiting for one to be read, and then another + * etc. + */ +/* + * bread_page 一次读四个缓冲块数据读到内存指定的地址处。它是一个完整的函数,因为同时读取四块可以 + * 获得速度上的好处,不用等着读一块,再读一块了。 + */ + +/** + * 读设备的一个页面的内容到指定内存地址处 + * @note 该函数仅用于mm/memory.c文件的do_no_page()函数中 + * @param[in] address 保存页面数据的地址 + * @param[in] dev 设备号 + * @param[in] b[4] 含有4个设备数据块号的数组 + * @retval void + */ +void bread_page(unsigned long address, int dev, int b[4]) +{ + struct buffer_head * bh[4]; + int i; + + /* 从高速缓冲中取指定设备和块号的的缓冲块。如果缓冲块中数据无效(未更新),则产生读设备请求从 + 设备上读取相应数据块 */ + for (i = 0; i < 4; i ++) { + if (b[i]) { + if ((bh[i] = getblk(dev, b[i]))) { + if (!bh[i]->b_uptodate) { + ll_rw_block(READ, bh[i]); + } + } + } else { + bh[i] = NULL; + } + } + /* 随后将4个缓冲块上的内容顺序复制到指定地址处,随后释放相应缓冲块 */ + for (i = 0; i < 4; i++, address += BLOCK_SIZE) { + if (bh[i]) { + wait_on_buffer(bh[i]); + if (bh[i]->b_uptodate) { + COPYBLK((unsigned long) bh[i]->b_data, address); + } + brelse(bh[i]); + } + } +} + +/* + * Ok, breada can be used as bread, but additionally to mark other + * blocks for reading as well. End the argument list with a negative + * number. + */ +/** + * 从指定设备读取指定的一些块 + * @param[in] dev 设备号 + * @param[in] first 要读取的第一个块号 + * @param[in] ... 要预读取的一系列块号 + * @retval 成功返回第1块的缓冲块头指针,失败返回NULL。 + */ +struct buffer_head * breada(int dev, int first, ...) +{ + va_list args; + struct buffer_head * bh, *tmp; + + va_start(args, first); + /* 读取第一块缓冲块 */ + if (!(bh = getblk(dev, first))) { + panic("bread: getblk returned NULL\n"); + } + if (!bh->b_uptodate) { + ll_rw_block(READ, bh); + } + /* 预读取可变参数表中的其他预读块号,但不引用 */ + while ((first = va_arg(args, int)) >= 0) { + tmp = getblk(dev, first); + if (tmp) { + if (!tmp->b_uptodate) { + ll_rw_block(READA, tmp); /* bug修复! 这里的 bh 改为 tmp */ + } + tmp->b_count --; /* 暂时释放掉该预读块 */ + } + } + va_end(args); + wait_on_buffer(bh); + /* 等待之后,缓冲区数据仍然有效,则返回 */ + if (bh->b_uptodate) { + return bh; + } + brelse(bh); + return (NULL); +} + +/** + * 缓冲区初始化 + * 缓冲区低端内存被初始化成缓冲头部,缓冲区高端内存被初始化缓冲区。 + * @note buffer_end是一个>=1M的值。该初始化函数在init/main.c调用。 + * @param[in] buffer_end 高速缓冲区结束的内存地址 + */ +void buffer_init(long buffer_end) +{ + struct buffer_head * h = start_buffer; + void * b; + int i; + /* 跳过640KB~1MB的内存空间,该段空间被显示内存和BIOS占用 */ + if (buffer_end == 1<<20) { + b = (void *) (640*1024); + } + else { + b = (void *) buffer_end; + } + while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { + h->b_dev = 0; + h->b_dirt = 0; + h->b_count = 0; + h->b_lock = 0; + h->b_uptodate = 0; + h->b_wait = NULL; + h->b_next = NULL; + h->b_prev = NULL; + h->b_data = (char *) b; + /* 以下两句形成双向链表 */ + h->b_prev_free = h - 1; + h->b_next_free = h + 1; + h ++; + NR_BUFFERS ++; + /* 同样为了跳过 640KB~1MB 的内存空间 */ + if (b == (void *) 0x100000) + b = (void *) 0xA0000; + } + h --; /* 让h指向最后一个有效缓冲块头 */ + free_list = start_buffer; /* 让空闲链表头指向头一个缓冲块 */ + free_list->b_prev_free = h; /* 链表头的b_prev_free指向前一项(即最后一项) */ + h->b_next_free = free_list; /* 表尾指向表头,形成环形双向链表 */ + /* 初始化hash表 */ + for (i = 0; i < NR_HASH; i++) { + hash_table[i]=NULL; + } +} diff --git a/linux-0.12/fs/char_dev.c b/linux-0.12/fs/char_dev.c new file mode 100755 index 0000000..f1f146d --- /dev/null +++ b/linux-0.12/fs/char_dev.c @@ -0,0 +1,159 @@ +/* + * linux/fs/char_dev.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include + +#include +#include + +extern int tty_read(unsigned minor,char * buf,int count); +extern int tty_write(unsigned minor,char * buf,int count); + +typedef int (*crw_ptr)(int rw, unsigned minor, char * buf, int count, off_t * pos); /* (新增)增加函数指针的返回值int */ + + +/** + * 串口终端读写操作 + * @param[in] rw 读写命令 + * @param[in] minor 终端子设备号 + * @param[in] buf 缓冲区 + * @param[in] count 读写字节数 + * @param[in] pos 读写操作当前指针(对于终端操作,该指针无用) + * @retval 实际读写的字节数.若失败则返回出错码 + */ +static int rw_ttyx(int rw, unsigned minor, char * buf, int count, off_t * pos) +{ + return ((rw == READ) ? tty_read(minor, buf, count) + : tty_write(minor, buf, count)); +} + +/** + * 终端读写操作(同rw_ttyx,增加了对进程是否有控制终端的检测) + * @param[in] rw 读写命令 + * @param[in] minor 终端子设备号 + * @param[in] buf 缓冲区 + * @param[in] count 读写字节数 + * @param[in] pos 读写操作当前指针(对于终端操作,该指针无用) + * @retval 实际读写的字节数.若失败则返回出错码 + */ +static int rw_tty(int rw,unsigned minor,char * buf,int count, off_t * pos) +{ + if (current->tty < 0) { + return -EPERM; + } + return rw_ttyx(rw, current->tty, buf, count, pos); +} + +/* 内存数据读写 */ +static int rw_ram(int rw,char * buf, int count, off_t *pos) +{ + return -EIO; +} + +/* 物理内存数据读写 */ +static int rw_mem(int rw,char * buf, int count, off_t * pos) +{ + return -EIO; +} + +/* 内核虚拟内存数据读写 */ +static int rw_kmem(int rw,char * buf, int count, off_t * pos) +{ + return -EIO; +} + + +/** + * 端口读写操作函数。 + * @param[in] rw 读写命令 + * @param[in] buf 缓冲区 + * @param[in] count 读写字节数 + * @param[in] pos 端口地址 + * @retval 实际读写的字节数 + */ +static int rw_port(int rw, char * buf, int count, off_t * pos) +{ + int i = *pos; + + while (count-- > 0 && i < 65536) { + if (rw == READ) { + put_fs_byte(inb(i), buf++); + } else { + outb(get_fs_byte(buf++), i); + } + i++; + } + i -= *pos; + *pos += i; + return i; +} + +/** + * 内存读写操作函数 + * @param[in] rw 读写命令 + * @param[in] buf 缓冲区 + * @param[in] count 读写字节数 + * @param[in] pos 地址 + * @retval 实际读写的字节数 + */ +static int rw_memory(int rw, unsigned minor, char * buf, int count, off_t * pos) +{ + switch(minor) { + case 0: + return rw_ram(rw, buf, count, pos); + case 1: + return rw_mem(rw, buf, count, pos); + case 2: + return rw_kmem(rw, buf, count, pos); + case 3: + return (rw == READ) ? 0 : count; /* rw_null */ + case 4: + return rw_port(rw, buf, count, pos); + default: + return -EIO; + } +} + +#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr))) + + +/* 字符设备读写函数指针表 */ +static crw_ptr crw_table[]={ + NULL, /* nodev */ + rw_memory, /* /dev/mem etc */ + NULL, /* /dev/fd */ + NULL, /* /dev/hd */ + rw_ttyx, /* /dev/ttyx */ + rw_tty, /* /dev/tty */ + NULL, /* /dev/lp */ + NULL}; /* unnamed pipes */ + + +/** + * 字符设备读写 + * @param[in] rw 读写命令 + * @param[in] dev 设备号 + * @param[in] buf 缓冲区 + * @param[in] count 读写字节数 + * @param[in] pos 读写指针 + * @retval 实际读/写字节数 + */ +int rw_char(int rw, int dev, char * buf, int count, off_t * pos) +{ + crw_ptr call_addr; + + if (MAJOR(dev) >= NRDEVS) { + return -ENODEV; + } + if (!(call_addr = crw_table[MAJOR(dev)])) { + return -ENODEV; + } + return call_addr(rw, MINOR(dev), buf, count, pos); +} diff --git a/linux-0.12/fs/exec.c b/linux-0.12/fs/exec.c new file mode 100755 index 0000000..ba58ba0 --- /dev/null +++ b/linux-0.12/fs/exec.c @@ -0,0 +1,546 @@ +/* + * linux/fs/exec.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * #!-checking implemented by tytso. + */ + /* + * #!开始脚本程序的检测代码部分是由tytso实现的. + */ + +/* + * Demand-loading implemented 01.12.91 - no need to read anything but + * the header into memory. The inode of the executable is put into + * "current->executable", and page faults do the actual loading. Clean. + * + * Once more I can proudly say that linux stood up to being changed: it + * was less than 2 hours work to get demand-loading completely implemented. + */ +/* + * 需求时加载实现于1991.12.1 - 只需将执行文件头部读进内存而无须将整个执行文件都加载进内存。执行 + * 文件的i节点被放在当前进程的可执行字段中"current->executable",页异常会进行执行文件的实际加载 + * 操作。这很完美。 + * + * 我可再一次自豪地说,linux经得起修改:只用了不到2小时的工作就完全实现了需求加载处理。 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern int sys_exit(int exit_code); +extern int sys_close(int fd); + +/* + * MAX_ARG_PAGES defines the number of pages allocated for arguments + * and envelope for the new program. 32 should suffice, this gives + * a maximum env+arg of 128kB ! + */ +/* + * MAX_ARG_PAGES定义了为新程序分配的给参数和环境变量使用的最大内存页数。32页内存应该足够了,这 + * 使得环境和参数(env+arg)空间的总和达到128KB! + */ +#define MAX_ARG_PAGES 32 + +/** + * 使用库文件 + * 为进程选择一个库文件,并替换进程当前库文件i节点字段值为这里指定库文件名的i节点指针。如果 + * library指针为空,则把进程当前的库文件释放掉。 + * @param[in] library 库文件名 + * @retval 成功返回0,失败返回出错码 + */ +int sys_uselib(const char * library) +{ + struct m_inode * inode; + unsigned long base; + + /* 根据进程的空间长度,判断是否为普通进程 */ + if (get_limit(0x17) != TASK_SIZE) { + return -EINVAL; + } + if (library) { + if (!(inode = namei(library))) { /* get library inode */ + return -ENOENT; + } + } else { + inode = NULL; + } +/* we should check filetypes (headers etc), but we don't */ +/* 我们应该检查一下文件类型(如头部信息等),但是我们还没有这样做。*/ + iput(current->library); + current->library = NULL; + base = get_base(current->ldt[2]); + base += LIBRARY_OFFSET; + free_page_tables(base, LIBRARY_SIZE); + current->library = inode; + return 0; +} + +/* + * create_tables() parses the env- and arg-strings in new user + * memory and creates the pointer tables from them, and puts their + * addresses on the "stack", returning the new stack pointer value. + */ +/* + * create_tables()函数在新任务内存中解析环境变量和参数字符串,由此创建指针表,并将它们的地址放 + * 到"栈"上,然后返回新栈的指针值。 + */ + +/** + * 在新任务中创建参数和环境变量指针表 + * @param[in] p 数据段中参数和环境信息偏移指针 + * @param[in] argc 参数个数 + * @param[in] envc 环境变量个数 + * @retval 栈指针值 + */ +static unsigned long * create_tables(char * p, int argc, int envc) +{ + unsigned long *argv, *envp; + unsigned long * sp; + + /* 栈指针是以4字节为边界进行寻址的,故为0xfffffffc */ + sp = (unsigned long *) (0xfffffffc & (unsigned long) p); + /* 多留一个位置用于存放NULL */ + sp -= envc + 1; + envp = sp; + sp -= argc + 1; + argv = sp; + put_fs_long((unsigned long)envp, --sp); + put_fs_long((unsigned long)argv, --sp); + put_fs_long((unsigned long)argc, --sp); + while (argc-- > 0) { + put_fs_long((unsigned long) p, argv++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0, argv); + while (envc-- > 0) { + put_fs_long((unsigned long) p, envp++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0, envp); + return sp; +} + +/* + * count() counts the number of arguments/envelopes + */ +/* + * count()函数计算命令行参数/环境变更的个数 + */ + +/** + * 统计参数指针数组中指针的个数 + * @param[in] argv 参数指针数组(最后一个指针项是NULL) + * @retval 参数个数 + */ +static int count(char ** argv) +{ + int i = 0; + char ** tmp; + + if ((tmp = argv)) { + while (get_fs_long((unsigned long *) (tmp++))) { + i++; + } + } + + return i; +} + +/* + * 'copy_string()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + * + * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies + * whether the string and the string array are from user or kernel segments: + * + * from_kmem argv * argv ** + * 0 user space user space + * 1 kernel space user space + * 2 kernel space kernel space + * + * We do this by playing games with the fs segment register. Since it + * it is expensive to load a segment register, we try to avoid calling + * set_fs() unless we absolutely have to. + */ +/* + * 'copy_string()'函数从用户内存空间复制参数/环境字符串到内核空闲页面中。这些已具有直接放到新用 + * 户内存中的格式。 + * + * 由TYT(Tytso)于1991.11.24日修改,增加了from_kmem参数,该参数指明了字符串或字符串数组是来自用 + * 户段还是内核段. + * + * from_kmem 指针 argv * 字符串 argv ** + * 0 用户空间 用户空间 + * 1 内核空间 用户空间 + * 2 内核空间 内核空间 + * + * 我们是通过巧妙处理fs段寄存器来操作的。由于加载一个段寄存器代价太高,所以我们尽量避免调用 + * set_fs(),除非实在必要。 + */ + +/** + * 复制指定个数的参数字符串到参数和环境空间中 + * 在do_execve()函数中,p初始化为指向参数表(128KB)空间的最后一个长字处,参数字符串是以堆栈操作 + * 方式逆向往其中复制存放的。因此p指针会随着复制信息的增加而逐渐减小,并始终指向参数字符串的头 + * 部。字符串来源标志from_kmem应该是TYT为了给execve()增添执行脚本文件的功能而新加的参数。当没 + * 有运行脚本文件的功能时,所有参数字符串都在用户数据空间中。 + * @param[in] argc 欲添加的参数个数 + * @param[in] argv 参数指针数组 + * @param[in] page 参数和环境空间页面指针数组 + * @param[in] p 参数表空间中偏移指针,始终指向已复制串的头部 + * @param[in] from_kmem 字符串来源标志 + * @retval 成功返回参数和环境空间当前头部指针,出错返回0 + */ +static unsigned long copy_strings(int argc, char ** argv, unsigned long *page, + unsigned long p, int from_kmem) +{ + char *tmp, *pag; + int len, offset = 0; + unsigned long old_fs, new_fs; + + if (!p) { + return 0; /* bullet-proofing */ + } + new_fs = get_ds(); + old_fs = get_fs(); + if (from_kmem == 2) { + set_fs(new_fs); + } + /* 从最后一个参数逆向开始复制 */ + while (argc-- > 0) { + if (from_kmem == 1) { + set_fs(new_fs); + } + if (!(tmp = (char *)get_fs_long(((unsigned long *)argv) + argc))) { + panic("argc is wrong"); + } + if (from_kmem == 1) { + set_fs(old_fs); + } + len = 0; /* remember zero-padding */ /* 串是以NULL结尾的 */ + do { + len++; + } while (get_fs_byte(tmp++)); + /* 不会发生,参数表空间够大(128KB) */ + if (p - len < 0) { /* this shouldn't happen - 128kB */ + set_fs(old_fs); + return 0; + } + /* 把参数对应的字符串中的逐个字符(从尾到头)地复制到参数和环境空间末端处 */ + while (len) { + --p; --tmp; --len; + if (--offset < 0) { + offset = p % PAGE_SIZE; + if (from_kmem == 2) { + set_fs(old_fs); + } + if (!(pag = (char *) page[p/PAGE_SIZE]) && + !(pag = (char *) (page[p/PAGE_SIZE] = get_free_page()))) { + return 0; + } + if (from_kmem == 2) { + set_fs(new_fs); + } + } + *(pag + offset) = get_fs_byte(tmp); + } + } + if (from_kmem == 2) { + set_fs(old_fs); + } + return p; +} + + +/** + * 修改任务的局部描述符表内容 + * 修改局部描述符表LDT中描述符的段基址和段限长,并将参数和环境空间页面放置在数据段末端。 + * @param[in] text_size 执行文件头部中a_text字段给出的代码段长度值 + * @param[in] page 参数和环境空间页面指针数组 + * @retval 数据段限长值(64MB) + */ +static unsigned long change_ldt(unsigned long text_size, unsigned long * page) +{ + unsigned long code_limit, data_limit, code_base, data_base; + int i; + + code_limit = TASK_SIZE; + data_limit = TASK_SIZE; + code_base = get_base(current->ldt[1]); + data_base = code_base; + set_base(current->ldt[1], code_base); + set_limit(current->ldt[1], code_limit); + set_base(current->ldt[2], data_base); + set_limit(current->ldt[2], data_limit); +/* make sure fs points to the NEW data segment */ + /* FS段寄存器中放入局部表数据段描述符的选择符(0x17) */ + __asm__("pushl $0x17\n\tpop %%fs"::); + /* 将参数和环境空间已存放数据的页面(最多有MAX_ARG_PAGES页)放到数据段末端 */ + data_base += data_limit - LIBRARY_SIZE; /* 库文件代码占用进程空间末端部分 */ + for (i = MAX_ARG_PAGES - 1; i >= 0; i--) { + data_base -= PAGE_SIZE; + if (page[i]) { + put_dirty_page(page[i], data_base); + } + } + return data_limit; +} + +/* + * 'do_execve()' executes a new program. + * + * NOTE! We leave 4MB free at the top of the data-area for a loadable + * library. + */ +/* + * 'do_execve()'函数执行一个新程序. + */ + +/** + * 加载并执行子进程(其他程序) 系统中断调用 + * 该函数是系统中断调用(int 0x80)功能号__NR_execve调用的函数。函数的参数是进入系统调用处理过程 + * 后直到调用本系统调用处理过程和调用本函数之前逐步压入栈中的值 + * @param[in] eip 调用系统中断的程序代码指针 + * @param[in] tmp 系统中断在调用sys_execve时的返回地址,无用 + * @param[in] filename 被执行程序文件名指针 + * @param[in] argv 命令行参数指针数组的指针 + * @param[in] envp 环境变更指针数组的指针 + * @retval 调用成功返回0;失败设置出错号,并返回-1 + */ +int do_execve(unsigned long * eip, long tmp, char * filename, + char ** argv, char ** envp) +{ + struct m_inode * inode; + struct buffer_head * bh; + struct exec ex; + unsigned long page[MAX_ARG_PAGES]; /* 参数和环境串空间页面指针数组 */ + int i, argc, envc; + int e_uid, e_gid; + int retval; + int sh_bang = 0; + unsigned long p = PAGE_SIZE * MAX_ARG_PAGES - 4; + + /* 参数eip[1]是调用本次系统调用的原用户程序代码段寄存器CS值,其中的段选择符当然必须是 + 当前任务的代码段选择符(0x000f)。 若不是该值,那么CS只能会是内核代码段的选择符0x0008。 + 但这是绝对不允许的,因为内核代码是常驻内存而不能被替换掉的。*/ + if ((0xffff & eip[1]) != 0x000f) { + panic("execve called from supervisor mode"); + } + for (i = 0; i < MAX_ARG_PAGES; i++) { /* clear page-table */ + page[i] = 0; + } + if (!(inode = namei(filename))) { /* get executables inode */ + return -ENOENT; + } + argc = count(argv); + envc = count(envp); + +restart_interp: + if (!S_ISREG(inode->i_mode)) { /* must be regular file */ + /* 必须是常规文件 */ + retval = -EACCES; + goto exec_error2; + } + i = inode->i_mode; + e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; /* 是否设置了执行时设置用户ID */ + e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; /* 是否设置了执行时设置组ID */ + /* 根据执行文件i节点中的属性,看看本进程是否有权执行它 */ + if (current->euid == inode->i_uid) { + i >>= 6; + } else if (in_group_p(inode->i_gid)) { + i >>= 3; + } + /* 当前用户无权限 | 文件可执行 | 当前用户是否是超级用户 */ + /* 0 | 1 | 0 */ + /* 0 | 0 | 1 */ + /* 0 | 0 | 0 */ + if (!(i & 1) && !((inode->i_mode & 0111) && suser())) { + retval = -ENOEXEC; + goto exec_error2; + } + /* 读取第一块数据 */ + if (!(bh = bread(inode->i_dev, inode->i_zone[0]))) { + retval = -EACCES; + goto exec_error2; + } + + ex = *((struct exec *) bh->b_data); /* read exec-header */ + /* “#!”开头则为脚本文件 */ + if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { + /* + * This section does the #! interpretation. + * Sorta complicated, but hopefully it will work. -TYT + */ + + char buf[128], *cp, *interp, *i_name, *i_arg; + unsigned long old_fs; + + strncpy(buf, bh->b_data + 2, 127); + brelse(bh); + iput(inode); + buf[127] = '\0'; + if ((cp = strchr(buf, '\n'))) { + *cp = '\0'; + for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp ++); + } + if (!cp || *cp == '\0') { + retval = -ENOEXEC; /* No interpreter name found */ + goto exec_error1; + } + interp = i_name = cp; + i_arg = 0; + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { + if (*cp == '/') { + i_name = cp + 1; + } + } + if (*cp) { + *cp++ = '\0'; + i_arg = cp; + } + /* + * OK, we've parsed out the interpreter name and + * (optional) argument. + */ + if (sh_bang++ == 0) { + p = copy_strings(envc, envp, page, p, 0); + p = copy_strings(--argc, argv+1, page, p, 0); + } + /* + * Splice in (1) the interpreter's name for argv[0] + * (2) (optional) argument to interpreter + * (3) filename of shell script + * + * This is done in reverse order, because of how the + * user environment and arguments are stored. + */ + /* + * 拼接 (1) argv[0]中放解释程序的名称 + * (2) (可选的)解释程序的参数 + * (3) 脚本程序的名称 + * + * 这是以逆序进行处理的,是由于用户环境和参数的存放方式造成的。 + */ + p = copy_strings(1, &filename, page, p, 1); + argc ++; + if (i_arg) { + p = copy_strings(1, &i_arg, page, p, 2); + argc++; + } + p = copy_strings(1, &i_name, page, p, 2); + argc ++; + /* 上面一段代码的主要功能: + 1. 复制环境变量 + 2. 复制程序参数,举例如下: + 命令行: $ example.sh -arg1 -arg2 + example.sh文件第一句: #!/bin/bash -argv1 -argv2 + 进程空间末尾的参数和环境空间中则存入了(从低到高): + bash -argv1 -argv2 example.sh -arg1 -arg2 + */ + if (!p) { + retval = -ENOMEM; + goto exec_error1; + } + /* + * OK, now restart the process with the interpreter's inode. + */ + /* namei是从用户数据空间(fs指向)取参数的,而interp处于内核数据空间,故临时设置fs + 指向内核空间 */ + old_fs = get_fs(); + set_fs(get_ds()); + if (!(inode = namei(interp))) { /* get executables inode */ + set_fs(old_fs); + retval = -ENOENT; + goto exec_error1; + } + set_fs(old_fs); + goto restart_interp; + } + brelse(bh); + /* 对这个内核来说,它仅支持ZMAGIC执行文件格式,不支持含有代码或数据重定位信息的执 + 行文件,执行文件实在太大或者执行文件残缺不全也不行 */ + if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || + ex.a_text + ex.a_data + ex.a_bss > 0x3000000 || + inode->i_size < ex.a_text + ex.a_data + ex.a_syms + N_TXTOFF(ex)) { + retval = -ENOEXEC; + goto exec_error2; + } + if (N_TXTOFF(ex) != BLOCK_SIZE) { + printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); + retval = -ENOEXEC; + goto exec_error2; + } + if (!sh_bang) { + p = copy_strings(envc, envp, page, p, 0); + p = copy_strings(argc, argv, page, p, 0); + if (!p) { + retval = -ENOMEM; + goto exec_error2; + } + } +/* OK, This is the point of no return */ +/* note that current->library stays unchanged by an exec */ +/* OK,下面开始就没有返回的地方了 */ +/* 注意,exec类函数不会改动 current->library */ + if (current->executable) { + iput(current->executable); + } + current->executable = inode; + current->signal = 0; + /* 复位原进程的所有信号处理句柄,忽略SIG_IGN的句柄 */ + for (i = 0; i < 32; i ++) { + current->sigaction[i].sa_mask = 0; + current->sigaction[i].sa_flags = 0; + if (current->sigaction[i].sa_handler != SIG_IGN) { + current->sigaction[i].sa_handler = NULL; + } + } + /* 根据close_on_exec关闭指定的文件,复位close_on_exec */ + for (i = 0; i < NR_OPEN; i ++) { + if ((current->close_on_exec >> i) & 1) { + sys_close(i); + } + } + current->close_on_exec = 0; + /* 释放原进程的代码段和数据段占用的物理页面及页表 */ + free_page_tables(get_base(current->ldt[1]), get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]), get_limit(0x17)); + if (last_task_used_math == current) { + last_task_used_math = NULL; + } + current->used_math = 0; + p += change_ldt(ex.a_text, page); + p -= LIBRARY_SIZE + MAX_ARG_PAGES * PAGE_SIZE; + p = (unsigned long) create_tables((char *)p, argc, envc); + current->brk = ex.a_bss + + (current->end_data = ex.a_data + + (current->end_code = ex.a_text)); + current->start_stack = p & 0xfffff000; + current->suid = current->euid = e_uid; + current->sgid = current->egid = e_gid; + /* 将原调用系统中断的程序在堆栈上的代码指针替换为指向新执行程序的入口点,并将栈指针 + 替换为新执行文件的栈指针 */ + eip[0] = ex.a_entry; /* eip, magic happens :-) */ + eip[3] = p; /* stack pointer */ + return 0; +exec_error2: + iput(inode); +exec_error1: + for (i = 0; i < MAX_ARG_PAGES; i ++) { + free_page(page[i]); + } + return(retval); +} diff --git a/linux-0.12/fs/fcntl.c b/linux-0.12/fs/fcntl.c new file mode 100755 index 0000000..7a3dd37 --- /dev/null +++ b/linux-0.12/fs/fcntl.c @@ -0,0 +1,117 @@ +/* + * linux/fs/fcntl.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include +#include +#include + +#include +#include + +extern int sys_close(int fd); + +/** + * 复制文件句柄(文件描述符) + * @param[in] fd 欲复制的文件句柄 + * @param[in] arg 指定新文件句柄的最小数值 + * @retval 成功返回新文件句柄,失败返回出错码 + */ +static int dupfd(unsigned int fd, unsigned int arg) +{ + if (fd >= NR_OPEN || !current->filp[fd]) { + return -EBADF; /* 文件句柄错误 */ + } + if (arg >= NR_OPEN) { + return -EINVAL; /* 参数非法 */ + } + /* 找到一个比arg大的最小的未使用的句柄值 */ + while (arg < NR_OPEN) { + if (current->filp[arg]) { + arg++; + } else { + break; + } + } + if (arg >= NR_OPEN) { + return -EMFILE; /* 打开文件太多 */ + } + current->close_on_exec &= ~(1<filp[arg] = current->filp[fd])->f_count++; + return arg; +} + +/** + * 复制文件句柄 + * @param[in] oldfd 欲复制的指定文件句柄 + * @param[in] newfd 新文件句柄值(如果newfd已打开,则首先关闭之) + * @retval 新文件句柄或出错码 + */ +int sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + sys_close(newfd); + return dupfd(oldfd, newfd); +} + +/** + * 复制文件句柄 + * @param[in] fildes 欲复制的指定文件句柄 + * @retval 新文件句柄(当前最小的未用句柄值)或出错码 + */ +int sys_dup(unsigned int fildes) +{ + return dupfd(fildes, 0); +} + + +/** + * 文件控制 + * @param[in] fd 文件句柄 + * @param[in] cmd 控制命令 + * @param[in] arg 针对不同的命令有不同的含义 + * 1. F_DUPFD,arg是新文件句可取的最小值 + * 2. F_SETFD,arg是要设置的文件句柄标志 + * 3. F_SETFL,arg是新的文件操作和访问模式 + * 4. F_GETLK、F_SETLK和F_SETLKW,arg是指向flock结构的指针(为 + * 实现文件上锁功能) + * @retval 若出错,则所有操作都返回 -1; + * 若成功,那么 + * 1. F_DUPFD,返回新文件句柄 + * 2. F_GETFD,返回文件句柄的当前执行时关闭标志close_on_exec + * 3. F_GETFL,返回文件操作和访问标志。 + */ +int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + + if (fd >= NR_OPEN || !(filp = current->filp[fd])) { + return -EBADF; + } + switch (cmd) { + case F_DUPFD: /* 复制句柄,返回新文件句柄 */ + return dupfd(fd, arg); + case F_GETFD: /* 获取文件句柄标志,返回文件句柄的当前执行时关闭标志 */ + return (current->close_on_exec>>fd)&1; + case F_SETFD: /* 设置文件句柄标志,arg=1设置关闭标志为1,arg=0设置关闭标志为0 */ + if (arg & 1) { + current->close_on_exec |= (1<close_on_exec &= ~(1<f_flags; + case F_SETFL: /* 设置文件状态标志和访问模式flag */ + filp->f_flags &= ~(O_APPEND | O_NONBLOCK); + filp->f_flags |= arg & (O_APPEND | O_NONBLOCK); + return 0; + case F_GETLK: case F_SETLK: case F_SETLKW: /* 未实现 */ + return -1; + default: + return -1; + } +} diff --git a/linux-0.12/fs/file_dev.c b/linux-0.12/fs/file_dev.c new file mode 100755 index 0000000..79c0f29 --- /dev/null +++ b/linux-0.12/fs/file_dev.c @@ -0,0 +1,121 @@ +/* + * linux/fs/file_dev.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/** + * 文件读函数 + * 根据i节点和文件结构,读取文件中数据。 + * @param[in] *inode i节点 + * @param[in] *filp + * @param[in] buf 指定用户空间中缓冲区的位置 + * @param[in] count 需要读取的字节数 + * @retval 实际读取的字节数,或出错号(小于0) +*/ +int file_read(struct m_inode * inode, struct file * filp, char * buf, int count) +{ + int left, chars, nr; + struct buffer_head * bh; + + if ((left = count) <= 0) { + return 0; + } + while (left) { + if ((nr = bmap(inode, (filp->f_pos)/BLOCK_SIZE))) { + if (!(bh = bread(inode->i_dev, nr))) { + break; + } + } else { + bh = NULL; + } + nr = filp->f_pos % BLOCK_SIZE; + chars = MIN( BLOCK_SIZE-nr, left ); + filp->f_pos += chars; + left -= chars; + if (bh) { + char * p = nr + bh->b_data; + while (chars-- > 0) { + put_fs_byte(*(p++), buf++); + } + brelse(bh); + } else { + while (chars-- > 0) { + put_fs_byte(0, buf++); + } + } + } + inode->i_atime = CURRENT_TIME; + return (count-left) ? (count-left) : -ERROR; +} + +/** + * 文件写函数 + * 根据i节点和文件结构信息,将用户数据写入文件中。 + * @param[in] *inode i节点指针 + * @param[in] *filp 文件结构指针 + * @param[in] buf 指定用户态中缓冲区的位置 + * @param[in] count 需要写入的字节数 + * @retval 成功返回实际写入的字节数,失败返回出错号(小于0) + */ +int file_write(struct m_inode * inode, struct file * filp, char * buf, int count) +{ + off_t pos; + int block, c; + struct buffer_head * bh; + char * p; + int i = 0; + +/* + * ok, append may not work when many processes are writing at the same time + * but so what. That way leads to madness anyway. + */ + if (filp->f_flags & O_APPEND) { /* 指定以追加方式,则将pos置文件尾 */ + pos = inode->i_size; + } else { + pos = filp->f_pos; + } + while (i < count) { + /* 获取逻辑块号 */ + if (!(block = create_block(inode, pos/BLOCK_SIZE))) { + break; + } + /* 读取指定数据块 */ + if (!(bh = bread(inode->i_dev, block))) { + break; + } + c = pos % BLOCK_SIZE; + p = c + bh->b_data; + bh->b_dirt = 1; + c = BLOCK_SIZE - c; + if (c > count - i) { + c = count - i; + } + pos += c; + if (pos > inode->i_size) { + inode->i_size = pos; + inode->i_dirt = 1; + } + i += c; + while (c-- > 0) { + *(p++) = get_fs_byte(buf++); + } + brelse(bh); + } + inode->i_mtime = CURRENT_TIME; + if (!(filp->f_flags & O_APPEND)) { + filp->f_pos = pos; + inode->i_ctime = CURRENT_TIME; + } + return (i ? i : -1); +} diff --git a/linux-0.12/fs/file_table.c b/linux-0.12/fs/file_table.c new file mode 100755 index 0000000..19fa30d --- /dev/null +++ b/linux-0.12/fs/file_table.c @@ -0,0 +1,10 @@ +/* + * linux/fs/file_table.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +/* 文件表数组(64项) */ +struct file file_table[NR_FILE]; diff --git a/linux-0.12/fs/inode.c b/linux-0.12/fs/inode.c new file mode 100755 index 0000000..8817c8d --- /dev/null +++ b/linux-0.12/fs/inode.c @@ -0,0 +1,562 @@ +/* + * linux/fs/inode.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include +#include + +/* 设置数据块总数指针数组。每个指针项指向指定主设备号的总块数数组hd_sizes[]。该总块数数组每一项对 + 应子设备号确定的一个子设备上所拥有的数据块总数(1块大小 = 1KB) */ +extern int *blk_size[]; + +struct m_inode inode_table[NR_INODE]={{0, }, }; /* 内存中i节点表(NR_INODE=64项) */ + +static void read_inode(struct m_inode *inode); /* 读指定i节点号的i节点信息 */ +static void write_inode(struct m_inode *inode); /* 写i节点信息到高速缓冲中 */ + +/** + * 等待指定的i节点可用 + * 如果i节点已被锁定,则将当前任务置为不可中断的等待状态,并添加到该i节点的等待队列i_wait中。直 + * 到该i节点解锁并明确地唤醒本任务。 + * @param[in] *inode i节点指针 + * @retval void + */ +static inline void wait_on_inode(struct m_inode *inode) +{ + cli(); + while (inode->i_lock) { + sleep_on(&inode->i_wait); + } + sti(); +} + +/** + * 锁定指定的i节点 + * 如果i节点已被锁定,则将当前任务置为不可中断的等待状态,并添加到该i节点的等待队列i_wait中。直 + * 到该i节点解锁并明确地唤醒本任务,然后对其上锁。 + * @param[in] *inode i节点指针 + * @retval void + */ +static inline void lock_inode(struct m_inode *inode) +{ + cli(); + while (inode->i_lock) { + sleep_on(&inode->i_wait); + } + inode->i_lock = 1; + sti(); +} + +/** + * 对指定的i节点解锁 + * 复位i节点的锁定标志,并明确地唤醒等待在此i节点等待队列i_wait上的所有进程 + * @param[in] *inode i节点指针 + * @retval void + */ +static inline void unlock_inode(struct m_inode *inode) +{ + inode->i_lock = 0; + wake_up(&inode->i_wait); +} + +/** + * 释放设备dev在内存i节点表中的所有i节点 + * @param[in] dev 设备号 + * @retval void + */ +void invalidate_inodes(int dev) +{ + int i; + struct m_inode *inode; + + inode = 0 + inode_table; /* 指向i节点表指针数组首项 */ + for(i = 0; i < NR_INODE; i++, inode++) { + wait_on_inode(inode); + if (inode->i_dev == dev) { + if (inode->i_count) { /* 若其引用数不为0,则显示出错警告 */ + printk("inode in use on removed disk\n\r"); + } + inode->i_dev = inode->i_dirt = 0; /* 释放i节点(置设备号为0) */ + } + } +} + +/** + * 同步所有i节点 + * 把内存i节点表中所有i节点与设备上i节点作同步操作 + * @retval void + */ +void sync_inodes(void) +{ + int i; + struct m_inode *inode; + + inode = 0 + inode_table; /* 指向i节点表指针数组首项 */ + for(i = 0; i < NR_INODE; i++, inode++) { + wait_on_inode(inode); + /* 判断该i节点是否已被修改并且不是管道节点 */ + if (inode->i_dirt && !inode->i_pipe) { + write_inode(inode); /* 则写盘(实际是写入缓冲区中) */ + } + } +} + +/** + * 文件数据块映射到盘块的处理操作 + * 把指定的文件数据块block对应到设备上逻辑块上,并返回逻辑块号。如果创建标志create置位,则在设 + * 备上对应逻辑块不存在时就申请新磁盘块,返回文件数据块block对应在设备上的逻辑块号(盘块号)。 + * @param[in] inode 文件的i节点指针 + * @param[in] block 文件中的数据块号(索引从0开始) + * @param[in] create 创建块标志 + * @retval 成功返回对应block的逻辑块块号,失败返回0 + */ +static int _bmap(struct m_inode * inode, int block, int create) +{ + struct buffer_head * bh; + int i; + + if (block < 0) { + panic("_bmap: block<0"); + } + /* block >= 直接块数 + 间接块数 + 二次间接块数 */ + if (block >= 7 + 512 + 512 * 512) { + panic("_bmap: block>big"); + } + + /**** block < 7,即直接块 ****/ + if (block < 7) { + /* create=1且i节点中对应该块的逻辑块字段为0,则需申请一磁盘块 */ + if (create && !inode->i_zone[block]) { + if ((inode->i_zone[block] = new_block(inode->i_dev))) { + inode->i_ctime = CURRENT_TIME; + inode->i_dirt = 1; + } + } + return inode->i_zone[block]; + } + + /**** 7 <= block < 7+512,即一次间接块 ****/ + block -= 7; + if (block < 512) { + /* create=1且i_zone[7]是0,表明文件是首次使用间接块,则需申请一磁盘块 */ + if (create && !inode->i_zone[7]) { + if ((inode->i_zone[7] = new_block(inode->i_dev))) { + inode->i_dirt = 1; + inode->i_ctime = CURRENT_TIME; + } + } + /* new_block()失败时,i_zone[7]为0;create=0时,i_zone[7]也为0。*/ + //// I think:下面改成 return inode->i_zone[8]也是可以的 + if (!inode->i_zone[7]) { + return 0; + } + /* 读取设备上该i节点的一次间接块 */ + if (!(bh = bread(inode->i_dev, inode->i_zone[7]))) { + return 0; + } + /* 间接块中第block项中的逻辑块号(盘块号)i,每一项占2个字节(这里的block已经减去7了) */ + i = ((unsigned short *)(bh->b_data))[block]; + /* i=0说明需要创建一个新逻辑块 */ + if (create && !i) { + if ((i = new_block(inode->i_dev))) { + ((unsigned short *) (bh->b_data))[block] = i; + bh->b_dirt = 1; + } + } + /* 最后释放该间接块占用的缓冲块,并返回磁盘上对应block的逻辑块块号 */ + brelse(bh); + return i; + } + + /**** 到这里,block已经减去7+512了,二次间接块 ****/ + block -= 512; + /* create && inode->i_zone[8]=0,则需申请一个磁盘块用于存放二次间接块的一级块信息 */ + if (create && !inode->i_zone[8]) { + if ((inode->i_zone[8] = new_block(inode->i_dev))) { + inode->i_dirt = 1; + inode->i_ctime = CURRENT_TIME; + } + } + /* new_block()失败时,i_zone[8]为0;create=0时,i_zone[8]也为0。*/ + //// I think:下面改成 return inode->i_zone[8]也是可以的 + if (!inode->i_zone[8]){ + return 0; + } + /* 读取二次间接块的一级块 */ + if (!(bh = bread(inode->i_dev, inode->i_zone[8]))) { + return 0; + } + /* block>>9即block/512,即取该一级块上第(block/512)项中的逻辑块号i */ + i = ((unsigned short *)bh->b_data)[block >> 9]; + /* i=0则需申请一磁盘块(逻辑块)作为二次间接块的二级块,并让二次间接块的一级块中第(block/512) + 项等于该二级块的块号i */ + if (create && !i) { + if ((i = new_block(inode->i_dev))) { + ((unsigned short *) (bh->b_data))[block >> 9] = i; + bh->b_dirt=1; /* 置位一级块的已修改标志 */ + } + } + brelse(bh); /* 释放二次间接块的一级块 */ + if (!i) { + return 0; + } + /* 读取二次间接块的二级块 */ + if (!(bh = bread(inode->i_dev, i))) { + return 0; + } + /* 取低9位,即第block项在二级块中的位置 */ + i = ((unsigned short *)bh->b_data)[block & 511]; + /* 第block项中逻辑块号为0的话,则申请一磁盘块(逻辑块),作为最终存放数据信息的块 */ + if (create && !i) { + if ((i = new_block(inode->i_dev))) { + ((unsigned short *) (bh->b_data))[block & 511] = i; + bh->b_dirt = 1; + } + } + /* 最后释放该二次间接块的二级块,返回磁盘上新申请的或原有的对应block的逻辑块块号 */ + brelse(bh); + return i; +} + +/** + * 取文件数据块block在设备上对应的逻辑块号 + * @param[in] inode 文件的内存i节点指针 + * @param[in] block 文件中的数据块号 + * @retval 成功则返回对应的逻辑块号,否则返回0 + */ +int bmap(struct m_inode * inode, int block) +{ + return _bmap(inode, block, 0); +} + +/** + * 取文件数据块block在设备上对应的逻辑块号(如果对应的逻辑块不存在就创建一块) + * @param[in] inode 文件的内存i节点指针 + * @param[in] block 文件中的数据块号 + * @retval 成功则返回对应的逻辑块号,否则返回0 + */ +int create_block(struct m_inode * inode, int block) +{ + return _bmap(inode, block, 1); +} + +/** + * 放回一个i节点 + * 该函数主要用于把i节点引用计数值递减1,并且若是管道i节点,则唤醒等待的进程。若是块设备文件i节 + * 点则刷新设备。并且若i节点的链接计数为0,则释放该i节点占用的所有磁盘逻辑块,并释放该i节点 + * @param[in] *inode 放回的i节点指针 + * @retval void + */ +void iput(struct m_inode * inode) +{ + /* 首先判断参数给出的i节点的有效性,并等待inode节点解锁(如果已经上锁的话) */ + if (!inode) { + return; + } + wait_on_inode(inode); + /* i节点的引用计数为0,表示该i节点已经是空闲的。内核再要求对其进行放回操作,说明内核有问题 */ + if (!inode->i_count) { + panic("iput: trying to free free inode"); + } + /* 如果是管道i节点,则唤醒等待该管道的进程,引用次数减1,如果还有引用则返回。否则释放管道 + 占用的内存页面,并复位该节点的引用计数值,已修改标志和管道标志,并返回。*/ + if (inode->i_pipe) { + wake_up(&inode->i_wait); + wake_up(&inode->i_wait2); + if (--inode->i_count) { + return; + } + /* 对于管道节点,inode->i_size存放着内存页地址。参见get_pipe_inode() */ + free_page(inode->i_size); + inode->i_count = 0; + inode->i_dirt = 0; + inode->i_pipe = 0; + return; + } + /* 设备号=0,则将此节点的引用计数递减1,返回。例如用于管道操作的i节点,其i节点的设备号为0 */ + if (!inode->i_dev) { + inode->i_count--; + return; + } + /* 如果是块设备文件的i节点,则i_zone[0]中是设备号,则刷新该设备。并等待i节点解锁 */ + if (S_ISBLK(inode->i_mode)) { + sync_dev(inode->i_zone[0]); + wait_on_inode(inode); + } +repeat: + /* 如果i节点引用计数大于1,则计数递减1后就直接返回(因为该i节点还有人在用,不能释放) */ + if (inode->i_count > 1) { + inode->i_count--; + return; + } + /* 如果i节点的链接数为0,则说明i节点对应文件被删除 */ + if (!inode->i_nlinks) { + /* 释放该i节点对应的所有逻辑块 */ + truncate(inode); + /* 从该设备的超级块中删除该i节点(复位i节点对应的i节点位图,清空i节点结构内容) */ + free_inode(inode); + return; + } + /* 如果该i节点已作过修改,则会更新该i节点,并等待该i节点解锁。由于这里在写i节点时需要等待 + 睡眠,此时其他进程有可能修改该i节点,因此在进程被唤醒后需要重复进行上述判断过程(repeat) */ + if (inode->i_dirt) { + write_inode(inode); /* we can sleep - so do again */ + wait_on_inode(inode); /* 因为我们睡眠了,所以要重复判断 */ + goto repeat; + } + /* 程序若能执行到此,说明该i节点的引用计数值i_count是1,链接数不为零,并且内容没有被修 + 改过。因此此时只要把i节点引用计数递减1,返回。此时该i节点的i_count=0,表示已释放 */ + inode->i_count--; + return; +} + +/** + * 从i节点表(inode_table)中获取一个空闲i节点项 + * 寻找引用计数count为0的i节点,并将其写盘后清零,返回其指针,引用计数被置1 + * @rerval 空闲i节点项的指针 + */ +struct m_inode * get_empty_inode(void) +{ + struct m_inode * inode; + static struct m_inode * last_inode = inode_table; /* 指向i节点表第1项 */ + int i; + + do { + inode = NULL; + for (i = NR_INODE; i; i--) { /* NR_INODE = 64 */ + if (++last_inode >= inode_table + NR_INODE) { + last_inode = inode_table; + } + /* last_inode所指向的i节点计数值为0,则说明可能找到空闲i节点项 */ + if (!last_inode->i_count) { + inode = last_inode; + /* 该i节点的已修改标志和和锁定标志均为0,则可以使用该i节点,退出循环 */ + if (!inode->i_dirt && !inode->i_lock) { + break; + } + } + } + /* 如果没有找到空闲i节点(inode=NULL),则将i节点表打印出来供调试使用,并停机 */ + if (!inode) { + for (i = 0 ; i < NR_INODE ; i++) { + printk("%04x: %6d\t", inode_table[i].i_dev, inode_table[i].i_num); + } + panic("No free inodes in mem"); + } + /* 等待该i节点解锁(如果又被上锁的话)。如果该i节点已修改标志被置位的话,则将该i节点 + 刷新(同步)。因为刷新时可能会睡眠,因此需要再次循环等待i节点解锁 */ + wait_on_inode(inode); + while (inode->i_dirt) { + write_inode(inode); + wait_on_inode(inode); + } + /* 如果i节点又被其他占用的话,则重新寻找空闲i节点。否则说明已找到符合要求的空闲i节点项 */ + } while (inode->i_count); + /*则将该i节点项内容清零,并置引用计数为1,返回该i节点指针 */ + memset(inode, 0, sizeof(*inode)); + inode->i_count = 1; + return inode; +} + +/** + * 获取管道节点 + * 首先扫描i节点表,寻找一个空闲i节点项,然后取得一页空闲内存供管道使用。然后将得到的i节点的引用 + * 计数置为2(读/写),初始化管道头和尾,置i节点的管道类型标志。 + * @retval 返回i节点指针,如果失败,则返回NULL + */ +struct m_inode * get_pipe_inode(void) +{ + struct m_inode * inode; + + /* 从内存i节点表中取得一个空闲i节点,如果找不到空闲i节点则返回NULL。*/ + if (!(inode = get_empty_inode())) { + return NULL; + } + /* 然后为该i节点申请一页内存。如果已没有空闲内存,则释放该i节点,并返回NULL。*/ + if (!(inode->i_size = get_free_page())) { + inode->i_count = 0; + return NULL; + } + /* 设置该i节点的引用计数为2,并复位管道头尾指针。i节点逻辑块号数组i_zone[]的i_zone[0]和 + i_zone[1]中分别用来存放管道头和管道尾指针。最后设置i节点是管道i节点标志并返回该i节点号 */ + inode->i_count = 2; /* sum of readers/writers */ + /* 读/写两者总计 */ + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; /* 复位管道头尾指针 */ + inode->i_pipe = 1; /* 置节点为管道使用标志 */ + return inode; +} + +/** + * 取得一个i节点 + * 从设备上读取指定节点号的i节点结构内容到内存i节点表中,并且返回该i节点指针。 + * @param[in] dev 设备号 + * @param[in] nr i节点号 + * @retval i节点指针 + */ +struct m_inode * iget(int dev, int nr) +{ + struct m_inode * inode, * empty; + + /* 首先判断参数有效性。若设备号是0,则表明内核代码问题 */ + if (!dev) { + panic("iget with dev==0"); + } + /* 预先从i节点表中取一个空闲i节点备用 */ + empty = get_empty_inode(); + /* 扫描i节点表。寻找参数指定节点号nr的i节点,并递增该节点的引用次数。如果当前扫描i节点的设备 + 号不等于指定的设备号或者节点号不等于指定的节点号,则继续扫描 */ + inode = inode_table; + while (inode < NR_INODE + inode_table) { + if (inode->i_dev != dev || inode->i_num != nr) { + inode++; + continue; + } + /* 如果找到指定设备号dev和节点号nr的i节点,则等待该节点解锁(如果已上锁的话)。在等待该节 + 点解锁过程中,i节点可能会发生变化。所以再次进行上述相同判断,如果发生了变化,则重新扫描 + 整个i节点表 */ + wait_on_inode(inode); + if (inode->i_dev != dev || inode->i_num != nr) { + inode = inode_table; + continue; + } + /* 到这里表示找到相应的i节点,于是将该i节点引用计数增1。然后再作进一步检查,看它是否是另 + 一个文件系统的安装点。若是则寻找被安装文件系统根节点并返回。如果该i节点的确是其他文件系 + 统的安装点,则在超级块表中搜寻安装在此i节点的超级块。如果没有找到,则显示出错信息,并放 + 回预获取的空闲节点empty,返回该i节点指针 */ + inode->i_count++; + if (inode->i_mount) { + int i; + for (i = 0; i < NR_SUPER; i++) { + if (super_block[i].s_imount == inode) { + break; + } + } + if (i >= NR_SUPER) { + printk("Mounted inode hasn't got sb\n"); + if (empty) { + iput(empty); + } + return inode; + } + /* 执行到这里表示已经找到安装到inode节点的文件系统超级块。于是将该i节点写盘放回, + 并从安装在此i节点上的文件系统超级块中取设备号,并令i节点号为ROOT_INO。然后重新 + 扫描整个i节点表,以获取该被安装文件系统的根i节点信息 */ + iput(inode); + dev = super_block[i].s_dev; + nr = ROOT_INO; + inode = inode_table; + continue; + } + /* 如果找到了相应的i节点。因此可以放弃本函数开始申请空闲i节点,返回找到的i节点指针 */ + if (empty) { + iput(empty); + } + return inode; + } + /* 如果在i节点表中没有找到指定的i节点,则利用前面申请的空闲i节点empty在i节点表中建立该i节 + 点。并从相应设备上读取该i节点信息,返回该i节点指针 */ + if (!empty) { + return (NULL); + } + inode = empty; + inode->i_dev = dev; + inode->i_num = nr; + read_inode(inode); + return inode; +} + +/** + * 读取指定i节点信息 + * 从设备上读取含有指定i节点信息的i节点盘块,然后复制到指定的i节点结构中。 + * @param[in] *inode i节点指针 + * @retval void + */ +static void read_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + int block; + + lock_inode(inode); + if (!(sb = get_super(inode->i_dev))) { /* 或取该i节点所在设备的超级块 */ + panic("trying to read inode without dev"); + } + + // 虽然i节点号从0开始编号,但第1个0号i节点不用,并且磁盘上也不保存对应的0号i节点结构。因此 + // 存放i节点的盘块的第1块上保存的是i节点号是1--16的i节点结构而不是0--15的。因此在上面计算i + // 节点号对应的i节点结构所在盘块时需要减1,即:B = (i节点号-1)/每块含有i节点结构数。例如, + // 节点号16的i节点结构应该在B=(16-1)/16 = 0的块上。这里我们从设备上读取该i节点所在逻辑块, + // 并复制指定i节点内容到inode指针所指位置处。 + + /* 该i节点所在设备逻辑块号 = + (启动块 + 超级块) + i节点位图块数 + 逻辑块位图块数 + (i节点号-1)/每块含有的i节点数 */ + block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + + (inode->i_num - 1) / INODES_PER_BLOCK; + + if (!(bh = bread(inode->i_dev, block))) { /* 将i节点所在逻辑块读取到高速缓冲中 */ + panic("unable to read i-node block"); + } + *(struct d_inode *)inode = + ((struct d_inode *)bh->b_data)[(inode->i_num - 1) % INODES_PER_BLOCK]; + /* 释放缓冲块,并解锁该i节点 */ + brelse(bh); + + /* 对于块设备文件,还需要设置i节点的文件最大长度值 */ + if (S_ISBLK(inode->i_mode)) { + int i = inode->i_zone[0]; /* 对于块设备文件,i_zone[0]中是设备号 */ + // TODO: 什么意思??? + if (blk_size[MAJOR(i)]) { + inode->i_size = 1024 * blk_size[MAJOR(i)][MINOR(i)]; + }else { + inode->i_size = 0x7fffffff; + } + } + unlock_inode(inode); +} + +/** + * 将i节点信息写入缓冲区中 + * 该函数把参数指定的i节点写入缓冲区相应的缓冲块中(待缓冲区刷新时会写入盘中)。 + * @param[in] *inode 待写入的i节点指针 + * @retval void + */ +static void write_inode(struct m_inode * inode) +{ + struct super_block * sb; + struct buffer_head * bh; + int block; + + lock_inode(inode); + /* 若该i节点没有被修改过或者该i节点的设备号等于零,则解锁该i节点并退出 */ + if (!inode->i_dirt || !inode->i_dev) { + unlock_inode(inode); + return; + } + /* 获取i节点所在超级块 */ + if (!(sb = get_super(inode->i_dev))) { + panic("trying to write inode without device"); + } + /* 该i节点所在的逻辑块号 = + (启动块 + 超级块) + i节点位图块数 + 逻辑块位图块数 + (i节点号 - 1)/每块含有的i节点数 */ + block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks + + (inode->i_num - 1) / INODES_PER_BLOCK; + if (!(bh = bread(inode->i_dev, block))) { + panic("unable to read i-node block"); + } + /* 修改i节点所在逻辑块中的i节点信息 */ + ((struct d_inode *)bh->b_data)[(inode->i_num - 1) % INODES_PER_BLOCK] + = *(struct d_inode *)inode; + /* 置缓冲区已修改标志,而i节点内容已经与缓冲区中的一致,因此修改标志置零 */ + bh->b_dirt = 1; + inode->i_dirt = 0; + + brelse(bh); + unlock_inode(inode); +} diff --git a/linux-0.12/fs/ioctl.c b/linux-0.12/fs/ioctl.c new file mode 100755 index 0000000..384472d --- /dev/null +++ b/linux-0.12/fs/ioctl.c @@ -0,0 +1,65 @@ +/* + * linux/fs/ioctl.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include + +#include + +extern int tty_ioctl(int dev, int cmd, int arg); +extern int pipe_ioctl(struct m_inode *pino, int cmd, int arg); + +/* 定义输入输出控制(ioctl)函数指针类型 */ +typedef int (*ioctl_ptr)(int dev,int cmd,int arg); + +/* 取系统中设备种数的宏 */ +#define NRDEVS ((sizeof (ioctl_table))/(sizeof (ioctl_ptr))) + +/* ioctl操作函数指针表 */ +static ioctl_ptr ioctl_table[]={ + NULL, /* nodev */ + NULL, /* /dev/mem */ + NULL, /* /dev/fd */ + NULL, /* /dev/hd */ + tty_ioctl, /* /dev/ttyx */ + tty_ioctl, /* /dev/tty */ + NULL, /* /dev/lp */ + NULL}; /* named pipes */ + +/** + * 输入输出控制 + * 该函数首先判断参数给出的文件描述符是否有效。然后根据对应i节点中文件属性判断文件类型,并根据 + * 具体文件类型调用相关的处理函数。 + * @param[in] fd 文件描述符 + * @param[in] cmd 命令码 + * @param[in] arg 参数 + * @retval 成功返回0,失败返回出错码 + */ +int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int dev, mode; + + if (fd >= NR_OPEN || !(filp = current->filp[fd])) { + return -EBADF; + } + if (filp->f_inode->i_pipe) { + return (filp->f_mode&1) ? pipe_ioctl(filp->f_inode,cmd,arg) : -EBADF; + } + mode = filp->f_inode->i_mode; + if (!S_ISCHR(mode) && !S_ISBLK(mode)) { + return -EINVAL; + } + dev = filp->f_inode->i_zone[0]; + if (MAJOR(dev) >= NRDEVS) { + return -ENODEV; + } + if (!ioctl_table[MAJOR(dev)]) { + return -ENOTTY; + } + return ioctl_table[MAJOR(dev)](dev,cmd,arg); +} diff --git a/linux-0.12/fs/namei.c b/linux-0.12/fs/namei.c new file mode 100755 index 0000000..0283cba --- /dev/null +++ b/linux-0.12/fs/namei.c @@ -0,0 +1,1089 @@ +/* + * linux/fs/namei.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * Some corrections by tytso. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* 由文件名查找对应i节点的内部函数 */ +static struct m_inode * _namei(const char * filename, struct m_inode * base, + int follow_links); + +// 例 "loveyou"[2] = 2["loveyou"] = *("loveyou"+2)= 'v' +/* 下面是访问模式宏。x是头文件include/fcntl.h中行7行开始定义的文件访问(打开)标志。这个宏根据文 + 件访问标志x的值来索引双引号中对应的数值。双引号中有4个八进制数值(实际表示4个控制字符): + "\004" - r 0 + "\002" - w 1 + "\006" - rw 2 + "\377" - wxrwxrwx 3 + 例如,如果x为2,则该宏返回八进制值006,表示rw。另外,其中O_ACCMODE = 00003,防止数组访问越界。 +*/ +#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE]) + +/* + * comment out this line if you want names > NAME_LEN chars to be + * truncated. Else they will be disallowed. + */ +/* + * 如果想让文件名长度 > NAME_LEN个的字符被截掉,就将下面定义注释掉。 + */ +/* #define NO_TRUNCATE */ + +#define MAY_EXEC 1 /* 可执行 */ +#define MAY_WRITE 2 /* 可写 */ +#define MAY_READ 4 /* 可读 */ + +/* + * permission() + * + * is used to check for read/write/execute permissions on a file. + * I don't know if we should look at just the euid or both euid and + * uid, but that should be easily changed. + */ +/* + * permission() + * 该函数用于检测一个文件的读/写/执行权限。我不知道是否只需检查euid,还是需要检查euid和uid两者, + * 不过这很容易修改。 + */ + +/** + * 检测文件访问许可权限 + * @brief 文件访问属性字段 |...|r|w|x|r|w|x|r|w|x| 低九位从高到低,分别是用户、组、其他的读/写/执行 + * + * @param inode 文件的i节点指针 + * @param mask 访问属性屏蔽码 + * @return 访问许可返回1,否则返回0 + */ +static int permission(struct m_inode * inode, int mask) +{ + + int mode = inode->i_mode; /* 文件访问属性 */ + + /* special case: not even root can read/write a deleted file */ + /* 特殊情况: 即使是超级用户(root)也不能读/写一个已被删除的文件. */ + if (inode->i_dev && !inode->i_nlinks) { + return 0; + } else if (current->euid == inode->i_uid) { /* 同用户 */ + mode >>= 6; + } else if (in_group_p(inode->i_gid)) { /* 同组 */ + mode >>= 3; + } + + if (((mode & mask & 0007) == mask) || suser()) { + return 1; + } + return 0; +} + +/* + * ok, we cannot use strncmp, as the name is not in our data space. + * Thus we'll have to use match. No big problem. Match also makes + * some sanity tests. + * + * NOTE! unlike strncmp, match returns 1 for success, 0 for failure. + */ +/* + * ok,我们不能使用strncmp字符串比较函数,因为名称不在我们的数据空间(不在内核空间)。因而我们只 + * 能使用match()。问题不大,match()同样也处理一些完整的测试。(注:第一句话的意思是因为 + * strncmp()比较用到的是DS和SS段寄存器,而在内核状态下,DS段指向的是内核空间,而name字符串不 + * 在内核空间。) + * + * 注意!与strncmp不同的是match()成功时返回1,失败时返回0。 + */ + +/** + * 指定长度字符串比较函数 + * @param len 比较的字符串长度 + * @param name 文件名指针 + * @param de 目录项结构 + * @return 相同返回1,不同返回0 + */ +static int match(int len, const char * name, struct dir_entry * de) +{ + register int same __asm__("ax"); + + /* 目录项指针空,或者目录项i节点等于0,或者要比较的字符串长度超过文件名长度 */ + if (!de || !de->inode || len > NAME_LEN) { + return 0; + } + /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ + /* ""当作"."来看待 ---> 这样就能处理象"/usr/lib//libc.a"那样的路径名 */ + if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) { + return 1; + } + /* 有点取巧,de->name[len] != '\0'说明de->name的长度大于len,长度不匹配 */ + if (len < NAME_LEN && de->name[len]) { + return 0; + } + /* 进行快速比较操作 */ + __asm__( + "cld\n\t" + "fs ; repe ; cmpsb\n\t" + "setz %%al" + :"=a" (same) + :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) + ); + + return same; +} + +/* + * find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the cache buffer in which the entry was found, and the entry + * itself (as a parameter - res_dir). It does NOT read the inode of the + * entry - you'll have to do that yourself if you want to. + * + * This also takes care of the few special cases due to '..'-traversal + * over a pseudo-root and a mount point. + */ +/* + * find_entry() + * + * 在指定目录中寻找一个与名字匹配的目录项。返回一个含有找到目录项的高速缓冲块以及目录项本身(作 + * 为一个参数--res_dir)。该函数并不读取目录项的i节点--如果需要的话则自己操作。 + * + * 由于有'..'目录项,因此在操作期间也会对几种特殊情况分别处理--比如横越一个伪根目录以及安装点。 + */ + +/** + * 查找指定目录和文件名的目录项 + * 该函数在指定目录的数据(文件)中搜索指定文件名的目录项。并对指定文件名是'..'的情况根据当前进 + * 行的相关设置进行特殊处理。 + * @param[in] *dir 指定目录i节点的指针 + * @param[in] name 文件名 + * @param[in] namelen 文件名长度 + * @param[in/out] res_dir 返回的指定目录项结构指针 + * @retval 成功则返回高速缓冲区指针,失败则返回空指针NULL + */ +static struct buffer_head * find_entry(struct m_inode ** dir, + const char * name, int namelen, struct dir_entry ** res_dir) +{ + int entries; + int block, i; + struct buffer_head * bh; + struct dir_entry * de; + struct super_block * sb; + +#ifdef NO_TRUNCATE + if (namelen > NAME_LEN) { + return NULL; + } +#else + if (namelen > NAME_LEN) { + namelen = NAME_LEN; + } +#endif + entries = (*dir)->i_size / (sizeof (struct dir_entry)); + *res_dir = NULL; + /* check for '..', as we might have to do some "magic" for it */ + /* 检查目录项'..',因为我们可能需要对其进行特殊处理 */ + // Q: 为什么不用name[0]和name[1]替代? + // A: 因为name指针的内容不在内核空间,内核状态下fs指向用户空间。 + if (namelen == 2 && get_fs_byte(name) == '.' && get_fs_byte(name+1) == '.') { + /* '..' in a pseudo-root results in a faked '.' (just change namelen) */ + /* 伪根中的'..'如同一个假'.'(只需改变名字长度) */ + if ((*dir) == current->root) { + namelen = 1; + } else if ((*dir)->i_num == ROOT_INO) { + /* '..' over a mount-point results in 'dir' being exchanged for the mounted + directory-inode. NOTE! We set mounted, so that we can iput the new dir */ + sb = get_super((*dir)->i_dev); + if (sb->s_imount) { + iput(*dir); + (*dir)=sb->s_imount; + (*dir)->i_count++; + } + } + } + if (!(block = (*dir)->i_zone[0])) { + return NULL; + } + if (!(bh = bread((*dir)->i_dev,block))) { + return NULL; + } + i = 0; + de = (struct dir_entry *) bh->b_data; + while (i < entries) { + if ((char *)de >= BLOCK_SIZE + bh->b_data) { + brelse(bh); + bh = NULL; + if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) || + !(bh = bread((*dir)->i_dev,block))) { + i += DIR_ENTRIES_PER_BLOCK; + continue; + } + de = (struct dir_entry *) bh->b_data; + } + if (match(namelen,name,de)) { + *res_dir = de; + return bh; + } + de++; + i++; + } + brelse(bh); + return NULL; +} + +/* + * add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as find_entry(). It returns NULL if it failed. + * + * NOTE!! The inode part of 'de' is left at 0 - which means you + * may not sleep between calling this and putting something into + * the entry, as someone else might have used it while you slept. + */ +/* + * add_entry() + * 使用与find_entry()同样的方法,往指定目录中添加一指定文件名的目录项。如果失败则返回NULL。 + * + * 注意!!'de'(指定目录项结构指针)的i节点部分被设置为0 - 这表示在调用该函数和往目录项中添 + * 加信息之间不能去睡眠,因为如果睡眠,那么其他人(进程)可能会使用该目录项。 + */ +/** + * 根据指定的目录和文件名添加目录项 + * @param[in] dir 指定目录的i节点 + * @param[in] name 文件名 + * @param[in] namelen 文件名长度 + * @param[in/out] res_dir 返回的目录项结构指针 + * @retval 成功返回高速缓冲区指针,失败返回NULL + */ +static struct buffer_head * add_entry(struct m_inode * dir, + const char * name, int namelen, struct dir_entry ** res_dir) +{ + int block,i; + struct buffer_head * bh; + struct dir_entry * de; + + *res_dir = NULL; +#ifdef NO_TRUNCATE + if (namelen > NAME_LEN) { + return NULL; + } +#else + if (namelen > NAME_LEN) { + namelen = NAME_LEN; + } +#endif + if (!namelen) { + return NULL; + } + if (!(block = dir->i_zone[0])) { + return NULL; + } + if (!(bh = bread(dir->i_dev,block))) { + return NULL; + } + i = 0; + de = (struct dir_entry *) bh->b_data; + while (1) { + if ((char *)de >= BLOCK_SIZE+bh->b_data) { + brelse(bh); + bh = NULL; + block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); + if (!block) + return NULL; + if (!(bh = bread(dir->i_dev,block))) { + i += DIR_ENTRIES_PER_BLOCK; + continue; + } + de = (struct dir_entry *) bh->b_data; + } + if (i*sizeof(struct dir_entry) >= dir->i_size) { + de->inode=0; + dir->i_size = (i+1)*sizeof(struct dir_entry); + dir->i_dirt = 1; + dir->i_ctime = CURRENT_TIME; + } + if (!de->inode) { + dir->i_mtime = CURRENT_TIME; + for (i=0; i < NAME_LEN ; i++) + de->name[i]=(ib_dirt = 1; + *res_dir = de; + return bh; + } + de++; + i++; + } + brelse(bh); + return NULL; +} + + +/** + * 查找符号链接的i节点 + * @param[in] dir 目录i节点 + * @param[in] inode 目录项i节点 + * @retval 返回符号链接到文件的i节点指针,出错返回NULL + */ +static struct m_inode * follow_link(struct m_inode * dir, struct m_inode * inode) +{ + unsigned short fs; + struct buffer_head * bh; + + if (!dir) { + dir = current->root; + dir->i_count ++; + } + if (!inode) { + iput(dir); + return NULL; + } + if (!S_ISLNK(inode->i_mode)) { + iput(dir); + return inode; + } + __asm__("mov %%fs,%0":"=r" (fs)); + if (fs != 0x17 || !inode->i_zone[0] || + !(bh = bread(inode->i_dev, inode->i_zone[0]))) { + iput(dir); + iput(inode); + return NULL; + } + iput(inode); + __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); + inode = _namei(bh->b_data,dir,0); + __asm__("mov %0,%%fs"::"r" (fs)); + brelse(bh); + return inode; +} + +/* + * get_dir() + * + * Getdir traverses the pathname until it hits the topmost directory. + * It returns NULL on failure. + */ +/* + * get_dir() + * + * 该函数根据给出的路径名进行搜索,直到达到最顶端的目录。如果失败,返回NULL。 + */ +/** + * 从指定目录开始搜寻指定路径名的目录(或文件名)的i节点 + * @param pathname 路径名 + * @param inode 指定起始目录的i节点 + * @return 成功返回目录或文件的i节点指针,失败时返回NULL. + */ +static struct m_inode * get_dir(const char * pathname, struct m_inode * inode) +{ + char c; + const char * thisname; + struct buffer_head * bh; + int namelen,inr; + struct dir_entry * de; + struct m_inode * dir; + + if (!inode) { + inode = current->pwd; + inode->i_count++; + } + if ((c=get_fs_byte(pathname))=='/') { + iput(inode); + inode = current->root; + pathname++; + inode->i_count++; + } + while (1) { + thisname = pathname; + if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { + iput(inode); + return NULL; + } + for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) + /* nothing */ ; + if (!c) + return inode; + if (!(bh = find_entry(&inode,thisname,namelen,&de))) { + iput(inode); + return NULL; + } + inr = de->inode; + brelse(bh); + dir = inode; + if (!(inode = iget(dir->i_dev,inr))) { + iput(dir); + return NULL; + } + if (!(inode = follow_link(dir,inode))) + return NULL; + } +} + +/* + * dir_namei() + * + * dir_namei() returns the inode of the directory of the + * specified name, and the name within that directory. + */ +/* + * dir_namei() + * + * dir_namei()函数返回指定目录名的i节点指针,以及在最顶层目录的名称。 + */ +/** + * 获取指定目录名的i节点指针,以及在最顶层目录的名称 + * @note 这里"最顶层目录"是指路径名中最靠近末端的目录 + * @param pathname 目录路径名 + * @param namelen 路径名长度 + * @param name 返回的最顶层目录名 + * @param base 搜索起始目录的i节点 + * @retval 成功返回指定目录名最顶层的i节点指针,出错时返回NULL。 + */ +static struct m_inode * dir_namei(const char * pathname, + int * namelen, const char ** name, struct m_inode * base) +{ + char c; + const char * basename; + struct m_inode * dir; + + if (!(dir = get_dir(pathname, base))) { + return NULL; + } + basename = pathname; + while ((c = get_fs_byte(pathname++))) { + if (c == '/') { + basename = pathname; + } + } + *namelen = pathname - basename - 1; + *name = basename; + return dir; +} + +/** + * 取指定路径名的i节点 + * @param[in] pathname 路径名 + * @param[in] base 搜索起点目录i节点 + * @param[in] follow_links 是否跟随符号链接的标志,1 - 需要,0 - 不需要 + * @retval 成功返回指定路径名的inode,失败返回NULL + */ +struct m_inode * _namei(const char * pathname, struct m_inode * base, + int follow_links) +{ + const char * basename; + int inr,namelen; + struct m_inode * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!(base = dir_namei(pathname, &namelen, &basename, base))) { + return NULL; + } + if (!namelen) { /* special case: '/usr/' etc */ + return base; + } + bh = find_entry(&base, basename, namelen, &de); + if (!bh) { + iput(base); + return NULL; + } + inr = de->inode; + brelse(bh); + if (!(inode = iget(base->i_dev, inr))) { + iput(base); + return NULL; + } + if (follow_links) { + inode = follow_link(base, inode); + } else { + iput(base); + } + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + return inode; +} + + +/** + * 取指定路径名的i节点,不跟随符号链接 + * @param[in] pathname 路径名 + * @retval 成功返回对应的i节点,失败返回NULL +*/ +struct m_inode * lnamei(const char * pathname) +{ + return _namei(pathname, NULL, 0); +} + +/* + * namei() + * + * is used by most simple commands to get the inode of a specified name. + * Open, link etc use their own routines, but this is enough for things + * like 'chmod' etc. + */ +/* + * namei() + * + * 该函数被许多简单命令用于取得指定路径名称的i节点。open,link等则使用它们自己的相应函数。但对于 + * 像修改模式"chmod"等这样的命令,该函数已足够用了。 + */ +/** + * 取指定路径名的i节点,跟随符号链接 + * @param[in] pathname 路径名 + * @retval 成功返回对应的i节点,失败返回NULL + */ +struct m_inode * namei(const char * pathname) +{ + return _namei(pathname, NULL, 1); +} + +/* + * open_namei() + * + * namei for open - this is in fact almost the whole open-routine. + */ +/* + * open()函数使用的namei函数 - 这其实几乎是完整的打开文件程序。 + */ + +/** + * 文件打开namei函数 + * @param[in] filename 文件名 + * @param[in] flag 打开文件标志 + * @param[in] mode 指定文件的许可属性 + * @param[in] res_inode 返回对应文件路径名的i节点指针 + * @retval 成功返回0,失败返回出错码 + */ +int open_namei(const char * pathname, int flag, int mode, + struct m_inode ** res_inode) +{ + const char * basename; + int inr,dev,namelen; + struct m_inode * dir, *inode; + struct buffer_head * bh; + struct dir_entry * de; + + if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) { + flag |= O_WRONLY; + } + mode &= 0777 & ~current->umask; + mode |= I_REGULAR; + if (!(dir = dir_namei(pathname,&namelen,&basename,NULL))) { + return -ENOENT; + } + if (!namelen) { /* special case: '/usr/' etc */ + if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { + *res_inode=dir; + return 0; + } + iput(dir); + return -EISDIR; + } + bh = find_entry(&dir, basename, namelen, &de); + if (!bh) { + if (!(flag & O_CREAT)) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EACCES; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_uid = current->euid; + inode->i_mode = mode; + inode->i_dirt = 1; + bh = add_entry(dir, basename, namelen, &de); + if (!bh) { + inode->i_nlinks--; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + *res_inode = inode; + return 0; + } + inr = de->inode; + dev = dir->i_dev; + brelse(bh); + if (flag & O_EXCL) { + iput(dir); + return -EEXIST; + } + if (!(inode = follow_link(dir,iget(dev,inr)))) { + return -EACCES; + } + if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || + !permission(inode,ACC_MODE(flag))) { + iput(inode); + return -EPERM; + } + inode->i_atime = CURRENT_TIME; + if (flag & O_TRUNC) { + truncate(inode); + } + *res_inode = inode; + return 0; +} + +int sys_mknod(const char * filename, int mode, int dev) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!suser()) { + return -EPERM; + } + if (!(dir = dir_namei(filename,&namelen,&basename, NULL))) { + return -ENOENT; + } + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_mode = mode; + if (S_ISBLK(mode) || S_ISCHR(mode)) { + inode->i_zone[0] = dev; + } + inode->i_mtime = inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + inode->i_nlinks=0; + iput(inode); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + iput(dir); + iput(inode); + brelse(bh); + return 0; +} + +int sys_mkdir(const char * pathname, int mode) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh, *dir_block; + struct dir_entry * de; + + if (!(dir = dir_namei(pathname,&namelen,&basename, NULL))) { + return -ENOENT; + } + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_size = 32; + inode->i_dirt = 1; + inode->i_mtime = inode->i_atime = CURRENT_TIME; + if (!(inode->i_zone[0]=new_block(inode->i_dev))) { + iput(dir); + inode->i_nlinks--; + iput(inode); + return -ENOSPC; + } + inode->i_dirt = 1; + if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) { + iput(dir); + inode->i_nlinks--; + iput(inode); + return -ERROR; + } + de = (struct dir_entry *) dir_block->b_data; + de->inode=inode->i_num; + strcpy(de->name,"."); + de++; + de->inode = dir->i_num; + strcpy(de->name,".."); + inode->i_nlinks = 2; + dir_block->b_dirt = 1; + brelse(dir_block); + inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); + inode->i_dirt = 1; + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + iput(dir); + inode->i_nlinks=0; + iput(inode); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + dir->i_nlinks++; + dir->i_dirt = 1; + iput(dir); + iput(inode); + brelse(bh); + return 0; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +static int empty_dir(struct m_inode * inode) +{ + int nr,block; + int len; + struct buffer_head * bh; + struct dir_entry * de; + + len = inode->i_size / sizeof (struct dir_entry); + if (len<2 || !inode->i_zone[0] || + !(bh=bread(inode->i_dev,inode->i_zone[0]))) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } + de = (struct dir_entry *) bh->b_data; + if (de[0].inode != inode->i_num || !de[1].inode || + strcmp(".",de[0].name) || strcmp("..",de[1].name)) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } + nr = 2; + de += 2; + while (nr= (void *) (bh->b_data+BLOCK_SIZE)) { + brelse(bh); + block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK); + if (!block) { + nr += DIR_ENTRIES_PER_BLOCK; + continue; + } + if (!(bh=bread(inode->i_dev,block))) + return 0; + de = (struct dir_entry *) bh->b_data; + } + if (de->inode) { + brelse(bh); + return 0; + } + de++; + nr++; + } + brelse(bh); + return 1; +} + +int sys_rmdir(const char * name) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!(dir = dir_namei(name,&namelen,&basename, NULL))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return -ENOENT; + } + if (!(inode = iget(dir->i_dev, de->inode))) { + iput(dir); + brelse(bh); + return -EPERM; + } + if ((dir->i_mode & S_ISVTX) && current->euid && + inode->i_uid != current->euid) { + iput(dir); + iput(inode); + brelse(bh); + return -EPERM; + } + if (inode->i_dev != dir->i_dev || inode->i_count>1) { + iput(dir); + iput(inode); + brelse(bh); + return -EPERM; + } + if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ + iput(inode); + iput(dir); + brelse(bh); + return -EPERM; + } + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + iput(dir); + brelse(bh); + return -ENOTDIR; + } + if (!empty_dir(inode)) { + iput(inode); + iput(dir); + brelse(bh); + return -ENOTEMPTY; + } + if (inode->i_nlinks != 2) + printk("empty directory has nlink!=2 (%d)",inode->i_nlinks); + de->inode = 0; + bh->b_dirt = 1; + brelse(bh); + inode->i_nlinks=0; + inode->i_dirt=1; + dir->i_nlinks--; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt=1; + iput(dir); + iput(inode); + return 0; +} + +int sys_unlink(const char * name) +{ + const char * basename; + int namelen; + struct m_inode * dir, * inode; + struct buffer_head * bh; + struct dir_entry * de; + + if (!(dir = dir_namei(name,&namelen,&basename, NULL))) + return -ENOENT; + if (!namelen) { + iput(dir); + return -ENOENT; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EPERM; + } + bh = find_entry(&dir,basename,namelen,&de); + if (!bh) { + iput(dir); + return -ENOENT; + } + if (!(inode = iget(dir->i_dev, de->inode))) { + iput(dir); + brelse(bh); + return -ENOENT; + } + if ((dir->i_mode & S_ISVTX) && !suser() && + current->euid != inode->i_uid && + current->euid != dir->i_uid) { + iput(dir); + iput(inode); + brelse(bh); + return -EPERM; + } + if (S_ISDIR(inode->i_mode)) { + iput(inode); + iput(dir); + brelse(bh); + return -EPERM; + } + if (!inode->i_nlinks) { + printk("Deleting nonexistent file (%04x:%d), %d\n", + inode->i_dev,inode->i_num,inode->i_nlinks); + inode->i_nlinks=1; + } + de->inode = 0; + bh->b_dirt = 1; + brelse(bh); + inode->i_nlinks--; + inode->i_dirt = 1; + inode->i_ctime = CURRENT_TIME; + iput(inode); + iput(dir); + return 0; +} + +int sys_symlink(const char * oldname, const char * newname) +{ + struct dir_entry * de; + struct m_inode * dir, * inode; + struct buffer_head * bh, * name_block; + const char * basename; + int namelen, i; + char c; + + dir = dir_namei(newname,&namelen,&basename, NULL); + if (!dir) + return -EACCES; + if (!namelen) { + iput(dir); + return -EPERM; + } + if (!permission(dir,MAY_WRITE)) { + iput(dir); + return -EACCES; + } + if (!(inode = new_inode(dir->i_dev))) { + iput(dir); + return -ENOSPC; + } + inode->i_mode = S_IFLNK | (0777 & ~current->umask); + inode->i_dirt = 1; + if (!(inode->i_zone[0]=new_block(inode->i_dev))) { + iput(dir); + inode->i_nlinks--; + iput(inode); + return -ENOSPC; + } + inode->i_dirt = 1; + if (!(name_block=bread(inode->i_dev,inode->i_zone[0]))) { + iput(dir); + inode->i_nlinks--; + iput(inode); + return -ERROR; + } + i = 0; + while (i < 1023 && (c=get_fs_byte(oldname++))) + name_block->b_data[i++] = c; + name_block->b_data[i] = 0; + name_block->b_dirt = 1; + brelse(name_block); + inode->i_size = i; + inode->i_dirt = 1; + bh = find_entry(&dir,basename,namelen,&de); + if (bh) { + inode->i_nlinks--; + iput(inode); + brelse(bh); + iput(dir); + return -EEXIST; + } + bh = add_entry(dir,basename,namelen,&de); + if (!bh) { + inode->i_nlinks--; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->inode = inode->i_num; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + iput(inode); + return 0; +} + +/** + * 为文件建立一个文件名目录项 + * @brief 为一个已存在的文件创建一个新链接(也称为硬连接 - hard link) + * + * @param oldname 原路径名 + * @param newname 新路径名 + * @return 成功返回0,失败返回出错号 + */ +int sys_link(const char * oldname, const char * newname) +{ + struct dir_entry * de; + struct m_inode * oldinode, * dir; + struct buffer_head * bh; + const char * basename; + int namelen; + + oldinode = namei(oldname); + if (!oldinode) { + return -ENOENT; + } + if (S_ISDIR(oldinode->i_mode)) { + iput(oldinode); + return -EPERM; + } + dir = dir_namei(newname, &namelen, &basename, NULL); + if (!dir) { + iput(oldinode); + return -EACCES; + } + if (!namelen) { + iput(oldinode); + iput(dir); + return -EPERM; + } + if (dir->i_dev != oldinode->i_dev) { + iput(dir); + iput(oldinode); + return -EXDEV; + } + if (!permission(dir, MAY_WRITE)) { + iput(dir); + iput(oldinode); + return -EACCES; + } + bh = find_entry(&dir, basename, namelen, &de); + if (bh) { + brelse(bh); + iput(dir); + iput(oldinode); + return -EEXIST; + } + bh = add_entry(dir, basename, namelen, &de); + if (!bh) { + iput(dir); + iput(oldinode); + return -ENOSPC; + } + de->inode = oldinode->i_num; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + oldinode->i_nlinks ++; + oldinode->i_ctime = CURRENT_TIME; + oldinode->i_dirt = 1; + iput(oldinode); + return 0; +} diff --git a/linux-0.12/fs/open.c b/linux-0.12/fs/open.c new file mode 100755 index 0000000..0128e1f --- /dev/null +++ b/linux-0.12/fs/open.c @@ -0,0 +1,353 @@ +/* + * linux/fs/open.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/** + * 取文件系统信息(未实现) + * 该系统调用用于返回已安装(mounted)文件系统的统计信息。 + * @param[in] dev 含有用户已安装文件系统的设备号 + * @param[in] ubuf 一个ustat结构缓冲区指针,用于存放系统返回的文件系统信息 + * @retval 成功时返回0,并且ubuf指向的ustate结构被添入文件系统总空闲块和空闲i节点数 + */ +int sys_ustat(int dev, struct ustat * ubuf) +{ + return -ENOSYS; +} + + +/** + * 设置文件访问和修改时间 + * @param[in] filename 文件名 + * @param[in] times 访问和修改时间结构指针 + * 1. times!=NULL,则取utimbuf结构中的时间信息来设置文件的访问 + 和修改时间 + * 2. times==NULL,则取系统当前时间来设置指定文件的访问和修改时间域 + * @retval 成功返回0,失败返回错误码 + */ +int sys_utime(char * filename, struct utimbuf * times) +{ + struct m_inode * inode; + long actime, modtime; + + if (!(inode = namei(filename))) { + return -ENOENT; + } + if (times) { + actime = get_fs_long((unsigned long *) ×->actime); + modtime = get_fs_long((unsigned long *) ×->modtime); + } else { + actime = modtime = CURRENT_TIME; + } + inode->i_atime = actime; + inode->i_mtime = modtime; + inode->i_dirt = 1; + iput(inode); + return 0; +} + +/* + * XXX should we use the real or effective uid? BSD uses the real uid, + * so as to make this call useful to setuid programs. + */ +/* + * XXX我们该用真实用户id(ruid)还是有效有户id(euid)?BSD系统使用了真实用户id,以使该调用可以 + * 供setuid程序使用。 + * (注:POSIX标准建议使用真实用户ID) + * (注1:英文注释开始的‘XXX’表示重要提示) + */ + +/** + * 检查文件的访问权限 + * @param[in] filename 文件名 + * @param[in] mode 检查的访问属性 + * @retval 如果访问允许的话,则返回0;否则,返回出错码 + */ +int sys_access(const char * filename, int mode) +{ + struct m_inode * inode; + int res, i_mode; + + mode &= 0007; + if (!(inode = namei(filename))) { + return -EACCES; + } + i_mode = res = inode->i_mode & 0777; + iput(inode); + if (current->uid == inode->i_uid) { + res >>= 6; + } + else if (current->gid == inode->i_gid) { + res >>= 6; + } + if ((res & 0007 & mode) == mode) { + return 0; + } + /* + * XXX we are doing this test last because we really should be + * swapping the effective with the real user id (temporarily), + * and then calling suser() routine. If we do call the + * suser() routine, it needs to be called last. + */ + if ((!current->uid) && + (!(mode & 1) || (i_mode & 0111))) + return 0; + return -EACCES; +} + + +/** + * 改变当前工作目录 系统调用 + * @param[in] filename 目录名 + * @retval 成功则返回0,否则返回出错码 + */ +int sys_chdir(const char * filename) +{ + struct m_inode * inode; + + if (!(inode = namei(filename))) + return -ENOENT; + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + return -ENOTDIR; + } + iput(current->pwd); + current->pwd = inode; + return (0); +} + + +/** + * 改变根目录 系统调用 + * 把指定的目录名设置成为当前进程的根目录“/” + * @param[in] filename 文件名 + * @retval 成功返回0,否则,返回出错码 + */ +int sys_chroot(const char * filename) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if (!S_ISDIR(inode->i_mode)) { + iput(inode); + return -ENOTDIR; + } + iput(current->root); + current->root = inode; + return (0); +} + + +/** + * 修改文件属性 系统调用 + * @param[in] filename 文件名 + * @param[in] mode 新的文件属性 + * @retval 成功返回0,失败返回出错码 + */ +int sys_chmod(const char * filename,int mode) +{ + struct m_inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + if ((current->euid != inode->i_uid) && !suser()) { + iput(inode); + return -EACCES; + } + inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); + inode->i_dirt = 1; + iput(inode); + return 0; +} + +/** + * 修改文件宿主 系统调用 + * @param[in] filename 文件名 + * @param[in] uid 用户ID + * @param[in] gid 组ID + * @retval 成功则返回0,失败返回出错码 + */ +int sys_chown(const char * filename,int uid,int gid) +{ + struct m_inode * inode; + + if (!(inode = namei(filename))) { + return -ENOENT; + } + if (!suser()) { + iput(inode); + return -EACCES; + } + inode->i_uid=uid; + inode->i_gid=gid; + inode->i_dirt=1; + iput(inode); + return 0; +} + + +/** + * 检查字符设备类型 + * 该函数仅用于下面文件打开系统调用sys_open(),用于检查若打开的文件是tty终端字符设备时,需要对 + * 当前进程的设置和对tty表的设置。 + * @param[in] inode i节点 + * @param[in] dev 设备号 + * @param[in] flag 文件操作标志 + * @retval 成功返回0,失败返回-1(对应字符设备不能打开) + */ +static int check_char_dev(struct m_inode * inode, int dev, int flag) +{ + struct tty_struct *tty; + int min; + + if (MAJOR(dev) == 4 || MAJOR(dev) == 5) { + if (MAJOR(dev) == 5) { + min = current->tty; + } else { + min = MINOR(dev); + } + if (min < 0) { + return -1; + } + if ((IS_A_PTY_MASTER(min)) && (inode->i_count>1)) { + return -1; + } + tty = TTY_TABLE(min); + if (!(flag & O_NOCTTY) && + current->leader && + current->tty<0 && + tty->session==0) { + current->tty = min; + tty->session= current->session; + tty->pgrp = current->pgrp; + } + if (flag & O_NONBLOCK) { + TTY_TABLE(min)->termios.c_cc[VMIN] =0; + TTY_TABLE(min)->termios.c_cc[VTIME] =0; + TTY_TABLE(min)->termios.c_lflag &= ~ICANON; + } + } + return 0; +} + + +/** + * 打开(或创建)文件 + * @note 实际上open的操作是将进程中的文件描述符指向了系统中的文件表项,该项又指向了打开的文件 + * 索引节点(inode)。 + * @param[in] filename 文件名 + * @param[in] flag 打开文件标志 + * @param[in] mode 文件属性 + * @retval 成功返回文件句柄,失败返回出错码 + */ +int sys_open(const char * filename, int flag, int mode) +{ + struct m_inode * inode; + struct file * f; + int i, fd; + + mode &= 0777 & ~current->umask; + + /* 在用户文件描述符表找到最小的文件描述符fd */ + for(fd = 0; fd < NR_OPEN; fd ++) { + if (!current->filp[fd]) { + break; + } + } + if (fd >= NR_OPEN) { + return -EINVAL; + } + current->close_on_exec &= ~(1<f_count) { + break; + } + } + if (i >= NR_FILE) { + return -EINVAL; + } + (current->filp[fd] = f)->f_count++; + /* 在内存的索引节点表中找到文件对应的i节点 */ + if ((i = open_namei(filename, flag, mode, &inode)) < 0) { + current->filp[fd] = NULL; + f->f_count = 0; + return i; + } +/* ttys are somewhat special (ttyxx major==4, tty major==5) */ + if (S_ISCHR(inode->i_mode)) { + if (check_char_dev(inode, inode->i_zone[0], flag)) { + iput(inode); + current->filp[fd] = NULL; + f->f_count = 0; + return -EAGAIN; + } + } +/* Likewise with block-devices: check for floppy_change */ + if (S_ISBLK(inode->i_mode)) { + check_disk_change(inode->i_zone[0]); + } + f->f_mode = inode->i_mode; + f->f_flags = flag; + f->f_count = 1; + f->f_inode = inode; + f->f_pos = 0; + return (fd); +} + + +/** + * 创建文件 + * @param[in] pathname 路径名 + * @param[in] mode 文件属性 + * @retval 成功返回文件句柄,失败返回出错码 + */ +int sys_creat(const char * pathname, int mode) +{ + return sys_open(pathname, O_CREAT | O_TRUNC, mode); +} + + +/** + * 关闭文件 + * @param[in] fd 文件句柄 + * @retval 成功返回0,失败返回出错码 + */ +int sys_close(unsigned int fd) +{ + struct file * filp; + + if (fd >= NR_OPEN) { + return -EINVAL; + } + current->close_on_exec &= ~(1<filp[fd])) { + return -EINVAL; + } + current->filp[fd] = NULL; + if (filp->f_count == 0) { + panic("Close: file count is 0"); + } + if (--filp->f_count) { + return (0); + } + iput(filp->f_inode); + return (0); +} diff --git a/linux-0.12/fs/pipe.c b/linux-0.12/fs/pipe.c new file mode 100755 index 0000000..83300db --- /dev/null +++ b/linux-0.12/fs/pipe.c @@ -0,0 +1,181 @@ +/* + * linux/fs/pipe.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include + +#include +#include /* for get_free_page */ +#include +#include + +/** + * 读管道 + * @param[in] inode 管道对应的i节点 + * @param[in/out] buf 用户数据缓冲区指针 + * @param[in] count 欲读取的字节数 + * @retval 成功返回读取长度,失败返回错误码 + */ +int read_pipe(struct m_inode * inode, char * buf, int count) +{ + int chars, size, read = 0; + + while (count > 0) { + while (!(size = PIPE_SIZE(*inode))) { + wake_up(& PIPE_WRITE_WAIT(*inode)); + /* 没有写进程,立即返回 */ + if (inode->i_count != 2) { /* are there any writers? */ + return read; + } + /* 没有阻塞信号,立即返回 */ + if (current->signal & ~current->blocked) { + return read ? read : -ERESTARTSYS; + } + interruptible_sleep_on(& PIPE_READ_WAIT(*inode)); + } + /* chars表示当次循环可读取的字节数 */ + chars = PAGE_SIZE - PIPE_TAIL(*inode); + if (chars > count) { + chars = count; + } + if (chars > size) { + chars = size; + } + count -= chars; + read += chars; + size = PIPE_TAIL(*inode); + PIPE_TAIL(*inode) += chars; + PIPE_TAIL(*inode) &= (PAGE_SIZE-1); + while (chars-->0) { + put_fs_byte(((char *)inode->i_size)[size++], buf++); + } + } + wake_up(& PIPE_WRITE_WAIT(*inode)); + return read; +} + + +/** + * 写管道 + * @param[in] inode 管道对应的i节点 + * @param[in] buf 数据缓冲区指针 + * @param[in] count 将写入管道的字节数 + * @retval 成功返回写入的长度,失败返回-1 + */ +int write_pipe(struct m_inode * inode, char * buf, int count) +{ + int chars, size, written = 0; + + while (count > 0) { + while (!(size = (PAGE_SIZE-1) - PIPE_SIZE(*inode))) { + wake_up(& PIPE_READ_WAIT(*inode)); + /* 没有读进程,发出SIGPIPE信号并立即返回 */ + if (inode->i_count != 2) { /* no readers */ + current->signal |= (1<<(SIGPIPE-1)); + return written ? written : -1; + } + sleep_on(& PIPE_WRITE_WAIT(*inode)); + } + /* chars表示当次循环可写入的字节数 */ + chars = PAGE_SIZE - PIPE_HEAD(*inode); + if (chars > count) { + chars = count; + } + if (chars > size) { + chars = size; + } + count -= chars; + written += chars; + size = PIPE_HEAD(*inode); + PIPE_HEAD(*inode) += chars; + PIPE_HEAD(*inode) &= (PAGE_SIZE-1); + while (chars-->0) { + ((char *)inode->i_size)[size++] = get_fs_byte(buf++); + } + } + wake_up(& PIPE_READ_WAIT(*inode)); + return written; +} + + +/** + * 创建管道 + * 在fildes所指的数组中创建一对指向一管道i节点的句柄 + * @param[in/out] fildes 文件句柄数组:fildes[0]用于读管道,fildes[1]用于写管道 + * @retval 成功返回0,出错返回-1 + */ +int sys_pipe(unsigned long * fildes) +{ + struct m_inode * inode; + struct file * f[2]; /* 文件结构数组 */ + int fd[2]; /* 文件句柄数组 */ + int i, j; + + /* 从系统文件表中取出两个空闲项 */ + j = 0; + for(i = 0; j < 2 && i < NR_FILE; i ++) { + if (!file_table[i].f_count) { + (f[j++] = i + file_table)->f_count++; + } + } + if (j == 1) { + f[0]->f_count = 0; + } + if (j < 2) { + return -1; + } + /* 在当前进程的文件结构指针数组中分配两个文件句柄,用于上面取出的文件表结构项 */ + j = 0; + for(i = 0; j < 2 && i < NR_OPEN; i ++) { + if (!current->filp[i]) { + current->filp[ fd[j] = i ] = f[j]; + j++; + } + } + if (j == 1) { + current->filp[fd[0]] = NULL; + } + if (j < 2) { + f[0]->f_count = f[1]->f_count = 0; + return -1; + } + /* 申请一个管道使用的i节点 */ + if (!(inode = get_pipe_inode())) { + current->filp[fd[0]] = current->filp[fd[1]] = NULL; + f[0]->f_count = f[1]->f_count = 0; + return -1; + } + /* 初始化 */ + f[0]->f_inode = f[1]->f_inode = inode; + f[0]->f_pos = f[1]->f_pos = 0; + f[0]->f_mode = 1; /* read */ + f[1]->f_mode = 2; /* write */ + + put_fs_long(fd[0], 0+fildes); + put_fs_long(fd[1], 1+fildes); + return 0; +} + +/** + * 管道io控制函数 + * @param[in] pino 管道i节点指针 + * @param[in] cmd 控制命令 + * @param[in] arg 参数 + * @retval 返回0表示执行成功,否则返回出错码 + */ +int pipe_ioctl(struct m_inode *pino, int cmd, int arg) +{ + switch (cmd) { + /* 取管道中当前可读数据的长度 */ + case FIONREAD: + verify_area((void *) arg, 4); + put_fs_long(PIPE_SIZE(*pino), (unsigned long *) arg); + return 0; + default: + return -EINVAL; + } +} diff --git a/linux-0.12/fs/read_write.c b/linux-0.12/fs/read_write.c new file mode 100755 index 0000000..c76c4ea --- /dev/null +++ b/linux-0.12/fs/read_write.c @@ -0,0 +1,156 @@ +/* + * linux/fs/read_write.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include + +#include +#include +#include + +#include /* import SEEK_SET,SEEK_CUR,SEEK_END */ + +extern int rw_char(int rw,int dev, char * buf, int count, off_t * pos); +extern int read_pipe(struct m_inode * inode, char * buf, int count); +extern int write_pipe(struct m_inode * inode, char * buf, int count); +extern int block_read(int dev, off_t * pos, char * buf, int count); +extern int block_write(int dev, off_t * pos, char * buf, int count); +extern int file_read(struct m_inode * inode, struct file * filp, char * buf, int count); +extern int file_write(struct m_inode * inode, struct file * filp, char * buf, int count); + +/** + * 重定位文件读写指针 系统调用 + * @param[in] fd 文件句柄 + * @param[in] offset 文件读写指针偏移值 + * @param[in] origin 偏移的起始位置,可有三种选择:SEEK_SET、SEEK_CUR、SEEK_END + * @retval 成功返回读写偏移值,失败返回失败码 + */ +int sys_lseek(unsigned int fd, off_t offset, int origin) +{ + struct file * file; + int tmp; + + if (fd >= NR_OPEN || !(file = current->filp[fd]) || !(file->f_inode) + || !IS_SEEKABLE(MAJOR(file->f_inode->i_dev))) { + return -EBADF; + } + if (file->f_inode->i_pipe) { /* 管道不能操作读写指针 */ + return -ESPIPE; + } + /* SEEK_CUR,SEEK_END分支中对相加值判断,既可过滤offset为负数且绝对值比文件长度大的情况, + 又可以过滤相加超过文件所能支持的最大值(off_t数据类型溢出的情况) */ + switch (origin) { + case SEEK_SET: /* 从文件开始处 */ + if (offset < 0) { + return -EINVAL; + } + file->f_pos = offset; + break; + case SEEK_CUR: /* 从当前读写位置 */ + if (file->f_pos + offset < 0) { + return -EINVAL; + } + file->f_pos += offset; + break; + case SEEK_END: /* 从文件尾处 */ + if ((tmp = file->f_inode->i_size + offset) < 0) { + return -EINVAL; + } + file->f_pos = tmp; + break; + default: + return -EINVAL; + } + return file->f_pos; +} + +/* TODO: 为什么只对读写管道操作判断是否有权限? */ + +/** + * 读文件 系统调用 + * @param[in] fd 文件句柄 + * @param[in] buf 缓冲区 + * @param[in] count 欲读字节数 + * @retval 成功返回读取的长度,失败返回错误码 + */ +int sys_read(unsigned int fd, char * buf, int count) +{ + struct file * file; + struct m_inode * inode; + + if (fd >= NR_OPEN || count < 0 || !(file = current->filp[fd])) { + return -EINVAL; + } + if (!count) { + return 0; + } + verify_area(buf, count); /* 验证存放数据的缓冲区内存限制 */ + /* 根据文件类型执行相应的读操作 */ + inode = file->f_inode; + if (inode->i_pipe) { /* 管道文件 */ + return (file->f_mode & 1) ? read_pipe(inode, buf, count) : -EIO; + } + if (S_ISCHR(inode->i_mode)) { /* 字符设备 */ + return rw_char(READ, inode->i_zone[0], buf, count, &file->f_pos); + } + if (S_ISBLK(inode->i_mode)) { /* 块设备 */ + return block_read(inode->i_zone[0], &file->f_pos, buf, count); + } + /* 目录文件或常规文件 */ + if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) { + if (count+file->f_pos > inode->i_size) { + count = inode->i_size - file->f_pos; + } + if (count <= 0) { + return 0; + } + return file_read(inode, file, buf, count); + } + /* 如果执行到这,说明无法判断文件类型 */ + printk("(Read)inode->i_mode=%06o\n\r", inode->i_mode); + return -EINVAL; +} + + +/** + * 写文件 系统调用 + * @param[in] fd 文件句柄 + * @param[in] buf 用户缓冲区 + * @param[in] count 欲写字节数 + * @retval 成功返回写入的长度,失败返回错误码 + */ +int sys_write(unsigned int fd, char * buf, int count) +{ + struct file * file; + struct m_inode * inode; + + if (fd >= NR_OPEN || count < 0 || !(file = current->filp[fd])) { + return -EINVAL; + } + if (!count) { + return 0; + } + + /* 根据文件类型执行相应的写操作 */ + inode = file->f_inode; + if (inode->i_pipe) { /* 管道 */ + /* file->f_mode & 2 即是否有写的权限 */ + return (file->f_mode & 2) ? write_pipe(inode, buf, count) : -EIO; + } + if (S_ISCHR(inode->i_mode)) { /* 字符设备 */ + return rw_char(WRITE, inode->i_zone[0], buf, count, &file->f_pos); + } + if (S_ISBLK(inode->i_mode)) { /* 块设备 */ + return block_write(inode->i_zone[0], &file->f_pos, buf, count); + } + if (S_ISREG(inode->i_mode)) { /* 文件 */ + return file_write(inode, file, buf, count); + } + /* 如果执行到这,说明无法判断文件类型 */ + printk("(Write)inode->i_mode=%06o\n\r", inode->i_mode); + return -EINVAL; +} diff --git a/linux-0.12/fs/select.c b/linux-0.12/fs/select.c new file mode 100755 index 0000000..be44461 --- /dev/null +++ b/linux-0.12/fs/select.c @@ -0,0 +1,393 @@ +/* + * This file contains the procedures for the handling of select + * + * Created for Linux based loosely upon Mathius Lattner's minix + * patches by Peter MacDonald. Heavily edited by Linus. + */ +/* + * 本文件含有处理select()系统调用的过程。 + * + * 这是Peter MacDonald基于Mathius Lattner提供给MINIX系统的补丁程序修改而成。 + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Ok, Peter made a complicated, but straightforward multiple_wait() function. + * I have rewritten this, taking some shortcuts: This code may not be easy to + * follow, but it should be free of race-conditions, and it's practical. If you + * understand what I'm doing here, then you understand how the linux sleep/wakeup + * mechanism works. + * + * Two very simple procedures, add_wait() and free_wait() make all the work. We + * have to have interrupts disabled throughout the select, but that's not really + * such a loss: sleeping automatically frees interrupts when we aren't in this + * task. + */ +/* + * OK,Peter编制了复杂但很直观的多个_wait()函数。我对这些函数进行了改写,以使之更简洁:这些代码 + * 可能不容易看懂,但是其中应该不会存在竞争条件问题,并且很实际。如果你能理解这里编制的代码,那么 + * 就说明你已经理解Linux中睡眠/唤醒的工作机制。 + * + * 两个很简单的过程,add_wait()和free_wait()执行了主要操作。在整个select处理过程中我们不得不禁 + * 止中断。但是这样做并不会带来太多的损失:因为当我们不在执行本任务时睡眠状态会自动释放中断(即其他 + * 任务会使用自己EFLAGS中的中断标志)。 + */ + +typedef struct { + struct task_struct * old_task; + struct task_struct ** wait_address; +} wait_entry; + +typedef struct { + int nr; + wait_entry entry[NR_OPEN * 3]; +} select_table; + +/** + * 把未准备好描述符的等待队列指针加入等待表wait_table中 + * @param[in] *wait_address 与描述符相关的等待队列头指针。如tty读缓冲队列secondary的 + * 等待队列头指针是proc_list + * @param[in] p do_select()中定义的等待表结构指针 + * @retval void + */ +static void add_wait(struct task_struct ** wait_address, select_table * p) +{ + int i; + + if (!wait_address) { + return; + } + for (i = 0; i < p->nr; i++) { + if (p->entry[i].wait_address == wait_address) { + return; + } + } + p->entry[p->nr].wait_address = wait_address; + p->entry[p->nr].old_task = * wait_address; + *wait_address = current; + p->nr ++; +} + + +/** + * 清空等待表 + * 本函数在do_select()函数中睡眠后被唤醒返回时被调用,用于唤醒等待表中处于各个等待队列上的其他 + * 任务,它与kernel/sched.c中sleep_on()函数的后半部分代码几乎完全相同,请参考对sleep_on()函数的 + * 说明。 + * @param[in] p 等待表结构指针 + * @return void + */ +static void free_wait(select_table * p) +{ + int i; + struct task_struct ** tpp; + + for (i = 0; i < p->nr ; i++) { + tpp = p->entry[i].wait_address; + while (*tpp && *tpp != current) { + (*tpp)->state = 0; + current->state = TASK_UNINTERRUPTIBLE; + schedule(); + } + if (!*tpp) { + printk("free_wait: NULL"); + } + if ((*tpp = p->entry[i].old_task)) { + (**tpp).state = 0; + } + } + p->nr = 0; +} + + +/** + * 根据文件i节点判断文件是不是字符终端设备文件 + * @param[in] inode i节点 + * @retval 若是则返回其tty结构指针,否则返回NULL + */ +static struct tty_struct * get_tty(struct m_inode * inode) +{ + int major, minor; + + if (!S_ISCHR(inode->i_mode)) { + return NULL; + } + if ((major = MAJOR(inode->i_zone[0])) != 5 && major != 4) { + return NULL; + } + if (major == 5) { + minor = current->tty; + } else { + minor = MINOR(inode->i_zone[0]); + } + if (minor < 0) { + return NULL; + } + return TTY_TABLE(minor); +} + +/* + * The check_XX functions check out a file. We know it's either + * a pipe, a character device or a fifo (fifo's not implemented) + */ +/* + * check_XX函数用于检查一个文件。我们知道该文件要么是管道文件、要么是字符设备文件,或者要么是 + * 一个FIFO(FIFO)还未实现。 + */ + +/** + * 检查读文件操作是否准备好,即终端读缓冲队列secondary是否有字符可读,或者管道文件是否不空。 + * @param[in] wait 等待表指针 + * @param[in] inode 文件i节点指针 + * @retval 若描述符可进行读操作则返回1,否则返回0 + */ +static int check_in(select_table * wait, struct m_inode * inode) +{ + struct tty_struct * tty; + + if ((tty = get_tty(inode))) { + if (!EMPTY(tty->secondary)) { + return 1; + } else { + add_wait(&tty->secondary->proc_list, wait); + } + } else if (inode->i_pipe) { + if (!PIPE_EMPTY(*inode)) { + return 1; + } else { + add_wait(&inode->i_wait, wait); + } + } + return 0; +} + +/** + * 检查文件写操作是否准备好,即终端写缓冲队列write_q中是否还有空闲位置可写,或者此时管道文件 + * 是否不满。 + * @param[in] wait 等待表指针 + * @param[in] inode 文件i节点指针 + * @retval 若描述符可进行写操作则返回1,否则返回0 + */ +static int check_out(select_table * wait, struct m_inode * inode) +{ + struct tty_struct * tty; + + if ((tty = get_tty(inode))) { + if (!FULL(tty->write_q)) { + return 1; + } else { + add_wait(&tty->write_q->proc_list, wait); + } + } else if (inode->i_pipe) { + if (!PIPE_FULL(*inode)) { + return 1; + } else { + add_wait(&inode->i_wait, wait); + } + } + return 0; +} + + +/** + * 检查文件是否处于异常状态。对于终端设备文件,目前内核总是返回0。对于管道文件,如果此时两个管 + * 道描述符中有一个或都已被关闭,则返回1,否则就把当前任务添加到管道i节点的等待队列上并返回0。 + * 返回0。 + * @param[in] wait 等待表指针 + * @param[in] inode 文件i节点指针 + * 若出现异常条件则返回1,否则返回0 + */ +static int check_ex(select_table * wait, struct m_inode * inode) +{ + struct tty_struct * tty; + + if ((tty = get_tty(inode))) { + if (!FULL(tty->write_q)) { + return 0; + } else { + return 0; + } + } else if (inode->i_pipe) { + if (inode->i_count < 2) { + return 1; + } else { + add_wait(&inode->i_wait, wait); + } + } + return 0; +} + + +/** + * do_select()是内核执行select()系统调用的实际处理函数。该函数首先检查描述符集中各个描述符的有 + * 效性,然后分别调用相关描述符集描述符检查函数check_XX()对每个描述符进行检查,同时统计描述符 + * 集中当前已经准备好的描述符个数。若有任何一个描述符已经准备好,本函数就会立刻返回,否则进程 + * 就会在本函数中进入睡眠状态,并在过了超时时间或者由于某个描述符所在等待队列上的进程被唤醒而 + * 使本进程继续运行。 + */ +int do_select(fd_set in, fd_set out, fd_set ex, + fd_set *inp, fd_set *outp, fd_set *exp) +{ + int count; + select_table wait_table; + int i; + fd_set mask; + + mask = in | out | ex; + for (i = 0 ; i < NR_OPEN ; i++,mask >>= 1) { + if (!(mask & 1)) { + continue; + } + if (!current->filp[i]) { + return -EBADF; + } + if (!current->filp[i]->f_inode) { + return -EBADF; + } + if (current->filp[i]->f_inode->i_pipe) { + continue; + } + if (S_ISCHR(current->filp[i]->f_inode->i_mode)) { + continue; + } + if (S_ISFIFO(current->filp[i]->f_inode->i_mode)) { + continue; + } + return -EBADF; + } +repeat: + wait_table.nr = 0; + *inp = *outp = *exp = 0; + count = 0; + mask = 1; + for (i = 0 ; i < NR_OPEN ; i++, mask += mask) { + if (mask & in) + if (check_in(&wait_table,current->filp[i]->f_inode)) { + *inp |= mask; + count++; + } + if (mask & out) + if (check_out(&wait_table,current->filp[i]->f_inode)) { + *outp |= mask; + count++; + } + if (mask & ex) + if (check_ex(&wait_table,current->filp[i]->f_inode)) { + *exp |= mask; + count++; + } + } + if (!(current->signal & ~current->blocked) && + (wait_table.nr || current->timeout) && !count) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + free_wait(&wait_table); + goto repeat; + } + free_wait(&wait_table); + return count; +} + +/* + * Note that we cannot return -ERESTARTSYS, as we change our input + * parameters. Sad, but there you are. We could do some tweaking in + * the library function ... + */ +/* + * 注意我们不能返回-ERESTARTSYS,因为我们会在select运行过程中改变输入参数值(*timeout)。很不幸, + * 但你也只能接受这个事实。不过我们可以在库函数中做些处理... + */ +/** + * select 系统调用 + * 该函数中的代码主要负责进行select功能操作前后的参数复制和转换工作。select主要的工作由 + * do_select()函数来完成。sys_select()会首先根据参数传递来的缓冲区指针从用户数据空间把select() + * 函数调用的参数分解复制到内核空间,然后设置需要等待的超时时间值timeout,接着调用do_select()执 + * 行select功能,返回后就把处理结果再复制回用户空间中。 + * @param[in] buffer 指向用户数据区中select()函数的第1个参数处 + * @retval 如果返回值小于0,表示执行时出现错误; + * 如果返回值等于0,表示在规定等待时间内没有描述符准备好操作; + * 如果返回值大于0,则表示已准备好的描述符数量。 + */ +int sys_select( unsigned long *buffer ) +{ +/* Perform the select(nd, in, out, ex, tv) system call. */ + int i; + fd_set res_in, in = 0, *inp; + fd_set res_out, out = 0, *outp; + fd_set res_ex, ex = 0, *exp; + fd_set mask; + struct timeval *tvp; + unsigned long timeout; + + mask = ~((~0) << get_fs_long(buffer++)); + inp = (fd_set *) get_fs_long(buffer++); + outp = (fd_set *) get_fs_long(buffer++); + exp = (fd_set *) get_fs_long(buffer++); + tvp = (struct timeval *) get_fs_long(buffer); + + if (inp) { + in = mask & get_fs_long(inp); + } + if (outp) { + out = mask & get_fs_long(outp); + } + if (exp) { + ex = mask & get_fs_long(exp); + } + timeout = 0xffffffff; + if (tvp) { + timeout = get_fs_long((unsigned long *)&tvp->tv_usec) / (1000000 / HZ); + timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ; + timeout += jiffies; + } + current->timeout = timeout; + cli(); + i = do_select(in, out, ex, &res_in, &res_out, &res_ex); + if (current->timeout > jiffies) { + timeout = current->timeout - jiffies; + } else { + timeout = 0; + } + sti(); + current->timeout = 0; + if (i < 0) + return i; + if (inp) { + verify_area(inp, 4); + put_fs_long(res_in, inp); + } + if (outp) { + verify_area(outp, 4); + put_fs_long(res_out, outp); + } + if (exp) { + verify_area(exp, 4); + put_fs_long(res_ex, exp); + } + if (tvp) { + verify_area(tvp, sizeof(*tvp)); + put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec); + timeout %= HZ; + timeout *= (1000000/HZ); + put_fs_long(timeout, (unsigned long *) &tvp->tv_usec); + } + if (!i && (current->signal & ~current->blocked)) { + return -EINTR; + } + return i; +} diff --git a/linux-0.12/fs/stat.c b/linux-0.12/fs/stat.c new file mode 100755 index 0000000..79806a6 --- /dev/null +++ b/linux-0.12/fs/stat.c @@ -0,0 +1,145 @@ +/* + * linux/fs/stat.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include +#include + +/** + * 复制文件状态信息 + * @param[in] inode 文件i节点 + * @param[in/out] statbuf 用户数据空间中stat文件状态结构指针,用于存放取得的状态信息 + * @retval void + */ +static void cp_stat(struct m_inode * inode, struct stat * statbuf) +{ + struct stat tmp; + int i; + + verify_area(statbuf, sizeof (struct stat)); + tmp.st_dev = inode->i_dev; + tmp.st_ino = inode->i_num; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlinks; + tmp.st_uid = inode->i_uid; + tmp.st_gid = inode->i_gid; + tmp.st_rdev = inode->i_zone[0]; /* 特殊文件的设备号 */ + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + + for (i = 0; i < sizeof(tmp); i++) { + put_fs_byte(((char *) &tmp)[i], i + (char *) statbuf); + } +} + + +/** + * 获取文件状态 + * 根据给定的文件名获取相关文件状态信息。 + * @param[in] filename 指定的文件名 + * @param[in/out] statbuf 存放状态信息的缓冲区指针 + * @retval 成功返回0,出错返回出错码 + */ +int sys_stat(char * filename, struct stat * statbuf) +{ + struct m_inode * inode; + + if (!(inode = namei(filename))) { + return -ENOENT; + } + cp_stat(inode, statbuf); + iput(inode); + return 0; +} + + +/** + * 获取文件状态 系统调用 + * 根据给定的文件名获取相关文件状态信息。文件路径名中有符号链接文件名,则取符号文件的状态。 + * @param[in] filename 指定的文件名 + * @param[in/out] statbuf 存放状态信息的缓冲区指针 + * @retval 成功返回0,出错返回出错码 + */ +int sys_lstat(char * filename, struct stat * statbuf) +{ + struct m_inode * inode; + + if (!(inode = lnamei(filename))) { + return -ENOENT; + } + cp_stat(inode,statbuf); + iput(inode); + return 0; +} + +/** + * 获取文件状态 + * 根据给定的文件名获取相关文件状态信息。 + * @param[in] fd 指定文件的句柄 + * @param[in/out] statbuf 存放状态信息的缓冲区指针 + * @retval 成功返回0,出错返回出错码 + */ +int sys_fstat(unsigned int fd, struct stat * statbuf) +{ + struct file * f; + struct m_inode * inode; + + if (fd >= NR_OPEN || !(f = current->filp[fd]) || !(inode = f->f_inode)) { + return -EBADF; + } + cp_stat(inode,statbuf); + return 0; +} + +/** + * 符号链接文件 系统调用 + * 该调用读取符号链接文件的内容(即该符号链接所指向文件的路径名字符串),并放到指定长度的用户缓 + * 冲区中。若缓冲区太小,就会截断符号链接的内容。 + * @param[in] path 符号链接文件路径名 + * @param[in/out] buf 用户缓冲区 + * @param[in] bufsiz 缓冲区长度 + * @retval 成功则返回放入缓冲区中的字符数;若失败则返回出错码 +*/ +int sys_readlink(const char * path, char * buf, int bufsiz) +{ + struct m_inode * inode; + struct buffer_head * bh; + int i; + char c; + + if (bufsiz <= 0) { + return -EBADF; + } + if (bufsiz > 1023) { + bufsiz = 1023; + } + verify_area(buf, bufsiz); + if (!(inode = lnamei(path))) { + return -ENOENT; + } + if (inode->i_zone[0]) { + bh = bread(inode->i_dev, inode->i_zone[0]); + } else { + bh = NULL; + } + iput(inode); + if (!bh) { + return 0; + } + i = 0; + while (i < bufsiz && (c = bh->b_data[i])) { + i++; + put_fs_byte(c,buf++); + } + brelse(bh); + return i; +} diff --git a/linux-0.12/fs/super.c b/linux-0.12/fs/super.c new file mode 100755 index 0000000..94064a0 --- /dev/null +++ b/linux-0.12/fs/super.c @@ -0,0 +1,424 @@ +/* + * linux/fs/super.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * super.c contains code to handle the super-block tables. + */ +/* + * super.c 程序中含有处理超级块表的代码。 + */ +#include +#include +#include +#include + +#include +#include + +int sync_dev(int dev); /* 同步高速缓冲到设备上 */ +void wait_for_keypress(void); /* 等待击键 */ + +/* set_bit uses setb, as gas doesn't recognize setc */ +/* set_bit()使用了setb指令,因为汇编编译器gas不能识别指令setc */ + +/** + * 测试指定位偏移处的位的值,并返回该原位值 + * 指令bt用于对位进行测试(Bit Test)。它会把地址addr(%3)和位偏移量bitnr(%2)指定的位的值放入 + * 进位标志CF中。指令setb用于根据进位标志CF设置操作数%al。如果CF=1,则%al=1,否则%al=0。 + * @param[in] bitnr 位偏移值 + * @param[in] addr 测试位操作的起始地址 + * @retval 原位值 + */ +#define set_bit(bitnr, addr) ({ \ + register int __res; \ + __asm__("bt %2, %3; setb %%al" \ + :"=a" (__res) \ + :"a" (0),"r" (bitnr),"m" (*(addr))); \ + __res; }) + +struct super_block super_block[NR_SUPER]; /* 超级块结构表数组 */ +/* this is initialized in init/main.c */ /* ROOT_DEV已在init/main.c中被初始化 */ +int ROOT_DEV = 0; /* 根文件系统设备号 */ + +/* 以下3个函数(lock_super(),free_super()和wait_on_super())的作用与inode.c文件中头3个函 + 数的作用相同,只是这里操作的对象换成了超级块 */ + +/** + * 锁定超级块 + * @param[in] sb 超级块指针 + * @retval void + */ +static void lock_super(struct super_block *sb) +{ + cli(); + while (sb->s_lock) { + sleep_on(&(sb->s_wait)); + } + sb->s_lock = 1; + sti(); +} + +/** + * 解锁指定超级块 + * @param[in] sb 超级块指针 + * @retval void + */ +static void free_super(struct super_block * sb) +{ + cli(); + sb->s_lock = 0; + wake_up(&(sb->s_wait)); + sti(); +} + +/** + * 睡眠等待超级块解锁 + * @param[in] sb 超级块指针 + * @retval void + */ +static void wait_on_super(struct super_block * sb) +{ + cli(); + while (sb->s_lock) { + sleep_on(&(sb->s_wait)); + } + sti(); +} + +/** + * 取指定设备的超级块 + * 在超级块表(数组)中搜索指定设备dev的超级块结构信息。若找到则返回超级块的指针,否则返回空指针。 + * @param[in] dev 设备号 + * @retval 超级块指针 + */ +struct super_block * get_super(int dev) +{ + struct super_block *s; + /* 若设备号为0,则返回空指针 */ + if (!dev) { + return NULL; + } + s = 0 + super_block; + while (s < NR_SUPER + super_block) { + + if (s->s_dev == dev) { + wait_on_super(s); + /* 在等待期间,该超级块项有可能被其他设备使用,因此等待返回后需再判断一次 */ + if (s->s_dev == dev) { + return s; + } + s = 0 + super_block; + } else { + s++; + } + } + return NULL; +} + +/** + * 释放指定设备的超级块 + * 释放设备所使用的超级块数组项(置s_dev = 0),并释放该设备i节点位图和逻辑块位图所占用的高速缓 + * 冲块。如果超级块对应的文件系统是根文件系统,或者其某个i节点上已经安装了其他的文件系统,则不能 + * 释放该超级块。 + * @param[in] dev 设备号 + * @retval void + */ +void put_super(int dev) +{ + struct super_block * sb; + int i; + /* 根文件系统设备的超级块不能被释放 */ + if (dev == ROOT_DEV) { + printk("root diskette changed: prepare for armageddon\n\r"); + return; + } + if (!(sb = get_super(dev))) { + return; + } + /* 超级块对应的文件系统的某个i节点上已经安装了其他的文件系统,则不能释放 */ + if (sb->s_imount) { + printk("Mounted disk changed - tssk, tssk\n\r"); + return; + } + lock_super(sb); + sb->s_dev = 0; /* 置超级块空闲 */ + /* 释放该设备上文件系统i节点位图和逻辑位图在缓冲区中所占用的缓冲块 */ + for(i = 0; i < I_MAP_SLOTS; i++) { + brelse(sb->s_imap[i]); + } + for(i = 0; i < Z_MAP_SLOTS; i++) { + brelse(sb->s_zmap[i]); + } + free_super(sb); + return; +} + +/** + * 读取指定设备的超级块 + * 如果指定设备dev上的文件系统超级块已经在超级块表中,则直接返回该超级块项的指针。否则就从设备 + * dev上读取超级块到缓冲块中,并复制到超级块表中,并返回超级块指针。 + * @param[in] dev 设备号 + * @retval 超级块指针 + */ +static struct super_block * read_super(int dev) +{ + struct super_block * s; + struct buffer_head * bh; + int i, block; + + if (!dev) { + return NULL; + } + /* 检查软盘是否更换 */ + check_disk_change(dev); + + if ((s = get_super(dev))) { + return s; + } + /* 在超级块数组中找到空项用于要读取的超级块 */ + for (s = 0 + super_block ;; s++) { + if (s >= NR_SUPER + super_block) { + return NULL; + } + if (!s->s_dev) { + break; + } + } + s->s_dev = dev; /* 用于dev设备上的文件系统 */ + s->s_isup = NULL; + s->s_imount = NULL; + s->s_time = 0; + s->s_rd_only = 0; + s->s_dirt = 0; + /* 从设备上读取超级块信息到bh指向的缓冲块中,再从缓冲块复制到超级块数组中 */ + lock_super(s); + if (!(bh = bread(dev, 1))) { + s->s_dev = 0; + free_super(s); + return NULL; + } + *((struct d_super_block *) s) = *((struct d_super_block *) bh->b_data); + brelse(bh); + + if (s->s_magic != SUPER_MAGIC) { /* linux0.12只支持MINIX文件系统1.0,魔数为0x137f */ + s->s_dev = 0; + free_super(s); + return NULL; + } + /* 读取设备上i节点位图和逻辑块位图数据 */ + for (i = 0; i < I_MAP_SLOTS; i++) { /* 初始化i节点位图和逻辑块位图 */ + s->s_imap[i] = NULL; + } + for (i = 0; i < Z_MAP_SLOTS; i++) { + s->s_zmap[i] = NULL; + } + block = 2; /* 0为引导块,1为超级块,2~x为i节点位图,(x+1)~y为逻辑块位图 */ + for (i = 0 ; i < s->s_imap_blocks ; i++) { /* 读取设备中i节点位图 */ + if ((s->s_imap[i] = bread(dev, block))) { + block++; + } else { + break; + } + } + for (i = 0 ; i < s->s_zmap_blocks ; i++) { /* 读取设备中逻辑块位图 */ + if ((s->s_zmap[i] = bread(dev, block))) { + block++; + } else { + break; + } + } + /* 如果读出的位图个数不等于位图应该占有的逻辑块数,说明文件系统位图信息有问题,超级块初始 + 化失败,则释放所有资源 */ + if (block != 2 + s->s_imap_blocks + s->s_zmap_blocks) { + for(i = 0; i < I_MAP_SLOTS; i++) { + brelse(s->s_imap[i]); + } + for(i = 0; i < Z_MAP_SLOTS; i++) { + brelse(s->s_zmap[i]); + } + s->s_dev = 0; /* 释放选定的超级块数组项 */ + free_super(s); + return NULL; + } + /* 0号i节点和0号逻辑块不可用 */ + s->s_imap[0]->b_data[0] |= 1; + s->s_zmap[0]->b_data[0] |= 1; + free_super(s); + return s; +} + +/** + * 卸载文件系统 + * @param[in] dev_name 文件系统所在设备的设备文件名 + * @retval 成功返回0,失败返回出错码 + */ +int sys_umount(char * dev_name) +{ + struct m_inode * inode; + struct super_block * sb; + int dev; + + if (!(inode = namei(dev_name))) { + return -ENOENT; + } + dev = inode->i_zone[0]; /* 对于设备文件,i_zone[0]存有设备号 */ + if (!S_ISBLK(inode->i_mode)) { /* 文件系统应该在块设备上 */ + iput(inode); + return -ENOTBLK; + } + iput(inode); + if (dev == ROOT_DEV) { /* 根文件系统不能被卸载 */ + return -EBUSY; + } + /* 在超级块表中没有找到该设备的超级块,或者已找到但是该设备上文件系统没有安装过 */ + if (!(sb = get_super(dev)) || !(sb->s_imount)) { + return -ENOENT; + } + /* 如果超级块所指明被安装到的i节点并没有置位其安装标志i_mount,则显示警告信息 */ + if (!sb->s_imount->i_mount) { + printk("Mounted inode has i_mount=0\n"); + } + for (inode = inode_table + 0 ; inode < inode_table + NR_INODE ; inode++) { + /* 有进程在使用该设备上的文件,则返回忙出错码 */ + if (inode->i_dev == dev && inode->i_count) { + return -EBUSY; + } + } + /* 开始卸载操作 */ + sb->s_imount->i_mount = 0; /* 复位被安装到的i节点的安装标志,释放该i节点 */ + iput(sb->s_imount); + sb->s_imount = NULL; + iput(sb->s_isup); /* 设备文件系统的根i节点,接着置超级块中被安装系统根i节点指针为空 */ + sb->s_isup = NULL; + /* 释放该设备上的超级块以及位图占用的高速缓冲块,同步高速缓冲到设备 */ + put_super(dev); + sync_dev(dev); + return 0; +} + +/** + * 安装文件系统 + * @param[in] dev_name 设备文件名 + * @param[in] dir_name 安装到的目录名 + * @param[in] rw_flag 被安装文件系统的可读写标志 + * @retval 成功返回0,失败返回出错号 + */ +int sys_mount(char *dev_name, char *dir_name, int rw_flag) +{ + struct m_inode *dev_i, *dir_i; + struct super_block * sb; + int dev; + + /* 检查设备名是否有效 */ + if (!(dev_i = namei(dev_name))) { + return -ENOENT; + } + dev = dev_i->i_zone[0]; /* 对于设备文件,i_zone[0]存有设备号 */ + if (!S_ISBLK(dev_i->i_mode)) { /* 文件系统应该在块设备上 */ + iput(dev_i); + return -EPERM; + } + iput(dev_i); + + /* 检查一下文件系统安装到的目录名是否有效 */ + if (!(dir_i = namei(dir_name))) { + return -ENOENT; + } + /* 如果该i节点的引用计数不为1(仅在这里引用),或者该i节点的节点号是根文件系统的节点号 */ + if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) { + iput(dir_i); + return -EBUSY; + } + if (!S_ISDIR(dir_i->i_mode)) { /* 安装点需要是一个目录名 */ + iput(dir_i); + return -EPERM; + } + /* 读取要安装文件系统的超级块信息 */ + if (!(sb = read_super(dev))) { + iput(dir_i); + return -EBUSY; + } + if (sb->s_imount) { /* 被安装的文件系统已经安装在其他地方 */ + iput(dir_i); + return -EBUSY; + } + if (dir_i->i_mount) { /* 将要安装到的i节点已经安装了文件系统 */ + iput(dir_i); + return -EPERM; + } + /* 设置被安装文件系统超级块的“被安装到i节点”字段指向安装到的目录名的i节点 */ + sb->s_imount = dir_i; + dir_i->i_mount = 1; + dir_i->i_dirt = 1; /* NOTE! we don't iput(dir_i) */ + /* 注意!这里没有用iput(dir_i) */ + return 0; /* we do that in umount */ + /* 这将在umount内操作 */ +} + +/** + * 安装根文件系统 + * 函数首先初始化文件表数组file_table[]和超级块表(数组),然后读取根文件系统超级块,并取得文 + * 件系统根i节点。最后统计并显示出根文件系统上的可用资源(空闲块数和空闲i节点数0。该函数会在系 + * 统开机进行初始化设置时(sys_setup())调用(blk_drv/hd.c) + * @retval void + */ +void mount_root(void) +{ + int i, free; + struct super_block * p; + struct m_inode * mi; + + /* 若磁盘i节点结构不是32字节,则出错停机 */ + if (32 != sizeof (struct d_inode)) { + panic("bad i-node size"); + } + /* 初始化系统中的文件表数组 */ + for(i = 0; i < NR_FILE; i++) { + file_table[i].f_count = 0; + } + if (MAJOR(ROOT_DEV) == 2) { /* 根文件系统所在设备是软盘,提示插入根文件系统盘 */ + printk("Insert root floppy and press ENTER\n\r"); + wait_for_keypress(); + } + /* 初始化超级块表 */ + for(p = &super_block[0]; p < &super_block[NR_SUPER]; p++) { + p->s_dev = 0; + p->s_lock = 0; + p->s_wait = NULL; + } + /* 做好以上"份外"的初始化工作之后,我们开始安装根文件系统 */ + /* 从根设备上读取文件系统超级块,并取得文件系统的根i节点(1号节点)在内存i节点表中的指针 */ + if (!(p = read_super(ROOT_DEV))) { + panic("Unable to mount root"); + } + if (!(mi = iget(ROOT_DEV, ROOT_INO))) { /* 在fs.h中ROOT_INO定义为1 */ + panic("Unable to read root i-node"); + } + mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ + /* 注意!从逻辑上讲,它已被引用了4次,而不是1次 */ + p->s_isup = p->s_imount = mi; /* 置被安装文件系统i节点和被安装到i节点字段为该i节点 */ + current->pwd = mi; /* 设置当前进程的当前工作目录和根目录i节点 */ + current->root = mi; + /* 对根文件系统上的资源作统计工作 */ + /* 统计该设备上空闲块数 */ + free = 0; + i = p->s_nzones; + while (-- i >= 0) { + if (!set_bit(i & 8191, p->s_zmap[i >> 13]->b_data)) { + free++; + } + } + printk("%d/%d free blocks\n\r", free, p->s_nzones); + /* 统计设备上空闲i节点数 */ + free = 0; + i = p->s_ninodes + 1; + while (-- i >= 0) { + if (!set_bit(i & 8191, p->s_imap[i >> 13]->b_data)) { + free++; + } + } + printk("%d/%d free inodes\n\r", free, p->s_ninodes); +} diff --git a/linux-0.12/fs/truncate.c b/linux-0.12/fs/truncate.c new file mode 100755 index 0000000..7570673 --- /dev/null +++ b/linux-0.12/fs/truncate.c @@ -0,0 +1,142 @@ +/* + * linux/fs/truncate.c + * + * (C) 1991 Linus Torvalds + */ +#include +#include + +/** + * 释放所有一次间接块 + * @param[in] dev 文件系统所有设备的设备号 + * @param[in] block 逻辑块号 + * @retval 成功返回1,失败返回0 + */ +static int free_ind(int dev, int block) +{ + struct buffer_head * bh; + unsigned short * p; + int i; + int block_busy; + + /* 如果逻辑块号为0,则返回 */ + if (!block) { + return 1; + } + block_busy = 0; + /* 读取一次间接块,并释放其上表明使用的所有逻辑块,然后释放该一次间接块的缓冲块 */ + if ((bh = bread(dev, block))) { + p = (unsigned short *) bh->b_data; /* 指向缓冲块数据区 */ + for (i = 0; i < 512; i++, p++) { /* 每个逻辑块上可有512个块号 */ + if (*p) { + if (free_block(dev, *p)) { /* 释放指定的设备逻辑块 */ + *p = 0; /* 清零 */ + bh->b_dirt = 1; /* 设置已修改标志 */ + } else { + block_busy = 1; /* 设置逻辑块没有释放标志 */ + } + } + } + brelse(bh); /* 然后释放间接块占用的缓冲块 */ + } + /* 最后释放设备上的一次间接块。但如果其中有逻辑块没有被释放,则返回0(失败) */ + if (block_busy) { + return 0; + } else { + return free_block(dev, block); /* 成功则返回1,否则返回0 */ + } +} + + +/** + * 释放所有二次间接块 + * @param[in] dev 文件系统所有设备的设备号 + * @param[in] block 逻辑块号 + * @retval 成功返回1,失败返回0 + */ +static int free_dind(int dev, int block) +{ + struct buffer_head * bh; + unsigned short * p; + int i; + int block_busy; + + if (!block) { + return 1; + } + block_busy = 0; + if ((bh = bread(dev, block))) { + p = (unsigned short *) bh->b_data; /* 指向缓冲块数据区 */ + for (i = 0; i < 512; i++, p++) { /* 每个逻辑块上可连接512个二级块 */ + if (*p) { + if (free_ind(dev, *p)) { /* 释放所有一次间接块 */ + *p = 0; /* 清零 */ + bh->b_dirt = 1; /* 设置已修改标志 */ + } else { + block_busy = 1; /* 设置逻辑块没有释放标志 */ + } + } + } + brelse(bh); /* 释放二次间接块占用的缓冲块 */ + } + /* 最后释放设备上的二次间接块。但如果其中有逻辑块没有被释放,则返回0(失败) */ + if (block_busy) { + return 0; + } else { + return free_block(dev, block); /* 最后释放存放第一间接块的逻辑块 */ + } +} + +/** + * 截断文件数据函数 + * 将节点对应的文件长度减0,并释放占用的设备空间 + * @param[in] inode + * @retval void + */ +void truncate(struct m_inode * inode) +{ + int i; + int block_busy; /* 有逻辑块没有被释放的标志 */ + + /* 如果不是常规文件、目录文件或链接项,则返回 */ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) { + return; + } + +repeat: + block_busy = 0; + /* 释放i节点的7个直接逻辑块 */ + for (i = 0; i < 7; i++) { + if (inode->i_zone[i]) { /* 如果块号不为0,则释放之 */ + if (free_block(inode->i_dev, inode->i_zone[i])) { + inode->i_zone[i] = 0; /* 块指针置0 */ + } else { + block_busy = 1; /* 若没有释放掉则置标志 */ + } + } + } + /* 释放所有一次间接块 */ + if (free_ind(inode->i_dev, inode->i_zone[7])) { + inode->i_zone[7] = 0; /* 块指针置0 */ + } else { + block_busy = 1; /* 若没有释放掉则置标志 */ + } + /* 释放所有二次间接块 */ + if (free_dind(inode->i_dev, inode->i_zone[8])) { + inode->i_zone[8] = 0; /* 块指针置0 */ + } else { + block_busy = 1; /* 若没有释放掉则置标志 */ + } + /* 设置i节点已修改标志,并且如果还有逻辑块由于“忙”而没有被释放,则把当前进程运行时间 + 片置0,以让当前进程先被切换去运行其他进程,稍等一会再重新执行释放操作 */ + inode->i_dirt = 1; + if (block_busy) { + current->counter = 0; /* 当前进程时间片置0 */ + schedule(); + goto repeat; + } + inode->i_size = 0; /* 文件大小置零 */ + inode->i_mtime = inode->i_ctime = CURRENT_TIME; +} + diff --git a/linux-0.12/include/a.out.h b/linux-0.12/include/a.out.h new file mode 100755 index 0000000..f33c39b --- /dev/null +++ b/linux-0.12/include/a.out.h @@ -0,0 +1,249 @@ +#ifndef _A_OUT_H +#define _A_OUT_H + +#define __GNU_EXEC_MACROS__ + +/* 文件头结构 */ +struct exec { + unsigned long a_magic;/* Use macros N_MAGIC, etc for access */ + /* 执行文件魔数 */ + unsigned a_text; /* length of text, in bytes */ + /* 代码长度 */ + unsigned a_data; /* length of data, in bytes */ + /* 数据长度 */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + /* 未初始化数据区的长度 */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + /* 符号表的长度 */ + unsigned a_entry; /* start address */ + /* 执行开始地址 */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + /* 代码重定位信息的长度 */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ + /* 数据重定位信息的长度 */ +}; + +/* 魔数:在很多类型的文件中,其起始的几个字节的内容是固定的,这几个字节的内容也被称为魔数(magic number)。 + 根据魔数就可以确定文件类型 */ +#ifndef N_MAGIC +#define N_MAGIC(exec) ((exec).a_magic) +#endif +/* OMAGIC和ZMAGIC的主要区别在于它们对各个部分的存储分配方式上。虽然该结构的总长度只有32B,但 + 是对于一个ZMAGIC类型的执行文件来说,其文件开始部分却需要专门留出1KB的空间给头结构使用。除被头 + 结构占用的32B以外,其余部分均为0。从1024字节之后才开始放置程序的正文段和数据段等信息。而对于一 + 个OMAGIC类型的.o模块文件来说,文件开始部分的32字节头结构后面紧接着就是代码区和数据区。 */ +#ifndef OMAGIC + +/* Code indicating object file or impure executable. */ +/* OMAGIC(Old Magic)指明文件是目标文件或者不纯的可执行文件 */ +#define OMAGIC 0407 + +/* Code indicating pure executable. */ +/* 文件为纯粹的可执行文件 */ +#define NMAGIC 0410 + +/* Code indicating demand-paged executable. */ +/* 文件为需求分页处理(demand-paged,即需求加载)的可执行文件 */ +#define ZMAGIC 0413 +#endif /* not OMAGIC */ + +#ifndef N_BADMAG +#define N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC && N_MAGIC(x) != ZMAGIC) +#endif + +#define _N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC && N_MAGIC(x) != ZMAGIC) + +#define _N_HDROFF(x) (SEGMENT_SIZE - sizeof (struct exec)) + +#ifndef N_TXTOFF +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : sizeof (struct exec)) +#endif + +#ifndef N_DATOFF +#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) +#endif + +#ifndef N_TRELOFF +#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data) +#endif + +#ifndef N_DRELOFF +#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize) +#endif + +#ifndef N_SYMOFF +#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize) +#endif + +#ifndef N_STROFF +#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms) +#endif + +/* Address of text segment in memory after it is loaded. */ +#ifndef N_TXTADDR +#define N_TXTADDR(x) 0 +#endif + +/* Address of data segment in memory after it is loaded. + Note that it is up to you to define SEGMENT_SIZE + on machines not listed here. */ +#if defined(vax) || defined(hp300) || defined(pyr) +#define SEGMENT_SIZE PAGE_SIZE +#endif +#ifdef hp300 +#define PAGE_SIZE 4096 +#endif +#ifdef sony +#define SEGMENT_SIZE 0x2000 +#endif /* Sony. */ +#ifdef is68k +#define SEGMENT_SIZE 0x20000 +#endif +#if defined(m68k) && defined(PORTAR) +#define PAGE_SIZE 0x400 +#define SEGMENT_SIZE PAGE_SIZE +#endif + +#define PAGE_SIZE 4096 +#define SEGMENT_SIZE 1024 + +#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1)) + +#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) + +#ifndef N_DATADDR +#define N_DATADDR(x) \ + (N_MAGIC(x)==OMAGIC ? (_N_TXTENDADDR(x)) : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) +#endif + +/* Address of bss segment in memory after it is loaded. */ +#ifndef N_BSSADDR +#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data) +#endif + +#ifndef N_NLIST_DECLARED +struct nlist { + union { + char *n_name; + struct nlist *n_next; + long n_strx; + } n_un; + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; +#endif + +#ifndef N_UNDF +#define N_UNDF 0 +#endif +#ifndef N_ABS +#define N_ABS 2 +#endif +#ifndef N_TEXT +#define N_TEXT 4 +#endif +#ifndef N_DATA +#define N_DATA 6 +#endif +#ifndef N_BSS +#define N_BSS 8 +#endif +#ifndef N_COMM +#define N_COMM 18 +#endif +#ifndef N_FN +#define N_FN 15 +#endif + +#ifndef N_EXT +#define N_EXT 1 +#endif +#ifndef N_TYPE +#define N_TYPE 036 +#endif +#ifndef N_STAB +#define N_STAB 0340 +#endif + +/* The following type indicates the definition of a symbol as being + an indirect reference to another symbol. The other symbol + appears as an undefined reference, immediately following this symbol. + + Indirection is asymmetrical. The other symbol's value will be used + to satisfy requests for the indirect symbol, but not vice versa. + If the other symbol does not have a definition, libraries will + be searched to find a definition. */ +#define N_INDR 0xa + +/* The following symbols refer to set elements. + All the N_SET[ATDB] symbols with the same name form one set. + Space is allocated for the set in the text section, and each set + element's value is stored into one word of the space. + The first word of the space is the length of the set (number of elements). + + The address of the set is made into an N_SETV symbol + whose name is the same as the name of the set. + This symbol acts like a N_DATA global symbol + in that it can satisfy undefined external references. */ + +/* These appear as input to LD, in a .o file. */ +#define N_SETA 0x14 /* Absolute set element symbol */ +#define N_SETT 0x16 /* Text set element symbol */ +#define N_SETD 0x18 /* Data set element symbol */ +#define N_SETB 0x1A /* Bss set element symbol */ + +/* This is output from LD. */ +#define N_SETV 0x1C /* Pointer to set vector in data area. */ + +#ifndef N_RELOCATION_INFO_DECLARED + +/* This structure describes a single relocation to be performed. + The text-relocation section of the file is a vector of these structures, + all of which apply to the text section. + Likewise, the data-relocation section applies to the data section. */ + +/* 重定位项的功能有两个。一是当代码段被重定位到一个不同的基地址处时, + 重定位项则用于指出需要修改的地方。二是在模块文件中存在对未定义符 + 号引用时,当此未定义符号最终被定义时链接程序就可以使用相应重定位 + 项对符号的值进行修正。由上面重定位记录项的结构可以看出,每个记录 + 项含有模块文件代码区(代码段)和数据区(数据段)中需要重定位处长 + 度为4B的地址以及规定如何具体进行重定位操作的信息。 */ + +/* 重定向信息部分 */ +struct relocation_info +{ + /* Address (within segment) to be relocated.*/ /* 段内需要重定位的地址 */ + int r_address; + /* The meaning of r_symbolnum depends on r_extern. */ + /* 含义与r_extern有关。指定符号表中一个符号或者一个段。*/ + unsigned int r_symbolnum:24; + /* Nonzero means value is a pc-relative offset and it should be relocated for changes in its + own address as well as for changes in the symbol or section specified. */ + unsigned int r_pcrel:1; /* 1位。PC相关标志。即它作为一个相对地址被用于指令当中 */ + /* Length (as exponent of 2) of the field to be relocated. Thus,a value of 2 indicates + 1<<2 bytes. */ + unsigned int r_length:2; /* 指定要被重定位字段长度(2的次方),0到3分别表示被重定位项的宽度是1B、2B、4B或8B */ + /* 1 => relocate with value of symbol. + r_symbolnum is the index of the symbol + in file's the symbol table. + 0 => relocate with the address of a segment. + r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS + (the N_EXT bit may be set also, but signifies nothing). */ + unsigned int r_extern:1; /* 外部标志位。1 - 以符号的值重定位。0 - 以段的地址重定位。*/ + /* Four bits that aren't used, but when writing an object file + it is desirable to clear them. */ + unsigned int r_pad:4; /* 没有使用的4个位,但最好将它们复位掉。*/ +}; + +/* 外部标志位r_extern控制着r_symbolnum的含义,指明重定位项参考的是段还是一个符号。如果该标志值 + 是0,那么该重定位项是一个普通的重定位项,此时r_symbolnum字段指定在哪个段中寻址定位。如果该标志 + 是1,那么该重定位项是对一个外部符号的引用,此时r_symbolnum指定目标文件中符号表中的一个符号,需 + 要使用符号的值进行重定位。*/ +#endif /* no N_RELOCATION_INFO_DECLARED. */ + +#endif /* __A_OUT_GNU_H__ */ diff --git a/linux-0.12/include/asm/io.h b/linux-0.12/include/asm/io.h new file mode 100755 index 0000000..dcf5123 --- /dev/null +++ b/linux-0.12/include/asm/io.h @@ -0,0 +1,48 @@ +/* 该文件中定义了对硬件IO端口访问的嵌入式汇编宏函数:outb()、inb()、outb_p()和inb_p() */ + +/** + * 硬件端口字节输出 + * @param[in] value 欲输出字节 + * @param[in] port 端口 + */ +#define outb(value, port) \ + __asm__ ("outb %%al,%%dx"::"a" (value),"d" (port)) + +/** + * 硬件端口字节输入 + * @param[in] port 端口 + * @retval 返回读取的字节 + */ +#define inb(port) ({ \ + unsigned char _v; \ + __asm__ volatile ("inb %%dx,%%al":"=a" (_v):"d" (port)); \ + _v; \ + }) + +/** + * 硬件端口字节输出(带延迟) + * 使用两条跳转语句来延迟一会儿 + * @param[in] value 欲输出字节 + * @param[in] port 端口 + */ +#define outb_p(value, port) \ + __asm__ ("outb %%al,%%dx\n" \ + "\tjmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:"::"a" (value),"d" (port)) + +/** + * 硬件端口字节输入(带延迟) + * 使用两条跳转语句来延迟一会儿 + * @param[in] port 端口 + * @retval 返回读取的字节 + */ +#define inb_p(port) ({ \ + unsigned char _v; \ + __asm__ volatile ( \ + "inb %%dx,%%al\n" \ + "\tjmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:":"=a" (_v):"d" (port)); \ + _v; \ + }) diff --git a/linux-0.12/include/asm/memory.h b/linux-0.12/include/asm/memory.h new file mode 100755 index 0000000..7b4eb3d --- /dev/null +++ b/linux-0.12/include/asm/memory.h @@ -0,0 +1,26 @@ +/* + * NOTE!!! memcpy(dest,src,n) assumes ds=es=normal data segment. This + * goes for all kernel functions (ds=es=kernel space, fs=local data, + * gs=null), as well as for all well-behaving user programs (ds=es= + * user data space). This is NOT a bug, as any user program that changes + * es deserves to die if it isn't careful. + */ +/* + * 注意!!!memcpy(dest, src, n) 假设段寄存器ds=es=通常数据段。在内核中使用的所有函数都基于假 + * 设(ds=es=内核空间,fs=局部数据空间,gs=null),具有良好行为的应用程序也是这样(ds=es=用户数 + * 据空间)。如果任何用户程序随意改动了es寄存器而出错,则并不是由于系统程序错误造成的。 + */ +/** + * 内存块复制 + * 从源地址src处开始复制n个字节到目的地址dest处。从ds:[esi]复制到es:[edi],共复制ecx(n)字节。 + * @param[in] dest 复制的目的地址 + * @param[in] src 复制的源地址 + * @param[in] n 复制字节数 + */ +#define memcpy(dest, src, n) ({ \ + void * _res = dest; \ + __asm__ ("cld;rep;movsb" \ + ::"D" ((long)(_res)),"S" ((long)(src)),"c" ((long) (n)) \ + ); \ + _res; \ +}) diff --git a/linux-0.12/include/asm/segment.h b/linux-0.12/include/asm/segment.h new file mode 100755 index 0000000..f0a7356 --- /dev/null +++ b/linux-0.12/include/asm/segment.h @@ -0,0 +1,110 @@ +/* 该文件定义了一些访问Intel CPU中段寄存器或与段寄存器有关的内存操作函数 */ + +/** + * 读取fs段中指定地址处的字节 + * @param[in] addr 指定的内存地址 + * @retval 返回内存fs[addr]处的字节 + */ +static inline unsigned char get_fs_byte(const char * addr) +{ + unsigned register char _v; + + __asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + +/** + * 读取fs段中指定地址处的字 + * @param[in] addr 指定的内存地址 + * @retval 返回内存fs[addr]处的字 + */ +static inline unsigned short get_fs_word(const unsigned short *addr) +{ + unsigned short _v; + + __asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + +/** + * 读取fs段中指定地址处的长字(4字节) + * @param[in] addr 指定的内存地址 + * @retval 返回内存fs[addr]处的长字 + */ +static inline unsigned long get_fs_long(const unsigned long *addr) +{ + unsigned long _v; + + __asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + +/** + * 将一字节存放在fs段中指定内存地址处 + * @param[in] val 字节值 + * @param[in] addr 内存地址 + */ +static inline void put_fs_byte(char val,char *addr) +{ + __asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +/** + * 将一字存放在fs段中指定内存地址处 + * @param[in] val 字值 + * @param[in] addr 内存地址 + */ +static inline void put_fs_word(short val,short * addr) +{ + __asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +/** + * 将一长字存放在fs段中指定内存地址处 + * @param[in] val 长字值 + * @param[in] addr 内存地址 + */ +static inline void put_fs_long(unsigned long val,unsigned long * addr) +{ + __asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr)); +} + +/* + * Someone who knows GNU asm better than I should double check the followig. + * It seems to work, but I don't know if I'm doing something subtly wrong. + * --- TYT, 11/24/91 + * [ nothing wrong here, Linus ] + */ + +/** + * 取fs段寄存器值(选择符) + * @retval fs段寄存器值 + */ +static inline unsigned long get_fs() +{ + unsigned short _v; + __asm__("mov %%fs,%%ax":"=a" (_v):); + return _v; +} + +/** + * 取ds段寄存器值 + * @retval ds段寄存器值 + */ +static inline unsigned long get_ds() +{ + unsigned short _v; + __asm__("mov %%ds,%%ax":"=a" (_v):); + return _v; +} + + +/** + * 设置fs段寄存器 + * @param[in] val 段值(选择符) + */ +static inline void set_fs(unsigned long val) +{ + __asm__("mov %0,%%fs"::"a" ((unsigned short) val)); +} + diff --git a/linux-0.12/include/asm/system.h b/linux-0.12/include/asm/system.h new file mode 100755 index 0000000..6ae4f68 --- /dev/null +++ b/linux-0.12/include/asm/system.h @@ -0,0 +1,119 @@ +/* 定义了设置或修改描述符/中断门等的嵌入式汇编宏 */ + +/* 利用iret指令实现从内核模式移到用户模式去执行初始任务0 */ +#define move_to_user_mode() \ +__asm__ ( \ + "movl %%esp,%%eax\n\t" \ + "pushl $0x17\n\t" \ + "pushl %%eax\n\t" \ + "pushfl\n\t" \ + "pushl $0x0f\n\t" \ + "pushl $1f\n\t" \ + "iret\n" \ +"1:\tmovl $0x17,%%eax\n\t" \ + "mov %%ax,%%ds\n\t" \ + "mov %%ax,%%es\n\t" \ + "mov %%ax,%%fs\n\t" \ + "mov %%ax,%%gs" \ + :::"ax") + +#define sti() __asm__ ("sti"::) /* 开中断 */ +#define cli() __asm__ ("cli"::) /* 关中断 */ +#define nop() __asm__ ("nop"::) /* 空操作 */ + +#define iret() __asm__ ("iret"::) /* 中断返回 */ + +/** + * 设置门描述符宏 + * @param[in] gate_addr 在中断描述符表中的偏移量 + * @param[in] type 门描述符类型 + * @param[in] dpl 特权级信息 + * @param[in] addr 中断或异常过程函数地址 + */ +#define _set_gate(gate_addr, type, dpl, addr) \ + __asm__ ("movw %%dx,%%ax\n\t" \ + "movw %0,%%dx\n\t" \ + "movl %%eax,%1\n\t" \ + "movl %%edx,%2" \ + : \ + : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ + "o" (*((char *) (gate_addr))), \ + "o" (*(4+(char *) (gate_addr))), \ + "d" ((char *) (addr)),"a" (0x00080000)) + +/** + * 设置中断门函数(自动屏蔽随后的中断) + * @param[in] n 中断号 + * @param[in] addr 中断程序偏移地址 + */ +#define set_intr_gate(n, addr) _set_gate(&idt[n], 14, 0, addr) + +/** + * 设置陷阱门函数 + * @param[in] n 中断号 + * param[in] addr 中断程序偏移地址 + */ +#define set_trap_gate(n, addr) _set_gate(&idt[n], 15, 0, addr) + +/** + * 设置系统陷阱门函数 + * @param[in] n 中断号 + * @param[in] addr 中断程序偏移直 + */ +#define set_system_gate(n, addr) _set_gate(&idt[n], 15, 3, addr) + +/** + * 设置段描述符函数(内核中没有用到) + * @param[in] gate_addr 描述符地址 + * @param[in] type 描述符中类型域值 + * @param[in] dpl 描述符特权层值 + * @param[in] base 段的基地址 + * @param[in] limit 段限长 + */ +#define _set_seg_desc(gate_addr, type, dpl, base, limit) { \ + *(gate_addr) = ((base) & 0xff000000) | \ + (((base) & 0x00ff0000)>>16) | \ + ((limit) & 0xf0000) | \ + ((dpl)<<13) | \ + (0x00408000) | \ + ((type)<<8); \ + *((gate_addr)+1) = (((base) & 0x0000ffff)<<16) | \ + ((limit) & 0x0ffff); } + + +/** + * 在全局表中设置任务状态段/局部表描述符 + * 状态段局部表段的长度均被设置成104字节。%0 - eax(地址addr);%1 - (描述符项n的地址); %2 - (描述 + * 符项n的地址偏移2处);%3 - (描述符项n的地址偏移4处); %4 - (描述符项n的地址偏移5处);%5 - (描述 + * 符项n的地址偏移6处);%6 - (描述符项n的地址偏移7处); + * @param[in] n 在全局表中描述符项n所对应的地址 + * @param[in] addr 状态段/局部表所在内存的基地址 + * @param[in] type 描述符中的标志类型字节 + */ +#define _set_tssldt_desc(n,addr,type) \ +__asm__ ( \ + "movw $104,%1\n\t" \ + "movw %%ax,%2\n\t" \ + "rorl $16,%%eax\n\t" \ + "movb %%al,%3\n\t" \ + "movb $" type ",%4\n\t" \ + "movb $0x00,%5\n\t" \ + "movb %%ah,%6\n\t" \ + "rorl $16,%%eax" \ + ::"a" (addr), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \ + "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \ + ) + +/** + * 在全局表中设置任务状态段描述符(任务状态段描述符的类型是0x89) + * @param[in] n 该描述符的指针 + * @param[in] addr 描述符项中段的基地址值 + */ +#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),addr, "0x89") + +/** + * 在全局表中设置局部表描述符(局部表段描述符的类型是0x82) + * @param[in] n 该描述符的指针 + * @param[in] addr 描述符项中段的基地址值 + */ +#define set_ldt_desc(n, addr) _set_tssldt_desc(((char *) (n)),addr, "0x82") diff --git a/linux-0.12/include/const.h b/linux-0.12/include/const.h new file mode 100755 index 0000000..885fcda --- /dev/null +++ b/linux-0.12/include/const.h @@ -0,0 +1,17 @@ +#ifndef _CONST_H +#define _CONST_H + +/* 定义缓冲使用内存的末端(代码中没有使用该常量) */ +#define BUFFER_END 0x200000 + +/* i节点数据结构中i_mode字段的各标志位 */ +#define I_TYPE 0170000 /* 指明i节点类型(类型屏蔽码) */ +#define I_DIRECTORY 0040000 /* 目录文件 */ +#define I_REGULAR 0100000 /* 常规文件,不是目录文件或特殊文件 */ +#define I_BLOCK_SPECIAL 0060000 /* 块设备特殊文件 */ +#define I_CHAR_SPECIAL 0020000 /* 字符设备特殊文件 */ +#define I_NAMED_PIPE 0010000 /* 命名管道节点 */ +#define I_SET_UID_BIT 0004000 /* 在执行时设置有效用户ID类型 */ +#define I_SET_GID_BIT 0002000 /* 在执行时设置有效组ID类型 */ + +#endif diff --git a/linux-0.12/include/ctype.h b/linux-0.12/include/ctype.h new file mode 100755 index 0000000..9b0b76d --- /dev/null +++ b/linux-0.12/include/ctype.h @@ -0,0 +1,41 @@ +#ifndef _CTYPE_H +#define _CTYPE_H + +#define _U 0x01 /* upper */ /* 该比特位用于大写字符[A-Z] */ +#define _L 0x02 /* lower */ /* 该比特位用于小写字符[a-z] */ +#define _D 0x04 /* digit */ /* 该比特位用于数字[0-9] */ +#define _C 0x08 /* cntrl */ /* 该比特位用于控制字符 */ +#define _P 0x10 /* punct */ /* 该比特位用于标点字符 */ +#define _S 0x20 /* white space (space/lf/tab) */ /* 空白字符,如空格,\t,\n等 */ +#define _X 0x40 /* hex digit */ /* 该比特位用于十六进制数字 */ +#define _SP 0x80 /* hard space (0x20) */ /* 该比特位用于空格字符(0x20) */ + +extern unsigned char _ctype[]; /* 字符特性数组(表),定义各个字符对应上面的属性 */ +extern char _ctmp; /* 一个临时字符变量(在定义lib/ctype.c中) */ + +/* 确定字符类型的宏,_ctype+1的原因是_ctype[1]对应ascii表的第一个字符 */ +#define isalnum(c) ((_ctype+1)[c]&(_U|_L|_D)) /* 字符或数字[A-Z],[a-z]或[0-9] */ +#define isalpha(c) ((_ctype+1)[c]&(_U|_L)) /* 字符 */ +#define iscntrl(c) ((_ctype+1)[c]&(_C)) /* 控制字符 */ +#define isdigit(c) ((_ctype+1)[c]&(_D)) /* 数字 */ +#define isgraph(c) ((_ctype+1)[c]&(_P|_U|_L|_D)) /* 图形字符 */ +#define islower(c) ((_ctype+1)[c]&(_L)) /* 小写字符 */ +#define isprint(c) ((_ctype+1)[c]&(_P|_U|_L|_D|_SP))/* 可打印字符 */ +#define ispunct(c) ((_ctype+1)[c]&(_P)) /* 标点符号 */ +#define isspace(c) ((_ctype+1)[c]&(_S)) /* 空白字符如空格,\f,\n,\r,\t,\w */ +#define isupper(c) ((_ctype+1)[c]&(_U)) /* 大写字符 */ +#define isxdigit(c) ((_ctype+1)[c]&(_D|_X)) /* 十六进制数字 */ + +/* bug修复!这里需要对c用括号包起来,用来应对参数c为a+b的情况。如果不加括号,展开则变成了 + (unsigned)a+b而不是(unsigned)(a+b)。 */ +#define isascii(c) (((unsigned) (c))<=0x7f) /* ASCII字符 */ +#define toascii(c) (((unsigned) (c))&0x7f) /* 转换成ASCII字符 */ + +/* 以下两个宏定义中使用一个临时变量_ctmp的原因是:在宏定义中,宏的参数只能被使用一次 */ +// TODO: 上面这句注释存疑? +/* 但对于多线程来说这是不安全的,因为两个或多个线程可能在同一时刻使用这个公共临时变量_ctmp */ +/* 因此从Linux 2.2.x版本开始更改为使用两个函数来取代这从个宏定义 */ +#define tolower(c) (_ctmp=c,isupper((int)_ctmp)?_ctmp-('A'-'a'):_ctmp)/* 转换成小写字符 */ +#define toupper(c) (_ctmp=c,islower((int)_ctmp)?_ctmp-('a'-'A'):_ctmp)/* 转换成大写字符 */ + +#endif diff --git a/linux-0.12/include/errno.h b/linux-0.12/include/errno.h new file mode 100755 index 0000000..7db7df3 --- /dev/null +++ b/linux-0.12/include/errno.h @@ -0,0 +1,80 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +/* + * ok, as I hadn't got any other source of information about + * possible error numbers, I was forced to use the same numbers + * as minix. + * Hopefully these are posix or something. I wouldn't know (and posix + * isn't telling me - they want $$$ for their f***ing standard). + * + * We don't use the _SIGN cludge of minix, so kernel returns must + * see to the sign by themselves. + * + * NOTE! Remember to change strerror() if you change this file! + */ +/* + * OK,由于我没有得到任何其他有关出错号的资料,我只能使用与 minix 系统相同的出错号了。希望这 + * 些是POSIX兼容的或者在一定程度上是这样的,我不知道(而且POSIX没有告诉我 - 要获得他们的标准需 + * 要出钱)。 + * + * 我们没有使用 minix 那样的 _SIGN 簇,所以内核的返回值必须自己辨别正负号。 + * + * 注意!如果你改变该文件的话,记着也要修改 strerror() 函数。 + */ + +/* 系统调用以及很多库函数返回一个特殊的值以表示操作失败或出错。这个值通常选择 -1或者其他一些 + 特定的值来表示。但是这个返回值仅说明错误发生了。如果需要知道出错的类型,就需要查看表示系统出 + 错号的变量errno。该变量即在errno.h文件中声明。在程序开始执行时该变量值被初始化为 0 */ +extern int errno; + +/* 在出错时,系统调用会把出错号放在变量 errno 中(负值),然后返回-1。因此程序若需要知道具体错 + 误号,就需要查看 errno 的值 */ + +#define ERROR 99 /* 一般错误 */ +#define EPERM 1 /* 操作没有许可 */ +#define ENOENT 2 /* 文件或目录不存在 */ +#define ESRCH 3 /* 指定的进程不存在 */ +#define EINTR 4 /* 中断的系统调用 */ +#define EIO 5 /* 输入/输出错 */ +#define ENXIO 6 /* 指定设备或地址不存在 */ +#define E2BIG 7 /* 参数列表太长 */ +#define ENOEXEC 8 /* 执行程序格式错误 */ +#define EBADF 9 /* 文件句柄(描述符)错误 */ +#define ECHILD 10 /* 子进程不存在 */ +#define EAGAIN 11 /* 资源暂不可用 */ +#define ENOMEM 12 /* 内存不足 */ +#define EACCES 13 /* 没有许可权限 */ +#define EFAULT 14 /* 地址错 */ +#define ENOTBLK 15 /* 不是块设备文件 */ +#define EBUSY 16 /* 资源正忙 */ +#define EEXIST 17 /* 文件已存在 */ +#define EXDEV 18 /* 非法连接 */ +#define ENODEV 19 /* 设备不存在 */ +#define ENOTDIR 20 /* 不是目录文件 */ +#define EISDIR 21 /* 是目录文件 */ +#define EINVAL 22 /* 参数无效 */ +#define ENFILE 23 /* 系统打开文件数太多 */ +#define EMFILE 24 /* 打开文件数太多 */ +#define ENOTTY 25 /* 不恰当的IO控制操作(没有tty终端) */ +#define ETXTBSY 26 /* 不再使用 */ +#define EFBIG 27 /* 文件太大 */ +#define ENOSPC 28 /* 设备已满(设备已经没有空间) */ +#define ESPIPE 29 /* 无效的文件指针重定位 */ +#define EROFS 30 /* 文件系统只读 */ +#define EMLINK 31 /* 连接太多 */ +#define EPIPE 32 /* 管道错 */ +#define EDOM 33 /* 域(domain)出错 */ +#define ERANGE 34 /* 结果太大 */ +#define EDEADLK 35 /* 避免资源死锁 */ +#define ENAMETOOLONG 36 /* 文件名太长 */ +#define ENOLCK 37 /* 没有锁定可用 */ +#define ENOSYS 38 /* 功能还没有实现 */ +#define ENOTEMPTY 39 /* 目录不空 */ + +/* Should never be seen by user programs */ +/* 应用程序不应该见到这两种错误号 */ +#define ERESTARTSYS 512 /* 重新执行系统调用 */ +#define ERESTARTNOINTR 513 /* 重新执行系统调用,无中断 */ + +#endif diff --git a/linux-0.12/include/fcntl.h b/linux-0.12/include/fcntl.h new file mode 100755 index 0000000..ad8988a --- /dev/null +++ b/linux-0.12/include/fcntl.h @@ -0,0 +1,79 @@ +#ifndef _FCNTL_H +#define _FCNTL_H + +#include + +/* open/fcntl - NOCTTY, NDELAY isn't implemented yet */ +/* open/fcntl - NOCTTY和NDELAY现在还没有实现 */ +#define O_ACCMODE 00003 /* 文件访问模式屏蔽码 */ +/* 文件访问模式:打开文件open()和文件控制函数fcntl()使用,同时只能使用三者之一 */ +#define O_RDONLY 00 /* 以只读方式打开文件 */ +#define O_WRONLY 01 /* 以只写方式打开文件 */ +#define O_RDWR 02 /* 以读写方式打开文件 */ +/* 文件创建和操作:用于open(),可与上面访问模式用'位或'的方式一起使用 */ + /* fcntl()不用 */ +#define O_CREAT 00100 /* not fcntl */ /* 如果文件不存在就创建 */ +#define O_EXCL 00200 /* not fcntl */ /* 独占使用文件标志 */ +#define O_NOCTTY 00400 /* not fcntl */ /* 不分配控制终端 */ +#define O_TRUNC 01000 /* not fcntl */ /* 若文件已存在且是写操作,则长度截为0 */ +#define O_APPEND 02000 /* 以追加方式打开,文件指针置为文件尾 */ +#define O_NONBLOCK 04000 /* not fcntl */ /* 非阻塞方式打开和操作文件 */ +#define O_NDELAY O_NONBLOCK /* 阻塞方式打开和操作文件 */ + +/* Defines for fcntl-commands. Note that currently + * locking isn't supported, and other things aren't really + * tested. + */ +/* + * 下面定义了fcntl的命令。注意目录锁定命令还没有支持,而其他命令实际上还没测试过。 + */ +/* 文件句柄(描述符)操作函数fcntl()的命令(cmd) */ +#define F_DUPFD 0 /* dup */ /* 拷贝文件句柄为最小数值的句柄 */ +#define F_GETFD 1 /* get f_flags */ /* 取句柄标志。仅1个标志FD_CLOEXEC */ +#define F_SETFD 2 /* set f_flags */ /* 设置句柄标志 */ +#define F_GETFL 3 /* more flags (cloexec) */ /* 取文件状态标志和访问模式 */ +#define F_SETFL 4 /* 设置文件状态标志和访问模式 */ +/* 下面3个是文件锁定命令。fnctl()的第三个参数lock是指向flock结构的指针(3个都未实现) */ +#define F_GETLK 5 /* not implemented */ /* 返回阻止锁定的flock结构 */ +#define F_SETLK 6 /* 设置(F_RDLCK或F_WRLCK)或清除(F_UNLCK)锁定 */ +#define F_SETLKW 7 /* 等待设置或清除锁定 */ + +/* for F_[GET|SET]FL */ +/* 用于F_GETFL或F_SETFL */ +/* 在执行exec()簇函数时需要关闭的文件句柄(执行时关闭 - Close On EXECution)*/ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + /* 实际上只要低位为1即可 */ + +/* Ok, these are locking features, and aren't implemented at any + * level. POSIX wants them. + */ +/* + * OK,以下是锁定类型,任何函数中都还没有实现。POSIX标准要求这些类型。 + */ +#define F_RDLCK 0 /* 共享或读文件锁定 */ +#define F_WRLCK 1 /* 独占或写文件锁定 */ +#define F_UNLCK 2 /* 文件解锁 */ + +/* Once again - not implemented, but ... */ +/* 同样 - 也还没有实现,但是... */ + +/* 文件锁定操作数据结构 */ +struct flock { + short l_type; /* 锁定类型(F_RDLCK,F_WRLCK,F_UNLCK) */ + short l_whence; /* 开始偏移(SEEK_SET,SEEK_CUR或SEEK_END) */ + off_t l_start; /* 阻塞锁定的开始处。相对偏移(字节数) */ + off_t l_len; /* 阻塞锁定的大小。如果是则为到文件末尾 */ + pid_t l_pid; /* 加锁的进程id */ +}; + +/**** 以下是使用上述标志或命令的函数原型 ****/ +/* 创建新文件或重写一个已存在的文件 */ +extern int creat(const char * filename,mode_t mode); + +/* 文件句柄操作 */ +extern int fcntl(int fildes,int cmd, ...); + +/* 打开文件 */ +extern int open(const char * filename, int flags, ...); + +#endif diff --git a/linux-0.12/include/linux/config.h b/linux-0.12/include/linux/config.h new file mode 100755 index 0000000..9e5f74f --- /dev/null +++ b/linux-0.12/include/linux/config.h @@ -0,0 +1,89 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +/* 内核配置头文件 */ + +/* + * Defines for what uname() should return + */ +/* 定义了uname()函数的返回值 */ +#define UTS_SYSNAME "Linux" +#define UTS_NODENAME "(none)" /* set by sethostname() */ +#define UTS_RELEASE "0" /* patchlevel */ +#define UTS_VERSION "0.12" +#define UTS_MACHINE "i386" /* hardware type */ + +/* Don't touch these, unless you really know what your doing. */ +/* 请不要随意修改下面定义值,除非你知道自己正在干什么。 */ +#define DEF_INITSEG 0x9000 /* 引导扇区程序将被移动到的段位置 */ +#define DEF_SYSSEG 0x1000 /* 系统模块被加载到内存的段位置 */ +#define DEF_SETUPSEG 0x9020 /* setup程序代码的段位置 */ +#define DEF_SYSSIZE 0x3000 /* 内核系统模块的默认最大节数(1节=16bit) */ + +/* + * The root-device is no longer hard-coded. You can change the default + * root-device by changing the line ROOT_DEV = XXX in boot/bootsect.s + */ +/* + * 根文件系统设备已不再是硬编码的了。通过修改boot/bootsect.s文件中行ROOT_DEV=XXX,你可以改变根 + * 设备的默认设置值。 + */ + +/* + * The keyboard is now defined in kernel/chr_dev/keyboard.S + */ + /* + * 现在键盘类型被放在kernel/chr_dev/keyboard.S程序中定义。 + */ + +/* + * Normally, Linux can get the drive parameters from the BIOS at + * startup, but if this for some unfathomable reason fails, you'd + * be left stranded. For this case, you can define HD_TYPE, which + * contains all necessary info on your harddisk. + * + * The HD_TYPE macro should look like this: + * + * #define HD_TYPE { head, sect, cyl, wpcom, lzone, ctl} + * + * In case of two harddisks, the info should be sepatated by + * commas: + * + * #define HD_TYPE { h,s,c,wpcom,lz,ctl },{ h,s,c,wpcom,lz,ctl } + */ + +/* + * 通常,Linux能够在启动时从BIOS中获取驱动器的参数,但是若由于未知原因而没有得到这些参数时,会 + * 使程序束手无策。对于这种情况,你可以定义HD_TYPE,其中包括硬盘的所有作息。 + * + * HD_TYPE宏应该像下面这样的形式: + * + * #define HD_TYPE { head, sect, cyl, wpcom, lzone, ctl} + * + * 对于有两个硬盘的情况,参数信息需用逗号分开: + * + * #define HD_TYPE { h,s,c,wpcom,lz,ctl },{ h,s,c,wpcom,lz,ctl } + */ + +/* + This is an example, two drives, first is type 2, second is type 3: + +#define HD_TYPE { 4,17,615,300,615,8 }, { 6,17,615,300,615,0 } + + NOTE: ctl is 0 for all drives with heads<=8, and ctl=8 for drives + with more than 8 heads. + + If you want the BIOS to tell what kind of drive you have, just + leave HD_TYPE undefined. This is the normal thing to do. +*/ +/* + * 下面是一个例子,两个硬盘,第1个是类型2,第2个是类型3: + * + * #define HD_TYPE { 4,17,615,300,615,8 }, { 6,17,615,300,615,0 } + * + * 注:对应所有硬盘,若其磁头数<=8,则ctl等于0,若磁头数多于8个,则ctl=8。 + * + * 如果你想让BIOS给出硬盘的类型,那么只需不定义HD_TYPE。这是默认操作。 + */ + +#endif diff --git a/linux-0.12/include/linux/fdreg.h b/linux-0.12/include/linux/fdreg.h new file mode 100644 index 0000000..01355af --- /dev/null +++ b/linux-0.12/include/linux/fdreg.h @@ -0,0 +1,71 @@ +/* + * This file contains some defines for the floppy disk controller. + * Various sources. Mostly "IBM Microcomputers: A Programmers + * Handbook", Sanches and Canton. + */ +#ifndef _FDREG_H +#define _FDREG_H + +extern int ticks_to_floppy_on(unsigned int nr); +extern void floppy_on(unsigned int nr); +extern void floppy_off(unsigned int nr); +extern void floppy_select(unsigned int nr); +extern void floppy_deselect(unsigned int nr); + +/* Fd controller regs. S&C, about page 340 */ +#define FD_STATUS 0x3f4 +#define FD_DATA 0x3f5 +#define FD_DOR 0x3f2 /* Digital Output Register */ +#define FD_DIR 0x3f7 /* Digital Input Register (read) */ +#define FD_DCR 0x3f7 /* Diskette Control Register (write)*/ + +/* Bits of main status register */ +#define STATUS_BUSYMASK 0x0F /* drive busy mask */ +#define STATUS_BUSY 0x10 /* FDC busy */ +#define STATUS_DMA 0x20 /* 0- DMA mode */ +#define STATUS_DIR 0x40 /* 0- cpu->fdc */ +#define STATUS_READY 0x80 /* Data reg ready */ + +/* Bits of FD_ST0 */ +#define ST0_DS 0x03 /* drive select mask */ +#define ST0_HA 0x04 /* Head (Address) */ +#define ST0_NR 0x08 /* Not Ready */ +#define ST0_ECE 0x10 /* Equipment chech error */ +#define ST0_SE 0x20 /* Seek end */ +#define ST0_INTR 0xC0 /* Interrupt code mask */ + +/* Bits of FD_ST1 */ +#define ST1_MAM 0x01 /* Missing Address Mark */ +#define ST1_WP 0x02 /* Write Protect */ +#define ST1_ND 0x04 /* No Data - unreadable */ +#define ST1_OR 0x10 /* OverRun */ +#define ST1_CRC 0x20 /* CRC error in data or addr */ +#define ST1_EOC 0x80 /* End Of Cylinder */ + +/* Bits of FD_ST2 */ +#define ST2_MAM 0x01 /* Missing Addess Mark (again) */ +#define ST2_BC 0x02 /* Bad Cylinder */ +#define ST2_SNS 0x04 /* Scan Not Satisfied */ +#define ST2_SEH 0x08 /* Scan Equal Hit */ +#define ST2_WC 0x10 /* Wrong Cylinder */ +#define ST2_CRC 0x20 /* CRC error in data field */ +#define ST2_CM 0x40 /* Control Mark = deleted */ + +/* Bits of FD_ST3 */ +#define ST3_HA 0x04 /* Head (Address) */ +#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */ +#define ST3_WP 0x40 /* Write Protect */ + +/* Values for FD_COMMAND */ +#define FD_RECALIBRATE 0x07 /* move to track 0 */ +#define FD_SEEK 0x0F /* seek track */ +#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */ +#define FD_WRITE 0xC5 /* write with MT, MFM */ +#define FD_SENSEI 0x08 /* Sense Interrupt Status */ +#define FD_SPECIFY 0x03 /* specify HUT etc */ + +/* DMA commands */ +#define DMA_READ 0x46 +#define DMA_WRITE 0x4A + +#endif diff --git a/linux-0.12/include/linux/fs.h b/linux-0.12/include/linux/fs.h new file mode 100755 index 0000000..495a09d --- /dev/null +++ b/linux-0.12/include/linux/fs.h @@ -0,0 +1,321 @@ +/* + * This file has definitions for some important file table + * structures etc. + */ +/* + * 本文件含有某些重要文件表结构的定义等。 + */ + +#ifndef _FS_H +#define _FS_H + +#include + +/* devices are as follows: (same as minix, so we can use the minix + * file system. These are major numbers.) + * + * 0 - unused (nodev) + * 1 - /dev/mem + * 2 - /dev/fd + * 3 - /dev/hd + * 4 - /dev/ttyx + * 5 - /dev/tty + * 6 - /dev/lp + * 7 - unnamed pipes + */ +/* + * 系统所含的设备如下:(与minix系统的一样,所以我们可以使用minix的文件系统。以下这些是主设备号。) + * + * 0 - unused (nodev) 没有用到 + * 1 - /dev/mem 内存设备 + * 2 - /dev/fd 软盘设备 + * 3 - /dev/hd 硬盘设备 + * 4 - /dev/ttyx tty串行终端设备 + * 5 - /dev/tty tty终端设备 + * 6 - /dev/lp 打印设备 + * 7 - unnamed pipes 没有命名的管道 + */ + +/* 判断设备是否是可以寻找定位的 */ +#define IS_SEEKABLE(x) ((x) >= 1 && (x) <= 3) + +/* 块设备操作类型 */ +#define READ 0 /* 读 */ +#define WRITE 1 /* 写 */ +#define READA 2 /* read-ahead - don't pause */ + /* 预读 */ +#define WRITEA 3 /* "write-ahead" - silly, but somewhat useful */ + /* 预写 */ + +void buffer_init(long buffer_end); /* 高速缓冲区初始化 */ + +#define MAJOR(a) (((unsigned)(a)) >> 8) /* 取高字节(主设备号) */ +#define MINOR(a) ((a) & 0xff) /* 取低字节(次设备号) */ + +#define NAME_LEN 14 /* 文件名长度值 */ +#define ROOT_INO 1 /* 根i节点 */ + +#define I_MAP_SLOTS 8 /* i节点位图的块数 */ +#define Z_MAP_SLOTS 8 /* 逻辑块(区段块)位图的块数 */ +#define SUPER_MAGIC 0x137F /* 文件系统魔数 */ + +#define NR_OPEN 20 /* 进程最多打开文件数 */ +#define NR_INODE 64 /* 系统同时最多使用i节点个数 */ +#define NR_FILE 64 /* 系统最多文件个数(文件数组长度) */ +#define NR_SUPER 8 /* 系统所含超级块个数(超级块数组长度) */ +#define NR_HASH 307 /* 缓冲区Hash表数组长度 */ +#define NR_BUFFERS nr_buffers /* 系统所含缓冲个数,初始化后不再改变 */ +#define BLOCK_SIZE 1024 /* 数据块长度(字节值) */ +#define BLOCK_SIZE_BITS 10 /* 数据块长度所占比特位数 */ +#ifndef NULL +#define NULL ((void *) 0) +#endif + +/* 每个逻辑块可存放的i节点数 */ +#define INODES_PER_BLOCK ((BLOCK_SIZE) / (sizeof (struct d_inode))) +/* 每个逻辑块可存放的目录项数 */ +#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE) / (sizeof (struct dir_entry))) + +#define PIPE_READ_WAIT(inode) ((inode).i_wait) +#define PIPE_WRITE_WAIT(inode) ((inode).i_wait2) + +#define PIPE_HEAD(inode) ((inode).i_zone[0]) /* 管道头部指针 */ +#define PIPE_TAIL(inode) ((inode).i_zone[1]) /* 管道尾部指针 */ +#define PIPE_SIZE(inode) ((PIPE_HEAD(inode) - PIPE_TAIL(inode)) & (PAGE_SIZE - 1)) /* 管道大小 */ +#define PIPE_EMPTY(inode) (PIPE_HEAD(inode) == PIPE_TAIL(inode)) /* 管道空 */ +#define PIPE_FULL(inode) (PIPE_SIZE(inode) == (PAGE_SIZE - 1)) /* 管道满 */ + +#define NIL_FILP ((struct file *)0) /* 空文件结构指针 */ +#define SEL_IN 1 +#define SEL_OUT 2 +#define SEL_EX 4 + +typedef char buffer_block[BLOCK_SIZE]; /* 块缓冲区 */ + +/* 缓冲块头数据结构(重要) */ +struct buffer_head { + char * b_data; /* pointer to data block (1024 bytes) */ + /* 数据指针 */ + unsigned long b_blocknr; /* block number */ + /* 块号 */ + unsigned short b_dev; /* device (0 = free) */ + /* 数据源的设备号 */ + unsigned char b_uptodate; /* 更新标志:表示数据是否已更新 */ + + unsigned char b_dirt; /* 0-clean, 1-dirty */ + /* 修改标志:0未修改,1已修改 */ + unsigned char b_count; /* users using this block */ + /* 使用用户数 */ + unsigned char b_lock; /* 0 - ok, 1 -locked */ + /* 缓冲区是否被锁定 */ + struct task_struct * b_wait; /* 指向等待该缓冲区解锁的任务 */ + + /* 这四个指针用于缓冲区的管理 */ + struct buffer_head * b_prev; /* hash队列上的前一块 */ + struct buffer_head * b_next; /* hash队列上的后一块 */ + struct buffer_head * b_prev_free; /* 空闲表上的前一块 */ + struct buffer_head * b_next_free; /* 空闲表上的后一块 */ +}; + +/* 磁盘上的索引节点(i节点)数据结构 */ +struct d_inode { + unsigned short i_mode; /* 文件类型和属性(rwx位) */ + unsigned short i_uid; /* 用户id(文件拥有者标识符) */ + unsigned long i_size; /* 文件大小(字节数) */ + unsigned long i_time; /* 修改时间(自1970.1.1.:0算起,秒) */ + unsigned char i_gid; /* 组id(文件拥有者所在的组) */ + unsigned char i_nlinks; /* 链接数(多少个文件目录项指向该i节点) */ + unsigned short i_zone[9]; /* 直接(0-6),间接(7)或双重间接(8)逻辑块号 */ + /* zone是区的意思,可译成区段,或逻辑块 */ +}; + +/* 内存中的索引节点(i节点)数据结构 */ +struct m_inode { + unsigned short i_mode; + unsigned short i_uid; + unsigned long i_size; + unsigned long i_mtime; + unsigned char i_gid; + unsigned char i_nlinks; + unsigned short i_zone[9]; + /* these are in memory also */ /* 以下是内存中特有的 */ + struct task_struct * i_wait; /* 等待该i节点的进程 */ + struct task_struct * i_wait2; /* for pipes */ + unsigned long i_atime; /* 最后访问时间 */ + unsigned long i_ctime; /* i节点自身修改时间 */ + unsigned short i_dev; /* i节点所在的设备号 */ + unsigned short i_num; /* i节点号 */ + unsigned short i_count; /* i节点被使用的次数,0表示该i节点空闲 */ + unsigned char i_lock; /* 锁定标志 */ + unsigned char i_dirt; /* 已修改(脏)标志 */ + unsigned char i_pipe; /* 管道标志 */ + unsigned char i_mount; /* 安装标志 */ + unsigned char i_seek; /* 搜寻标志(lseek时) */ + unsigned char i_update; /* 更新标志 */ +}; + +/* 文件结构(用于在文件句柄与i节点之间建立关系) */ +struct file { + unsigned short f_mode; /* 文件操作模式(RW位) */ + unsigned short f_flags; /* 文件打开和控制的标志 */ + unsigned short f_count; /* 对应文件引用计数值 */ + struct m_inode *f_inode; /* 指向对应i节点 */ + off_t f_pos; /* 文件位置(读写偏移值) */ +}; + +/* 内存中的超级块结构 */ +struct super_block { + unsigned short s_ninodes; /* 节点数 */ + unsigned short s_nzones; /* 逻辑块数 */ + unsigned short s_imap_blocks; /* i节点位图所占用的数据块数 */ + unsigned short s_zmap_blocks; /* 逻辑块位图所占用的数据块数 */ + unsigned short s_firstdatazone; /* 第一个数据逻辑块号 */ + unsigned short s_log_zone_size; /* log2(数据块数/逻辑块) */ + unsigned long s_max_size; /* 文件最大长度 */ + unsigned short s_magic; /* 文件系统魔数 */ + /* These are only in memory */ /* 以下是内存中特有的 */ + struct buffer_head * s_imap[8]; /* i节点位图缓冲块指针数组(占用8块,可表示64M) */ + struct buffer_head * s_zmap[8]; /* 逻辑块位图缓冲块指针数组(占用8块) */ + unsigned short s_dev; /* 超级块所在设备号 */ + struct m_inode * s_isup; /* 被安装的文件系统根目录的i节点(isup-superi) */ + struct m_inode * s_imount; /* 被安装到的i节点 */ + unsigned long s_time; /* 修改时间 */ + struct task_struct * s_wait; /* 等待该超级块的进程 */ + unsigned char s_lock; /* 被锁定标志 */ + unsigned char s_rd_only; /* 只读标志 */ + unsigned char s_dirt; /* 已修改(脏)标志 */ +}; + +/* 磁盘上的超级块结构 */ +struct d_super_block { + unsigned short s_ninodes; /* 节点数 */ + unsigned short s_nzones; /* 逻辑块数 */ + unsigned short s_imap_blocks; /* i节点位图所占用的数据块数 */ + unsigned short s_zmap_blocks; /* 逻辑块位图所占用的数据块数 */ + unsigned short s_firstdatazone; /* 第一个数据逻辑块号 */ + unsigned short s_log_zone_size; /* log(数据块数/逻辑块) */ + unsigned long s_max_size; /* 文件最大长度 */ + unsigned short s_magic; /* 文件系统魔数 */ +}; + +/* 文件目录项结构 */ +struct dir_entry { + unsigned short inode; /* i节点号 */ + char name[NAME_LEN]; /* 文件名,长度NAME_LEN=14 */ +}; + +extern struct m_inode inode_table[NR_INODE]; /* 定义i节点表数组(64项) */ +extern struct file file_table[NR_FILE]; /* 文件表数组(64项) */ +extern struct super_block super_block[NR_SUPER];/* 超级块数组(8项) */ +extern struct buffer_head * start_buffer; /* 缓冲区起始内存位置 */ +extern int nr_buffers; + +/**** 磁盘操作函数原型 ****/ +/* 检测驱动器中软盘是否改变 */ +extern void check_disk_change(int dev); + +/* 检测指定软驱中软盘更换情况 */ +extern int floppy_change(unsigned int nr); + +/* 设置启动指定驱动器所需等待时间(设置等待定时器) */ +extern int ticks_to_floppy_on(unsigned int dev); + +/* 启动指定驱动器 */ +extern void floppy_on(unsigned int dev); + +/* 关闭指定的软盘驱动器*/ +extern void floppy_off(unsigned int dev); + + +/**** 文件系统操作管理用的函数原型 ****/ +/* 将i节点指定的文件截为0 */ +extern void truncate(struct m_inode * inode); + +/* 刷新i节点信息 */ +extern void sync_inodes(void); + +/* 等待指定的i节点 */ +extern void wait_on(struct m_inode * inode); + +/* 逻辑块(区段,磁盘块)位图操作。*/ +extern int bmap(struct m_inode * inode, int block); + +/* 创建数据块block在设备上对应的逻辑块 */ +extern int create_block(struct m_inode * inode, int block); + +/* 获取指定路径名的i节点号 */ +extern struct m_inode * namei(const char * pathname); + +/* 取指定路径名的i节点,不跟随符号链接 */ +extern struct m_inode * lnamei(const char * pathname); + +/* 根据路径名为打开文件操作作准备 */ +extern int open_namei(const char * pathname, int flag, int mode, + struct m_inode ** res_inode); + +/* 释放一个i节点(回写入设备)*/ +extern void iput(struct m_inode * inode); + +/* 从设备读取指定节点号的一个i节点 */ +extern struct m_inode * iget(int dev, int nr); + +/* 从i节点表中获取一个空闲i节点项 */ +extern struct m_inode * get_empty_inode(void); + +/* 获取(申请)管道节点 */ +extern struct m_inode * get_pipe_inode(void); + +/* 在哈希表中查找指定的数据块 */ +extern struct buffer_head * get_hash_table(int dev, int block); + +/* 从设备读取指定块 */ +extern struct buffer_head * getblk(int dev, int block); + +/* 读/写数据块 */ +extern void ll_rw_block(int rw, struct buffer_head * bh); + +/* 读/写数据页面 */ +extern void ll_rw_page(int rw, int dev, int nr, char * buffer); + +/* 释放指定缓冲块 */ +extern void brelse(struct buffer_head * buf); + +/* 读取指定的数据块 */ +extern struct buffer_head * bread(int dev, int block); + +/* 读取设备上一个页面(4个缓冲块)的内容到指定内存地址处 */ +extern void bread_page(unsigned long addr, int dev, int b[4]); + +/* 读取头一个指定的数据块,并标记后续将要读的块 */ +extern struct buffer_head * breada(int dev, int block, ...); + +/* 向设备dev申请一个磁盘块 */ +extern int new_block(int dev); + +/* 释放设备数据区中的逻辑块 */ +extern int free_block(int dev, int block); + +/* 为设备dev建立一个新i节点 */ +extern struct m_inode * new_inode(int dev); + +/* 释放一个i节点 */ +extern void free_inode(struct m_inode * inode); + +/* 刷新指定设备缓冲区块 */ +extern int sync_dev(int dev); + +/* 读取指定设备的超级块 */ +extern struct super_block * get_super(int dev); + +/* 释放指定设备的超级块 */ +extern void put_super(int dev); + +/* 释放设备dev在内存i节点表中的所有i节点 */ +extern void invalidate_inodes(int dev); + +extern int ROOT_DEV; + +/* 安装根文件系统 */ +extern void mount_root(void); + +#endif diff --git a/linux-0.12/include/linux/hdreg.h b/linux-0.12/include/linux/hdreg.h new file mode 100644 index 0000000..e6c593f --- /dev/null +++ b/linux-0.12/include/linux/hdreg.h @@ -0,0 +1,65 @@ +/* + * This file contains some defines for the AT-hd-controller. + * Various sources. Check out some definitions (see comments with + * a ques). + */ +#ifndef _HDREG_H +#define _HDREG_H + +/* Hd controller regs. Ref: IBM AT Bios-listing */ +#define HD_DATA 0x1f0 /* _CTL when writing */ +#define HD_ERROR 0x1f1 /* see err-bits */ +#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ +#define HD_SECTOR 0x1f3 /* starting sector */ +#define HD_LCYL 0x1f4 /* starting cylinder */ +#define HD_HCYL 0x1f5 /* high byte of starting cyl */ +#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ +#define HD_STATUS 0x1f7 /* see status-bits */ +#define HD_PRECOMP HD_ERROR /* same io address, read=error, write=precomp */ +#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ + +#define HD_CMD 0x3f6 + +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 /* Corrected error */ +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +/* Values for HD_COMMAND */ +#define WIN_RESTORE 0x10 +#define WIN_READ 0x20 +#define WIN_WRITE 0x30 +#define WIN_VERIFY 0x40 +#define WIN_FORMAT 0x50 +#define WIN_INIT 0x60 +#define WIN_SEEK 0x70 +#define WIN_DIAGNOSE 0x90 +#define WIN_SPECIFY 0x91 + +/* Bits for HD_ERROR */ +#define MARK_ERR 0x01 /* Bad address mark ? */ +#define TRK0_ERR 0x02 /* couldn't find track 0 */ +#define ABRT_ERR 0x04 /* ? */ +#define ID_ERR 0x10 /* ? */ +#define ECC_ERR 0x40 /* ? */ +#define BBD_ERR 0x80 /* ? */ + +struct partition { + unsigned char boot_ind; /* 0x80 - active (unused) */ + unsigned char head; /* ? */ + unsigned char sector; /* ? */ + unsigned char cyl; /* ? */ + unsigned char sys_ind; /* ? */ + unsigned char end_head; /* ? */ + unsigned char end_sector; /* ? */ + unsigned char end_cyl; /* ? */ + unsigned int start_sect; /* starting sector counting from 0 */ + unsigned int nr_sects; /* nr of sectors in partition */ +}; + +#endif diff --git a/linux-0.12/include/linux/head.h b/linux-0.12/include/linux/head.h new file mode 100755 index 0000000..32ac08f --- /dev/null +++ b/linux-0.12/include/linux/head.h @@ -0,0 +1,21 @@ +#ifndef _HEAD_H +#define _HEAD_H + +/* 段描述符的数据结构。该结构仅说明每个描述符是由8个字节构成,每个描述符表共有256项。 */ +typedef struct desc_struct { + unsigned long a,b; +} desc_table[256]; + +extern unsigned long pg_dir[1024]; /* 内存页目录数组。每个目录项为4字节,从物理地址0开始 */ +extern desc_table idt, gdt; /* 中断描述符表,全局描述符表 */ + +#define GDT_NUL 0 /* 全局描述符表的第0项,不用 */ +#define GDT_CODE 1 /* 第1项,内核代码段描述符项 */ +#define GDT_DATA 2 /* 第2项,内核数据段描述符项 */ +#define GDT_TMP 3 /* 第3项,系统段描述符(Linux没有使用) */ + +#define LDT_NUL 0 /* 每个局部描述符表的第0项,不用 */ +#define LDT_CODE 1 /* 第1项,用户程序代码段描述符项 */ +#define LDT_DATA 2 /* 第2项,用户程序数据段描述符项 */ + +#endif diff --git a/linux-0.12/include/linux/kernel.h b/linux-0.12/include/linux/kernel.h new file mode 100644 index 0000000..2bb5875 --- /dev/null +++ b/linux-0.12/include/linux/kernel.h @@ -0,0 +1,39 @@ +/* + * 'kernel.h' contains some often-used function prototypes etc + */ +void verify_area(void * addr, int count);/* 验证给定地址开始的内在块是否超限,若超限则追加内存 */ +volatile void panic(const char * str); /* 显示内核出错信息,然后进入死循环 */ +volatile void do_exit(long error_code); /* 进程退出处理 */ +int printf(const char * fmt, ...); /* 标准打印显示函数 */ +int printk(const char * fmt, ...); /* 内核专用的打印信息函数 */ +void console_print(const char * str); /* 控制台显示函数 */ +int tty_write(unsigned ch,char * buf,int count);/* 往tty上写指定长度的字符串 */ +void * malloc(unsigned int size); /* 通用内核内存分配函数 */ +void free_s(void * obj, int size); /* 释放指定对象占用的内存 */ +extern void hd_times_out(void); /* 硬盘处理超时 */ +extern void sysbeepstop(void); /* 停止蜂鸣 */ +extern void blank_screen(void); /* 黑屏处理 */ +extern void unblank_screen(void); /* 恢复被黑屏的屏幕 */ + +extern int beepcount; /* 蜂鸣时间滴答计数 */ +extern int hd_timeout; /* 硬盘超时滴答值 */ +extern int blankinterval; /* 设定的屏幕黑屏间隔时间 */ +extern int blankcount; /* 黑屏时间计数 */ + +#define free(x) free_s((x), 0) + +/* + * This is defined as a macro, but at some point this might become a + * real subroutine that sets a flag if it returns true (to do + * BSD-style accounting where the process is flagged if it uses root + * privs). The implication of this is that you should do normal + * permissions checks first, and check suser() last. + */ +/* + * 下面函数是以宏的形式定义的,但是在某方面来看它可以成为一个真正的子程序,如果返回是true时它 + * 将设置标志(如果使用root用户权限的进程设置了标志,则用于执行BSD方式的讨账处理)。这意味着你 + * 应该首先执行常规权限检查,最后再检测suser()。 + */ + +#define suser() (current->euid == 0) /* 检测是否为超级用户 */ + diff --git a/linux-0.12/include/linux/log_print.h b/linux-0.12/include/linux/log_print.h new file mode 100644 index 0000000..e78602f --- /dev/null +++ b/linux-0.12/include/linux/log_print.h @@ -0,0 +1,16 @@ +#ifndef _LOG_PRINT_H +#define _LOG_PRINT_H + +#define LOG_INFO 0 +#define LOG_DEBUG 1 +#define LOG_WARN 2 + +#define DEBUG + +#ifndef DEBUG +#define DEBUG_PRINT(format, ...) +#else +#define DEBUG_PRINT(format, ...) log_print(LOG_DEBUG, "[%s:%d][%s] " format, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__) +#endif + +#endif diff --git a/linux-0.12/include/linux/math_emu.h b/linux-0.12/include/linux/math_emu.h new file mode 100644 index 0000000..abe0cc2 --- /dev/null +++ b/linux-0.12/include/linux/math_emu.h @@ -0,0 +1,185 @@ +/* + * linux/include/linux/math_emu.h + * + * (C) 1991 Linus Torvalds + */ +#ifndef _LINUX_MATH_EMU_H +#define _LINUX_MATH_EMU_H + +#include + +struct info { + long ___math_ret; + long ___orig_eip; + long ___edi; + long ___esi; + long ___ebp; + long ___sys_call_ret; + long ___eax; + long ___ebx; + long ___ecx; + long ___edx; + long ___orig_eax; + long ___fs; + long ___es; + long ___ds; + long ___eip; + long ___cs; + long ___eflags; + long ___esp; + long ___ss; +}; + +#define EAX (info->___eax) +#define EBX (info->___ebx) +#define ECX (info->___ecx) +#define EDX (info->___edx) +#define ESI (info->___esi) +#define EDI (info->___edi) +#define EBP (info->___ebp) +#define ESP (info->___esp) +#define EIP (info->___eip) +#define ORIG_EIP (info->___orig_eip) +#define EFLAGS (info->___eflags) +#define DS (*(unsigned short *) &(info->___ds)) +#define ES (*(unsigned short *) &(info->___es)) +#define FS (*(unsigned short *) &(info->___fs)) +#define CS (*(unsigned short *) &(info->___cs)) +#define SS (*(unsigned short *) &(info->___ss)) + +void __math_abort(struct info *, unsigned int); + +#define math_abort(x,y) \ +(((volatile void (*)(struct info *,unsigned int)) __math_abort)((x),(y))) + +/* + * Gcc forces this stupid alignment problem: I want to use only two longs + * for the temporary real 64-bit mantissa, but then gcc aligns out the + * structure to 12 bytes which breaks things in math_emulate.c. Shit. I + * want some kind of "no-alignt" pragma or something. + */ + +typedef struct { + long a,b; + short exponent; +} temp_real; + +typedef struct { + short m0,m1,m2,m3; + short exponent; +} temp_real_unaligned; + +#define real_to_real(a,b) \ +((*(long long *) (b) = *(long long *) (a)),((b)->exponent = (a)->exponent)) + +typedef struct { + long a,b; +} long_real; + +typedef long short_real; + +typedef struct { + long a,b; + short sign; +} temp_int; + +struct swd { + int ie:1; + int de:1; + int ze:1; + int oe:1; + int ue:1; + int pe:1; + int sf:1; + int ir:1; + int c0:1; + int c1:1; + int c2:1; + int top:3; + int c3:1; + int b:1; +}; + +#define I387 (current->tss.i387) +#define SWD (*(struct swd *) &I387.swd) +#define ROUNDING ((I387.cwd >> 10) & 3) +#define PRECISION ((I387.cwd >> 8) & 3) + +#define BITS24 0 +#define BITS53 2 +#define BITS64 3 + +#define ROUND_NEAREST 0 +#define ROUND_DOWN 1 +#define ROUND_UP 2 +#define ROUND_0 3 + +#define CONSTZ (temp_real_unaligned) {0x0000,0x0000,0x0000,0x0000,0x0000} +#define CONST1 (temp_real_unaligned) {0x0000,0x0000,0x0000,0x8000,0x3FFF} +#define CONSTPI (temp_real_unaligned) {0xC235,0x2168,0xDAA2,0xC90F,0x4000} +#define CONSTLN2 (temp_real_unaligned) {0x79AC,0xD1CF,0x17F7,0xB172,0x3FFE} +#define CONSTLG2 (temp_real_unaligned) {0xF799,0xFBCF,0x9A84,0x9A20,0x3FFD} +#define CONSTL2E (temp_real_unaligned) {0xF0BC,0x5C17,0x3B29,0xB8AA,0x3FFF} +#define CONSTL2T (temp_real_unaligned) {0x8AFE,0xCD1B,0x784B,0xD49A,0x4000} + +#define set_IE() (I387.swd |= 1) +#define set_DE() (I387.swd |= 2) +#define set_ZE() (I387.swd |= 4) +#define set_OE() (I387.swd |= 8) +#define set_UE() (I387.swd |= 16) +#define set_PE() (I387.swd |= 32) + +#define set_C0() (I387.swd |= 0x0100) +#define set_C1() (I387.swd |= 0x0200) +#define set_C2() (I387.swd |= 0x0400) +#define set_C3() (I387.swd |= 0x4000) + +/* ea.c */ + +char * ea(struct info * __info, unsigned short __code); + +/* convert.c */ + +void short_to_temp(const short_real * __a, temp_real * __b); +void long_to_temp(const long_real * __a, temp_real * __b); +void temp_to_short(const temp_real * __a, short_real * __b); +void temp_to_long(const temp_real * __a, long_real * __b); +void real_to_int(const temp_real * __a, temp_int * __b); +void int_to_real(const temp_int * __a, temp_real * __b); + +/* get_put.c */ + +void get_short_real(temp_real *, struct info *, unsigned short); +void get_long_real(temp_real *, struct info *, unsigned short); +void get_temp_real(temp_real *, struct info *, unsigned short); +void get_short_int(temp_real *, struct info *, unsigned short); +void get_long_int(temp_real *, struct info *, unsigned short); +void get_longlong_int(temp_real *, struct info *, unsigned short); +void get_BCD(temp_real *, struct info *, unsigned short); +void put_short_real(const temp_real *, struct info *, unsigned short); +void put_long_real(const temp_real *, struct info *, unsigned short); +void put_temp_real(const temp_real *, struct info *, unsigned short); +void put_short_int(const temp_real *, struct info *, unsigned short); +void put_long_int(const temp_real *, struct info *, unsigned short); +void put_longlong_int(const temp_real *, struct info *, unsigned short); +void put_BCD(const temp_real *, struct info *, unsigned short); + +/* add.c */ + +void fadd(const temp_real *, const temp_real *, temp_real *); + +/* mul.c */ + +void fmul(const temp_real *, const temp_real *, temp_real *); + +/* div.c */ + +void fdiv(const temp_real *, const temp_real *, temp_real *); + +/* compare.c */ + +void fcom(const temp_real *, const temp_real *); +void fucom(const temp_real *, const temp_real *); +void ftst(const temp_real *); + +#endif diff --git a/linux-0.12/include/linux/mm.h b/linux-0.12/include/linux/mm.h new file mode 100755 index 0000000..9516916 --- /dev/null +++ b/linux-0.12/include/linux/mm.h @@ -0,0 +1,48 @@ +#ifndef _MM_H +#define _MM_H + +#define PAGE_SIZE 4096 /* 定义页面大小(字节数) */ + +#include +#include + +extern int SWAP_DEV; + +#define read_swap_page(nr,buffer) ll_rw_page(READ,SWAP_DEV,(nr),(buffer)); +#define write_swap_page(nr,buffer) ll_rw_page(WRITE,SWAP_DEV,(nr),(buffer)); + +extern unsigned long get_free_page(void); +extern unsigned long put_dirty_page(unsigned long page,unsigned long address); +extern void free_page(unsigned long addr); +extern void init_swapping(void); +void swap_free(int page_nr); +void swap_in(unsigned long *table_ptr); + +static inline void oom(void) +{ + printk("out of memory\n\r"); + do_exit(SIGSEGV); +} + +/* 刷新页变换高速缓冲(TLB)宏函数 */ +/* 为了提高地址转换的效率,CPU将最近使用的页表数据存放在芯片中高速缓冲中。在修改过页表信息之后,就 + 需要刷新该缓冲区。这里使用重新加载页目录基址寄存器CR3的方法来进行刷新。下面eax=0是页目录的基址。*/ +#define invalidate() __asm__("movl %%eax,%%cr3"::"a" (0)) + +/* these are not to be changed without changing head.s etc */ +#define LOW_MEM 0x100000 /* 物理内存地址低端1MB */ +extern unsigned long HIGH_MEMORY; /* 物理内存地址高端 */ +#define PAGING_MEMORY (15*1024*1024) /* 可分页的物理内存大小 */ +#define PAGING_PAGES (PAGING_MEMORY>>12) /* 可分页的物理内存的页面数 */ +#define MAP_NR(addr) (((addr)-LOW_MEM)>>12) /* 将物理内存地址映射成物理内存页面号 */ +#define USED 100 /* 物理内存被占用 */ + +extern unsigned char mem_map [ PAGING_PAGES ]; + +#define PAGE_DIRTY 0x40 /* 脏位 */ +#define PAGE_ACCESSED 0x20 /* 已访问位 */ +#define PAGE_USER 0x04 /* 用户/超级用户位 */ +#define PAGE_RW 0x02 /* 页面读写位 */ +#define PAGE_PRESENT 0x01 /* 页面存在位 */ + +#endif diff --git a/linux-0.12/include/linux/sched.h b/linux-0.12/include/linux/sched.h new file mode 100644 index 0000000..2e9a356 --- /dev/null +++ b/linux-0.12/include/linux/sched.h @@ -0,0 +1,330 @@ +#ifndef _SCHED_H +#define _SCHED_H + +#define HZ 100 /* 时钟滴答数, 1秒钟100个 */ + +#define NR_TASKS 64 /* 系统同时容纳的最多任务数 */ +#define TASK_SIZE 0x04000000 /* 进程的长度 */ +#define LIBRARY_SIZE 0x00400000 /* 动态加载库的长度 */ + +#if (TASK_SIZE & 0x3fffff) +#error "TASK_SIZE must be multiple of 4M" +#endif + +#if (LIBRARY_SIZE & 0x3fffff) +#error "LIBRARY_SIZE must be a multiple of 4M" +#endif + +#if (LIBRARY_SIZE >= (TASK_SIZE/2)) +#error "LIBRARY_SIZE too damn big!" +#endif + +#if (((TASK_SIZE>>16)*NR_TASKS) != 0x10000) +#error "TASK_SIZE*NR_TASKS must be 4GB" +#endif + +#define LIBRARY_OFFSET (TASK_SIZE - LIBRARY_SIZE) /* 动态库被加载的位置 */ + +#define CT_TO_SECS(x) ((x) / HZ) /* 滴答数转换成秒 */ +#define CT_TO_USECS(x) (((x) % HZ) * 1000000/HZ) /* 滴答数转换成微秒 */ + +#define FIRST_TASK task[0] +#define LAST_TASK task[NR_TASKS-1] + +#include +#include +#include +#include +#include +#include +#include + +#if (NR_OPEN > 32) +#error "Currently the close-on-exec-flags and select masks are in one long, max 32 files/proc" +#endif + +#define TASK_RUNNING 0 /* 任务正在运行或已准备就绪 */ +#define TASK_INTERRUPTIBLE 1 /* 任务处于可中断等待状态 */ +#define TASK_UNINTERRUPTIBLE 2 /* 任务处于不可中断等待状态 */ +#define TASK_ZOMBIE 3 /* 任务处于僵死状态,已经停止,但父进程还没发出信号 */ +#define TASK_STOPPED 4 /* 任务已停止 */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +extern int copy_page_tables(unsigned long from, unsigned long to, long size); +extern int free_page_tables(unsigned long from, unsigned long size); + +extern void sched_init(void); +extern void schedule(void); +extern void trap_init(void); +extern void panic(const char * str); +extern int tty_write(unsigned minor,char * buf,int count); + +typedef int (*fn_ptr)(); + +struct i387_struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ +}; + +struct tss_struct { + long back_link; /* 16 high bits zero */ + long esp0; + long ss0; /* 16 high bits zero */ + long esp1; + long ss1; /* 16 high bits zero */ + long esp2; + long ss2; /* 16 high bits zero */ + long cr3; + long eip; + long eflags; + long eax,ecx,edx,ebx; + long esp; + long ebp; + long esi; + long edi; + long es; /* 16 high bits zero */ + long cs; /* 16 high bits zero */ + long ss; /* 16 high bits zero */ + long ds; /* 16 high bits zero */ + long fs; /* 16 high bits zero */ + long gs; /* 16 high bits zero */ + long ldt; /* 16 high bits zero */ + long trace_bitmap; /* bits: trace 0, bitmap 16-31 */ + struct i387_struct i387; +}; + +/* 任务(进程)数据结构,或称为进程描述符 */ +struct task_struct { +/* these are hardcoded - don't touch */ +/* 硬编码字段 */ + long state; /* -1 unrunnable, 0 runnable, >0 stopped */ + /* 任务运行状态 -1 不可运行,0 可运行(就绪), >0 已停止 */ + long counter; /* 任务运行时间计数(递减)(滴答数),运行时间片 */ + long priority; /* 优先级 */ + long signal; /* 信号位图 */ + struct sigaction sigaction[32]; /* 信号执行属性结构,对应信号将要执行的操作和标志信息 */ + long blocked; /* 进程信号屏蔽码(对应信号位图) */ /* bitmap of masked signals */ + +/* various fields */ +/* 可变字段 */ + int exit_code; /* 退出码 */ + unsigned long start_code; /* 代码段地址 */ + unsigned long end_code; /* 代码段长度(字节数) */ + unsigned long end_data; /* 代码段加数据段的长度 (字节数)*/ + unsigned long brk; /* 总长度(字节数) */ + unsigned long start_stack; /* 堆栈段地址 */ + long pid; /* 进程标识号(进程号) */ + long pgrp; /* 进程组号 */ + long session; /* 会话号 */ + long leader; /* 会话首领 */ + int groups[NGROUPS]; /* 进程所属组号(一个进程可属于多个组) */ + /* + * pointers to parent process, youngest child, younger sibling, + * older sibling, respectively. (p->father can be replaced with + * p->p_pptr->pid) + */ + struct task_struct *p_pptr; /* 指向父进程的指针 */ + struct task_struct *p_cptr; /* 指向最新子进程的指针 */ + struct task_struct *p_ysptr; /* 指向比自己后创建的相邻进程的指针 */ + struct task_struct *p_osptr; /* 指向比自己早创建的相邻进程的指针 */ + unsigned short uid; /* 用户id */ + unsigned short euid; /* 有效用户id */ + unsigned short suid; /* 保存的设置用户id */ + unsigned short gid; /* 组id */ + unsigned short egid; /* 有效组id */ + unsigned short sgid; /* 保存的设置组id */ + unsigned long timeout; /* 内核定时超时值 */ + unsigned long alarm; /* 报警定时值(滴答数) */ + long utime; /* 用户态运行时间(滴答数) */ + long stime; /* 内核态运行时间(滴答数) */ + long cutime; /* 子进程用户态运行时间 */ + long cstime; /* 子进程内核态运行时间 */ + long start_time; /* 进程开始运行时刻 */ + struct rlimit rlim[RLIM_NLIMITS]; /* 进程资源使用统计数组 */ + unsigned int flags; /* per process flags, defined below */ + /* 各进程的标志 */ + unsigned short used_math; /* 是否使用了协处理器的标志 */ +/* file system info */ + int tty; /* -1 if no tty, so it must be signed */ + /* 进程使用tty终端的子设备号。-1表示没有使用 */ + unsigned short umask; /* 文件创建属性屏蔽位 */ + struct m_inode * pwd; /* 当前工作目录i节点结构指针 */ + struct m_inode * root; /* 根目录i节点结构指针 */ + struct m_inode * executable; /* 执行文件i节点结构指针 */ + struct m_inode * library; /* 被加载库文件i节点结构指针 */ + unsigned long close_on_exec; /* 执行时关闭文件句柄位图标志 */ + struct file * filp[NR_OPEN]; /* 文件结构指针表,最多32项。表项号即是文件描述符的值 */ +/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ + struct desc_struct ldt[3]; /* 局部描述符表, 0 - 空,1 - 代码段cs,2 - 数据和堆栈段ds&ss */ +/* tss for this task */ + struct tss_struct tss; /* 进程的任务状态段信息结构 */ +}; + +/* + * Per process flags + */ +/* 每个进程的标志 */ /* 打印对齐警告信息。还未实现,仅用于486 */ +#define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */ + /* Not implemented yet, only for 486*/ + +/* + * INIT_TASK is used to set up the first task table, touch at + * your own risk!. Base=0, limit=0x9ffff (=640kB) + */ +#define INIT_TASK \ +/* state etc */ { 0,15,15, \ +/* signals */ 0,{{},},0, \ +/* ec,brk... */ 0,0,0,0,0,0, \ +/* pid etc.. */ 0,0,0,0, \ +/* suppl grps*/ {NOGROUP,}, \ +/* proc links*/ &init_task.task,0,0,0, \ +/* uid etc */ 0,0,0,0,0,0, \ +/* timeout */ 0,0,0,0,0,0,0, \ +/* rlimits */ { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \ + {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \ + {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, \ +/* flags */ 0, \ +/* math */ 0, \ +/* fs info */ -1,0022,NULL,NULL,NULL,NULL,0, \ +/* filp */ {NULL,}, \ + { \ + {0,0}, \ +/* ldt */ {0x9f,0xc0fa00}, \ + {0x9f,0xc0f200}, \ + }, \ +/*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\ + 0,0,0,0,0,0,0,0, \ + 0,0,0x17,0x17,0x17,0x17,0x17,0x17, \ + _LDT(0),0x80000000, \ + {} \ + }, \ +} + +extern struct task_struct *task[NR_TASKS]; /* 任务指针数组 */ +extern struct task_struct *last_task_used_math; /* 上一个使用过协处理器的进程 */ +extern struct task_struct *current; /* 当前运行进程结构指针变量 */ +extern unsigned long volatile jiffies; /* 从开机开始算起的滴答数 */ +extern unsigned long startup_time; /* 开机时间,从1970:0:0:0:0开始计时的秒数 */ +extern int jiffies_offset; /* 用于累计需要调整的时间滴答数 */ + +#define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ) /* 当前时间(秒数) */ + +extern void add_timer(long jiffies, void (*fn)(void)); +extern void sleep_on(struct task_struct ** p); +extern void interruptible_sleep_on(struct task_struct ** p); +extern void wake_up(struct task_struct ** p); +extern int in_group_p(gid_t grp); + +/* + * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall + * 4-TSS0, 5-LDT0, 6-TSS1 etc ... + */ +/* 全局表中任务0的状态段(TSS)和局部描述符表(LDT)的描述符的选择符索引号 */ +#define FIRST_TSS_ENTRY 4 +#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1) + +/* 每个描述符占8字节,因此FIRST_TSS_ENTRY<<3表示该描述符在GDT表中的起始偏移位置。 + 因为每个任务使用1个TSS和1个LDT描述符,共占用16字节,因此需要n<<4来表示对应 + TSS起始位置 */ + +/* 第n个任务的TSS段描述符的偏移值 */ +#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3)) + +/* 第n个任务的LDT段描述符的偏移值 */ +#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3)) + +#define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n))) +#define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n))) +#define str(n) \ +__asm__("str %%ax\n\t" \ + "subl %2,%%eax\n\t" \ + "shrl $4,%%eax" \ + :"=a" (n) \ + :"a" (0),"i" (FIRST_TSS_ENTRY<<3)) +/* + * switch_to(n) should switch tasks to task nr n, first + * checking that n isn't the current task, in which case it does nothing. + * This also clears the TS-flag if the task we switched to has used + * tha math co-processor latest. + */ +#define switch_to(n) { \ +struct {long a,b;} __tmp; \ +__asm__("cmpl %%ecx,current\n\t" \ + "je 1f\n\t" \ + "movw %%dx,%1\n\t" \ + "xchgl %%ecx,current\n\t" \ + "ljmp *%0\n\t" \ + "cmpl %%ecx,last_task_used_math\n\t" \ + "jne 1f\n\t" \ + "clts\n" \ + "1:" \ + ::"m" (*&__tmp.a),"m" (*&__tmp.b), \ + "d" (_TSS(n)),"c" ((long) task[n])); \ +} + +/* 页面地址对准(在内核代码中没有任何地方引用!!)*/ +#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000) + +/* 设置位于地址addr处描述符中的各基地址字段 */ +#define _set_base(addr,base) \ +__asm__ ( \ + "push %%edx\n\t" \ + "movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %%dl,%1\n\t" \ + "movb %%dh,%2\n\t" \ + "pop %%edx" \ + ::"m" (*((addr)+2)), \ + "m" (*((addr)+4)), \ + "m" (*((addr)+7)), \ + "d" (base) \ + ) + +/* 设置位于地址addr处描述符中的段限长字段 */ +#define _set_limit(addr,limit) \ +__asm__("movw %%dx,%0\n\t" \ + "rorl $16,%%edx\n\t" \ + "movb %1,%%dh\n\t" \ + "andb $0xf0,%%dh\n\t" \ + "orb %%dh,%%dl\n\t" \ + "movb %%dl,%1" \ + ::"m" (*(addr)), \ + "m" (*((addr)+6)), \ + "d" (limit) \ + ) + +#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base ) +#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 ) + +/* 从地址addr处描述符中取段基地址 */ +#define _get_base(addr) ({ \ +unsigned long __base; \ +__asm__("movb %3,%%dh\n\t" \ + "movb %2,%%dl\n\t" \ + "shll $16,%%edx\n\t" \ + "movw %1,%%dx" \ + :"=&d" (__base) \ + :"m" (*((addr)+2)), \ + "m" (*((addr)+4)), \ + "m" (*((addr)+7))); \ +__base;}) + +/* 取局部描述符表中ldt所指段描述符中的基地址 */ +#define get_base(ldt) _get_base( ((char *)&(ldt)) ) + +/* 取段选择符segment指定的描述符中的段限长值 */ +#define get_limit(segment) ({ \ + unsigned long __limit; \ + __asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \ + __limit;}) + +#endif diff --git a/linux-0.12/include/linux/sys.h b/linux-0.12/include/linux/sys.h new file mode 100644 index 0000000..18510c5 --- /dev/null +++ b/linux-0.12/include/linux/sys.h @@ -0,0 +1,115 @@ +/* + * Why isn't this a .c file? Enquiring minds.... + */ +/* + * 为什么这不是一个.c文件?动动脑筋自己想想.... + */ + +extern int sys_setup(); +extern int sys_exit(); +extern int sys_fork(); +extern int sys_read(); +extern int sys_write(); +extern int sys_open(); +extern int sys_close(); +extern int sys_waitpid(); +extern int sys_creat(); +extern int sys_link(); +extern int sys_unlink(); +extern int sys_execve(); +extern int sys_chdir(); +extern int sys_time(); +extern int sys_mknod(); +extern int sys_chmod(); +extern int sys_chown(); +extern int sys_break(); +extern int sys_stat(); +extern int sys_lseek(); +extern int sys_getpid(); +extern int sys_mount(); +extern int sys_umount(); +extern int sys_setuid(); +extern int sys_getuid(); +extern int sys_stime(); +extern int sys_ptrace(); +extern int sys_alarm(); +extern int sys_fstat(); +extern int sys_pause(); +extern int sys_utime(); +extern int sys_stty(); +extern int sys_gtty(); +extern int sys_access(); +extern int sys_nice(); +extern int sys_ftime(); +extern int sys_sync(); +extern int sys_kill(); +extern int sys_rename(); +extern int sys_mkdir(); +extern int sys_rmdir(); +extern int sys_dup(); +extern int sys_pipe(); +extern int sys_times(); +extern int sys_prof(); +extern int sys_brk(); +extern int sys_setgid(); +extern int sys_getgid(); +extern int sys_signal(); +extern int sys_geteuid(); +extern int sys_getegid(); +extern int sys_acct(); +extern int sys_phys(); +extern int sys_lock(); +extern int sys_ioctl(); +extern int sys_fcntl(); +extern int sys_mpx(); +extern int sys_setpgid(); +extern int sys_ulimit(); +extern int sys_uname(); +extern int sys_umask(); +extern int sys_chroot(); +extern int sys_ustat(); +extern int sys_dup2(); +extern int sys_getppid(); +extern int sys_getpgrp(); +extern int sys_setsid(); +extern int sys_sigaction(); +extern int sys_sgetmask(); +extern int sys_ssetmask(); +extern int sys_setreuid(); +extern int sys_setregid(); +extern int sys_sigpending(); +extern int sys_sigsuspend(); +extern int sys_sethostname(); +extern int sys_setrlimit(); +extern int sys_getrlimit(); +extern int sys_getrusage(); +extern int sys_gettimeofday(); +extern int sys_settimeofday(); +extern int sys_getgroups(); +extern int sys_setgroups(); +extern int sys_select(); +extern int sys_symlink(); +extern int sys_lstat(); +extern int sys_readlink(); +extern int sys_uselib(); + +/* 系统调用处理程序的指针数组表 */ +fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, +sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, +sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod, +sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount, +sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm, +sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access, +sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir, +sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, +sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, +sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit, +sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, +sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask, +sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending, sys_sethostname, +sys_setrlimit, sys_getrlimit, sys_getrusage, sys_gettimeofday, +sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink, +sys_lstat, sys_readlink, sys_uselib }; + +/* So we don't have to do any more manual updating.... */ +int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr); diff --git a/linux-0.12/include/linux/tty.h b/linux-0.12/include/linux/tty.h new file mode 100755 index 0000000..ba6ac5f --- /dev/null +++ b/linux-0.12/include/linux/tty.h @@ -0,0 +1,100 @@ +/* + * 'tty.h' defines some structures used by tty_io.c and some defines. + * + * NOTE! Don't touch this without checking that nothing in rs_io.s or + * con_io.s breaks. Some constants are hardwired into the system (mainly + * offsets into 'tty_queue' + */ + +#ifndef _TTY_H +#define _TTY_H + +#define MAX_CONSOLES 8 +#define NR_SERIALS 2 +#define NR_PTYS 4 + +extern int NR_CONSOLES; + +#include + +#define TTY_BUF_SIZE 1024 + +struct tty_queue { + unsigned long data; + unsigned long head; + unsigned long tail; + struct task_struct * proc_list; + char buf[TTY_BUF_SIZE]; +}; + +#define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00) +#define IS_A_SERIAL(min) (((min) & 0xC0) == 0x40) +#define IS_A_PTY(min) ((min) & 0x80) +#define IS_A_PTY_MASTER(min) (((min) & 0xC0) == 0x80) +#define IS_A_PTY_SLAVE(min) (((min) & 0xC0) == 0xC0) +#define PTY_OTHER(min) ((min) ^ 0x40) + +#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1)) +#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1)) +#define EMPTY(a) ((a)->head == (a)->tail) +#define LEFT(a) (((a)->tail-(a)->head-1)&(TTY_BUF_SIZE-1)) +#define LAST(a) ((a)->buf[(TTY_BUF_SIZE-1)&((a)->head-1)]) +#define FULL(a) (!LEFT(a)) +#define CHARS(a) (((a)->head-(a)->tail)&(TTY_BUF_SIZE-1)) +#define GETCH(queue,c) \ +(void)({c=(queue)->buf[(queue)->tail];INC((queue)->tail);}) +#define PUTCH(c,queue) \ +(void)({(queue)->buf[(queue)->head]=(c);INC((queue)->head);}) + +#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) +#define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT]) +#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE]) +#define KILL_CHAR(tty) ((tty)->termios.c_cc[VKILL]) +#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF]) +#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART]) +#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) +#define SUSPEND_CHAR(tty) ((tty)->termios.c_cc[VSUSP]) + +/* tty数据结构 */ +struct tty_struct { + struct termios termios; /* 终端io属性和控制字符数据结构 */ + int pgrp; /* 所属进程组 */ + int session; /* 会话号 */ + int stopped; /* 停止标志 */ + void (*write)(struct tty_struct * tty); /* tty写函数指针 */ + struct tty_queue *read_q; /* tty读队列 */ + struct tty_queue *write_q; /* tty写队列 */ + struct tty_queue *secondary;/* tty辅助队列(存放规范模式字符序列) */ + }; + +extern struct tty_struct tty_table[]; +extern int fg_console; + +#define TTY_TABLE(nr) \ +(tty_table + ((nr) ? (((nr) < 64)?(nr)-1:(nr)) : fg_console)) + +/* intr=^C quit=^| erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Z eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" + +void rs_init(void); +void con_init(void); +void tty_init(void); + +int tty_read(unsigned c, char * buf, int n); +int tty_write(unsigned c, char * buf, int n); + +void con_write(struct tty_struct * tty); +void rs_write(struct tty_struct * tty); +void mpty_write(struct tty_struct * tty); +void spty_write(struct tty_struct * tty); + +void copy_to_cooked(struct tty_struct * tty); + +void update_screen(void); + +#endif diff --git a/linux-0.12/include/signal.h b/linux-0.12/include/signal.h new file mode 100755 index 0000000..fd2882e --- /dev/null +++ b/linux-0.12/include/signal.h @@ -0,0 +1,77 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include + +typedef int sig_atomic_t; /* 信号原子类型操作 */ +typedef unsigned int sigset_t; /* 32 bits */ + +#define _NSIG 32 /* 信号种类 */ +#define NSIG _NSIG + +#define SIGHUP 1 /* Hang Up -- 挂起控制终端或进程 */ +#define SIGINT 2 /* Interrupt -- 来自键盘的中断 */ +#define SIGQUIT 3 /* Quit -- 来自键盘的退出 */ +#define SIGILL 4 /* Illeagle -- 非法指令 */ +#define SIGTRAP 5 /* Trap -- 跟踪断点 */ +#define SIGABRT 6 /* Abort -- 异常结束 */ +#define SIGIOT 6 /* IO Trap -- 同上 */ +#define SIGUNUSED 7 /* Unused -- 没有使用 */ +#define SIGFPE 8 /* FPE -- 协处理器出错 */ +#define SIGKILL 9 /* Kill -- 强迫进程终止 */ +#define SIGUSR1 10 /* User1 -- 用户信号 1,进程可使用 */ +#define SIGSEGV 11 /* Segment Violation -- 无效内存引用 */ +#define SIGUSR2 12 /* User2 -- 用户信号 2,进程可使用 */ +#define SIGPIPE 13 /* Pipe -- 管道写出错,无读者 */ +#define SIGALRM 14 /* Alarm -- 实时定时器报警 */ +#define SIGTERM 15 /* Terminate -- 进程终止 */ +#define SIGSTKFLT 16 /* Stack Fault -- 栈出错(协处理器) */ +#define SIGCHLD 17 /* Child -- 子进程停止或被终止 */ +#define SIGCONT 18 /* Continue -- 恢复进程继续执行 */ +#define SIGSTOP 19 /* Stop -- 停止进程的执行 */ +#define SIGTSTP 20 /* TTY Stop -- tty 发出停止进程,可忽略 */ +#define SIGTTIN 21 /* TTY In -- 后台进程请求输入 */ +#define SIGTTOU 22 /* TTY Out -- 后台进程请求输出 */ + +/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */ +/* 上面原注释已经过时,因为在0.12内核中已经实现了sigaction()。下面是sigaction结构 +sa_flags标志字段可取的符号常数值 */ +#define SA_NOCLDSTOP 1 /* 当子进程处于停止状态,就不对SIGCHLD处理 */ +#define SA_INTERRUPT 0x20000000 /* 系统调用被信号中断后不重新启动系统调用 */ +#define SA_NOMASK 0x40000000 /* 不阻止在指定的信号处理程序中再收到该信号 */ +#define SA_ONESHOT 0x80000000 /* 信号句柄一旦被调用过就恢复到默认处理句柄 */ + +#define SIG_BLOCK 0 /* for blocking signals */ /* 在阻塞信号集中加上给定信号 */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ /* 从阻塞信号集中删除指定信号 */ +#define SIG_SETMASK 2 /* for setting the signal mask */ /* 设置阻塞信号集 */ + +#define SIG_DFL ((void (*)(int))0) /* default signal handling */ /* 默认信号处理程序(信号句柄) */ +#define SIG_IGN ((void (*)(int))1) /* ignore signal */ /* 忽略信号的处理程序 */ +#define SIG_ERR ((void (*)(int))-1) /* error return from signal */ /* 信号处理返回错误 */ + +#ifdef notdef +#define sigemptyset(mask) ((*(mask) = 0), 1) /* 将mask清零 */ +#define sigfillset(mask) ((*(mask) = ~0), 1) /* 将mask所有比特位置位 */ +#endif + +struct sigaction { + void (*sa_handler)(int); /* 对应某信号指定要采取的行动 */ + sigset_t sa_mask; /* 对信号的屏蔽码,在信号程序执行时将阻塞对这些信号的处理 */ + int sa_flags; /* 改变信号处理过程的信号集 */ + void (*sa_restorer)(void);/* 恢复函数指针,由函数库Libc提供,用于清理用户态堆栈 */ +}; + +void (*signal(int _sig, void (*_func)(int)))(int); +int raise(int sig); +int kill(pid_t pid, int sig); +int sigaddset(sigset_t *mask, int signo); +int sigdelset(sigset_t *mask, int signo); +int sigemptyset(sigset_t *mask); +int sigfillset(sigset_t *mask); +int sigismember(sigset_t *mask, int signo); /* 1 - is, 0 - not, -1 error */ +int sigpending(sigset_t *set); +int sigprocmask(int how, sigset_t *set, sigset_t *oldset); +int sigsuspend(sigset_t *sigmask); +int sigaction(int sig, struct sigaction *act, struct sigaction *oldact); + +#endif /* _SIGNAL_H */ diff --git a/linux-0.12/include/stdarg.h b/linux-0.12/include/stdarg.h new file mode 100755 index 0000000..246d713 --- /dev/null +++ b/linux-0.12/include/stdarg.h @@ -0,0 +1,30 @@ +#ifndef _STDARG_H +#define _STDARG_H + +typedef char *va_list; + +/* Amount of space required in an argument list for an arg of type TYPE. + TYPE may alternatively be an expression whose type is used. */ +/* 下面给出了类型为TYPE的arg参数列表所要求的空间容量。TYPE也可以是使用该类型的一个表达式 */ + + +#define __va_rounded_size(TYPE) \ + (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) + +#ifndef __sparc__ +#define va_start(AP, LASTARG) \ + (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#else +#define va_start(AP, LASTARG) \ + (__builtin_saveregs (), \ + AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#endif + +void va_end (va_list); /* Defined in gnulib */ +#define va_end(AP) + +#define va_arg(AP, TYPE) \ + (AP += __va_rounded_size (TYPE), \ + *((TYPE *) (AP - __va_rounded_size (TYPE)))) + +#endif /* _STDARG_H */ diff --git a/linux-0.12/include/stddef.h b/linux-0.12/include/stddef.h new file mode 100644 index 0000000..697c4f4 --- /dev/null +++ b/linux-0.12/include/stddef.h @@ -0,0 +1,18 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef long ptrdiff_t; +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned long size_t; +#endif + +#undef NULL +#define NULL ((void *)0) + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif diff --git a/linux-0.12/include/string.h b/linux-0.12/include/string.h new file mode 100644 index 0000000..bef2f07 --- /dev/null +++ b/linux-0.12/include/string.h @@ -0,0 +1,413 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +extern char * strerror(int errno); + +/* + * This string-include defines all string functions as inline + * functions. Use gcc. It also assumes ds=es=data space, this should be + * normal. Most of the string-functions are rather heavily hand-optimized, + * see especially strtok,strstr,str[c]spn. They should work, but are not + * very easy to understand. Everything is done entirely within the register + * set, making the functions fast and clean. String instructions have been + * used through-out, making for "slightly" unclear code :-) + * + * (C) 1991 Linus Torvalds + */ + +static inline char * strcpy(char * dest,const char *src) +{ +__asm__("cld\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + ::"S" (src),"D" (dest) + ); +return dest; +} + +static inline char * strncpy(char * dest,const char *src,int count) +{ +__asm__("cld\n" + "1:\tdecl %2\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "rep\n\t" + "stosb\n" + "2:" + ::"S" (src),"D" (dest),"c" (count) + ); +return dest; +} + +static inline char * strcat(char * dest,const char * src) +{ +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff) + ); +return dest; +} + +static inline char * strncat(char * dest,const char * src,int count) +{ +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n\t" + "movl %4,%3\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %2,%2\n\t" + "stosb" + ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count) + ); +return dest; +} + +static inline int strcmp(const char * cs,const char * ct) +{ +register int __res; +__asm__("cld\n" + "1:\tlodsb\n\t" + "scasb\n\t" + "jne 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "jmp 3f\n" + "2:\tmovl $1,%%eax\n\t" + "jl 3f\n\t" + "negl %%eax\n" + "3:" + :"=a" (__res):"D" (cs),"S" (ct) + ); +return __res; +} + +static inline int strncmp(const char * cs,const char * ct,int count) +{ +register int __res; +__asm__("cld\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "scasb\n\t" + "jne 3f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %%eax,%%eax\n\t" + "jmp 4f\n" + "3:\tmovl $1,%%eax\n\t" + "jl 4f\n\t" + "negl %%eax\n" + "4:" + :"=a" (__res):"D" (cs),"S" (ct),"c" (count) + ); +return __res; +} + +static inline char * strchr(const char * s,char c) +{ +register char * __res; +__asm__("cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "je 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "movl $1,%1\n" + "2:\tmovl %1,%0\n\t" + "decl %0" + :"=a" (__res):"S" (s),"0" (c) + ); +return __res; +} + +static inline char * strrchr(const char * s,char c) +{ +register char * __res; +__asm__("cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "jne 2f\n\t" + "movl %%esi,%0\n\t" + "decl %0\n" + "2:\ttestb %%al,%%al\n\t" + "jne 1b" + :"=d" (__res):"0" (0),"S" (s),"a" (c) + ); +return __res; +} + +static inline int strspn(const char * cs, const char * ct) +{ +register char * __res; +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + ); +return __res-cs; +} + +static inline int strcspn(const char * cs, const char * ct) +{ +register char * __res; +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + ); +return __res-cs; +} + +static inline char * strpbrk(const char * cs,const char * ct) +{ +register char * __res; +__asm__("cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n\t" + "decl %0\n\t" + "jmp 3f\n" + "2:\txorl %0,%0\n" + "3:" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + ); +return __res; +} + +static inline char * strstr(const char * cs,const char * ct) +{ +register char * __res; +__asm__("cld\n\t" \ + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */ + "movl %%ecx,%%edx\n" + "1:\tmovl %4,%%edi\n\t" + "movl %%esi,%%eax\n\t" + "movl %%edx,%%ecx\n\t" + "repe\n\t" + "cmpsb\n\t" + "je 2f\n\t" /* also works for empty string, see above */ + "xchgl %%eax,%%esi\n\t" + "incl %%esi\n\t" + "cmpb $0,-1(%%eax)\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "2:" + :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct) + ); +return __res; +} + +static inline int strlen(const char * s) +{ +register int __res; +__asm__("cld\n\t" + "repne\n\t" + "scasb\n\t" + "notl %0\n\t" + "decl %0" + :"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff) + ); +return __res; +} + +char * ___strtok; + +static inline char * strtok(char * s,const char * ct) +{ +register char * __res; +__asm__("testl %1,%1\n\t" + "jne 1f\n\t" + "testl %0,%0\n\t" + "je 8f\n\t" + "movl %0,%1\n" + "1:\txorl %0,%0\n\t" + "movl $-1,%%ecx\n\t" + "xorl %%eax,%%eax\n\t" + "cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "je 7f\n\t" /* empty delimeter-string */ + "movl %%ecx,%%edx\n" + "2:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 7f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 2b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 7f\n\t" + "movl %1,%0\n" + "3:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 5f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 3b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 5f\n\t" + "movb $0,(%1)\n\t" + "incl %1\n\t" + "jmp 6f\n" + "5:\txorl %1,%1\n" + "6:\tcmpb $0,(%0)\n\t" + "jne 7f\n\t" + "xorl %0,%0\n" + "7:\ttestl %0,%0\n\t" + "jne 8f\n\t" + "movl %0,%1\n" + "8:" + :"=b" (__res),"=S" (___strtok) + :"0" (___strtok),"1" (s),"g" (ct) + ); +return __res; +} + +static inline void * memcpy(void * dest,const void * src, int n) +{ +__asm__("cld\n\t" + "rep\n\t" + "movsb" + ::"c" (n),"S" (src),"D" (dest) + ); +return dest; +} + +static inline void * memmove(void * dest,const void * src, int n) +{ +if (dest + +struct stat { + dev_t st_dev; /* 含有文件的设备号 */ + ino_t st_ino; /* 文件i节点号 */ + umode_t st_mode; /* 文件类型和属性(见下面) */ + nlink_t st_nlink; /* 指定文件的连接数 */ + uid_t st_uid; /* 文件的用户(标识)号 */ + gid_t st_gid; /* 文件的组号 */ + dev_t st_rdev; /* 设备号(如果文件是特殊的字符文件或块文件) */ + off_t st_size; /* 文件大小(字节数)(如果文件是常规文件) */ + time_t st_atime; /* 上次(最后)访问时间 */ + time_t st_mtime; /* 最后修改时间 */ + time_t st_ctime; /* 最后节点修改时间 */ +}; + +// 下面是为st_mode字段所用的值定义的符号名称。这些值均用八进制表示,为便于记忆,这些符号名称均 +// 为一些英文单词的首字母或缩写组合而成。例如名称S_IFMT的每个字母分别代表单词State,Inode,File, +// Mask和Type;而名称S_IFREG则是State,Inode,File和REGular几个大写字母的组合;名称S_IRWXU是 +// State,Inode,Read,Write,Execute和User的组合。其他名称可以此类推。 + +/* 文件类型: */ +#define S_IFMT 00170000 /* 文件类型位屏蔽码(8进制表示) */ +#define S_IFLNK 0120000 /* 符号链接 */ +#define S_IFREG 0100000 /* 常规文件 */ +#define S_IFBLK 0060000 /* 块特殊(设备)文件,如磁盘dev/fd0 */ +#define S_IFDIR 0040000 /* 目录 */ +#define S_IFCHR 0020000 /* 字符设备文件 */ +#define S_IFIFO 0010000 /* FIFO特殊文件 */ + +/* 文件属性位: */ +// S_ISUID用于测试文件的set-user-ID标志是否置位。若该标志置位,则当执行该文件时,进程的有效用 +// 户ID将被设置为该文件宿主的用户ID。S_ISGID则是针对组ID进行相同处理。 +#define S_ISUID 0004000 /* 执行时设置用户ID(set-user-ID) */ +#define S_ISGID 0002000 /* 执行时设置组ID(set-group-ID) */ +#define S_ISVTX 0001000 /* 对于目录,受限删除标志 */ + +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* 是否为符号链接文件 */ +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* 是否为常规文件 */ +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* 是否为目录文件 */ +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* 是否为字符设备文件 */ +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* 是否为块设备文件 */ +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* 是否为FIFO特殊文件 */ + +/* 文件访问权限:*/ +/* User */ +#define S_IRWXU 00700 /* 宿主可以读、写、执行/搜索(名称最后字母代表User) */ +#define S_IRUSR 00400 /* 宿主读许可 */ +#define S_IWUSR 00200 /* 宿主写许可 */ +#define S_IXUSR 00100 /* 宿主执行/搜索许可 */ + +/* Group */ +#define S_IRWXG 00070 /* 组成员可以读、写、执行/搜索(名称最后字母代表Group) */ +#define S_IRGRP 00040 /* 组成员读许可 */ +#define S_IWGRP 00020 /* 组成员写许可 */ +#define S_IXGRP 00010 /* 组成员执行/搜索许可 */ + +/* Other */ +#define S_IRWXO 00007 /* 其他人读、写、执行/搜索许可(名称最后字母O代表Other) */ +#define S_IROTH 00004 /* 其他人读许可(最后3个字母代表Other)*/ +#define S_IWOTH 00002 /* 其他人写许可 */ +#define S_IXOTH 00001 /* 其他人执行/搜索许可 */ + +/* 修改文件属性 */ +extern int chmod(const char *_path, mode_t mode); + +/* 取指定文件句柄的文件状态信息 */ +extern int fstat(int fildes, struct stat *stat_buf); + +/* 创建目录 */ +extern int mkdir(const char *_path, mode_t mode); + +/* 创建管道文件 */ +extern int mkfifo(const char *_path, mode_t mode); + +/* 取指定文件名的文件状态信息 */ +extern int stat(const char *filename, struct stat *stat_buf); + +/* 设置属性屏蔽码 */ +extern mode_t umask(mode_t mask); + +#endif diff --git a/linux-0.12/include/sys/time.h b/linux-0.12/include/sys/time.h new file mode 100644 index 0000000..bb1fb7d --- /dev/null +++ b/linux-0.12/include/sys/time.h @@ -0,0 +1,63 @@ +#ifndef _SYS_TIME_H +#define _SYS_TIME_H + +/* gettimofday returns this */ +struct timeval { + long tv_sec; /* seconds */ /* 秒 */ + long tv_usec; /* microseconds */ /* 微秒 */ +}; + +struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +#define DST_NONE 0 /* not on dst */ +#define DST_USA 1 /* USA style dst */ +#define DST_AUST 2 /* Australian style dst */ +#define DST_WET 3 /* Western European dst */ +#define DST_MET 4 /* Middle European dst */ +#define DST_EET 5 /* Eastern European dst */ +#define DST_CAN 6 /* Canada */ +#define DST_GB 7 /* Great Britain and Eire */ +#define DST_RUM 8 /* Rumania */ +#define DST_TUR 9 /* Turkey */ +#define DST_AUSTALT 10 /* Australian style with shift in 1986 */ + +#define FD_SET(fd,fdsetp) (*(fdsetp) |= (1 << (fd))) +#define FD_CLR(fd,fdsetp) (*(fdsetp) &= ~(1 << (fd))) +#define FD_ISSET(fd,fdsetp) ((*(fdsetp) >> fd) & 1) +#define FD_ZERO(fdsetp) (*(fdsetp) = 0) + +/* + * Operations on timevals. + * + * NB: timercmp does not work for >= or <=. + */ +#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#define timercmp(tvp, uvp, cmp) \ + ((tvp)->tv_sec cmp (uvp)->tv_sec || \ + (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec) +#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) + +/* + * Names of the interval timers, and structure + * defining a timer setting. + */ +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +struct itimerval { + struct timeval it_interval; /* timer interval */ + struct timeval it_value; /* current value */ +}; + +#include +#include + +int gettimeofday(struct timeval * tp, struct timezone * tz); +int select(int width, fd_set * readfds, fd_set * writefds, + fd_set * exceptfds, struct timeval * timeout); + +#endif /*_SYS_TIME_H*/ diff --git a/linux-0.12/include/sys/times.h b/linux-0.12/include/sys/times.h new file mode 100644 index 0000000..2a11408 --- /dev/null +++ b/linux-0.12/include/sys/times.h @@ -0,0 +1,15 @@ +#ifndef _TIMES_H +#define _TIMES_H + +#include + +struct tms { + time_t tms_utime; /* 用户使用CPU的时间 */ + time_t tms_stime; /* 系统(内核)CPU时间 */ + time_t tms_cutime; /* 已终止的子进程使用的用户CPU时间 */ + time_t tms_cstime; /* 已终止的子进程使用的系统CPU时间 */ +}; + +extern time_t times(struct tms * tp); + +#endif diff --git a/linux-0.12/include/sys/types.h b/linux-0.12/include/sys/types.h new file mode 100644 index 0000000..75afec9 --- /dev/null +++ b/linux-0.12/include/sys/types.h @@ -0,0 +1,54 @@ +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; +#endif + +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef long ptrdiff_t; +#endif + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +typedef int pid_t; +typedef unsigned short uid_t; +typedef unsigned short gid_t; +typedef unsigned short dev_t; +typedef unsigned short ino_t; +typedef unsigned short mode_t; +typedef unsigned short umode_t; +typedef unsigned char nlink_t; +typedef int daddr_t; +typedef long off_t; +typedef unsigned char u_char; +typedef unsigned short ushort; + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned long tcflag_t; + +typedef unsigned long fd_set; + +typedef struct { int quot,rem; } div_t; +typedef struct { long quot,rem; } ldiv_t; + +/* 文件系统参数结构,用于ustat()函数 */ +// Q: 为什么这个结构体没有放在文件系统相关的头文件fs.h中? +struct ustat { + daddr_t f_tfree; /* 系统总空闲块数 */ + ino_t f_tinode; /* 总空闲i节点数 */ + char f_fname[6]; /* 文件系统名称 */ + char f_fpack[6]; /* 文件系统压缩名称 */ +}; + +#endif diff --git a/linux-0.12/include/sys/utsname.h b/linux-0.12/include/sys/utsname.h new file mode 100644 index 0000000..355e37a --- /dev/null +++ b/linux-0.12/include/sys/utsname.h @@ -0,0 +1,17 @@ +#ifndef _SYS_UTSNAME_H +#define _SYS_UTSNAME_H + +#include +#include + +struct utsname { + char sysname[9]; /* 当前运行系统的名称 */ + char nodename[MAXHOSTNAMELEN+1]; /* 与实现相关的网络中节点名称(主机名称) */ + char release[9]; /* 本操作系统实现的当前发行级别 */ + char version[9]; /* 本次发行的操作系统版本级别 */ + char machine[9]; /* 系统运行的硬件类型名称 */ +}; + +extern int uname(struct utsname * utsbuf); + +#endif diff --git a/linux-0.12/include/sys/wait.h b/linux-0.12/include/sys/wait.h new file mode 100644 index 0000000..d6c3360 --- /dev/null +++ b/linux-0.12/include/sys/wait.h @@ -0,0 +1,24 @@ +#ifndef _SYS_WAIT_H +#define _SYS_WAIT_H + +#include + +#define _LOW(v) ( (v) & 0377) +#define _HIGH(v) ( ((v) >> 8) & 0377) + +/* options for waitpid, WUNTRACED not supported */ +#define WNOHANG 1 +#define WUNTRACED 2 + +#define WIFEXITED(s) (!((s)&0xFF)) +#define WIFSTOPPED(s) (((s)&0xFF)==0x7F) +#define WEXITSTATUS(s) (((s)>>8)&0xFF) +#define WTERMSIG(s) ((s)&0x7F) +#define WCOREDUMP(s) ((s)&0x80) +#define WSTOPSIG(s) (((s)>>8)&0xFF) +#define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF) + +pid_t wait(int *stat_loc); +pid_t waitpid(pid_t pid, int *stat_loc, int options); + +#endif diff --git a/linux-0.12/include/termios.h b/linux-0.12/include/termios.h new file mode 100755 index 0000000..8bf7fab --- /dev/null +++ b/linux-0.12/include/termios.h @@ -0,0 +1,226 @@ +#ifndef _TERMIOS_H +#define _TERMIOS_H + +#include + +#define TTY_BUF_SIZE 1024 + +/* 0x54 is just a magic number to make these relatively uniqe ('T') */ + +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCGETA 0x5405 +#define TCSETA 0x5406 +#define TCSETAW 0x5407 +#define TCSETAF 0x5408 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TCFLSH 0x540B +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define FIONREAD 0x541B +#define TIOCINQ FIONREAD + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +#define NCCS 17 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0040000 +#define FF0 0000000 +#define FF1 0040000 + +/* c_cflag bit meaning */ +#define CBAUD 0000017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CIBAUD 03600000 /* input baud rate (not used) */ +#define CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* modem lines */ +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +extern speed_t cfgetispeed(struct termios *termios_p); +extern speed_t cfgetospeed(struct termios *termios_p); +extern int cfsetispeed(struct termios *termios_p, speed_t speed); +extern int cfsetospeed(struct termios *termios_p, speed_t speed); +extern int tcdrain(int fildes); +extern int tcflow(int fildes, int action); +extern int tcflush(int fildes, int queue_selector); +extern int tcgetattr(int fildes, struct termios *termios_p); +extern int tcsendbreak(int fildes, int duration); +extern int tcsetattr(int fildes, int optional_actions, + struct termios *termios_p); + +#endif diff --git a/linux-0.12/include/time.h b/linux-0.12/include/time.h new file mode 100644 index 0000000..fbb4e73 --- /dev/null +++ b/linux-0.12/include/time.h @@ -0,0 +1,49 @@ +#ifndef _TIME_H +#define _TIME_H + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#define CLOCKS_PER_SEC 100 + +typedef long clock_t; + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +#define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 1000 == 0)) + +clock_t clock(void); +time_t time(time_t * tp); +double difftime(time_t time2, time_t time1); +time_t mktime(struct tm * tp); + +char * asctime(const struct tm * tp); +char * ctime(const time_t * tp); +struct tm * gmtime(const time_t *tp); +struct tm *localtime(const time_t * tp); +size_t strftime(char * s, size_t smax, const char * fmt, const struct tm * tp); +void tzset(void); + +#endif diff --git a/linux-0.12/include/unistd.h b/linux-0.12/include/unistd.h new file mode 100755 index 0000000..a73ca92 --- /dev/null +++ b/linux-0.12/include/unistd.h @@ -0,0 +1,311 @@ +#ifndef _UNISTD_H +#define _UNISTD_H + +/* ok, this may be a joke, but I'm working on it */ +/* ok,这也许是个玩笑,但我正在着手处理 */ + +/* 下面符号常数指出符合IEEE标准1003.1实现的版本号,是一个整数值 */ +#define _POSIX_VERSION 198808L + +/* chown()和fchown()的使用受限于进程的权限 */ +#define _POSIX_CHOWN_RESTRICTED /* only root can do a chown (I think..) */ + /* 只有超级用户可以执行chown(我想..) */ +/* 长于(NAME_MAX)的路径名将产生错误,而不会自动截断 */ +#define _POSIX_NO_TRUNC /* no pathname truncation (but see in kernel) */ + /* 路径名不截断(但是请看内核代码)*/ +// 下面这个符号将定义成字符值,该值禁止终端对其的处理。 +// _POSIX_VDISABLE 用于控制终端某些特殊字符的功能。当一个终端termios结构中c_cc[]数组某项 +// 字符代码值等于_POSIX_VDISABLE的值时,表示禁止使用相应的特殊字符。 +#define _POSIX_VDISABLE '\0' /* character to disable things like ^C */ + /* 禁止像^C这样的字符 */ +/* 系统实现支持作业控制 */ +#define _POSIX_JOB_CONTROL + +/* 每个进程都有一保存的set-user-ID和一保存的set-group-ID */ +#define _POSIX_SAVED_IDS /* Implemented, for whatever good it is */ + /* 已经实现。 */ +#define STDIN_FILENO 0 /* 标准输入文件句柄号 */ +#define STDOUT_FILENO 1 /* 标准输出文件句柄号 */ +#define STDERR_FILENO 2 /* 标准出错文件句柄号 */ + +#ifndef NULL +#define NULL ((void *)0) /* 定义空指针 */ +#endif + +/* access */ /* 文件访问 */ +// 以下定义的符号常数用于 access() 函数。 +#define F_OK 0 /* 检测文件是否存在 */ +#define X_OK 1 /* 检测是否可执行(搜索)*/ +#define W_OK 2 /* 检测是否可写 */ +#define R_OK 4 /* 检测是否可读 */ + +/* lseek */ /* 文件指针重定位 */ +// 以下符号常数用于 lseek() 和 fcntl() 函数。 +#define SEEK_SET 0 /* 将文件读写指针设置为偏移值 */ +#define SEEK_CUR 1 /* 将文件读写指针设置为当前值加上偏移值 */ +#define SEEK_END 2 /* 将文件读写指针设置为文件长度加上偏移值 */ + +/* _SC stands for System Configuration. We don't use them much */ +/* _SC 表示系统配置。我们很少使用 */ +/* 下面的符号常数用于sysconf() */ +#define _SC_ARG_MAX 1 /* 最大变量数 */ +#define _SC_CHILD_MAX 2 /* 子进程最大数 */ +#define _SC_CLOCKS_PER_SEC 3 /* 每秒嘀嗒数 */ +#define _SC_NGROUPS_MAX 4 /* 最大组数 */ +#define _SC_OPEN_MAX 5 /* 最大打开文件数 */ +#define _SC_JOB_CONTROL 6 /* 作业控制 */ +#define _SC_SAVED_IDS 7 /* 保存的标识符 */ +#define _SC_VERSION 8 /* 版本 */ + +/* more (possibly) configurable things - now pathnames */ +/* 更多的(可能的)可配置参数 - 现在用于路径名 */ +#define _PC_LINK_MAX 1 /* 连接最大数 */ +#define _PC_MAX_CANON 2 /* 最大常规文件数 */ +#define _PC_MAX_INPUT 3 /* 最大输入长度 */ +#define _PC_NAME_MAX 4 /* 名称最大长度 */ +#define _PC_PATH_MAX 5 /* 路径最大长度 */ +#define _PC_PIPE_BUF 6 /* 管道缓冲大小 */ +#define _PC_NO_TRUNC 7 /* 文件名不截断 */ +#define _PC_VDISABLE 8 +#define _PC_CHOWN_RESTRICTED 9 /* 改变宿主受限 */ + +#include +#include +#include +#include +#include +#include + +/* 以下是实现的系统调用符号常数,用作系统调用函数表中索引值(参见include/linux/sys.h) */ +#ifdef __LIBRARY__ + +#define __NR_setup 0 /* used only by init, to get system going */ + /* __NR_setup仅用于初始化,以启动系统 */ +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_chown 16 +#define __NR_break 17 +#define __NR_stat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_fstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_phys 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_uname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_lstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 + +/**** 以下定义系统调用嵌入式汇编宏函数 ****/ +// Tip: 在宏定义中,若在两个标记之间有两个连续的井号'##',则表示在宏替换时会把这两个标记符号连 +// 接在一起。例如下面的__NR_##name,在替换了参数name(例如fork)之后,最后在程序中出现的将会是符 +// 号__NR_fork。 + +/* 不带参数的系统调用函数 type_name(void) */ +#define _syscall0(type, name) \ +type name(void) \ +{ \ + long __res; \ + __asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name)); \ + if (__res >= 0) { \ + return (type) __res; \ + } \ + errno = -__res; \ + return -1; \ +} + +/* 有1个参数的系统调用函数 type_name(atype a) */ +#define _syscall1(type, name, atype, a) \ +type name(atype a) \ +{ \ + long __res; \ + __asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name), "b" ((long)(a))); \ + if (__res >= 0) \ + return (type) __res; \ + errno = -__res; \ + return -1; \ +} + +/* 有2个参数的系统调用函数 type_name(atype a,btype b) */ +#define _syscall2(type, name, atype, a, btype, b) \ +type name(atype a, btype b) \ +{ \ + long __res; \ + __asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name), "b" ((long)(a)), "c" ((long)(b))); \ + if (__res >= 0) \ + return (type) __res; \ + errno = -__res; \ + return -1; \ +} + +/* 有3个参数的系统调用函数 type_name(atype a,btype b,ctype c) */ +#define _syscall3(type, name, atype, a, btype, b, ctype, c) \ +type name(atype a, btype b, ctype c) \ +{ \ + long __res; \ + __asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name), "b" ((long)(a)), "c" ((long)(b)), "d" ((long)(c))); \ + if (__res >= 0) \ + return (type) __res; \ + errno = -__res; \ + return -1; \ +} + +#endif /* __LIBRARY__ */ + +extern int errno; + +int access(const char * filename, mode_t mode); +int acct(const char * filename); +int alarm(int sec); +int brk(void * end_data_segment); +void * sbrk(ptrdiff_t increment); +int chdir(const char * filename); +int chmod(const char * filename, mode_t mode); +int chown(const char * filename, uid_t owner, gid_t group); +int chroot(const char * filename); +int close(int fildes); +int creat(const char * filename, mode_t mode); +int dup(int fildes); +int execve(const char * filename, char ** argv, char ** envp); +int execv(const char * pathname, char ** argv); +int execvp(const char * file, char ** argv); +int execl(const char * pathname, char * arg0, ...); +int execlp(const char * file, char * arg0, ...); +int execle(const char * pathname, char * arg0, ...); +volatile void exit(int status); +volatile void _exit(int status); +int fcntl(int fildes, int cmd, ...); +int fork(void); +int getpid(void); +int getuid(void); +int geteuid(void); +int getgid(void); +int getegid(void); +int ioctl(int fildes, int cmd, ...); +int kill(pid_t pid, int signal); +int link(const char * filename1, const char * filename2); +int lseek(int fildes, off_t offset, int origin); +int mknod(const char * filename, mode_t mode, dev_t dev); +int mount(const char * specialfile, const char * dir, int rwflag); +int nice(int val); +int open(const char * filename, int flag, ...); +int pause(void); +int pipe(int * fildes); +int read(int fildes, char * buf, off_t count); +int setpgrp(void); +int setpgid(pid_t pid,pid_t pgid); +int setuid(uid_t uid); +int setgid(gid_t gid); +void (*signal(int sig, void (*fn)(int)))(int); +int stat(const char * filename, struct stat * stat_buf); +int fstat(int fildes, struct stat * stat_buf); +int stime(time_t * tptr); +int sync(void); +time_t time(time_t * tloc); +time_t times(struct tms * tbuf); +int ulimit(int cmd, long limit); +mode_t umask(mode_t mask); +int umount(const char * specialfile); +int uname(struct utsname * name); +int unlink(const char * filename); +int ustat(dev_t dev, struct ustat * ubuf); +int utime(const char * filename, struct utimbuf * times); +pid_t waitpid(pid_t pid,int * wait_stat,int options); +pid_t wait(int * wait_stat); +int write(int fildes, const char * buf, off_t count); +int dup2(int oldfd, int newfd); +int getppid(void); +pid_t getpgrp(void); +pid_t setsid(void); +int sethostname(char *name, int len); +int setrlimit(int resource, struct rlimit *rlp); +int getrlimit(int resource, struct rlimit *rlp); +int getrusage(int who, struct rusage *rusage); +int gettimeofday(struct timeval *tv, struct timezone *tz); +int settimeofday(struct timeval *tv, struct timezone *tz); +int getgroups(int gidsetlen, gid_t *gidset); +int setgroups(int gidsetlen, gid_t *gidset); +int select(int width, fd_set * readfds, fd_set * writefds, + fd_set * exceptfds, struct timeval * timeout); + +#endif diff --git a/linux-0.12/include/utime.h b/linux-0.12/include/utime.h new file mode 100755 index 0000000..533e39e --- /dev/null +++ b/linux-0.12/include/utime.h @@ -0,0 +1,14 @@ +#ifndef _UTIME_H +#define _UTIME_H + +#include /* I know - shouldn't do this, but .. */ + +struct utimbuf { + time_t actime; /* 文件访问时间。从1970.1.1:0:0:0开始的秒数 */ + time_t modtime; /* 文件修改时间。同上 */ +}; + +/* 设置文件访问和修改时间函数 */ +extern int utime(const char *filename, struct utimbuf *times); + +#endif diff --git a/linux-0.12/init/main.c b/linux-0.12/init/main.c new file mode 100755 index 0000000..f430b9e --- /dev/null +++ b/linux-0.12/init/main.c @@ -0,0 +1,312 @@ +/* + * linux/init/main.c + * + * (C) 1991 Linus Torvalds + */ +#define __LIBRARY__ /* 为了包括定义在 unistd.h 中的内嵌汇编代码等信息 */ +#include +#include + +/* + * we need this inline - forking from kernel space will result + * in NO COPY ON WRITE (!!!), until an execve is executed. This + * is no problem, but for the stack. This is handled by not letting + * main() use the stack at all after fork(). Thus, no function + * calls - which means inline code for fork too, as otherwise we + * would use the stack upon exit from 'fork()'. + * + * Actually only pause and fork are needed inline, so that there + * won't be any messing with the stack from main(), but we define + * some others too. + */ + +/* + * 我们需要下面这些内嵌语句 - 从内核空间创建进程将导致没有写时复制(COPY ON WRITE)!!!直到执行一 + * 个execve调用。这对堆栈可能带来问题。处理方法是在fork()调用后不让main()使用任何堆栈。因此就不 + * 能有函数调用 - 这意味着fork也要使用内嵌代码,否则我们在从fork()退出时就要使用堆栈了。 + * + * 实际上只有pause和fork需要使用内嵌方式,以保证从main()中不会弄乱堆栈,但是我们同时还定义了其 + * 他一些函数。 + */ + +/* Linux在内核空间创建进程时不使用写时复制技术(Copy on write)。main()在移动到用户模式(到任务0) + 后执行内嵌方式的 fork() 和 pause(),因此可保证不使用任务0的用户栈。在执行move_to_user_mode()之 + 后,本程序 main() 就以任务0的身份在运行了。而任务0是所有将创建子进程的父进程。当它创建一个子进 + 程时(init进程),由于任务1代码属于内核空间,因此没有使用写时复制功能。此时任务0的用户栈就是任务 + 1的用户栈,即它们共同使用一个栈空间。因此希望在 main.c 运行在任务0的环境下时不要有对堆栈的任何 + 操作,以免弄乱堆栈。而在再次执行fork() 并执行过 execve() 函数后,被加载程序已不属于内核空间, + 因此可以使用写时复制技术了。*/ + +// int fork() 系统调用:创建进程 +_syscall0(int, fork) +// int pause() 系统调用:暂停进程的执行,直到收到一个信号 +_syscall0(int, pause) +// int setup(void * BIOS) 系统调用:仅用于linux初始化(仅在这个程序中被调用) +_syscall1(int, setup, void *, BIOS) +// int sync() 系统调用:更新文件系统 +_syscall0(int, sync) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include /* 日志打印功能 */ + +static char printbuf[1024]; /* 静态字符串数组,用作内核显示信息的缓存。*/ + +extern char *strcpy(); +extern int vsprintf(); +extern void init(void); /* 初始化 */ +extern void blk_dev_init(void); /* 块设备初始化blk_drv/ll_re_blk.c */ +extern void chr_dev_init(void); /* 字符设备初始化chr_drv/tty_io.c */ +extern void hd_init(void); /* 硬盘初始化blk_drv/hd.c */ +extern void floppy_init(void); /* 软驱初始化blk_drv/floppy.c */ +extern void mem_init(long start, long end); /* 内存管理初始化mm/memory.c */ +extern long rd_init(long mem_start, int length);/* 虚拟盘初始化blk_drv/ramdisk.c */ +extern long kernel_mktime(struct tm * tm); /* 计算系统开机启动时间(秒) */ + +/* 内核专用sprintf()函数,产生格式化信息并输出到指定缓冲区str中 */ +static int sprintf(char * str, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(str, fmt, args); + va_end(args); + return i; +} + +/* + * This is set up by the setup-routine at boot-time + */ +/* + * 这些数据由内核引导期间的setup.s程序设置。 + */ +#define EXT_MEM_K (*(unsigned short *)0x90002) /* 1MB以后的扩展内存大小(KB) */ +#define CON_ROWS ((*(unsigned short *)0x9000e) & 0xff) /* 选定的控制台屏幕的行数 */ +#define CON_COLS (((*(unsigned short *)0x9000e) & 0xff00) >> 8) /* ...列数 */ +#define DRIVE_INFO (*(struct drive_info *)0x90080) /* 硬盘参数表32字节内容 */ +#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) /* 根文件系统所在设备号 */ +#define ORIG_SWAP_DEV (*(unsigned short *)0x901FA) /* 交换文件所在设备号 */ + +/* + * Yeah, yeah, it's ugly, but I cannot find how to do this correctly + * and this seems to work. I anybody has more info on the real-time + * clock I'd be interested. Most of this was trial and error, and some + * bios-listing reading. Urghh. + */ +/* 这段宏读取CMOS实时时钟数据。outb_p和inb_p是include/asm/io.h中定义的端口输入输出宏 */ +#define CMOS_READ(addr) ({ \ + outb_p(0x80 | addr, 0x70); \ + inb_p(0x71); \ +}) + +/* 将BCD码转换成二进制数值 */ +#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) + +/* CMOS的访问时间很慢。为了减小时间误差,在读取了下面循环中所有数值后,若此时CMOS中秒值 + 发生了变化,则重新读取。这样能控制误差在1s内 */ +static void time_init(void) +{ + struct tm time; + + do { + time.tm_sec = CMOS_READ(0); + time.tm_min = CMOS_READ(2); + time.tm_hour = CMOS_READ(4); + time.tm_mday = CMOS_READ(7); + time.tm_mon = CMOS_READ(8); /* 当前月份(1~12) */ + time.tm_year = CMOS_READ(9); + } while (time.tm_sec != CMOS_READ(0)); + BCD_TO_BIN(time.tm_sec); + BCD_TO_BIN(time.tm_min); + BCD_TO_BIN(time.tm_hour); + BCD_TO_BIN(time.tm_mday); + BCD_TO_BIN(time.tm_mon); + BCD_TO_BIN(time.tm_year); + time.tm_mon--; /* ti_mon中的月份范围是 0 ~ 11 */ + startup_time = kernel_mktime(&time);/* 计算开机时间。*/ +} + +static long memory_end = 0; /* 机器所具有的物理内存容量 */ +static long buffer_memory_end = 0; /* 高速缓冲区末端地址 */ +static long main_memory_start = 0; /* 主内存开始的位置 */ +static char term[32]; /* 终端设置字符串 */ + +/* 读取并执行/etc/rc文件时所使用的命令行参数和环境参数 */ +static char * argv_rc[] = { "/bin/sh", NULL }; +static char * envp_rc[] = { "HOME=/", NULL ,NULL }; + +/* 运行登录shell时所使用的命令行和环境参数 */ +/* argv[0]中的字符“-”是传递shell程序sh的一个标示位,通过这个标示位,sh程序会作为shell程序执行 */ +static char * argv[] = { "-/bin/sh", NULL }; +static char * envp[] = { "HOME=/usr/root", NULL, NULL }; + +/* 用于存放硬盘参数表 */ +struct drive_info { char dummy[32]; } drive_info; + +/* 内核初始化主程序 (void -> int 去除编译警告,实际为void) */ +int main(void) /* This really is void, no error here. */ + /* 这里真的是 void,没有问题 */ +{ /* The startup routine assumes (well, ...) this */ + /* 因为在 head.s 就是这么假设的(把 main 的地址压入堆栈的时候) */ +/* + * Interrupts are still disabled. Do necessary setups, then enable them + */ +/* + * 此时中断还被禁止的,做完必要的设置后就将其开启。 + */ + ROOT_DEV = ORIG_ROOT_DEV; + SWAP_DEV = ORIG_SWAP_DEV; + sprintf(term, "TERM=con%dx%d", CON_COLS, CON_ROWS); + envp[1] = term; + envp_rc[1] = term; + drive_info = DRIVE_INFO; + + /* 根据机器物理内存容量设置高速缓冲区和主内存区的起始地址 */ + memory_end = (1 << 20) + (EXT_MEM_K << 10); /* 1M + 扩展内存大小 */ + memory_end &= 0xfffff000; /* 忽略不到4K(1页)的内存 */ + if (memory_end > 16 * 1024 * 1024) { /* 最多管理16M内存 */ + memory_end = 16 * 1024 * 1024; + } + + if (memory_end > 12 * 1024 * 1024) { + buffer_memory_end = 4 * 1024 * 1024; + } else if (memory_end > 6 * 1024 * 1024) { + buffer_memory_end = 2 * 1024 * 1024; + } else { + buffer_memory_end = 1 * 1024 * 1024; + } + main_memory_start = buffer_memory_end; +#ifdef RAMDISK /* 如果定义了虚拟盘,则主内存还得相应减少 */ + main_memory_start += rd_init(main_memory_start, RAMDISK*1024); +#endif + +/* 以下是内核进行所有方面的初始化工作 */ + mem_init(main_memory_start, memory_end);/* 主内存区初始化 */ + trap_init(); /* 陷阱门初始化 */ + blk_dev_init(); /* 块设备初始化 */ + chr_dev_init(); /* 字符设备初始化 */ + tty_init(); /* tty初始化 */ + time_init(); /* 设置开机启动时间 */ + sched_init(); /* 调度程序初始化 */ + buffer_init(buffer_memory_end); /* 缓冲管理初始化 */ + hd_init(); /* 硬盘初始化 */ + floppy_init(); /* 软驱初始化 */ + + sti(); /* 开启中断 */ + move_to_user_mode(); + if (!fork()) { /* we count on this going ok */ + /* 创建任务1(init进程) */ + init(); + } +/* + * NOTE!! For any other task 'pause()' would mean we have to get a + * signal to awaken, but task0 is the sole exception (see 'schedule()') + * as task 0 gets activated at every idle moment (when no other tasks + * can run). For task0 'pause()' just means we go check if some other + * task can run, and if not we return here. + */ +/* + * 注意!! 对于任何其他的任务,“pause()”将意味着我们必须等待收到信号才会返回就绪态,但任务0 + * 是唯一例外的情况(参见“schedule()”),因为任务0在任何空闲时间里都会被激活,因此对于任务 + * 0 “pause()”仅意味着我们返回来查看是否有其他任务可以运行,如果没有的话,我们就在这里一直循 + * 环执行。 + */ + + /* 调度函数发现系统中没有其他程序可以运行就会切换到任务0 */ + for(;;) { + __asm__("int $0x80"::"a" (__NR_pause)); + } +} + + +int printf(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + write(1, printbuf, i = vsprintf(printbuf, fmt, args)); + va_end(args); + return i; +} + +/* init()函数主要完成4件事: + * 1. 安装根文件系统 + * 2. 显示系统信息 + * 3. 运行系统初始资源配置文件rc中的命令 + * 4. 执行用户登录shell程序 +*/ +void init(void) +{ + int pid, i; + + setup((void *) &drive_info); + + (void) open("/dev/tty1", O_RDWR, 0); /* stdin */ + (void) dup(0); /* stdout */ + (void) dup(0); /* stderr */ + + printf("%d buffers = %d bytes buffer space\n\r", NR_BUFFERS, NR_BUFFERS * BLOCK_SIZE); + printf("Free mem: %d bytes\n\r", memory_end - main_memory_start); + + /* fork出任务2 */ + if (!(pid = fork())) { + /* 将stdin重定向到/etc/rc文件,shell程序会在运行完/etc/rc中设置的命令后退出 */ + close(0); + if (open("/etc/rc", O_RDONLY, 0)) { + _exit(1); + } + execve("/bin/sh", argv_rc, envp_rc); + _exit(2); + } + + if (pid > 0) { /* init进程等待任务2退出 */ + while (pid != wait(&i)) { + /* nothing */; + } + } + /* 系统将始终在这个循环中 */ + while (1) { + if ((pid = fork()) < 0) { + printf("Fork failed in init\r\n"); + continue; + } + /* 新的子进程,关闭句柄(0,1,2),新创建一个会话并设置进程组号,然后重新打开/dev/tty0作 + 为stdin,并复制成stdout和stderr。以登录方式再次执行/bin/sh */ + if (!pid) { + close(0);close(1);close(2); + setsid(); + (void) open("/dev/tty1", O_RDWR, 0); + (void) dup(0); + (void) dup(0); + _exit(execve("/bin/sh", argv, envp)); + } + /* 然后父进程再次运行wait()等待 */ + while (1) { + if (pid == wait(&i)) { + break; + } + } + printf("\n\rchild %d died with code %04x\n\r", pid, i); + sync(); + } + _exit(0); /* NOTE! _exit, not exit() */ + /* _exit和exit都能用于正常终止一个函数。但_exit()直接是一个sys_exit的系统调用,而exit()则 + 是普通函数库中的一个函数。它会先进行一些清除操作,例如调用执行各终止处理程序,关闭所有标 + 准IO等,然后调用sys_exit。*/ +} diff --git a/linux-0.12/kernel/Makefile b/linux-0.12/kernel/Makefile new file mode 100644 index 0000000..a2e5b53 --- /dev/null +++ b/linux-0.12/kernel/Makefile @@ -0,0 +1,90 @@ +# +# Makefile for the FREAX-kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +include ../Rules.make + +LDFLAGS = -x #-s +CFLAGS =-g -Wall -O -fstrength-reduce -fomit-frame-pointer\ + -finline-functions -nostdinc -fno-builtin -I../include +CPP += -E -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = sched.o sys_call.o traps.o asm.o fork.o \ + panic.o printk.o vsprintf.o sys.o exit.o \ + signal.o mktime.o + +kernel.o: $(OBJS) + $(LD) -m elf_i386 -r -o kernel.o $(OBJS) + @sync + +clean: + rm -f core *.o *.a tmp_make keyboard.s + for i in *.c;do rm -f `basename $$i .c`.s;done + (cd chr_drv; make clean) + (cd blk_drv; make clean) + (cd math; make clean) + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + (cd chr_drv; make dep) + (cd blk_drv; make dep) + +### Dependencies: +exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \ + ../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \ + ../include/time.h ../include/sys/resource.h ../include/linux/tty.h \ + ../include/termios.h ../include/asm/segment.h +fork.s fork.o : fork.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/asm/segment.h ../include/asm/system.h +mktime.s mktime.o : mktime.c ../include/time.h +panic.s panic.o : panic.c ../include/linux/kernel.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/signal.h ../include/sys/param.h \ + ../include/sys/time.h ../include/time.h ../include/sys/resource.h +printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \ + ../include/linux/kernel.h +sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ + ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ + ../include/linux/sys.h ../include/linux/fdreg.h ../include/asm/system.h \ + ../include/asm/io.h ../include/asm/segment.h +signal.s signal.o : signal.c ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \ + ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ + ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ + ../include/asm/segment.h ../include/errno.h +sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/linux/tty.h ../include/termios.h \ + ../include/linux/config.h ../include/asm/segment.h ../include/sys/times.h \ + ../include/sys/utsname.h ../include/string.h +traps.s traps.o : traps.c ../include/string.h ../include/linux/head.h \ + ../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h ../include/asm/system.h ../include/asm/segment.h \ + ../include/asm/io.h +vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h diff --git a/linux-0.12/kernel/asm.s b/linux-0.12/kernel/asm.s new file mode 100644 index 0000000..6149722 --- /dev/null +++ b/linux-0.12/kernel/asm.s @@ -0,0 +1,151 @@ +/* + * linux/kernel/asm.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * asm.s contains the low-level code for most hardware faults. + * page_exception is handled by the mm, so that isn't here. This + * file also handles (hopefully) fpu-exceptions due to TS-bit, as + * the fpu must be properly saved/resored. This hasn't been tested. + */ + +.globl divide_error, debug, nmi, int3, overflow, bounds, invalid_op +.globl double_fault, coprocessor_segment_overrun +.globl invalid_TSS, segment_not_present, stack_segment +.globl general_protection, coprocessor_error, irq13, reserved +.globl alignment_check + +divide_error: + pushl $do_divide_error +no_error_code: + xchgl %eax,(%esp) + pushl %ebx + pushl %ecx + pushl %edx + pushl %edi + pushl %esi + pushl %ebp + push %ds + push %es + push %fs + pushl $0 # "error code" + lea 44(%esp),%edx + pushl %edx + movl $0x10,%edx + mov %dx,%ds + mov %dx,%es + mov %dx,%fs + call *%eax + addl $8,%esp + pop %fs + pop %es + pop %ds + popl %ebp + popl %esi + popl %edi + popl %edx + popl %ecx + popl %ebx + popl %eax + iret + + debug: + pushl $do_int3 # _do_debug + jmp no_error_code + +nmi: + pushl $do_nmi + jmp no_error_code + +int3: + pushl $do_int3 + jmp no_error_code + +overflow: + pushl $do_overflow + jmp no_error_code + +bounds: + pushl $do_bounds + jmp no_error_code + +invalid_op: + pushl $do_invalid_op + jmp no_error_code + +coprocessor_segment_overrun: + pushl $do_coprocessor_segment_overrun + jmp no_error_code + +reserved: + pushl $do_reserved + jmp no_error_code + +irq13: + pushl %eax + xorb %al,%al + outb %al,$0xF0 + movb $0x20,%al + outb %al,$0x20 + jmp 1f +1: jmp 1f +1: outb %al,$0xA0 + popl %eax + jmp coprocessor_error + +double_fault: + pushl $do_double_fault +error_code: + xchgl %eax,4(%esp) # error code <-> %eax + xchgl %ebx,(%esp) # &function <-> %ebx + pushl %ecx + pushl %edx + pushl %edi + pushl %esi + pushl %ebp + push %ds + push %es + push %fs + pushl %eax # error code + lea 44(%esp),%eax # offset + pushl %eax + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + mov %ax,%fs + call *%ebx + addl $8,%esp + pop %fs + pop %es + pop %ds + popl %ebp + popl %esi + popl %edi + popl %edx + popl %ecx + popl %ebx + popl %eax + iret + +invalid_TSS: + pushl $do_invalid_TSS + jmp error_code + +segment_not_present: + pushl $do_segment_not_present + jmp error_code + +stack_segment: + pushl $do_stack_segment + jmp error_code + +general_protection: + pushl $do_general_protection + jmp error_code + +alignment_check: + pushl $do_alignment_check + jmp error_code + diff --git a/linux-0.12/kernel/blk_drv/Makefile b/linux-0.12/kernel/blk_drv/Makefile new file mode 100644 index 0000000..ddd5ad7 --- /dev/null +++ b/linux-0.12/kernel/blk_drv/Makefile @@ -0,0 +1,68 @@ +# +# Makefile for the FREAX-kernel block device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +include ../../Rules.make + +LDFLAGS = -x +CFLAGS = -g -Wall -fstrength-reduce -fomit-frame-pointer \ + -finline-functions -nostdinc -fno-builtin -I../../include +CPP += -E -I../../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o + +blk_drv.a: $(OBJS) + $(AR) rcs blk_drv.a $(OBJS) + @sync + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +floppy.s floppy.o : floppy.c ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h \ + ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ + ../../include/sys/resource.h ../../include/linux/fdreg.h \ + ../../include/asm/system.h ../../include/asm/io.h \ + ../../include/asm/segment.h blk.h +hd.s hd.o : hd.c ../../include/linux/config.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h \ + ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ + ../../include/sys/resource.h ../../include/linux/hdreg.h \ + ../../include/asm/system.h ../../include/asm/io.h \ + ../../include/asm/segment.h blk.h +ll_rw_blk.s ll_rw_blk.o : ll_rw_blk.c ../../include/errno.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h \ + ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ + ../../include/sys/resource.h ../../include/asm/system.h blk.h +ramdisk.s ramdisk.o : ramdisk.c ../../include/string.h ../../include/linux/config.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h \ + ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ + ../../include/sys/resource.h ../../include/asm/system.h \ + ../../include/asm/segment.h ../../include/asm/memory.h blk.h diff --git a/linux-0.12/kernel/blk_drv/blk.h b/linux-0.12/kernel/blk_drv/blk.h new file mode 100644 index 0000000..47ccf51 --- /dev/null +++ b/linux-0.12/kernel/blk_drv/blk.h @@ -0,0 +1,164 @@ +#ifndef _BLK_H +#define _BLK_H + +#define NR_BLK_DEV 7 +/* + * NR_REQUEST is the number of entries in the request-queue. + * NOTE that writes may use only the low 2/3 of these: reads + * take precedence. + * + * 32 seems to be a reasonable number: enough to get some benefit + * from the elevator-mechanism, but not so much as to lock a lot of + * buffers when they are in the queue. 64 seems to be too many (easily + * long pauses in reading when heavy writing/syncing is going on) + */ +#define NR_REQUEST 32 + +/* + * Ok, this is an expanded form so that we can use the same + * request for paging requests when that is implemented. In + * paging, 'bh' is NULL, and 'waiting' is used to wait for + * read/write completion. + */ +struct request { + int dev; /* -1 if no request */ + int cmd; /* READ or WRITE */ + int errors; + unsigned long sector; + unsigned long nr_sectors; + char * buffer; + struct task_struct * waiting; + struct buffer_head * bh; + struct request * next; +}; + +/* + * This is used in the elevator algorithm: Note that + * reads always go before writes. This is natural: reads + * are much more time-critical than writes. + */ +#define IN_ORDER(s1,s2) \ +((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \ +((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \ +(s1)->sector < (s2)->sector)))) + +struct blk_dev_struct { + void (*request_fn)(void); + struct request * current_request; +}; + +extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; +extern struct request request[NR_REQUEST]; +extern struct task_struct * wait_for_request; + +extern int * blk_size[NR_BLK_DEV]; + +#ifdef MAJOR_NR + +/* + * Add entries as needed. Currently the only block devices + * supported are hard-disks and floppies. + */ + +#if (MAJOR_NR == 1) +/* ram disk */ +#define DEVICE_NAME "ramdisk" +#define DEVICE_REQUEST do_rd_request +#define DEVICE_NR(device) ((device) & 7) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == 2) +/* floppy */ +#define DEVICE_NAME "floppy" +#define DEVICE_INTR do_floppy +#define DEVICE_REQUEST do_fd_request +#define DEVICE_NR(device) ((device) & 3) +#define DEVICE_ON(device) floppy_on(DEVICE_NR(device)) +#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device)) + +#elif (MAJOR_NR == 3) +/* harddisk */ +#define DEVICE_NAME "harddisk" +#define DEVICE_INTR do_hd +#define DEVICE_TIMEOUT hd_timeout +#define DEVICE_REQUEST do_hd_request +#define DEVICE_NR(device) (MINOR(device)/5) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif +/* unknown blk device */ +#error "unknown blk device" + +#endif + +#define CURRENT (blk_dev[MAJOR_NR].current_request) +#define CURRENT_DEV DEVICE_NR(CURRENT->dev) + +#ifdef DEVICE_INTR +void (*DEVICE_INTR)(void) = NULL; +#endif +#ifdef DEVICE_TIMEOUT +int DEVICE_TIMEOUT = 0; +#define SET_INTR(x) (DEVICE_INTR = (x),DEVICE_TIMEOUT = 200) +#else +#define SET_INTR(x) (DEVICE_INTR = (x)) +#endif +static void (DEVICE_REQUEST)(void); + +static void unlock_buffer(struct buffer_head * bh) +{ + if (!bh->b_lock) + printk(DEVICE_NAME ": free buffer being unlocked\n"); + bh->b_lock=0; + wake_up(&bh->b_wait); +} + +static void end_request(int uptodate) +{ + DEVICE_OFF(CURRENT->dev); + if (CURRENT->bh) { + CURRENT->bh->b_uptodate = uptodate; + unlock_buffer(CURRENT->bh); + } + if (!uptodate) { + printk(DEVICE_NAME " I/O error\n\r"); + printk("dev %04x, block %d\n\r",CURRENT->dev, + CURRENT->bh->b_blocknr); + } + wake_up(&CURRENT->waiting); + wake_up(&wait_for_request); + CURRENT->dev = -1; + CURRENT = CURRENT->next; +} + +#ifdef DEVICE_TIMEOUT +#define CLEAR_DEVICE_TIMEOUT DEVICE_TIMEOUT = 0; +#else +#define CLEAR_DEVICE_TIMEOUT +#endif + +#ifdef DEVICE_INTR +#define CLEAR_DEVICE_INTR DEVICE_INTR = 0; +#else +#define CLEAR_DEVICE_INTR +#endif + +#define INIT_REQUEST \ +repeat: \ + if (!CURRENT) {\ + CLEAR_DEVICE_INTR \ + CLEAR_DEVICE_TIMEOUT \ + return; \ + } \ + if (MAJOR(CURRENT->dev) != MAJOR_NR) \ + panic(DEVICE_NAME ": request list destroyed"); \ + if (CURRENT->bh) { \ + if (!CURRENT->bh->b_lock) \ + panic(DEVICE_NAME ": block not locked"); \ + } + +#endif + +#endif diff --git a/linux-0.12/kernel/blk_drv/floppy.c b/linux-0.12/kernel/blk_drv/floppy.c new file mode 100644 index 0000000..b5a1643 --- /dev/null +++ b/linux-0.12/kernel/blk_drv/floppy.c @@ -0,0 +1,481 @@ +/* + * linux/kernel/floppy.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 02.12.91 - Changed to static variables to indicate need for reset + * and recalibrate. This makes some things easier (output_byte reset + * checking etc), and means less interrupt jumping in case of errors, + * so the code is hopefully easier to understand. + */ + +/* + * This file is certainly a mess. I've tried my best to get it working, + * but I don't like programming floppies, and I have only one anyway. + * Urgel. I should check for more errors, and do more graceful error + * recovery. Seems there are problems with several drives. I've tried to + * correct them. No promises. + */ + +/* + * As with hd.c, all routines within this file can (and will) be called + * by interrupts, so extreme caution is needed. A hardware interrupt + * handler may not sleep, or a kernel panic will happen. Thus I cannot + * call "floppy-on" directly, but have to set a special timer interrupt + * etc. + * + * Also, I'm not certain this works on more than 1 floppy. Bugs may + * abund. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR 2 +#include "blk.h" + +static int recalibrate = 0; +static int reset = 0; +static int seek = 0; + +extern unsigned char current_DOR; + +#define immoutb_p(val,port) \ +__asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port)) + +#define TYPE(x) ((x)>>2) +#define DRIVE(x) ((x)&0x03) +/* + * Note that MAX_ERRORS=8 doesn't imply that we retry every bad read + * max 8 times - some types of errors increase the errorcount by 2, + * so we might actually retry only 5-6 times before giving up. + */ +#define MAX_ERRORS 8 + +/* + * globals used by 'result()' + */ +#define MAX_REPLIES 7 +static unsigned char reply_buffer[MAX_REPLIES]; +#define ST0 (reply_buffer[0]) +#define ST1 (reply_buffer[1]) +#define ST2 (reply_buffer[2]) +#define ST3 (reply_buffer[3]) + +/* + * This struct defines the different floppy types. Unlike minix + * linux doesn't have a "search for right type"-type, as the code + * for that is convoluted and weird. I've got enough problems with + * this driver as it is. + * + * The 'stretch' tells if the tracks need to be boubled for some + * types (ie 360kB diskette in 1.2MB drive etc). Others should + * be self-explanatory. + */ +static struct floppy_struct { + unsigned int size, sect, head, track, stretch; + unsigned char gap,rate,spec1; +} floppy_type[] = { + { 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */ + { 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */ + { 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */ + { 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */ + { 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */ + { 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */ + { 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */ + { 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */ +}; + +/* + * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps + * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), + * H is head unload time (1=16ms, 2=32ms, etc) + * + * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc) + * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). + */ + +extern void floppy_interrupt(void); +extern char tmp_floppy_area[1024]; + +/* + * These are global variables, as that's the easiest way to give + * information to interrupts. They are the data used for the current + * request. + */ +static int cur_spec1 = -1; +static int cur_rate = -1; +static struct floppy_struct * floppy = floppy_type; +static unsigned char current_drive = 0; +static unsigned char sector = 0; +static unsigned char head = 0; +static unsigned char track = 0; +static unsigned char seek_track = 0; +static unsigned char current_track = 255; +static unsigned char command = 0; +unsigned char selected = 0; +struct task_struct * wait_on_floppy_select = NULL; + +void floppy_deselect(unsigned int nr) +{ + if (nr != (current_DOR & 3)) + printk("floppy_deselect: drive not selected\n\r"); + selected = 0; + wake_up(&wait_on_floppy_select); +} + +/* + * floppy-change is never called from an interrupt, so we can relax a bit + * here, sleep etc. Note that floppy-on tries to set current_DOR to point + * to the desired drive, but it will probably not survive the sleep if + * several floppies are used at the same time: thus the loop. + */ +int floppy_change(unsigned int nr) +{ +repeat: + floppy_on(nr); + while ((current_DOR & 3) != nr && selected) + sleep_on(&wait_on_floppy_select); + if ((current_DOR & 3) != nr) + goto repeat; + if (inb(FD_DIR) & 0x80) { + floppy_off(nr); + return 1; + } + floppy_off(nr); + return 0; +} + +#define copy_buffer(from,to) \ +__asm__("cld ; rep ; movsl" \ + ::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \ + ) + +static void setup_DMA(void) +{ + long addr = (long) CURRENT->buffer; + + cli(); + if (addr >= 0x100000) { + addr = (long) tmp_floppy_area; + if (command == FD_WRITE) + copy_buffer(CURRENT->buffer,tmp_floppy_area); + } +/* mask DMA 2 */ + immoutb_p(4|2,10); +/* output command byte. I don't know why, but everyone (minix, */ +/* sanches & canton) output this twice, first to 12 then to 11 */ + __asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t" + "outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:":: + "a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE))); +/* 8 low bits of addr */ + immoutb_p(addr,4); + addr >>= 8; +/* bits 8-15 of addr */ + immoutb_p(addr,4); + addr >>= 8; +/* bits 16-19 of addr */ + immoutb_p(addr,0x81); +/* low 8 bits of count-1 (1024-1=0x3ff) */ + immoutb_p(0xff,5); +/* high 8 bits of count-1 */ + immoutb_p(3,5); +/* activate DMA 2 */ + immoutb_p(0|2,10); + sti(); +} + +static void output_byte(char byte) +{ + int counter; + unsigned char status; + + if (reset) + return; + for(counter = 0 ; counter < 10000 ; counter++) { + status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR); + if (status == STATUS_READY) { + outb(byte,FD_DATA); + return; + } + } + reset = 1; + printk("Unable to send byte to FDC\n\r"); +} + +static int result(void) +{ + int i = 0, counter, status; + + if (reset) + return -1; + for (counter = 0 ; counter < 10000 ; counter++) { + status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY); + if (status == STATUS_READY) + return i; + if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) { + if (i >= MAX_REPLIES) + break; + reply_buffer[i++] = inb_p(FD_DATA); + } + } + reset = 1; + printk("Getstatus times out\n\r"); + return -1; +} + +static void bad_flp_intr(void) +{ + CURRENT->errors++; + if (CURRENT->errors > MAX_ERRORS) { + floppy_deselect(current_drive); + end_request(0); + } + if (CURRENT->errors > MAX_ERRORS/2) + reset = 1; + else + recalibrate = 1; +} + +/* + * Ok, this interrupt is called after a DMA read/write has succeeded, + * so we check the results, and copy any buffers. + */ +static void rw_interrupt(void) +{ + if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) { + if (ST1 & 0x02) { + printk("Drive %d is write protected\n\r",current_drive); + floppy_deselect(current_drive); + end_request(0); + } else + bad_flp_intr(); + do_fd_request(); + return; + } + if (command == FD_READ && (unsigned long)(CURRENT->buffer) >= 0x100000) + copy_buffer(tmp_floppy_area,CURRENT->buffer); + floppy_deselect(current_drive); + end_request(1); + do_fd_request(); +} + +inline void setup_rw_floppy(void) +{ + setup_DMA(); + do_floppy = rw_interrupt; + output_byte(command); + output_byte(head<<2 | current_drive); + output_byte(track); + output_byte(head); + output_byte(sector); + output_byte(2); /* sector size = 512 */ + output_byte(floppy->sect); + output_byte(floppy->gap); + output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ + if (reset) + do_fd_request(); +} + +/* + * This is the routine called after every seek (or recalibrate) interrupt + * from the floppy controller. Note that the "unexpected interrupt" routine + * also does a recalibrate, but doesn't come here. + */ +static void seek_interrupt(void) +{ +/* sense drive status */ + output_byte(FD_SENSEI); + if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) { + bad_flp_intr(); + do_fd_request(); + return; + } + current_track = ST1; + setup_rw_floppy(); +} + +/* + * This routine is called when everything should be correctly set up + * for the transfer (ie floppy motor is on and the correct floppy is + * selected). + */ +static void transfer(void) +{ + if (cur_spec1 != floppy->spec1) { + cur_spec1 = floppy->spec1; + output_byte(FD_SPECIFY); + output_byte(cur_spec1); /* hut etc */ + output_byte(6); /* Head load time =6ms, DMA */ + } + if (cur_rate != floppy->rate) + outb_p(cur_rate = floppy->rate,FD_DCR); + if (reset) { + do_fd_request(); + return; + } + if (!seek) { + setup_rw_floppy(); + return; + } + do_floppy = seek_interrupt; + if (seek_track) { + output_byte(FD_SEEK); + output_byte(head<<2 | current_drive); + output_byte(seek_track); + } else { + output_byte(FD_RECALIBRATE); + output_byte(head<<2 | current_drive); + } + if (reset) + do_fd_request(); +} + +/* + * Special case - used after a unexpected interrupt (or reset) + */ +static void recal_interrupt(void) +{ + output_byte(FD_SENSEI); + if (result()!=2 || (ST0 & 0xE0) == 0x60) + reset = 1; + else + recalibrate = 0; + do_fd_request(); +} + +void unexpected_floppy_interrupt(void) +{ + output_byte(FD_SENSEI); + if (result()!=2 || (ST0 & 0xE0) == 0x60) + reset = 1; + else + recalibrate = 1; +} + +static void recalibrate_floppy(void) +{ + recalibrate = 0; + current_track = 0; + do_floppy = recal_interrupt; + output_byte(FD_RECALIBRATE); + output_byte(head<<2 | current_drive); + if (reset) + do_fd_request(); +} + +static void reset_interrupt(void) +{ + output_byte(FD_SENSEI); + (void) result(); + output_byte(FD_SPECIFY); + output_byte(cur_spec1); /* hut etc */ + output_byte(6); /* Head load time =6ms, DMA */ + do_fd_request(); +} + +/* + * reset is done by pulling bit 2 of DOR low for a while. + */ +static void reset_floppy(void) +{ + int i; + + reset = 0; + cur_spec1 = -1; + cur_rate = -1; + recalibrate = 1; + printk("Reset-floppy called\n\r"); + cli(); + do_floppy = reset_interrupt; + outb_p(current_DOR & ~0x04,FD_DOR); + for (i=0 ; i<100 ; i++) + __asm__("nop"); + outb(current_DOR,FD_DOR); + sti(); +} + +static void floppy_on_interrupt(void) +{ +/* We cannot do a floppy-select, as that might sleep. We just force it */ + selected = 1; + if (current_drive != (current_DOR & 3)) { + current_DOR &= 0xFC; + current_DOR |= current_drive; + outb(current_DOR,FD_DOR); + add_timer(2,&transfer); + } else + transfer(); +} + +void do_fd_request(void) +{ + unsigned int block; + + seek = 0; + if (reset) { + reset_floppy(); + return; + } + if (recalibrate) { + recalibrate_floppy(); + return; + } + INIT_REQUEST; + floppy = (MINOR(CURRENT->dev)>>2) + floppy_type; + if (current_drive != CURRENT_DEV) + seek = 1; + current_drive = CURRENT_DEV; + block = CURRENT->sector; + if (block+2 > floppy->size) { + end_request(0); + goto repeat; + } + sector = block % floppy->sect; + block /= floppy->sect; + head = block % floppy->head; + track = block / floppy->head; + seek_track = track << floppy->stretch; + if (seek_track != current_track) + seek = 1; + sector++; + if (CURRENT->cmd == READ) + command = FD_READ; + else if (CURRENT->cmd == WRITE) + command = FD_WRITE; + else + panic("do_fd_request: unknown command"); + add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt); +} + +static int floppy_sizes[] ={ + 0, 0, 0, 0, + 360, 360 ,360, 360, + 1200,1200,1200,1200, + 360, 360, 360, 360, + 720, 720, 720, 720, + 360, 360, 360, 360, + 720, 720, 720, 720, + 1440,1440,1440,1440 +}; + +/** + * 软盘初始化 + * @param[in] void + * @retval void + */ + +void floppy_init(void) +{ + blk_size[MAJOR_NR] = floppy_sizes; + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + set_trap_gate(0x26, &floppy_interrupt); + outb(inb_p(0x21)&~0x40,0x21); +} diff --git a/linux-0.12/kernel/blk_drv/hd.c b/linux-0.12/kernel/blk_drv/hd.c new file mode 100644 index 0000000..986d548 --- /dev/null +++ b/linux-0.12/kernel/blk_drv/hd.c @@ -0,0 +1,384 @@ +/* + * linux/kernel/hd.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This is the low-level hd interrupt support. It traverses the + * request-list, using interrupts to jump between functions. As + * all the functions are called within interrupts, we may not + * sleep. Special care is recommended. + * + * modified by Drew Eckhardt to check nr of hd's from the CMOS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR 3 +#include "blk.h" + +#define CMOS_READ(addr) ({ \ +outb_p(0x80|addr,0x70); \ +inb_p(0x71); \ +}) + +/* Max read/write errors/sector */ +#define MAX_ERRORS 7 +#define MAX_HD 2 + +static void recal_intr(void); +static void bad_rw_intr(void); + +static int recalibrate = 0; +static int reset = 0; + +/* + * This struct defines the HD's and their types. + */ +struct hd_i_struct { + int head,sect,cyl,wpcom,lzone,ctl; + }; +#ifdef HD_TYPE +struct hd_i_struct hd_info[] = { HD_TYPE }; +#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) +#else +struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; +static int NR_HD = 0; +#endif + +static struct hd_struct { + long start_sect; + long nr_sects; +} hd[5*MAX_HD]={{0,0},}; + +static int hd_sizes[5*MAX_HD] = {0, }; + +#define port_read(port,buf,nr) \ +__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr)) + +#define port_write(port,buf,nr) \ +__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr)) + +extern void hd_interrupt(void); +extern void rd_load(void); + +/* This may be used only once, enforced by 'static int callable' */ +int sys_setup(void * BIOS) +{ + static int callable = 1; + int i,drive; + unsigned char cmos_disks; + struct partition *p; + struct buffer_head * bh; + + if (!callable) + return -1; + callable = 0; +#ifndef HD_TYPE + for (drive=0 ; drive<2 ; drive++) { + hd_info[drive].cyl = *(unsigned short *) BIOS; + hd_info[drive].head = *(unsigned char *) (2+BIOS); + hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); + hd_info[drive].ctl = *(unsigned char *) (8+BIOS); + hd_info[drive].lzone = *(unsigned short *) (12+BIOS); + hd_info[drive].sect = *(unsigned char *) (14+BIOS); + BIOS += 16; + } + if (hd_info[1].cyl) + NR_HD=2; + else + NR_HD=1; +#endif + for (i=0 ; i are the primary drives in the system, and + the ones reflected as drive 1 or 2. + + The first drive is stored in the high nibble of CMOS + byte 0x12, the second in the low nibble. This will be + either a 4 bit drive type or 0xf indicating use byte 0x19 + for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. + + Needless to say, a non-zero value means we have + an AT controller hard disk for that drive. + + + */ + + if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) + if (cmos_disks & 0x0f) + NR_HD = 2; + else + NR_HD = 1; + else + NR_HD = 0; + for (i = NR_HD ; i < 2 ; i++) { + hd[i*5].start_sect = 0; + hd[i*5].nr_sects = 0; + } + for (drive=0 ; driveb_data[510] != 0x55 || (unsigned char) + bh->b_data[511] != 0xAA) { + printk("Bad partition table on drive %d\n\r",drive); + panic(""); + } + p = 0x1BE + (void *)bh->b_data; + for (i=1;i<5;i++,p++) { + hd[i+5*drive].start_sect = p->start_sect; + hd[i+5*drive].nr_sects = p->nr_sects; + } + brelse(bh); + } + for (i=0 ; i<5*MAX_HD ; i++) + hd_sizes[i] = hd[i].nr_sects>>1 ; + blk_size[MAJOR_NR] = hd_sizes; + if (NR_HD) + printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); + rd_load(); + init_swapping(); + mount_root(); + return (0); +} + +static int controller_ready(void) +{ + int retries = 100000; + + while (--retries && (inb_p(HD_STATUS)&0x80)); + return (retries); +} + +static int win_result(void) +{ + int i=inb_p(HD_STATUS); + + if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) + == (READY_STAT | SEEK_STAT)) + return(0); /* ok */ + if (i&1) i=inb(HD_ERROR); + return (1); +} + +static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, + unsigned int head,unsigned int cyl,unsigned int cmd, + void (*intr_addr)(void)) +{ + register int port asm("dx"); + + if (drive>1 || head>15) + panic("Trying to write bad sector"); + if (!controller_ready()) + panic("HD controller not ready"); + SET_INTR(intr_addr); + outb_p(hd_info[drive].ctl,HD_CMD); + port=HD_DATA; + outb_p(hd_info[drive].wpcom>>2,++port); + outb_p(nsect,++port); + outb_p(sect,++port); + outb_p(cyl,++port); + outb_p(cyl>>8,++port); + outb_p(0xA0|(drive<<4)|head,++port); + outb(cmd,++port); +} + +static int drive_busy(void) +{ + unsigned int i; + unsigned char c; + + for (i = 0; i < 50000; i++) { + c = inb_p(HD_STATUS); + c &= (BUSY_STAT | READY_STAT | SEEK_STAT); + if (c == (READY_STAT | SEEK_STAT)) + return 0; + } + printk("HD controller times out\n\r"); + return(1); +} + +static void reset_controller(void) +{ + int i; + + outb(4,HD_CMD); + for(i = 0; i < 1000; i++) nop(); + outb(hd_info[0].ctl & 0x0f ,HD_CMD); + if (drive_busy()) + printk("HD-controller still busy\n\r"); + if ((i = inb(HD_ERROR)) != 1) + printk("HD-controller reset failed: %02x\n\r",i); +} + +static void reset_hd(void) +{ + static int i; + +repeat: + if (reset) { + reset = 0; + i = -1; + reset_controller(); + } else if (win_result()) { + bad_rw_intr(); + if (reset) + goto repeat; + } + i++; + if (i < NR_HD) { + hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1, + hd_info[i].cyl,WIN_SPECIFY,&reset_hd); + } else + do_hd_request(); +} + +void unexpected_hd_interrupt(void) +{ + printk("Unexpected HD interrupt\n\r"); + reset = 1; + do_hd_request(); +} + +static void bad_rw_intr(void) +{ + if (++CURRENT->errors >= MAX_ERRORS) + end_request(0); + if (CURRENT->errors > MAX_ERRORS/2) + reset = 1; +} + +static void read_intr(void) +{ + if (win_result()) { + bad_rw_intr(); + do_hd_request(); + return; + } + port_read(HD_DATA,CURRENT->buffer,256); + CURRENT->errors = 0; + CURRENT->buffer += 512; + CURRENT->sector++; + if (--CURRENT->nr_sectors) { + SET_INTR(&read_intr); + return; + } + end_request(1); + do_hd_request(); +} + +static void write_intr(void) +{ + if (win_result()) { + bad_rw_intr(); + do_hd_request(); + return; + } + if (--CURRENT->nr_sectors) { + CURRENT->sector++; + CURRENT->buffer += 512; + SET_INTR(&write_intr); + port_write(HD_DATA,CURRENT->buffer,256); + return; + } + end_request(1); + do_hd_request(); +} + +static void recal_intr(void) +{ + if (win_result()) + bad_rw_intr(); + do_hd_request(); +} + +void hd_times_out(void) +{ + if (!CURRENT) + return; + printk("HD timeout"); + if (++CURRENT->errors >= MAX_ERRORS) + end_request(0); + SET_INTR(NULL); + reset = 1; + do_hd_request(); +} + +void do_hd_request(void) +{ + int i,r; + unsigned int block,dev; + unsigned int sec,head,cyl; + unsigned int nsect; + + INIT_REQUEST; + dev = MINOR(CURRENT->dev); + block = CURRENT->sector; + if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) { + end_request(0); + goto repeat; + } + block += hd[dev].start_sect; + dev /= 5; + __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), + "r" (hd_info[dev].sect)); + __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), + "r" (hd_info[dev].head)); + sec++; + nsect = CURRENT->nr_sectors; + if (reset) { + recalibrate = 1; + reset_hd(); + return; + } + if (recalibrate) { + recalibrate = 0; + hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0, + WIN_RESTORE,&recal_intr); + return; + } + if (CURRENT->cmd == WRITE) { + hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr); + for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) + /* nothing */ ; + if (!r) { + bad_rw_intr(); + goto repeat; + } + port_write(HD_DATA,CURRENT->buffer,256); + } else if (CURRENT->cmd == READ) { + hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); + } else + panic("unknown hd-command"); +} + +void hd_init(void) +{ + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + set_intr_gate(0x2E,&hd_interrupt); + outb_p(inb_p(0x21)&0xfb,0x21); + outb(inb_p(0xA1)&0xbf,0xA1); +} diff --git a/linux-0.12/kernel/blk_drv/ll_rw_blk.c b/linux-0.12/kernel/blk_drv/ll_rw_blk.c new file mode 100755 index 0000000..9435de5 --- /dev/null +++ b/linux-0.12/kernel/blk_drv/ll_rw_blk.c @@ -0,0 +1,305 @@ +/* + * linux/kernel/blk_dev/ll_rw.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This handles all read/write requests to block devices + */ +#include +#include +#include +#include + +#include "blk.h" + +/* + * The request-struct contains all necessary data + * to load a nr of sectors into memory + */ +/* + * 请求结构中含有加载 nr 个扇区数据到内存中去的所有必须的信息。 + */ +/* 请求项数组队列,共有NR_REQUEST = 32个请求项 */ +struct request request[NR_REQUEST]; + +/* + * used to wait on when there are no free requests + */ +/* + * 是用于在请求数组没有空闲项时进程的临时等待处。 + */ +struct task_struct * wait_for_request = NULL; + +/* blk_dev_struct is: + * do_request-address + * next-request + */ +/* + * blk_dev_struct块设备结构是:(参见文件kernel/blk_drv/blk.h) + * do_request-address // 对应主设备号的请求处理程序指针 + * current-request // 该设备的下一个请求 + */ +// 块设备数组。该数组使用主设备号作为索引。实际内容将在各块设备驱动程序初始化时填入。 +// 例如,硬盘驱动程序初始化时(hd.c),第一条语句即用于设备blk_dev[3]的内容。 +struct blk_dev_struct blk_dev[NR_BLK_DEV] = { + { NULL, NULL }, /* no_dev */ /* 0 - 无设备 */ + { NULL, NULL }, /* dev mem */ /* 1 - 内存 */ + { NULL, NULL }, /* dev fd */ /* 2 - 软驱设备 */ + { NULL, NULL }, /* dev hd */ /* 3 - 硬盘设备 */ + { NULL, NULL }, /* dev ttyx */ /* 4 - ttyx设备 */ + { NULL, NULL }, /* dev tty */ /* 5 - tty设备 */ + { NULL, NULL } /* dev lp */ /* 6 - lp打印机设备 */ +}; + +/* + * blk_size contains the size of all block-devices: + * + * blk_size[MAJOR][MINOR] + * + * if (!blk_size[MAJOR]) then no minor size checking is done. + */ +/* + * blk_size数组含有所有块设备的大小(块总数): + * blk_size[MAJOR][MINOR] + * 如果(!blk_size[MAJOR]),则不必检测子设备的块总数. + */ +// 设备数据块总数指针数组.每个指针项指向指定主设备号的总块数组.该总块数数组每一项对应子 +// 设备号确定的一个子设备上所拥有的数据总数(1块大小 = 1KB). +int * blk_size[NR_BLK_DEV] = { NULL, NULL, }; + +// 锁定指定缓冲块 +// +// 如果指定的缓冲块已经被其他任务锁定,则使自己睡眠(不可中断的等待),直到被执行解锁 +// 缓冲块的任务明确地唤醒 + +static inline void lock_buffer(struct buffer_head * bh) +{ + cli(); /* 清中断许可 */ + while (bh->b_lock){ /* 如果缓冲区已被锁定则睡眠,直到缓冲区解锁 */ + sleep_on(&bh->b_wait); + } + bh->b_lock = 1; /* 立刻锁定缓冲区 */ + sti(); /* 开中断 */ +} + +// 释放(解锁)锁定的缓冲区 +// +// 该函数与hlk.h文件中的同名函数完全一样。 +static inline void unlock_buffer(struct buffer_head * bh) +{ + if (!bh->b_lock) // 如果该缓冲区没有被锁定,则打印出错信息. + printk("ll_rw_block.c: buffer not locked\n\r"); + bh->b_lock = 0; // 清锁定标志. + wake_up(&bh->b_wait); // 唤醒等待该缓冲区的任务. +} + +/* + * add-request adds a request to the linked list. + * It disables interrupts so that it can muck with the + * request-lists in peace. + * + * Note that swapping requests always go before other requests, + * and are done in the order they appear. + */ +/* + * add-request()向链表中加入一项请求项.它会关闭中断,这样就能安全地处理请求链表了. + * + * 注意,交换请求总是在其他请求之前操作,并且以它们出现在顺序完成. + */ +// 向链表中加入请求项. +// 参数dev是指定块设备结构指针,该结构中有处理请求项函数指针和当前正在请求项指针; +// req是已设置好内容的请求项结构指针. +// 本函数把已经设置好的请求项req添加到指定设备的请求项链表中.如果该设备在当前请求项指针为空,则可以设置req为当前请求项并立刻调用设备请求 +// 项处理函数.否则就把req请求项插入到该请求项链表中. +static void add_request(struct blk_dev_struct * dev, struct request * req) +{ + // 首先对参数提供的请求项的指针和标志作初始设置.置空请求项中的下一请求项指针,关中断并清除请求项相关缓冲区脏标志. + struct request * tmp; + + req->next = NULL; + cli(); // 关中断 + if (req->bh) + req->bh->b_dirt = 0; // 清缓冲区"脏"标志. + // 然后查看指定设备是否有当前请求项,即查看设备是否正忙.如果指定设备dev当前请求项(current_equest)字段为空,则表示目前该设备没有请求项,本次是 + // 第1个请求项,也是唯一的一个.因此可将块设备当前请求指针直接指向该请求项,并立刻执行相应设备的请求函数. + if (!(tmp = dev->current_request)) { + dev->current_request = req; + sti(); // 开中断. + (dev->request_fn)(); // 执行请求函数,对于硬盘是do_hd_request(). + return; + } + // 如果目前该设备已经有当前请求项在处理,则首先利用电梯算法搜索最佳插入位置,然后将当前请求项插入到请求链表中.在搜索过程中,如果判断出欲插入 + // 请求项的缓冲块头指针空,即没有缓冲块,那么就需要找一个项,其已经有可用的缓冲块.因此若当前插入位置(tmp之后)处的空闲项缓冲块头指针不空,就选择这个位置 + // 于是退出循环并把请求项插入此处.最后开中断并退出函数.电梯算法的作用是让磁盘磁头的移动距离最小,从而改善(减少)硬盘访问时间. + // 下面for循环中if语句用于把req所指请求项与请求队列(链表)中已有的请求项作比较,找出req插入该队列的正确位置顺序.然后中断循环,并把req插入到该队列正确位置处. + for ( ; tmp->next ; tmp = tmp->next) { + if (!req->bh) { + if (tmp->next->bh) { + break; + } else { + continue; + } + } + if ((IN_ORDER(tmp, req)||!IN_ORDER(tmp, tmp->next)) && IN_ORDER(req, tmp->next)) + break; + } + req->next = tmp->next; + tmp->next = req; + sti(); +} + +// 创建请求项并插入请求队列中. +// 参数major是主设备号;rw是指定命令;bh是存放数据的缓冲区头指针. +static void make_request(int major, int rw, struct buffer_head * bh) +{ + struct request * req; + int rw_ahead; + + /* WRITEA/READA is special case - it is not really needed, so if the */ + /* buffer is locked, we just forget about it, else it's a normal read */ + /* WRITEA/READA是一种特殊情况 - 它们并非必要,所以如果缓冲区已经上锁,我们就不用管它,否则它只是一个一般的读操作. */ + // 这里'READ'和'WRITE'后面的'A'字符代表英文单词Ahead,表示预读/写数据块的意思. + // 该函数首先对命令READA/WRITEA的情况进行一些处理.对于这两个命令,当指定的缓冲区正在使用而已被上锁时,就放弃预读/写请求.否则就作为普通 + // READ/WRITE命令进行操作.另外,如果参数给出的命令既不是READ也不是WRITE,则表示内核程序有错,显示出错信息并停机.注意,在修改命令之前这里 + // 已为参数是否为预读/写命令设置了标志rw_ahead. + if ((rw_ahead = (rw == READA || rw == WRITEA))) { + if (bh->b_lock) + return; + if (rw == READA) + rw = READ; + else + rw = WRITE; + } + if (rw != READ && rw != WRITE) + panic("Bad block dev command, must be R/W/RA/WA"); + lock_buffer(bh); // 锁定缓冲块 + // 如果是WRITE操作并且缓冲块未修改,或是READ操作并且缓冲块已更新,则直接返回缓冲区块。 + if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { + unlock_buffer(bh); + return; + } +repeat: + /* we don't allow the write-requests to fill up the queue completely: + * we want some room for reads: they take precedence. The last third + * of the requests are only for reads. + */ + /* + * 我们不能让队列中全都是写请求项:我们需要为读请求保留一些空间:读操作是优先的.请求队列的后三分之一空间仅用于读请求项. + */ + // 好,现在我们必须为本函数生成并添加读/写请求项了.首先我们需要在请求数组中寻找到一个空闲项(糟)来存放新请求项.搜索过程从请求数组末端开始. + // 根据上述要求,对于读命令请求,我们直接从队列末尾开始搜索,而对于写请求就只能从队列2/3处向队列头处搜索空项填入.于是我们开始从后向前搜索, + // 当请求结构request的设备字段dev值=-1时,表示该项未被占用(空闲).如果没有一项是空闲的(此时请求项数组指针已经搜索越过头部),则查看此次请求 + // 是否是提前读/写(READA或WRITEA),如果是则放弃此次请求操作.否则让本次请求操作先睡眠(以等待请求队列腾出空项),过一会儿再来搜索请求队列. + if (rw == READ) + req = request + NR_REQUEST; // 对于读请求,将指针指向队列尾部. + else + req = request + ((NR_REQUEST * 2) / 3); // 对于写请求,指针指向队列2/3处. + /* find an empty request */ + /* 搜索一个空请求项 */ + while (--req >= request) + if (req->dev < 0) + break; + /* if none found, sleep on new requests: check for rw_ahead */ + /* 如果没有找到空闲项,则让该次请求操作睡眠:需检查是否提前读/写 */ + if (req < request) { // 如果已搜索到头(队列无空项) + if (rw_ahead) { // 则若是提前读/写请求,则退出. + unlock_buffer(bh); + return; + } + sleep_on(&wait_for_request); // 否则就睡眠,过会再查看请求队列. + goto repeat; + } + /* fill up the request-info, and add it to the queue */ + /* 向空闲请求项中填写请求信息,并将其加入队列中 */ + // OK,程序执行到这里表示已找到一个空闲请求项.于是我们在设置好的新请求项后就调用add_request()把它添加到请求队列中,立刻退出.请求结构请参见blk_drv/blk.h. + // req->sector是读写操作的起始扇区号,req->buffer是请求项存放数据的缓冲区. + req->dev = bh->b_dev; // 设备号. + req->cmd = rw; // 命令(READ/WRITE). + req->errors = 0; // 操作时产生的错误次数. + req->sector = bh->b_blocknr << 1; // 起始扇区.块号转换成扇区号(1块=2扇区). + req->nr_sectors = 2; // 本请求项需要读写的扇区数. + req->buffer = bh->b_data; // 请求项缓冲区指针指向需读写的数据缓冲区. + req->waiting = NULL; // 任务等待操作执行完成的地方. + req->bh = bh; // 缓冲块头指针. + req->next = NULL; // 指向下一请求项. + add_request(major + blk_dev, req); // 将请求项加入队列中(blk_dev[major],reg). +} + +// 低级页面读写函数(Low Level Read Write Pagk). +// 以页面(4K)为单位访问设备数据,即每次读/写8个扇区.参见下面ll_rw_blk()函数. +void ll_rw_page(int rw, int dev, int page, char * buffer) +{ + struct request * req; + unsigned int major = MAJOR(dev); + + // 首先对函数参数的合法性进行检测.如果设备主设备号不存在或者该设备的请求操作函数不存在,则显示出错信息,并返回.如果参数给出的命令既不是 + // READ也不是WRITE,则表示内核程序有错,显示出错信息并停机. + if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { + printk("Trying to read nonexistent block-device\n\r"); + return; + } + if (rw != READ && rw != WRITE) + panic("Bad block dev command, must be R/W"); + // 在参数检测操作完成后,我们现在需要为本次操作建立请求项.首先我们需要在请求数组中寻找到一个空闲项(糟)来存放新请求项.搜索过程从请求数组末端 + // 开始.于是我们开始从后向前搜索,当请求结构request的设备字段值<0时,表示该项未被占用(空闲).如果没有一项是空闲的(此时请求项数组指针已经搜索越过 + // 头部),则让本次请求操作先睡眠(以等待请求队列腾出空项),过一会再来搜索请求队列. +repeat: + req = request + NR_REQUEST; // 将指针指向队列尾部. + while (--req >= request) + if (req->dev < 0) + break; + if (req < request) { + sleep_on(&wait_for_request); // 睡眠,过会再查看请求队列. + goto repeat; + } + /* fill up the request-info, and add it to the queue */ + /* 向空闲请求项中填写请求信息,并将其加入队列中 */ + // OK,程序执行到这里表示已找到一个空闲请求项.于是我们设置好新请求项,把当前进程置为不可中断睡眠中断后,就去调用add_request()把它添加到请求队列中, + // 然后直接调用调度函数让当前进程睡眠等待页面从交换设备中读入.这里不像make_request()函数那样直接退出函数而调用了schedule(),是因为make_request() + // 函数仅读2个扇区数据.而这里需要对交换设备读/写8个扇区,需要花较长的时间.因此当前进程肯定需要等待而睡眠.因此这里直接就让进程去睡眠了,省得在程序其他地方 + // 还要进行这些判断操作. + req->dev = dev; // 设备号 + req->cmd = rw; // 命令(READ/WRITE)start_code + req->errors = 0; // 读写操作错误计数 + req->sector = page << 3; // 起始读写扇区 + req->nr_sectors = 8; // 读写扇区数 + req->buffer = buffer; // 数据缓冲区 + req->waiting = current; // 当前进程进入该请求等待队列 + req->bh = NULL; // 无缓冲块头指针(不用高速缓冲) + req->next = NULL; // 下一个请求项指针 + current->state = TASK_UNINTERRUPTIBLE; // 置为不可中断状态 + add_request(major + blk_dev, req); // 将请求项加入队列中. + // 当前进程需要读取8个扇区的数据因此需要睡眠,因此调用调度程序选择进程运行 + schedule(); +} + +// 低级数据块读写函数(Low Level Read Write Block) +// 该函数是块设备驱动程序与系统其他部分的接口函数.通常在fs/buffer.c程序中被调用. +// 主要功能是创建块设备读写请求项并插入到指定块设备请求队列.实际的读写操作则是由设备的request_fn()函数完成.对于硬盘操作,该函数是do_hd_request();对于软盘操作 +// 该函数是do_fd_request();对于虚拟盘则是do_rd_request().另外,在调用该函数之前,调用者需要首先把读/写块设备的信息保存在缓冲块头结构中,如设备号,块号. +// 参数:rw - READ,READA,WRITE或WRITEA是命令;bh - 数据缓冲块头指针. +void ll_rw_block(int rw, struct buffer_head * bh) +{ + unsigned int major; /* 主设备号(对于硬盘是3) */ + + // 如果设备主设备号不存在或者该设备的请求操作函数不存在,则显示出错信息,并返回.否则创建请求项并插入请求队列 */ + if ((major = MAJOR(bh->b_dev)) >= NR_BLK_DEV || + !(blk_dev[major].request_fn)) { + printk("Trying to read nonexistent block-device\n\r"); + return; + } + make_request(major, rw, bh); +} + +/* 初始化块设备的请求数组 */ +void blk_dev_init(void) +{ + int i; + + for (i = 0; i < NR_REQUEST; i++) { + request[i].dev = -1; + request[i].next = NULL; + } +} diff --git a/linux-0.12/kernel/blk_drv/ramdisk.c b/linux-0.12/kernel/blk_drv/ramdisk.c new file mode 100644 index 0000000..dc99f7c --- /dev/null +++ b/linux-0.12/kernel/blk_drv/ramdisk.c @@ -0,0 +1,125 @@ +/* + * linux/kernel/blk_drv/ramdisk.c + * + * Written by Theodore Ts'o, 12/2/91 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR 1 +#include "blk.h" + +char *rd_start; +int rd_length = 0; + +void do_rd_request(void) +{ + int len; + char *addr; + + INIT_REQUEST; + addr = rd_start + (CURRENT->sector << 9); + len = CURRENT->nr_sectors << 9; + if ((MINOR(CURRENT->dev) != 1) || (addr+len > rd_start+rd_length)) { + end_request(0); + goto repeat; + } + if (CURRENT-> cmd == WRITE) { + (void ) memcpy(addr, + CURRENT->buffer, + len); + } else if (CURRENT->cmd == READ) { + (void) memcpy(CURRENT->buffer, + addr, + len); + } else + panic("unknown ramdisk-command"); + end_request(1); + goto repeat; +} + +/* + * Returns amount of memory which needs to be reserved. + */ +long rd_init(long mem_start, int length) +{ + int i; + char *cp; + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + rd_start = (char *) mem_start; + rd_length = length; + cp = rd_start; + for (i=0; i < length; i++) + *cp++ = '\0'; + return(length); +} + +/* + * If the root device is the ram disk, try to load it. + * In order to do this, the root device is originally set to the + * floppy, and we later change it to be ram disk. + */ +void rd_load(void) +{ + struct buffer_head *bh; + struct super_block s; + int block = 256; /* Start at block 256 */ + int i = 1; + int nblocks; + char *cp; /* Move pointer */ + + if (!rd_length) + return; + printk("Ram disk: %d bytes, starting at 0x%x\n", rd_length, + (int) rd_start); + if (MAJOR(ROOT_DEV) != 2) + return; + bh = breada(ROOT_DEV,block+1,block,block+2,-1); + if (!bh) { + printk("Disk error while looking for ramdisk!\n"); + return; + } + *((struct d_super_block *) &s) = *((struct d_super_block *) bh->b_data); + brelse(bh); + if (s.s_magic != SUPER_MAGIC) + /* No ram disk image present, assume normal floppy boot */ + return; + nblocks = s.s_nzones << s.s_log_zone_size; + if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) { + printk("Ram disk image too big! (%d blocks, %d avail)\n", + nblocks, rd_length >> BLOCK_SIZE_BITS); + return; + } + printk("Loading %d bytes into ram disk... 0000k", + nblocks << BLOCK_SIZE_BITS); + cp = rd_start; + while (nblocks) { + if (nblocks > 2) + bh = breada(ROOT_DEV, block, block+1, block+2, -1); + else + bh = bread(ROOT_DEV, block); + if (!bh) { + printk("I/O error on block %d, aborting load\n", + block); + return; + } + (void) memcpy(cp, bh->b_data, BLOCK_SIZE); + brelse(bh); + printk("\010\010\010\010\010%4dk",i); + cp += BLOCK_SIZE; + block++; + nblocks--; + i++; + } + printk("\010\010\010\010\010done \n"); + ROOT_DEV=0x0101; +} diff --git a/linux-0.12/kernel/chr_drv/Makefile b/linux-0.12/kernel/chr_drv/Makefile new file mode 100644 index 0000000..a796e22 --- /dev/null +++ b/linux-0.12/kernel/chr_drv/Makefile @@ -0,0 +1,85 @@ +# +# Makefile for the FREAX-kernel character device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +include ../../Rules.make + +LDFLAGS = -x +CFLAGS = -g -Wall -fstrength-reduce -fomit-frame-pointer \ + -finline-functions -nostdinc -fno-builtin -I../../include +CPP += -E -I../../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \ + tty_ioctl.o pty.o + +chr_drv.a: $(OBJS) + $(AR) rcs chr_drv.a $(OBJS) + @sync + +keyboard.s: keyboard.S + $(CPP) -traditional keyboard.S -o keyboard.s + +clean: + rm -f core *.o *.a tmp_make keyboard.s + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +console.s console.o : console.c ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h \ + ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ + ../../include/sys/resource.h ../../include/linux/tty.h \ + ../../include/termios.h ../../include/linux/config.h ../../include/asm/io.h \ + ../../include/asm/system.h ../../include/asm/segment.h \ + ../../include/string.h ../../include/errno.h +pty.s pty.o : pty.c ../../include/linux/tty.h ../../include/termios.h \ + ../../include/sys/types.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/linux/mm.h ../../include/linux/kernel.h \ + ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \ + ../../include/time.h ../../include/sys/resource.h \ + ../../include/asm/system.h ../../include/asm/io.h +serial.s serial.o : serial.c ../../include/linux/tty.h ../../include/termios.h \ + ../../include/sys/types.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/linux/mm.h ../../include/linux/kernel.h \ + ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \ + ../../include/time.h ../../include/sys/resource.h \ + ../../include/asm/system.h ../../include/asm/io.h +tty_io.s tty_io.o : tty_io.c ../../include/ctype.h ../../include/errno.h \ + ../../include/signal.h ../../include/sys/types.h ../../include/unistd.h \ + ../../include/sys/stat.h ../../include/sys/time.h ../../include/time.h \ + ../../include/sys/times.h ../../include/sys/utsname.h \ + ../../include/sys/param.h ../../include/sys/resource.h \ + ../../include/utime.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/linux/mm.h ../../include/linux/kernel.h \ + ../../include/linux/tty.h ../../include/termios.h \ + ../../include/asm/segment.h ../../include/asm/system.h +tty_ioctl.s tty_ioctl.o : tty_ioctl.c ../../include/errno.h ../../include/termios.h \ + ../../include/sys/types.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/linux/mm.h ../../include/linux/kernel.h \ + ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \ + ../../include/time.h ../../include/sys/resource.h ../../include/linux/tty.h \ + ../../include/asm/io.h ../../include/asm/segment.h \ + ../../include/asm/system.h diff --git a/linux-0.12/kernel/chr_drv/console.c b/linux-0.12/kernel/chr_drv/console.c new file mode 100644 index 0000000..e8ad731 --- /dev/null +++ b/linux-0.12/kernel/chr_drv/console.c @@ -0,0 +1,1026 @@ +/* + * linux/kernel/console.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * console.c + * + * This module implements the console io functions + * 'void con_init(void)' + * 'void con_write(struct tty_queue * queue)' + * Hopefully this will be a rather complete VT102 implementation. + * + * Beeping thanks to John T Kohl. + * + * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics + * Chars, and VT100 enhancements by Peter MacDonald. + */ + +/* + * NOTE!!! We sometimes disable and enable interrupts for a short while + * (to put a word in video IO), but this will work even for keyboard + * interrupts. We know interrupts aren't enabled when getting a keyboard + * interrupt, as we use trap-gates. Hopefully all is well. + */ + +/* + * Code to check for different video-cards mostly by Galen Hunt, + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +/* 该符号常量定义终端IO结构的默认数据 */ +#define DEF_TERMIOS \ + (struct termios) { \ + ICRNL, \ + OPOST | ONLCR, \ + 0, \ + IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, \ + 0, \ + INIT_C_CC \ + } + +/* + * These are set up by the setup-routine at boot-time: + */ + +#define ORIG_X (*(unsigned char *)0x90000) +#define ORIG_Y (*(unsigned char *)0x90001) +#define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004) +#define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff) +#define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8) +#define ORIG_VIDEO_LINES ((*(unsigned short *)0x9000e) & 0xff) +#define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008) +#define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a) +#define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c) + +#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ +#define VIDEO_TYPE_CGA 0x11 /* CGA Display */ +#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ +#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */ + +#define NPAR 16 + +int NR_CONSOLES = 0; + +extern void keyboard_interrupt(void); + +static unsigned char video_type; /* Type of display being used */ +static unsigned long video_num_columns; /* Number of text columns */ +static unsigned long video_mem_base; /* Base of video memory */ +static unsigned long video_mem_term; /* End of video memory */ +static unsigned long video_size_row; /* Bytes per row */ +static unsigned long video_num_lines; /* Number of test lines */ +static unsigned char video_page; /* Initial video page */ +static unsigned short video_port_reg; /* Video register select port */ +static unsigned short video_port_val; /* Video register value port */ +static int can_do_colour = 0; + +static struct { + unsigned short vc_video_erase_char; + unsigned char vc_attr; + unsigned char vc_def_attr; + int vc_bold_attr; + unsigned long vc_ques; + unsigned long vc_state; + unsigned long vc_restate; + unsigned long vc_checkin; + unsigned long vc_origin; /* Used for EGA/VGA fast scroll */ + unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */ + unsigned long vc_pos; + unsigned long vc_x,vc_y; + unsigned long vc_top,vc_bottom; + unsigned long vc_npar,vc_par[NPAR]; + unsigned long vc_video_mem_start; /* Start of video RAM */ + unsigned long vc_video_mem_end; /* End of video RAM (sort of) */ + unsigned int vc_saved_x; + unsigned int vc_saved_y; + unsigned int vc_iscolor; + char * vc_translate; +} vc_cons [MAX_CONSOLES]; + +#define origin (vc_cons[currcons].vc_origin) +#define scr_end (vc_cons[currcons].vc_scr_end) +#define pos (vc_cons[currcons].vc_pos) +#define top (vc_cons[currcons].vc_top) +#define bottom (vc_cons[currcons].vc_bottom) +#define x (vc_cons[currcons].vc_x) +#define y (vc_cons[currcons].vc_y) +#define state (vc_cons[currcons].vc_state) +#define restate (vc_cons[currcons].vc_restate) +#define checkin (vc_cons[currcons].vc_checkin) +#define npar (vc_cons[currcons].vc_npar) +#define par (vc_cons[currcons].vc_par) +#define ques (vc_cons[currcons].vc_ques) +#define attr (vc_cons[currcons].vc_attr) +#define saved_x (vc_cons[currcons].vc_saved_x) +#define saved_y (vc_cons[currcons].vc_saved_y) +#define translate (vc_cons[currcons].vc_translate) +#define video_mem_start (vc_cons[currcons].vc_video_mem_start) +#define video_mem_end (vc_cons[currcons].vc_video_mem_end) +#define def_attr (vc_cons[currcons].vc_def_attr) +#define video_erase_char (vc_cons[currcons].vc_video_erase_char) +#define iscolor (vc_cons[currcons].vc_iscolor) + +int blankinterval = 0; +int blankcount = 0; + +static void sysbeep(void); + +/* + * this is what the terminal answers to a ESC-Z or csi0c + * query (= vt100 response). + */ +#define RESPONSE "\033[?1;2c" + +static char * translations[] = { +/* normal 7-bit ascii */ + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~ ", +/* vt100 graphics */ + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ " + "\004\261\007\007\007\007\370\361\007\007\275\267\326\323\327\304" + "\304\304\304\304\307\266\320\322\272\363\362\343\\007\234\007 " +}; + +#define NORM_TRANS (translations[0]) +#define GRAF_TRANS (translations[1]) + +/* NOTE! gotoxy thinks x==video_num_columns is ok */ +static inline void gotoxy(int currcons, int new_x,unsigned int new_y) +{ + if (new_x > video_num_columns || new_y >= video_num_lines) + return; + x = new_x; + y = new_y; + pos = origin + y*video_size_row + (x<<1); +} + +static inline void set_origin(int currcons) +{ + if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM) + return; + if (currcons != fg_console) + return; + cli(); + outb_p(12, video_port_reg); + outb_p(0xff&((origin-video_mem_base)>>9), video_port_val); + outb_p(13, video_port_reg); + outb_p(0xff&((origin-video_mem_base)>>1), video_port_val); + sti(); +} + +static void scrup(int currcons) +{ + if (bottom<=top) + return; + if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM) + { + if (!top && bottom == video_num_lines) { + origin += video_size_row; + pos += video_size_row; + scr_end += video_size_row; + if (scr_end > video_mem_end) { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl video_num_columns,%1\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((video_num_lines-1)*video_num_columns>>1), + "D" (video_mem_start), + "S" (origin) + ); + scr_end -= origin-video_mem_start; + pos -= origin-video_mem_start; + origin = video_mem_start; + } else { + __asm__("cld\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" (video_num_columns), + "D" (scr_end-video_size_row) + ); + } + set_origin(currcons); + } else { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*top), + "S" (origin+video_size_row*(top+1)) + ); + } + } + else /* Not EGA/VGA */ + { + __asm__("cld\n\t" + "rep\n\t" + "movsl\n\t" + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*top), + "S" (origin+video_size_row*(top+1)) + ); + } +} + +static void scrdown(int currcons) +{ + if (bottom <= top) + return; + if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM) + { + __asm__("std\n\t" + "rep\n\t" + "movsl\n\t" + "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*bottom-4), + "S" (origin+video_size_row*(bottom-1)-4) + ); + } + else /* Not EGA/VGA */ + { + __asm__("std\n\t" + "rep\n\t" + "movsl\n\t" + "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ + "movl video_num_columns,%%ecx\n\t" + "rep\n\t" + "stosw" + ::"a" (video_erase_char), + "c" ((bottom-top-1)*video_num_columns>>1), + "D" (origin+video_size_row*bottom-4), + "S" (origin+video_size_row*(bottom-1)-4) + ); + } +} + +static void lf(int currcons) +{ + if (y+1top) { + y--; + pos -= video_size_row; + return; + } + scrdown(currcons); +} + +static void cr(int currcons) +{ + pos -= x<<1; + x=0; +} + +static void del(int currcons) +{ + if (x) { + pos -= 2; + x--; + *(unsigned short *)pos = video_erase_char; + } +} + +static void csi_J(int currcons, int vpar) +{ + long count; + long start; + + switch (vpar) { + case 0: /* erase from cursor to end of display */ + count = (scr_end-pos)>>1; + start = pos; + break; + case 1: /* erase from start to cursor */ + count = (pos-origin)>>1; + start = origin; + break; + case 2: /* erase whole display */ + count = video_num_columns * video_num_lines; + start = origin; + break; + default: + return; + } + __asm__("cld\n\t" + "rep\n\t" + "stosw\n\t" + ::"c" (count), + "D" (start),"a" (video_erase_char) + ); +} + +static void csi_K(int currcons, int vpar) +{ + long count; + long start; + + switch (vpar) { + case 0: /* erase from cursor to end of line */ + if (x>=video_num_columns) + return; + count = video_num_columns-x; + start = pos; + break; + case 1: /* erase from start of line to cursor */ + start = pos - (x<<1); + count = (x>4)&0xf)? + (attr&0xf0)|(((attr&0xf)+1)%0xf): + newattr); + } + } + break; + case 5: attr=attr|0x80;break; /* blinking */ + case 7: attr=(attr<<4)|(attr>>4);break; /* negative */ + case 22: attr=attr&0xf7;break; /* not bold */ + case 24: attr=attr&0xfe;break; /* not underline */ + case 25: attr=attr&0x7f;break; /* not blinking */ + case 27: attr=def_attr;break; /* positive image */ + case 39: attr=(attr & 0xf0)|(def_attr & 0x0f); break; + case 49: attr=(attr & 0x0f)|(def_attr & 0xf0); break; + default: + if (!can_do_colour) + break; + iscolor = 1; + if ((par[i]>=30) && (par[i]<=38)) + attr = (attr & 0xf0) | (par[i]-30); + else /* Background color */ + if ((par[i]>=40) && (par[i]<=48)) + attr = (attr & 0x0f) | ((par[i]-40)<<4); + else + break; + } +} + +static inline void set_cursor(int currcons) +{ + blankcount = blankinterval; + if (currcons != fg_console) + return; + cli(); + outb_p(14, video_port_reg); + outb_p(0xff&((pos-video_mem_base)>>9), video_port_val); + outb_p(15, video_port_reg); + outb_p(0xff&((pos-video_mem_base)>>1), video_port_val); + sti(); +} + +static inline void hide_cursor(int currcons) +{ + outb_p(14, video_port_reg); + outb_p(0xff&((scr_end-video_mem_base)>>9), video_port_val); + outb_p(15, video_port_reg); + outb_p(0xff&((scr_end-video_mem_base)>>1), video_port_val); +} + +static void respond(int currcons, struct tty_struct * tty) +{ + char * p = RESPONSE; + + cli(); + while (*p) { + PUTCH(*p,tty->read_q); + p++; + } + sti(); + copy_to_cooked(tty); +} + +static void insert_char(int currcons) +{ + int i=x; + unsigned short tmp, old = video_erase_char; + unsigned short * p = (unsigned short *) pos; + + while (i++=video_num_columns) + return; + i = x; + while (++i < video_num_columns) { + *p = *(p+1); + p++; + } + *p = video_erase_char; +} + +static void delete_line(int currcons) +{ + int oldtop,oldbottom; + + oldtop=top; + oldbottom=bottom; + top=y; + bottom = video_num_lines; + scrup(currcons); + top=oldtop; + bottom=oldbottom; +} + +static void csi_at(int currcons, unsigned int nr) +{ + if (nr > video_num_columns) + nr = video_num_columns; + else if (!nr) + nr = 1; + while (nr--) + insert_char(currcons); +} + +static void csi_L(int currcons, unsigned int nr) +{ + if (nr > video_num_lines) + nr = video_num_lines; + else if (!nr) + nr = 1; + while (nr--) + insert_line(currcons); +} + +static void csi_P(int currcons, unsigned int nr) +{ + if (nr > video_num_columns) + nr = video_num_columns; + else if (!nr) + nr = 1; + while (nr--) + delete_char(currcons); +} + +static void csi_M(int currcons, unsigned int nr) +{ + if (nr > video_num_lines) + nr = video_num_lines; + else if (!nr) + nr=1; + while (nr--) + delete_line(currcons); +} + +static void save_cur(int currcons) +{ + saved_x=x; + saved_y=y; +} + +static void restore_cur(int currcons) +{ + gotoxy(currcons,saved_x, saved_y); +} + + +enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, + ESsetterm, ESsetgraph }; + +void con_write(struct tty_struct * tty) +{ + int nr; + char c; + int currcons; + + currcons = tty - tty_table; + if ((currcons>=MAX_CONSOLES) || (currcons<0)) + panic("con_write: illegal tty"); + + nr = CHARS(tty->write_q); + while (nr--) { + if (tty->stopped) + break; + GETCH(tty->write_q,c); + if (c == 24 || c == 26) + state = ESnormal; + switch(state) { + case ESnormal: + if (c>31 && c<127) { + if (x>=video_num_columns) { + x -= video_num_columns; + pos -= video_size_row; + lf(currcons); + } + __asm__("movb %2,%%ah\n\t" + "movw %%ax,%1\n\t" + ::"a" (translate[c-32]), + "m" (*(short *)pos), + "m" (attr) + ); + pos += 2; + x++; + } else if (c==27) + state=ESesc; + else if (c==10 || c==11 || c==12) + lf(currcons); + else if (c==13) + cr(currcons); + else if (c==ERASE_CHAR(tty)) + del(currcons); + else if (c==8) { + if (x) { + x--; + pos -= 2; + } + } else if (c==9) { + c=8-(x&7); + x += c; + pos += c<<1; + if (x>video_num_columns) { + x -= video_num_columns; + pos -= video_size_row; + lf(currcons); + } + c=9; + } else if (c==7) + sysbeep(); + else if (c == 14) + translate = GRAF_TRANS; + else if (c == 15) + translate = NORM_TRANS; + break; + case ESesc: + state = ESnormal; + switch (c) + { + case '[': + state=ESsquare; + break; + case 'E': + gotoxy(currcons,0,y+1); + break; + case 'M': + ri(currcons); + break; + case 'D': + lf(currcons); + break; + case 'Z': + respond(currcons,tty); + break; + case '7': + save_cur(currcons); + break; + case '8': + restore_cur(currcons); + break; + case '(': case ')': + state = ESsetgraph; + break; + case 'P': + state = ESsetterm; + break; + case '#': + state = -1; + break; + case 'c': + tty->termios = DEF_TERMIOS; + state = restate = ESnormal; + checkin = 0; + top = 0; + bottom = video_num_lines; + break; + /* case '>': Numeric keypad */ + /* case '=': Appl. keypad */ + } + break; + case ESsquare: + for(npar=0;npar='0' && c<='9') { + par[npar]=10*par[npar]+c-'0'; + break; + } else state=ESgotpars; + case ESgotpars: + state = ESnormal; + if (ques) + { ques =0; + break; + } + switch(c) { + case 'G': case '`': + if (par[0]) par[0]--; + gotoxy(currcons,par[0],y); + break; + case 'A': + if (!par[0]) par[0]++; + gotoxy(currcons,x,y-par[0]); + break; + case 'B': case 'e': + if (!par[0]) par[0]++; + gotoxy(currcons,x,y+par[0]); + break; + case 'C': case 'a': + if (!par[0]) par[0]++; + gotoxy(currcons,x+par[0],y); + break; + case 'D': + if (!par[0]) par[0]++; + gotoxy(currcons,x-par[0],y); + break; + case 'E': + if (!par[0]) par[0]++; + gotoxy(currcons,0,y+par[0]); + break; + case 'F': + if (!par[0]) par[0]++; + gotoxy(currcons,0,y-par[0]); + break; + case 'd': + if (par[0]) par[0]--; + gotoxy(currcons,x,par[0]); + break; + case 'H': case 'f': + if (par[0]) par[0]--; + if (par[1]) par[1]--; + gotoxy(currcons,par[1],par[0]); + break; + case 'J': + csi_J(currcons,par[0]); + break; + case 'K': + csi_K(currcons,par[0]); + break; + case 'L': + csi_L(currcons,par[0]); + break; + case 'M': + csi_M(currcons,par[0]); + break; + case 'P': + csi_P(currcons,par[0]); + break; + case '@': + csi_at(currcons,par[0]); + break; + case 'm': + csi_m(currcons); + break; + case 'r': + if (par[0]) par[0]--; + if (!par[1]) par[1] = video_num_lines; + if (par[0] < par[1] && + par[1] <= video_num_lines) { + top=par[0]; + bottom=par[1]; + } + break; + case 's': + save_cur(currcons); + break; + case 'u': + restore_cur(currcons); + break; + case 'l': /* blank interval */ + case 'b': /* bold attribute */ + if (!((npar >= 2) && + ((par[1]-13) == par[0]) && + ((par[2]-17) == par[0]))) + break; + if ((c=='l')&&(par[0]>=0)&&(par[0]<=60)) + { + blankinterval = HZ*60*par[0]; + blankcount = blankinterval; + } + if (c=='b') + vc_cons[currcons].vc_bold_attr + = par[0]; + } + break; + case ESfunckey: + state = ESnormal; + break; + case ESsetterm: /* Setterm functions. */ + state = ESnormal; + if (c == 'S') { + def_attr = attr; + video_erase_char = (video_erase_char&0x0ff) | (def_attr<<8); + } else if (c == 'L') + ; /*linewrap on*/ + else if (c == 'l') + ; /*linewrap off*/ + break; + case ESsetgraph: + state = ESnormal; + if (c == '0') + translate = GRAF_TRANS; + else if (c == 'B') + translate = NORM_TRANS; + break; + default: + state = ESnormal; + } + } + set_cursor(currcons); +} + +/* + * void con_init(void); + * + * This routine initalizes console interrupts, and does nothing + * else. If you want the screen to clear, call tty_write with + * the appropriate escape-sequece. + * + * Reads the information preserved by setup.s to determine the current display + * type and sets everything accordingly. + */ +void con_init(void) +{ + register unsigned char a; + char *display_desc = "????"; + char *display_ptr; + int currcons = 0; + long base, term; + long video_memory; + + video_num_columns = ORIG_VIDEO_COLS; + video_size_row = video_num_columns * 2; + video_num_lines = ORIG_VIDEO_LINES; + video_page = ORIG_VIDEO_PAGE; + video_erase_char = 0x0720; + blankcount = blankinterval; + + if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */ + { + video_mem_base = 0xb0000; + video_port_reg = 0x3b4; + video_port_val = 0x3b5; + if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) + { + video_type = VIDEO_TYPE_EGAM; + video_mem_term = 0xb8000; + display_desc = "EGAm"; + } + else + { + video_type = VIDEO_TYPE_MDA; + video_mem_term = 0xb2000; + display_desc = "*MDA"; + } + } + else /* If not, it is color. */ + { + can_do_colour = 1; + video_mem_base = 0xb8000; + video_port_reg = 0x3d4; + video_port_val = 0x3d5; + if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) + { + video_type = VIDEO_TYPE_EGAC; + video_mem_term = 0xc0000; + display_desc = "EGAc"; + } + else + { + video_type = VIDEO_TYPE_CGA; + video_mem_term = 0xba000; + display_desc = "*CGA"; + } + } + video_memory = video_mem_term - video_mem_base; + NR_CONSOLES = video_memory / (video_num_lines * video_size_row); + if (NR_CONSOLES > MAX_CONSOLES) + NR_CONSOLES = MAX_CONSOLES; + if (!NR_CONSOLES) + NR_CONSOLES = 1; + video_memory /= NR_CONSOLES; + + /* Let the user known what kind of display driver we are using */ + + display_ptr = ((char *)video_mem_base) + video_size_row - 8; + while (*display_desc) + { + *display_ptr++ = *display_desc++; + display_ptr++; + } + + /* Initialize the variables used for scrolling (mostly EGA/VGA) */ + + base = origin = video_mem_start = video_mem_base; + term = video_mem_end = base + video_memory; + scr_end = video_mem_start + video_num_lines * video_size_row; + top = 0; + bottom = video_num_lines; + attr = 0x07; + def_attr = 0x07; + restate = state = ESnormal; + checkin = 0; + ques = 0; + iscolor = 0; + translate = NORM_TRANS; + vc_cons[0].vc_bold_attr = -1; + + gotoxy(currcons,ORIG_X,ORIG_Y); + for (currcons = 1; currconsNR_CONSOLES)) + return -EIO; + currcons--; + sptr = (char *) origin; + for (l=video_num_lines*video_num_columns; l>0 ; l--) + put_fs_byte(*sptr++,buf++); + return(0); +} + +void blank_screen() +{ + if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM) + return; +/* blank here. I can't find out how to do it, though */ +} + +void unblank_screen() +{ + if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM) + return; +/* unblank here */ +} + +void console_print(const char * b) +{ + int currcons = fg_console; + char c; + + while ((c = *(b++))) { + if (c == 10) { + cr(currcons); + lf(currcons); + continue; + } + if (c == 13) { + cr(currcons); + continue; + } + if (x>=video_num_columns) { + x -= video_num_columns; + pos -= video_size_row; + lf(currcons); + } + __asm__("movb %2,%%ah\n\t" + "movw %%ax,%1\n\t" + ::"a" (c), + "m" (*(short *)pos), + "m" (attr) + ); + pos += 2; + x++; + } + set_cursor(currcons); +} diff --git a/linux-0.12/kernel/chr_drv/keyboard.S b/linux-0.12/kernel/chr_drv/keyboard.S new file mode 100644 index 0000000..6dbe367 --- /dev/null +++ b/linux-0.12/kernel/chr_drv/keyboard.S @@ -0,0 +1,599 @@ +/* + * linux/kernel/keyboard.S + * + * (C) 1991 Linus Torvalds + */ + +/* + * Thanks to Alfred Leung for US keyboard patches + * Wolfgang Thiel for German keyboard patches + * Marc Corsini for the French keyboard + */ + +/* KBD_FINNISH for Finnish keyboards + * KBD_US for US-type + * KBD_GR for German keyboards + * KBD_FR for Frech keyboard + */ +#define KBD_FINNISH + +.text +.globl keyboard_interrupt + +/* + * these are for the keyboard read functions + */ +size = 1024 /* must be a power of two ! And MUST be the same + as in tty_io.c !!!! */ +head = 4 +tail = 8 +proc_list = 12 +buf = 16 + +mode: .byte 0 /* caps, alt, ctrl and shift mode */ +leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */ +e0: .byte 0 + +/* + * con_int is the real interrupt routine that reads the + * keyboard scan-code and converts it into the appropriate + * ascii character(s). + */ +keyboard_interrupt: + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + push %ds + push %es + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl blankinterval,%eax + movl %eax,blankcount + xorl %eax,%eax /* %eax is scan code */ + inb $0x60,%al + cmpb $0xe0,%al + je set_e0 + cmpb $0xe1,%al + je set_e1 + call *key_table(,%eax,4) + movb $0,e0 +e0_e1: inb $0x61,%al + jmp 1f +1: jmp 1f +1: orb $0x80,%al + jmp 1f +1: jmp 1f +1: outb %al,$0x61 + jmp 1f +1: jmp 1f +1: andb $0x7F,%al + outb %al,$0x61 + movb $0x20,%al + outb %al,$0x20 + pushl $0 + call do_tty_interrupt + addl $4,%esp + pop %es + pop %ds + popl %edx + popl %ecx + popl %ebx + popl %eax + iret +set_e0: movb $1,e0 + jmp e0_e1 +set_e1: movb $2,e0 + jmp e0_e1 + +/* + * This routine fills the buffer with max 8 bytes, taken from + * %ebx:%eax. (%edx is high). The bytes are written in the + * order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero. + */ +put_queue: + pushl %ecx + pushl %edx + movl table_list,%edx # read-queue for console + movl head(%edx),%ecx +1: movb %al,buf(%edx,%ecx) + incl %ecx + andl $size-1,%ecx + cmpl tail(%edx),%ecx # buffer full - discard everything + je 3f + shrdl $8,%ebx,%eax + je 2f + shrl $8,%ebx + jmp 1b +2: movl %ecx,head(%edx) + movl proc_list(%edx),%ecx + testl %ecx,%ecx + je 3f + movl $0,(%ecx) +3: popl %edx + popl %ecx + ret + +ctrl: movb $0x04,%al + jmp 1f +alt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: orb %al,mode + ret +unctrl: movb $0x04,%al + jmp 1f +unalt: movb $0x10,%al +1: cmpb $0,e0 + je 2f + addb %al,%al +2: notb %al + andb %al,mode + ret + +lshift: + orb $0x01,mode + ret +unlshift: + andb $0xfe,mode + ret +rshift: + orb $0x02,mode + ret +unrshift: + andb $0xfd,mode + ret + +caps: testb $0x80,mode + jne 1f + xorb $4,leds + xorb $0x40,mode + orb $0x80,mode +set_leds: + call kb_wait + movb $0xed,%al /* set leds command */ + outb %al,$0x60 + call kb_wait + movb leds,%al + outb %al,$0x60 + ret +uncaps: andb $0x7f,mode + ret +scroll: + testb $0x03,mode + je 1f + call show_mem + jmp 2f +1: call show_state +2: xorb $1,leds + jmp set_leds +num: xorb $2,leds + jmp set_leds + +/* + * curosr-key/numeric keypad cursor keys are handled here. + * checking for numeric keypad etc. + */ +cursor: + subb $0x47,%al + jb 1f + cmpb $12,%al + ja 1f + jne cur2 /* check for ctrl-alt-del */ + testb $0x0c,mode + je cur2 + testb $0x30,mode + jne reboot +cur2: cmpb $0x01,e0 /* e0 forces cursor movement */ + je cur + testb $0x02,leds /* not num-lock forces cursor */ + je cur + testb $0x03,mode /* shift forces cursor */ + jne cur + xorl %ebx,%ebx + movb num_table(%eax),%al + jmp put_queue +1: ret + +cur: movb cur_table(%eax),%al + cmpb $'9,%al + ja ok_cur + movb $'~,%ah +ok_cur: shll $16,%eax + movw $0x5b1b,%ax + xorl %ebx,%ebx + jmp put_queue + +#if defined(KBD_FR) +num_table: + .ascii "789 456 1230." +#else +num_table: + .ascii "789 456 1230," +#endif +cur_table: + .ascii "HA5 DGC YB623" + +/* + * this routine handles function keys + */ +func: + subb $0x3B,%al + jb end_func + cmpb $9,%al + jbe ok_func + subb $18,%al + cmpb $10,%al + jb end_func + cmpb $11,%al + ja end_func +ok_func: + testb $0x10,mode + jne alt_func + cmpl $4,%ecx /* check that there is enough room */ + jl end_func + movl func_table(,%eax,4),%eax + xorl %ebx,%ebx + jmp put_queue +alt_func: + pushl %eax + call change_console + popl %eax +end_func: + ret + +/* + * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc. + */ +func_table: + .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b + .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b + .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b + +#if defined(KBD_FINNISH) +key_map: + .byte 0,27 + .ascii "1234567890+'" + .byte 127,9 + .ascii "qwertyuiop}" + .byte 0,13,0 + .ascii "asdfghjkl|{" + .byte 0,0 + .ascii "'zxcvbnm,.-" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + +shift_map: + .byte 0,27 + .ascii "!\"#$%&/()=?`" + .byte 127,9 + .ascii "QWERTYUIOP]^" + .byte 13,0 + .ascii "ASDFGHJKL\\[" + .byte 0,0 + .ascii "*ZXCVBNM;:_" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + +#elif defined(KBD_US) + +key_map: + .byte 0,27 + .ascii "1234567890-=" + .byte 127,9 + .ascii "qwertyuiop[]" + .byte 13,0 + .ascii "asdfghjkl;'" + .byte '`,0 + .ascii "\\zxcvbnm,./" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + + +shift_map: + .byte 0,27 + .ascii "!@#$%^&*()_+" + .byte 127,9 + .ascii "QWERTYUIOP{}" + .byte 13,0 + .ascii "ASDFGHJKL:\"" + .byte '~,0 + .ascii "|ZXCVBNM<>?" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + +#elif defined(KBD_GR) + +key_map: + .byte 0,27 + .ascii "1234567890\\'" + .byte 127,9 + .ascii "qwertzuiop@+" + .byte 13,0 + .ascii "asdfghjkl[]^" + .byte 0,'# + .ascii "yxcvbnm,.-" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + + +shift_map: + .byte 0,27 + .ascii "!\"#$%&/()=?`" + .byte 127,9 + .ascii "QWERTZUIOP\\*" + .byte 13,0 + .ascii "ASDFGHJKL{}~" + .byte 0,'' + .ascii "YXCVBNM;:_" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0@\0$\0\0{[]}\\\0" + .byte 0,0 + .byte '@,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + + +#elif defined(KBD_FR) + +key_map: + .byte 0,27 + .ascii "&{\"'(-}_/@)=" + .byte 127,9 + .ascii "azertyuiop^$" + .byte 13,0 + .ascii "qsdfghjklm|" + .byte '`,0,42 /* coin sup gauche, don't know, [*|mu] */ + .ascii "wxcvbn,;:!" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '< + .fill 10,1,0 + +shift_map: + .byte 0,27 + .ascii "1234567890]+" + .byte 127,9 + .ascii "AZERTYUIOP<>" + .byte 13,0 + .ascii "QSDFGHJKLM%" + .byte '~,0,'# + .ascii "WXCVBN?./\\" + .byte 0,'*,0,32 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte '-,0,0,0,'+ /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '> + .fill 10,1,0 + +alt_map: + .byte 0,0 + .ascii "\0~#{[|`\\^@]}" + .byte 0,0 + .byte '@,0,0,0,0,0,0,0,0,0,0 + .byte '~,13,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0 /* 36-39 */ + .fill 16,1,0 /* 3A-49 */ + .byte 0,0,0,0,0 /* 4A-4E */ + .byte 0,0,0,0,0,0,0 /* 4F-55 */ + .byte '| + .fill 10,1,0 + +#else +#error "KBD-type not defined" +#endif +/* + * do_self handles "normal" keys, ie keys that don't change meaning + * and which have just one character returns. + */ +do_self: + lea alt_map,%ebx + testb $0x20,mode /* alt-gr */ + jne 1f + lea shift_map,%ebx + testb $0x03,mode + jne 1f + lea key_map,%ebx +1: movb (%ebx,%eax),%al + orb %al,%al + je none + testb $0x4c,mode /* ctrl or caps */ + je 2f + cmpb $'a,%al + jb 2f + cmpb $'},%al + ja 2f + subb $32,%al +2: testb $0x0c,mode /* ctrl */ + je 3f + cmpb $64,%al + jb 3f + cmpb $64+32,%al + jae 3f + subb $64,%al +3: testb $0x10,mode /* left alt */ + je 4f + orb $0x80,%al +4: andl $0xff,%eax + xorl %ebx,%ebx + call put_queue +none: ret + +/* + * minus has a routine of it's own, as a 'E0h' before + * the scan code for minus means that the numeric keypad + * slash was pushed. + */ +minus: cmpb $1,e0 + jne do_self + movl $'/,%eax + xorl %ebx,%ebx + jmp put_queue + +/* + * This table decides which routine to call when a scan-code has been + * gotten. Most routines just call do_self, or none, depending if + * they are make or break. + */ +key_table: + .long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */ + .long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */ + .long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */ + .long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */ + .long do_self,do_self,do_self,do_self /* 10-13 q w e r */ + .long do_self,do_self,do_self,do_self /* 14-17 t y u i */ + .long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */ + .long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */ + .long do_self,do_self,do_self,do_self /* 20-23 d f g h */ + .long do_self,do_self,do_self,do_self /* 24-27 j k l | */ + .long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */ + .long do_self,do_self,do_self,do_self /* 2C-2F z x c v */ + .long do_self,do_self,do_self,do_self /* 30-33 b n m , */ + .long do_self,minus,rshift,do_self /* 34-37 . - rshift * */ + .long alt,do_self,caps,func /* 38-3B alt sp caps f1 */ + .long func,func,func,func /* 3C-3F f2 f3 f4 f5 */ + .long func,func,func,func /* 40-43 f6 f7 f8 f9 */ + .long func,num,scroll,cursor /* 44-47 f10 num scr home */ + .long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */ + .long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */ + .long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */ + .long none,none,do_self,func /* 54-57 sysreq ? < f11 */ + .long func,none,none,none /* 58-5B f12 ? ? ? */ + .long none,none,none,none /* 5C-5F ? ? ? ? */ + .long none,none,none,none /* 60-63 ? ? ? ? */ + .long none,none,none,none /* 64-67 ? ? ? ? */ + .long none,none,none,none /* 68-6B ? ? ? ? */ + .long none,none,none,none /* 6C-6F ? ? ? ? */ + .long none,none,none,none /* 70-73 ? ? ? ? */ + .long none,none,none,none /* 74-77 ? ? ? ? */ + .long none,none,none,none /* 78-7B ? ? ? ? */ + .long none,none,none,none /* 7C-7F ? ? ? ? */ + .long none,none,none,none /* 80-83 ? br br br */ + .long none,none,none,none /* 84-87 br br br br */ + .long none,none,none,none /* 88-8B br br br br */ + .long none,none,none,none /* 8C-8F br br br br */ + .long none,none,none,none /* 90-93 br br br br */ + .long none,none,none,none /* 94-97 br br br br */ + .long none,none,none,none /* 98-9B br br br br */ + .long none,unctrl,none,none /* 9C-9F br unctrl br br */ + .long none,none,none,none /* A0-A3 br br br br */ + .long none,none,none,none /* A4-A7 br br br br */ + .long none,none,unlshift,none /* A8-AB br br unlshift br */ + .long none,none,none,none /* AC-AF br br br br */ + .long none,none,none,none /* B0-B3 br br br br */ + .long none,none,unrshift,none /* B4-B7 br br unrshift br */ + .long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */ + .long none,none,none,none /* BC-BF br br br br */ + .long none,none,none,none /* C0-C3 br br br br */ + .long none,none,none,none /* C4-C7 br br br br */ + .long none,none,none,none /* C8-CB br br br br */ + .long none,none,none,none /* CC-CF br br br br */ + .long none,none,none,none /* D0-D3 br br br br */ + .long none,none,none,none /* D4-D7 br br br br */ + .long none,none,none,none /* D8-DB br ? ? ? */ + .long none,none,none,none /* DC-DF ? ? ? ? */ + .long none,none,none,none /* E0-E3 e0 e1 ? ? */ + .long none,none,none,none /* E4-E7 ? ? ? ? */ + .long none,none,none,none /* E8-EB ? ? ? ? */ + .long none,none,none,none /* EC-EF ? ? ? ? */ + .long none,none,none,none /* F0-F3 ? ? ? ? */ + .long none,none,none,none /* F4-F7 ? ? ? ? */ + .long none,none,none,none /* F8-FB ? ? ? ? */ + .long none,none,none,none /* FC-FF ? ? ? ? */ + +/* + * kb_wait waits for the keyboard controller buffer to empty. + * there is no timeout - if the buffer doesn't empty, we hang. + */ +kb_wait: + pushl %eax +1: inb $0x64,%al + testb $0x02,%al + jne 1b + popl %eax + ret +/* + * This routine reboots the machine by asking the keyboard + * controller to pulse the reset-line low. + */ +reboot: + call kb_wait + movw $0x1234,0x472 /* don't do memory check */ + movb $0xfc,%al /* pulse reset and A20 low */ + outb %al,$0x64 +die: jmp die diff --git a/linux-0.12/kernel/chr_drv/pty.c b/linux-0.12/kernel/chr_drv/pty.c new file mode 100644 index 0000000..43407dc --- /dev/null +++ b/linux-0.12/kernel/chr_drv/pty.c @@ -0,0 +1,63 @@ +/* + * linux/kernel/chr_drv/pty.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * pty.c + * + * This module implements the pty functions + * void mpty_write(struct tty_struct * queue); + * void spty_write(struct tty_struct * queue); + */ + +#include +#include +#include +#include + +static inline void pty_copy(struct tty_struct * from, struct tty_struct * to) +{ + char c; + + while (!from->stopped && !EMPTY(from->write_q)) { + if (FULL(to->read_q)) { + if (FULL(to->secondary)) + break; + copy_to_cooked(to); + continue; + } + GETCH(from->write_q,c); + PUTCH(c,to->read_q); + if (current->signal & ~current->blocked) + break; + } + copy_to_cooked(to); + wake_up(&from->write_q->proc_list); +} + +/* + * This routine gets called when tty_write has put something into + * the write_queue. It copies the input to the output-queue of it's + * slave. + */ +void mpty_write(struct tty_struct * tty) +{ + int nr = tty - tty_table; + + if ((nr >> 6) != 2) + printk("bad mpty\n\r"); + else + pty_copy(tty,tty+64); +} + +void spty_write(struct tty_struct * tty) +{ + int nr = tty - tty_table; + + if ((nr >> 6) != 3) + printk("bad spty\n\r"); + else + pty_copy(tty,tty-64); +} diff --git a/linux-0.12/kernel/chr_drv/rs_io.s b/linux-0.12/kernel/chr_drv/rs_io.s new file mode 100644 index 0000000..3659432 --- /dev/null +++ b/linux-0.12/kernel/chr_drv/rs_io.s @@ -0,0 +1,148 @@ +/* + * linux/kernel/rs_io.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * rs_io.s + * + * This module implements the rs232 io interrupts. + */ + +.text +.globl rs1_interrupt, rs2_interrupt + +size = 1024 /* must be power of two ! + and must match the value + in tty_io.c!!! */ + +/* these are the offsets into the read/write buffer structures */ +rs_addr = 0 +head = 4 +tail = 8 +proc_list = 12 +buf = 16 + +startup = 256 /* chars left in write queue when we restart it */ + +/* + * These are the actual interrupt routines. They look where + * the interrupt is coming from, and take appropriate action. + */ +.align 2 +rs1_interrupt: + pushl $table_list+8 + jmp rs_int +.align 2 +rs2_interrupt: + pushl $table_list+16 +rs_int: + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + push %es + push %ds /* as this is an interrupt, we cannot */ + pushl $0x10 /* know that bs is ok. Load it */ + pop %ds + pushl $0x10 + pop %es + movl 24(%esp),%edx + movl (%edx),%edx + movl rs_addr(%edx),%edx + addl $2,%edx /* interrupt ident. reg */ +rep_int: + xorl %eax,%eax + inb %dx,%al + testb $1,%al + jne end + cmpb $6,%al /* this shouldn't happen, but ... */ + ja end + movl 24(%esp),%ecx + pushl %edx + subl $2,%edx + call *jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */ + popl %edx + jmp rep_int +end: movb $0x20,%al + outb %al,$0x20 /* EOI */ + pop %ds + pop %es + popl %eax + popl %ebx + popl %ecx + popl %edx + addl $4,%esp # jump over _table_list entry + iret + +jmp_table: + .long modem_status,write_char,read_char,line_status + +.align 2 +modem_status: + addl $6,%edx /* clear intr by reading modem status reg */ + inb %dx,%al + ret + +.align 2 +line_status: + addl $5,%edx /* clear intr by reading line status reg. */ + inb %dx,%al + ret + +.align 2 +read_char: + inb %dx,%al + movl %ecx,%edx + subl $table_list,%edx + shrl $3,%edx + movl (%ecx),%ecx # read-queue + movl head(%ecx),%ebx + movb %al,buf(%ecx,%ebx) + incl %ebx + andl $size-1,%ebx + cmpl tail(%ecx),%ebx + je 1f + movl %ebx,head(%ecx) +1: addl $63,%edx + pushl %edx + call do_tty_interrupt + addl $4,%esp + ret + +.align 2 +write_char: + movl 4(%ecx),%ecx # write-queue + movl head(%ecx),%ebx + subl tail(%ecx),%ebx + andl $size-1,%ebx # nr chars in queue + je write_buffer_empty + cmpl $startup,%ebx + ja 1f + movl proc_list(%ecx),%ebx # wake up sleeping process + testl %ebx,%ebx # is there any? + je 1f + movl $0,(%ebx) +1: movl tail(%ecx),%ebx + movb buf(%ecx,%ebx),%al + outb %al,%dx + incl %ebx + andl $size-1,%ebx + movl %ebx,tail(%ecx) + cmpl head(%ecx),%ebx + je write_buffer_empty + ret +.align 2 +write_buffer_empty: + movl proc_list(%ecx),%ebx # wake up sleeping process + testl %ebx,%ebx # is there any? + je 1f + movl $0,(%ebx) +1: incl %edx + inb %dx,%al + jmp 1f +1: jmp 1f +1: andb $0xd,%al /* disable transmit interrupt */ + outb %al,%dx + ret diff --git a/linux-0.12/kernel/chr_drv/serial.c b/linux-0.12/kernel/chr_drv/serial.c new file mode 100644 index 0000000..e60f10d --- /dev/null +++ b/linux-0.12/kernel/chr_drv/serial.c @@ -0,0 +1,60 @@ +/* + * linux/kernel/serial.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * serial.c + * + * This module implements the rs232 io functions + * void rs_write(struct tty_struct * queue); + * void rs_init(void); + * and all interrupts pertaining to serial IO. + */ + +#include +#include +#include +#include + +#define WAKEUP_CHARS (TTY_BUF_SIZE/4) + +extern void rs1_interrupt(void); +extern void rs2_interrupt(void); + +static void init(int port) +{ + outb_p(0x80,port+3); /* set DLAB of line control reg */ + outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */ + outb_p(0x00,port+1); /* MS of divisor */ + outb_p(0x03,port+3); /* reset DLAB */ + outb_p(0x0b,port+4); /* set DTR,RTS, OUT_2 */ + outb_p(0x0d,port+1); /* enable all intrs but writes */ + (void)inb(port); /* read data port to reset things (?) */ +} + +void rs_init(void) +{ + set_intr_gate(0x24,rs1_interrupt); + set_intr_gate(0x23,rs2_interrupt); + init(tty_table[64].read_q->data); + init(tty_table[65].read_q->data); + outb(inb_p(0x21)&0xE7,0x21); +} + +/* + * This routine gets called when tty_write has put something into + * the write_queue. It must check wheter the queue is empty, and + * set the interrupt register accordingly + * + * void _rs_write(struct tty_struct * tty); + */ +void rs_write(struct tty_struct * tty) +{ + cli(); + if (!EMPTY(tty->write_q)) { + outb(inb_p(tty->write_q->data+1)|0x02,tty->write_q->data+1); + } + sti(); +} diff --git a/linux-0.12/kernel/chr_drv/tty_io.c b/linux-0.12/kernel/chr_drv/tty_io.c new file mode 100755 index 0000000..ef6b8dc --- /dev/null +++ b/linux-0.12/kernel/chr_drv/tty_io.c @@ -0,0 +1,504 @@ +/* + * linux/kernel/tty_io.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles + * or rs-channels. It also implements echoing, cooked mode etc. + * + * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. + */ + +/* + * 'tty_io.c'给tty终端一种非相关的感觉,不管它们是控制台还是串行终端.该程序同样实现了回显,规 + * 范(熟)模式等. + * Kill-line,谢谢 John T Kohl。他同时还纠正了当 VMIN = VTIME = 0 时的问题。 + */ + + +#include +#include +#include +#include + +#define ALRMMASK (1<<(SIGALRM-1)) + +#include +#include +#include +#include + +int kill_pg(int pgrp, int sig, int priv); +/* 判断一个进程组是否是孤儿进程 */ +int is_orphaned_pgrp(int pgrp); + +#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f) +#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f) +#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f) + +#define L_CANON(tty) _L_FLAG((tty),ICANON) +#define L_ISIG(tty) _L_FLAG((tty),ISIG) +#define L_ECHO(tty) _L_FLAG((tty),ECHO) +#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) +#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) +#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) +#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) +#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP) + +#define I_UCLC(tty) _I_FLAG((tty),IUCLC) +#define I_NLCR(tty) _I_FLAG((tty),INLCR) +#define I_CRNL(tty) _I_FLAG((tty),ICRNL) +#define I_NOCR(tty) _I_FLAG((tty),IGNCR) +#define I_IXON(tty) _I_FLAG((tty),IXON) + +#define O_POST(tty) _O_FLAG((tty),OPOST) +#define O_NLCR(tty) _O_FLAG((tty),ONLCR) +#define O_CRNL(tty) _O_FLAG((tty),OCRNL) +#define O_NLRET(tty) _O_FLAG((tty),ONLRET) +#define O_LCUC(tty) _O_FLAG((tty),OLCUC) + +#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD) +#define C_HUP(tty) (C_SPEED((tty)) == B0) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define QUEUES (3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS)) +static struct tty_queue tty_queues[QUEUES]; +struct tty_struct tty_table[256]; + +#define con_queues tty_queues +#define rs_queues ((3*MAX_CONSOLES) + tty_queues) +#define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues) +#define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues) + +#define con_table tty_table +#define rs_table (64+tty_table) +#define mpty_table (128+tty_table) +#define spty_table (192+tty_table) + +int fg_console = 0; + +/* + * these are the tables used by the machine code handlers. + * you can implement virtual consoles. + */ +struct tty_queue * table_list[]={ + con_queues + 0, con_queues + 1, + rs_queues + 0, rs_queues + 1, + rs_queues + 3, rs_queues + 4 + }; + +void change_console(unsigned int new_console) +{ + if (new_console == fg_console || new_console >= NR_CONSOLES) + return; + fg_console = new_console; + table_list[0] = con_queues + 0 + fg_console*3; + table_list[1] = con_queues + 1 + fg_console*3; + update_screen(); +} + +static void sleep_if_empty(struct tty_queue * queue) +{ + cli(); + while (!(current->signal & ~current->blocked) && EMPTY(queue)) + interruptible_sleep_on(&queue->proc_list); + sti(); +} + +static void sleep_if_full(struct tty_queue * queue) +{ + if (!FULL(queue)) + return; + cli(); + while (!(current->signal & ~current->blocked) && LEFT(queue)<128) + interruptible_sleep_on(&queue->proc_list); + sti(); +} + +void wait_for_keypress(void) +{ + sleep_if_empty(tty_table[fg_console].secondary); +} + +void copy_to_cooked(struct tty_struct * tty) +{ + signed char c; + + if (!(tty->read_q || tty->write_q || tty->secondary)) { + printk("copy_to_cooked: missing queues\n\r"); + return; + } + while (1) { + if (EMPTY(tty->read_q)) + break; + if (FULL(tty->secondary)) + break; + GETCH(tty->read_q,c); + if (c==13) { + if (I_CRNL(tty)) + c=10; + else if (I_NOCR(tty)) + continue; + } else if (c==10 && I_NLCR(tty)) + c=13; + if (I_UCLC(tty)) + c = tolower(c); + if (L_CANON(tty)) { + if ((KILL_CHAR(tty) != _POSIX_VDISABLE) && + (c==KILL_CHAR(tty))) { + /* deal with killing the input line */ + while(!(EMPTY(tty->secondary) || + (c=LAST(tty->secondary))==10 || + ((EOF_CHAR(tty) != _POSIX_VDISABLE) && + (c==EOF_CHAR(tty))))) { + if (L_ECHO(tty)) { + if (c<32) + PUTCH(127,tty->write_q); + PUTCH(127,tty->write_q); + tty->write(tty); + } + DEC(tty->secondary->head); + } + continue; + } + if ((ERASE_CHAR(tty) != _POSIX_VDISABLE) && + (c==ERASE_CHAR(tty))) { + if (EMPTY(tty->secondary) || + (c=LAST(tty->secondary))==10 || + ((EOF_CHAR(tty) != _POSIX_VDISABLE) && + (c==EOF_CHAR(tty)))) + continue; + if (L_ECHO(tty)) { + if (c<32) + PUTCH(127,tty->write_q); + PUTCH(127,tty->write_q); + tty->write(tty); + } + DEC(tty->secondary->head); + continue; + } + } + if (I_IXON(tty)) { + if ((STOP_CHAR(tty) != _POSIX_VDISABLE) && + (c==STOP_CHAR(tty))) { + tty->stopped=1; + tty->write(tty); + continue; + } + if ((START_CHAR(tty) != _POSIX_VDISABLE) && + (c==START_CHAR(tty))) { + tty->stopped=0; + tty->write(tty); + continue; + } + } + if (L_ISIG(tty)) { + if ((INTR_CHAR(tty) != _POSIX_VDISABLE) && + (c==INTR_CHAR(tty))) { + kill_pg(tty->pgrp, SIGINT, 1); + continue; + } + if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) && + (c==QUIT_CHAR(tty))) { + kill_pg(tty->pgrp, SIGQUIT, 1); + continue; + } + if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) && + (c==SUSPEND_CHAR(tty))) { + if (!is_orphaned_pgrp(tty->pgrp)) + kill_pg(tty->pgrp, SIGTSTP, 1); + continue; + } + } + if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE && + c==EOF_CHAR(tty))) + tty->secondary->data++; + if (L_ECHO(tty)) { + if (c==10) { + PUTCH(10,tty->write_q); + PUTCH(13,tty->write_q); + } else if (c<32) { + if (L_ECHOCTL(tty)) { + PUTCH('^',tty->write_q); + PUTCH(c+64,tty->write_q); + } + } else + PUTCH(c,tty->write_q); + tty->write(tty); + } + PUTCH(c,tty->secondary); + } + wake_up(&tty->secondary->proc_list); +} + +/* + * Called when we need to send a SIGTTIN or SIGTTOU to our process + * group + * + * We only request that a system call be restarted if there was if the + * default signal handler is being used. The reason for this is that if + * a job is catching SIGTTIN or SIGTTOU, the signal handler may not want + * the system call to be restarted blindly. If there is no way to reset the + * terminal pgrp back to the current pgrp (perhaps because the controlling + * tty has been released on logout), we don't want to be in an infinite loop + * while restarting the system call, and have it always generate a SIGTTIN + * or SIGTTOU. The default signal handler will cause the process to stop + * thus avoiding the infinite loop problem. Presumably the job-control + * cognizant parent will fix things up before continuging its child process. + */ +int tty_signal(int sig, struct tty_struct *tty) +{ + if (is_orphaned_pgrp(current->pgrp)) + return -EIO; /* don't stop an orphaned pgrp */ + (void) kill_pg(current->pgrp,sig,1); + if ((current->blocked & (1<<(sig-1))) || + ((int) current->sigaction[sig-1].sa_handler == 1)) + return -EIO; /* Our signal will be ignored */ + else if (current->sigaction[sig-1].sa_handler) + return -EINTR; /* We _will_ be interrupted :-) */ + else + return -ERESTARTSYS; /* We _will_ be interrupted :-) */ + /* (but restart after we continue) */ +} + +int tty_read(unsigned channel, char * buf, int nr) +{ + struct tty_struct * tty; + struct tty_struct * other_tty = NULL; + char c, * b=buf; + int minimum,time; + + if (channel > 255) + return -EIO; + tty = TTY_TABLE(channel); + if (!(tty->write_q || tty->read_q || tty->secondary)) + return -EIO; + if ((current->tty == channel) && (tty->pgrp != current->pgrp)) + return(tty_signal(SIGTTIN, tty)); + if (channel & 0x80) + other_tty = tty_table + (channel ^ 0x40); + time = 10L*tty->termios.c_cc[VTIME]; + minimum = tty->termios.c_cc[VMIN]; + if (L_CANON(tty)) { + minimum = nr; + current->timeout = 0xffffffff; + time = 0; + } else if (minimum) + current->timeout = 0xffffffff; + else { + minimum = nr; + if (time) + current->timeout = time + jiffies; + time = 0; + } + if (minimum>nr) + minimum = nr; + while (nr>0) { + if (other_tty) + other_tty->write(other_tty); + cli(); + if (EMPTY(tty->secondary) || (L_CANON(tty) && + !FULL(tty->read_q) && !tty->secondary->data)) { + if (!current->timeout || + (current->signal & ~current->blocked)) { + sti(); + break; + } + if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty)) + break; + interruptible_sleep_on(&tty->secondary->proc_list); + sti(); + continue; + } + sti(); + do { + GETCH(tty->secondary,c); + if ((EOF_CHAR(tty) != _POSIX_VDISABLE && + c==EOF_CHAR(tty)) || c==10) + tty->secondary->data--; + if ((EOF_CHAR(tty) != _POSIX_VDISABLE && + c==EOF_CHAR(tty)) && L_CANON(tty)) + break; + else { + put_fs_byte(c,b++); + if (!--nr) + break; + } + if (c==10 && L_CANON(tty)) + break; + } while (nr>0 && !EMPTY(tty->secondary)); + wake_up(&tty->read_q->proc_list); + if (time) + current->timeout = time+jiffies; + if (L_CANON(tty) || b-buf >= minimum) + break; + } + current->timeout = 0; + if ((current->signal & ~current->blocked) && !(b-buf)) + return -ERESTARTSYS; + return (b-buf); +} + +int tty_write(unsigned channel, char * buf, int nr) +{ + static int cr_flag = 0; + struct tty_struct * tty; + char c, *b = buf; + + if (channel > 255) + return -EIO; + tty = TTY_TABLE(channel); + if (!(tty->write_q || tty->read_q || tty->secondary)) + return -EIO; + if (L_TOSTOP(tty) && + (current->tty == channel) && (tty->pgrp != current->pgrp)) + return(tty_signal(SIGTTOU, tty)); + while (nr>0) { + sleep_if_full(tty->write_q); + if (current->signal & ~current->blocked) + break; + while (nr>0 && !FULL(tty->write_q)) { + c=get_fs_byte(b); + if (O_POST(tty)) { + if (c=='\r' && O_CRNL(tty)) + c='\n'; + else if (c=='\n' && O_NLRET(tty)) + c='\r'; + if (c=='\n' && !cr_flag && O_NLCR(tty)) { + cr_flag = 1; + PUTCH(13,tty->write_q); + continue; + } + if (O_LCUC(tty)) + c=toupper(c); + } + b++; nr--; + cr_flag = 0; + PUTCH(c,tty->write_q); + } + tty->write(tty); + if (nr>0) + schedule(); + } + return (b-buf); +} + +/* + * Jeh, sometimes I really like the 386. + * This routine is called from an interrupt, + * and there should be absolutely no problem + * with sleeping even in an interrupt (I hope). + * Of course, if somebody proves me wrong, I'll + * hate intel for all time :-). We'll have to + * be careful and see to reinstating the interrupt + * chips before calling this, though. + * + * I don't think we sleep here under normal circumstances + * anyway, which is good, as the task sleeping might be + * totally innocent. + */ +void do_tty_interrupt(int tty) +{ + copy_to_cooked(TTY_TABLE(tty)); +} + +void chr_dev_init(void) +{ +} + +/** + * tty终端初始化函数 + * 初始化所有终端缓冲队列,初始化串口终端和控制台终端 + */ +void tty_init(void) +{ + int i; + + /* 初始化所有终端的缓冲队列结构,设置初值 */ + for (i = 0; i < QUEUES; i++) { + tty_queues[i] = (struct tty_queue) {0, 0, 0, 0, ""}; + } + /* 对于串行终端的读/写缓冲队列,将它们的data字段设置为串行端口基地址值.串口1是 + 0x3f8,串口2是0x2f8 */ + rs_queues[0] = (struct tty_queue) {0x3f8, 0, 0, 0, ""}; + rs_queues[1] = (struct tty_queue) {0x3f8, 0, 0, 0, ""}; + rs_queues[3] = (struct tty_queue) {0x2f8, 0, 0, 0, ""}; + rs_queues[4] = (struct tty_queue) {0x2f8, 0, 0, 0, ""}; + /* 初步设置所有终端的tty结构 */ + for (i = 0; i < 256; i++) { + tty_table[i] = (struct tty_struct) { + {0, 0, 0, 0, 0, INIT_C_CC}, + 0, 0, 0, NULL, NULL, NULL, NULL + }; + } + con_init(); + for (i = 0; i < NR_CONSOLES; i++) { + con_table[i] = (struct tty_struct) { + {ICRNL, /* change incoming CR to NL */ /* CR转NL */ + OPOST|ONLCR,/* change outgoing NL to CRNL */ /* NL转CRNL */ + 0, + IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, + 0, /* console termio */ + INIT_C_CC}, + 0, /* initial pgrp */ + 0, /* initial session */ + 0, /* initial stopped */ + con_write, + con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3 + }; + } + for (i = 0; i < NR_SERIALS; i++) { + rs_table[i] = (struct tty_struct) { + {0, /* no translation */ + 0, /* no translation */ + B2400 | CS8, + 0, + 0, + INIT_C_CC}, + 0, + 0, + 0, + rs_write, + rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3 + }; + } + /* 初始化伪终端使用的tty结构 */ + for (i = 0; i < NR_PTYS; i++) { + mpty_table[i] = (struct tty_struct) { + {0, /* no translation */ + 0, /* no translation */ + B9600 | CS8, + 0, + 0, + INIT_C_CC}, + 0, + 0, + 0, + mpty_write, + mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3 + }; + spty_table[i] = (struct tty_struct) { + {0, /* no translation */ + 0, /* no translation */ + B9600 | CS8, + IXON | ISIG | ICANON, + 0, + INIT_C_CC}, + 0, + 0, + 0, + spty_write, + spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3 + }; + } + /* 初始化串行中断处理程序和串行接口1和2(serial.c),并显示系统含有的虚拟控制台 + 数NR_CONSOLES和伪终端数NR_PTYS。 */ + rs_init(); + printk("%d virtual consoles\n\r", NR_CONSOLES); + printk("%d pty's\n\r", NR_PTYS); +} diff --git a/linux-0.12/kernel/chr_drv/tty_ioctl.c b/linux-0.12/kernel/chr_drv/tty_ioctl.c new file mode 100644 index 0000000..c3ac9a1 --- /dev/null +++ b/linux-0.12/kernel/chr_drv/tty_ioctl.c @@ -0,0 +1,251 @@ +/* + * linux/kernel/chr_drv/tty_ioctl.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +extern int session_of_pgrp(int pgrp); +extern int tty_signal(int sig, struct tty_struct *tty); + +static unsigned short quotient[] = { + 0, 2304, 1536, 1047, 857, + 768, 576, 384, 192, 96, + 64, 48, 24, 12, 6, 3 +}; + +static void change_speed(struct tty_struct * tty) +{ + unsigned short port,quot; + + if (!(port = tty->read_q->data)) + return; + quot = quotient[tty->termios.c_cflag & CBAUD]; + cli(); + outb_p(0x80,port+3); /* set DLAB */ + outb_p(quot & 0xff,port); /* LS of divisor */ + outb_p(quot >> 8,port+1); /* MS of divisor */ + outb(0x03,port+3); /* reset DLAB */ + sti(); +} + +static void flush(struct tty_queue * queue) +{ + cli(); + queue->head = queue->tail; + sti(); +} + +static void wait_until_sent(struct tty_struct * tty) +{ + /* do nothing - not implemented */ +} + +static void send_break(struct tty_struct * tty) +{ + /* do nothing - not implemented */ +} + +static int get_termios(struct tty_struct * tty, struct termios * termios) +{ + int i; + + verify_area(termios, sizeof (*termios)); + for (i=0 ; i< (sizeof (*termios)) ; i++) + put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios ); + return 0; +} + +static int set_termios(struct tty_struct * tty, struct termios * termios, + int channel) +{ + int i, retsig; + + /* If we try to set the state of terminal and we're not in the + foreground, send a SIGTTOU. If the signal is blocked or + ignored, go ahead and perform the operation. POSIX 7.2) */ + if ((current->tty == channel) && (tty->pgrp != current->pgrp)) { + retsig = tty_signal(SIGTTOU, tty); + if (retsig == -ERESTARTSYS || retsig == -EINTR) + return retsig; + } + for (i=0 ; i< (sizeof (*termios)) ; i++) + ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios); + change_speed(tty); + return 0; +} + +static int get_termio(struct tty_struct * tty, struct termio * termio) +{ + int i; + struct termio tmp_termio; + + verify_area(termio, sizeof (*termio)); + tmp_termio.c_iflag = tty->termios.c_iflag; + tmp_termio.c_oflag = tty->termios.c_oflag; + tmp_termio.c_cflag = tty->termios.c_cflag; + tmp_termio.c_lflag = tty->termios.c_lflag; + tmp_termio.c_line = tty->termios.c_line; + for(i=0 ; i < NCC ; i++) + tmp_termio.c_cc[i] = tty->termios.c_cc[i]; + for (i=0 ; i< (sizeof (*termio)) ; i++) + put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio ); + return 0; +} + +/* + * This only works as the 386 is low-byt-first + */ +static int set_termio(struct tty_struct * tty, struct termio * termio, + int channel) +{ + int i, retsig; + struct termio tmp_termio; + + if ((current->tty == channel) && (tty->pgrp != current->pgrp)) { + retsig = tty_signal(SIGTTOU, tty); + if (retsig == -ERESTARTSYS || retsig == -EINTR) + return retsig; + } + for (i=0 ; i< (sizeof (*termio)) ; i++) + ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio); + *(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag; + *(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag; + *(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag; + *(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag; + tty->termios.c_line = tmp_termio.c_line; + for(i=0 ; i < NCC ; i++) + tty->termios.c_cc[i] = tmp_termio.c_cc[i]; + change_speed(tty); + return 0; +} + +int tty_ioctl(int dev, int cmd, int arg) +{ + struct tty_struct * tty; + int pgrp; + + if (MAJOR(dev) == 5) { + dev=current->tty; + if (dev<0) + panic("tty_ioctl: dev<0"); + } else + dev=MINOR(dev); + tty = tty_table + (dev ? ((dev < 64)? dev-1:dev) : fg_console); + switch (cmd) { + case TCGETS: + return get_termios(tty,(struct termios *) arg); + case TCSETSF: + flush(tty->read_q); /* fallthrough */ + case TCSETSW: + wait_until_sent(tty); /* fallthrough */ + case TCSETS: + return set_termios(tty,(struct termios *) arg, dev); + case TCGETA: + return get_termio(tty,(struct termio *) arg); + case TCSETAF: + flush(tty->read_q); /* fallthrough */ + case TCSETAW: + wait_until_sent(tty); /* fallthrough */ + case TCSETA: + return set_termio(tty,(struct termio *) arg, dev); + case TCSBRK: + if (!arg) { + wait_until_sent(tty); + send_break(tty); + } + return 0; + case TCXONC: + switch (arg) { + case TCOOFF: + tty->stopped = 1; + tty->write(tty); + return 0; + case TCOON: + tty->stopped = 0; + tty->write(tty); + return 0; + case TCIOFF: + if (STOP_CHAR(tty)) + PUTCH(STOP_CHAR(tty),tty->write_q); + return 0; + case TCION: + if (START_CHAR(tty)) + PUTCH(START_CHAR(tty),tty->write_q); + return 0; + } + return -EINVAL; /* not implemented */ + case TCFLSH: + if (arg==0) + flush(tty->read_q); + else if (arg==1) + flush(tty->write_q); + else if (arg==2) { + flush(tty->read_q); + flush(tty->write_q); + } else + return -EINVAL; + return 0; + case TIOCEXCL: + return -EINVAL; /* not implemented */ + case TIOCNXCL: + return -EINVAL; /* not implemented */ + case TIOCSCTTY: + return -EINVAL; /* set controlling term NI */ + case TIOCGPGRP: + verify_area((void *) arg,4); + put_fs_long(tty->pgrp,(unsigned long *) arg); + return 0; + case TIOCSPGRP: + if ((current->tty < 0) || + (current->tty != dev) || + (tty->session != current->session)) + return -ENOTTY; + pgrp=get_fs_long((unsigned long *) arg); + if (pgrp < 0) + return -EINVAL; + if (session_of_pgrp(pgrp) != current->session) + return -EPERM; + tty->pgrp = pgrp; + return 0; + case TIOCOUTQ: + verify_area((void *) arg,4); + put_fs_long(CHARS(tty->write_q),(unsigned long *) arg); + return 0; + case TIOCINQ: + verify_area((void *) arg,4); + put_fs_long(CHARS(tty->secondary), + (unsigned long *) arg); + return 0; + case TIOCSTI: + return -EINVAL; /* not implemented */ + case TIOCGWINSZ: + return -EINVAL; /* not implemented */ + case TIOCSWINSZ: + return -EINVAL; /* not implemented */ + case TIOCMGET: + return -EINVAL; /* not implemented */ + case TIOCMBIS: + return -EINVAL; /* not implemented */ + case TIOCMBIC: + return -EINVAL; /* not implemented */ + case TIOCMSET: + return -EINVAL; /* not implemented */ + case TIOCGSOFTCAR: + return -EINVAL; /* not implemented */ + case TIOCSSOFTCAR: + return -EINVAL; /* not implemented */ + default: + return -EINVAL; + } +} diff --git a/linux-0.12/kernel/exit.c b/linux-0.12/kernel/exit.c new file mode 100755 index 0000000..29ea5ab --- /dev/null +++ b/linux-0.12/kernel/exit.c @@ -0,0 +1,446 @@ +/* + * linux/kernel/exit.c + * + * (C) 1991 Linus Torvalds + */ + +#define DEBUG_PROC_TREE /* 定义符号"调度进程树" */ + +#include +#include +#include + +#include +#include +#include +#include + +int sys_pause(void); +int sys_close(int fd); + + +/** + * 释放指定进程占用的任务槽及其任务数据结构占用的内在页面 + * 该函数在后面的sys_kill()和sys_waitpid()函数中被调用。扫描任务指针数组表task[]以寻找指定的任 + * 务。如果找到,则首先清空该任务槽,然后释放该任务数据结构所占用的内在页面,最后执行调度函数并 + * 返回立即退出。如果在任务数组表中没有找到指定任务对应的项,则内核panic。 + * @param[in] p 任务数据结构指针 + * @retval void + */ +/*static*/ void release(struct task_struct * p) +{ + int i; + + if (!p) + return; + if (p == current) { + printk("task releasing itself\n\r"); + return; + } + for (i = 1 ; i < NR_TASKS ; i++) + if (task[i] == p) { + task[i] = NULL; + /* Update links */ + if (p->p_osptr) + p->p_osptr->p_ysptr = p->p_ysptr; + if (p->p_ysptr) + p->p_ysptr->p_osptr = p->p_osptr; + else + p->p_pptr->p_cptr = p->p_osptr; + free_page((long)p); + schedule(); + return; + } + panic("trying to release non-existent task"); +} + +#ifdef DEBUG_PROC_TREE +/* + * Check to see if a task_struct pointer is present in the task[] array + * Return 0 if found, and 1 if not found. + */ + +/** + * 检测task[]数组中是否存在任务结构指针p + * @param[in] *p 任务结构体指针 + * @return 存在返回0,不存在返回1 + */ +int bad_task_ptr(struct task_struct *p) +{ + int i; + + if (!p) + return 0; + for (i = 0 ; i < NR_TASKS ; i++) + if (task[i] == p) + return 0; + return 1; +} + +/* + * This routine scans the pid tree and make sure the rep invarient still + * holds. Used for debugging only, since it's very slow.... + * + * It looks a lot scarier than it really is.... we're doing �nothing more + * than verifying the doubly-linked list found�in p_ysptr and p_osptr, + * and checking it corresponds with the process tree defined by p_cptr and + * p_pptr; + */ +void audit_ptree() +{ + int i; + + for (i=1 ; ip_pptr)) + printk("Warning, pid %d's parent link is bad\n", + task[i]->pid); + if (bad_task_ptr(task[i]->p_cptr)) + printk("Warning, pid %d's child link is bad\n", + task[i]->pid); + if (bad_task_ptr(task[i]->p_ysptr)) + printk("Warning, pid %d's ys link is bad\n", + task[i]->pid); + if (bad_task_ptr(task[i]->p_osptr)) + printk("Warning, pid %d's os link is bad\n", + task[i]->pid); + if (task[i]->p_pptr == task[i]) + printk("Warning, pid %d parent link points to self\n"); + if (task[i]->p_cptr == task[i]) + printk("Warning, pid %d child link points to self\n"); + if (task[i]->p_ysptr == task[i]) + printk("Warning, pid %d ys link points to self\n"); + if (task[i]->p_osptr == task[i]) + printk("Warning, pid %d os link points to self\n"); + if (task[i]->p_osptr) { + if (task[i]->p_pptr != task[i]->p_osptr->p_pptr) + printk( + "Warning, pid %d older sibling %d parent is %d\n", + task[i]->pid, task[i]->p_osptr->pid, + task[i]->p_osptr->p_pptr->pid); + if (task[i]->p_osptr->p_ysptr != task[i]) + printk( + "Warning, pid %d older sibling %d has mismatched ys link\n", + task[i]->pid, task[i]->p_osptr->pid); + } + if (task[i]->p_ysptr) { + if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr) + printk( + "Warning, pid %d younger sibling %d parent is %d\n", + task[i]->pid, task[i]->p_osptr->pid, + task[i]->p_osptr->p_pptr->pid); + if (task[i]->p_ysptr->p_osptr != task[i]) + printk( + "Warning, pid %d younger sibling %d has mismatched os link\n", + task[i]->pid, task[i]->p_ysptr->pid); + } + if (task[i]->p_cptr) { + if (task[i]->p_cptr->p_pptr != task[i]) + printk( + "Warning, pid %d youngest child %d has mismatched parent link\n", + task[i]->pid, task[i]->p_cptr->pid); + if (task[i]->p_cptr->p_ysptr) + printk( + "Warning, pid %d youngest child %d has non-NULL ys link\n", + task[i]->pid, task[i]->p_cptr->pid); + } + } +} +#endif /* DEBUG_PROC_TREE */ + +static inline int send_sig(long sig,struct task_struct * p,int priv) +{ + if (!p) + return -EINVAL; + if (!priv && (current->euid!=p->euid) && !suser()) + return -EPERM; + if ((sig == SIGKILL) || (sig == SIGCONT)) { + if (p->state == TASK_STOPPED) + p->state = TASK_RUNNING; + p->exit_code = 0; + p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) | + (1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) ); + } + /* If the signal will be ignored, don't even post it */ + if ((int) p->sigaction[sig-1].sa_handler == 1) + return 0; + /* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */ + if ((sig >= SIGSTOP) && (sig <= SIGTTOU)) + p->signal &= ~(1<<(SIGCONT-1)); + /* Actually deliver the signal */ + p->signal |= (1<<(sig-1)); + return 0; +} + +int session_of_pgrp(int pgrp) +{ + struct task_struct **p; + + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if ((*p)->pgrp == pgrp) + return((*p)->session); + return -1; +} + +int kill_pg(int pgrp, int sig, int priv) +{ + struct task_struct **p; + int err,retval = -ESRCH; + int found = 0; + + if (sig<1 || sig>32 || pgrp<=0) + return -EINVAL; + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if ((*p)->pgrp == pgrp) { + if (sig && (err = send_sig(sig,*p,priv))) + retval = err; + else + found++; + } + return(found ? 0 : retval); +} + +int kill_proc(int pid, int sig, int priv) +{ + struct task_struct **p; + + if (sig<1 || sig>32) + return -EINVAL; + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if ((*p)->pid == pid) + return(sig ? send_sig(sig,*p,priv) : 0); + return(-ESRCH); +} + +/* + * POSIX specifies that kill(-1,sig) is unspecified, but what we have + * is probably wrong. Should make it like BSD or SYSV. + */ +int sys_kill(int pid,int sig) +{ + struct task_struct **p = NR_TASKS + task; + int err, retval = 0; + + if (!pid) + return(kill_pg(current->pid,sig,0)); + if (pid == -1) { + while (--p > &FIRST_TASK) + if ((err = send_sig(sig,*p,0))) + retval = err; + return(retval); + } + if (pid < 0) + return(kill_pg(-pid,sig,0)); + /* Normal kill */ + return(kill_proc(pid,sig,0)); +} + +/* + * Determine if a process group is "orphaned", according to the POSIX + * definition in 2.2.2.52. Orphaned process groups are not to be affected + * by terminal-generated stop signals. Newly orphaned process groups are + * to receive a SIGHUP and a SIGCONT. + * + * "I ask you, have you ever known what it is to be an orphan?" + */ +int is_orphaned_pgrp(int pgrp) +{ + struct task_struct **p; + + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (!(*p) || + ((*p)->pgrp != pgrp) || + ((*p)->state == TASK_ZOMBIE) || + ((*p)->p_pptr->pid == 1)) + continue; + if (((*p)->p_pptr->pgrp != pgrp) && + ((*p)->p_pptr->session == (*p)->session)) + return 0; + } + return(1); /* (sighing) "Often!" */ +} + +static int has_stopped_jobs(int pgrp) +{ + struct task_struct ** p; + + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if ((*p)->pgrp != pgrp) + continue; + if ((*p)->state == TASK_STOPPED) + return(1); + } + return(0); +} + +volatile void do_exit(long code) +{ + struct task_struct *p; + int i; + + free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); + for (i=0 ; ifilp[i]) + sys_close(i); + iput(current->pwd); + current->pwd = NULL; + iput(current->root); + current->root = NULL; + iput(current->executable); + current->executable = NULL; + iput(current->library); + current->library = NULL; + current->state = TASK_ZOMBIE; + current->exit_code = code; + /* + * Check to see if any process groups have become orphaned + * as a result of our exiting, and if they have any stopped + * jobs, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2) + * + * Case i: Our father is in a different pgrp than we are + * and we were the only connection outside, so our pgrp + * is about to become orphaned. + */ + if ((current->p_pptr->pgrp != current->pgrp) && + (current->p_pptr->session == current->session) && + is_orphaned_pgrp(current->pgrp) && + has_stopped_jobs(current->pgrp)) { + kill_pg(current->pgrp,SIGHUP,1); + kill_pg(current->pgrp,SIGCONT,1); + } + /* Let father know we died */ + current->p_pptr->signal |= (1<<(SIGCHLD-1)); + + /* + * This loop does two things: + * + * A. Make init inherit all the child processes + * B. Check to see if any process groups have become orphaned + * as a result of our exiting, and if they have any stopped + * jons, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2) + */ + if ((p = current->p_cptr)) { + while (1) { + p->p_pptr = task[1]; + if (p->state == TASK_ZOMBIE) + task[1]->signal |= (1<<(SIGCHLD-1)); + /* + * process group orphan check + * Case ii: Our child is in a different pgrp + * than we are, and it was the only connection + * outside, so the child pgrp is now orphaned. + */ + if ((p->pgrp != current->pgrp) && + (p->session == current->session) && + is_orphaned_pgrp(p->pgrp) && + has_stopped_jobs(p->pgrp)) { + kill_pg(p->pgrp,SIGHUP,1); + kill_pg(p->pgrp,SIGCONT,1); + } + if (p->p_osptr) { + p = p->p_osptr; + continue; + } + /* + * This is it; link everything into init's children + * and leave + */ + p->p_osptr = task[1]->p_cptr; + task[1]->p_cptr->p_ysptr = p; + task[1]->p_cptr = current->p_cptr; + current->p_cptr = 0; + break; + } + } + if (current->leader) { + struct task_struct **p; + struct tty_struct *tty; + + if (current->tty >= 0) { + tty = TTY_TABLE(current->tty); + if (tty->pgrp>0) + kill_pg(tty->pgrp, SIGHUP, 1); + tty->pgrp = 0; + tty->session = 0; + } + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if ((*p)->session == current->session) + (*p)->tty = -1; + } + if (last_task_used_math == current) + last_task_used_math = NULL; +#ifdef DEBUG_PROC_TREE + audit_ptree(); +#endif + schedule(); +} + +/* int -> void */ +void sys_exit(int error_code) +{ + do_exit((error_code&0xff)<<8); +} + +int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) +{ + int flag; + struct task_struct *p; + unsigned long oldblocked; + + verify_area(stat_addr,4); +repeat: + flag=0; + for (p = current->p_cptr ; p ; p = p->p_osptr) { + if (pid>0) { + if (p->pid != pid) + continue; + } else if (!pid) { + if (p->pgrp != current->pgrp) + continue; + } else if (pid != -1) { + if (p->pgrp != -pid) + continue; + } + switch (p->state) { + case TASK_STOPPED: + if (!(options & WUNTRACED) || + !p->exit_code) + continue; + put_fs_long((p->exit_code << 8) | 0x7f, + stat_addr); + p->exit_code = 0; + return p->pid; + case TASK_ZOMBIE: + current->cutime += p->utime; + current->cstime += p->stime; + flag = p->pid; + put_fs_long(p->exit_code, stat_addr); + release(p); +#ifdef DEBUG_PROC_TREE + audit_ptree(); +#endif + return flag; + default: + flag=1; + continue; + } + } + if (flag) { + if (options & WNOHANG) + return 0; + current->state=TASK_INTERRUPTIBLE; + oldblocked = current->blocked; + current->blocked &= ~(1<<(SIGCHLD-1)); + schedule(); + current->blocked = oldblocked; + if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1)))) + return -ERESTARTSYS; + else + goto repeat; + } + return -ECHILD; +} + + diff --git a/linux-0.12/kernel/fork.c b/linux-0.12/kernel/fork.c new file mode 100644 index 0000000..ead8101 --- /dev/null +++ b/linux-0.12/kernel/fork.c @@ -0,0 +1,233 @@ +/* + * linux/kernel/fork.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 'fork.c' contains the help-routines for the 'fork' system call + * (see also system_call.s), and some misc functions ('verify_area'). + * Fork is rather simple, once you get the hang of it, but the memory + * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' + */ +/* + * 'fork.c'中含有系统调用'fork'的辅助子程序,以及一些其他函数('verify_area')。一旦你了解了 + * fork,就会发现它非常简单的,但内存管理却有些难度。 + */ + +#include + +#include +#include +#include +#include + +/* 写页面验证 */ +extern void write_verify(unsigned long address); + +/* 最新进程号,其值会由get_empty_process()生成,会不断增加,无上限;系统同时容纳的最多任务 + 数有上限(NR_TASKS = 64) */ +static long last_pid = 0; + +/** + * 进程空间区域的写前验证 + * 对于80386 CPU,在执行内核代码时用户空间中的R/W标志起不了作用,写时复制机制失效了。所以 + * 我们得手动做这个写前验证。 + * @param[in] addr 需要写验证的逻辑地址起始位置 + * @param[in] size 需要写验证的长度(单位为字节) + * @retval void + */ +void verify_area(void * addr, int size) +{ + unsigned long start; + + start = (unsigned long) addr; + size += start & 0xfff; + start &= 0xfffff000; + start += get_base(current->ldt[2]); + while (size > 0) { + size -= 4096; + write_verify(start); + start += 4096; + } +} + +/** + * 复制内存页表 + * 该函数为新任务在线性地址空间中设置代码段和数据段基址,限长,并复制页表。由于Linux系统采用写时复制 + * (copy on write)技术,因此这里仅为新进程设置自己的页目录表项和页表项,而没有实际为新进程分配物理内 + * 存页面。此时新进程与其父进程共享所有内存页面。 + * @param[in] nr 新任务号 + * @param[in] p 新任务的数据结构指针 + * @retval 成功返回0,失败返回出错号 +*/ +static int copy_mem(int nr, struct task_struct * p) +{ + unsigned long old_data_base, new_data_base, data_limit; + unsigned long old_code_base, new_code_base, code_limit; + + code_limit = get_limit(0x0f); + data_limit = get_limit(0x17); + old_code_base = get_base(current->ldt[1]); + old_data_base = get_base(current->ldt[2]); + if (old_data_base != old_code_base) { + panic("We don't support separate I&D"); + } + if (data_limit < code_limit) { + panic("Bad data_limit"); + } + new_data_base = new_code_base = nr * TASK_SIZE; + p->start_code = new_code_base; + set_base(p->ldt[1], new_code_base); + set_base(p->ldt[2], new_data_base); + if (copy_page_tables(old_data_base, new_data_base, data_limit)) { + free_page_tables(new_data_base, data_limit); + return -ENOMEM; + } + return 0; +} + +/* + * Ok, this is the main fork-routine. It copies the system process + * information (task[nr]) and sets up the necessary registers. It + * also copies the data segment in it's entirety. + */ +/* + * OK,下面是主要的fork子程序。它复制系统进程信息(task[n]),并且设置必要的寄存器。它还整个地复制数据段(也是 + * 代码段)。 + */ + +/** + * 复制进程 + * sys_call.s中sys_fork会首先调用find_empty_process会更新last_pid,然后压入一些参数,再调用copy_process。 + * @param[in] nr,ebp,edi,esi,gs find_empty_process分配的任务数组项号nr,调用copy_process之前 + * 入栈的gs,esi,edi,ebp + * @param[in] none sys_fork函数入栈的返回地址 + * @param[in] ebx,ecx,edx,orig_eax,fs,es,ds system_call时入栈的段寄存器ds,es,fs和edx,ecx,ebx + * @param[in] eip,cs,eflags,esp,ss CPU执行中断指令压入的用户栈地址ss和esp,标志eflags和返回地址cs和eip + * @return 成功返回最新的PID,失败返回错误号 + */ +int copy_process(int nr, long ebp, long edi, long esi, long gs, long none, + long ebx, long ecx, long edx, long orig_eax, + long fs, long es, long ds, + long eip, long cs, long eflags, long esp, long ss) +{ + struct task_struct *p; + int i; + struct file *f; + /* 为新任务数据结构分配内存 */ + p = (struct task_struct *) get_free_page(); + if (!p) { + return -EAGAIN; + } + task[nr] = p; + *p = *current; /* NOTE! this doesn't copy the supervisor stack */ + + /* 对复制来的进程结构内容进行一些修改。先将新进程的状态置为不可中断等待状态,以防止内核调度其执行 */ + p->state = TASK_UNINTERRUPTIBLE; + p->pid = last_pid; + p->counter = p->priority; + p->signal = 0; + p->alarm = 0; + p->leader = 0; /* process leadership doesn't inherit */ + p->utime = p->stime = 0; + p->cutime = p->cstime = 0; + p->start_time = jiffies; + + /* 修改任务状态段TSS内容 */ + p->tss.back_link = 0; + p->tss.esp0 = PAGE_SIZE + (long) p; /* (PAGE_SIZE + (long) p)让esp0正好指向该页顶端 */ + p->tss.ss0 = 0x10; + p->tss.eip = eip; + p->tss.eflags = eflags; + p->tss.eax = 0; + p->tss.ecx = ecx; + p->tss.edx = edx; + p->tss.ebx = ebx; + p->tss.esp = esp; + p->tss.ebp = ebp; + p->tss.esi = esi; + p->tss.edi = edi; + p->tss.es = es & 0xffff; + p->tss.cs = cs & 0xffff; + p->tss.ss = ss & 0xffff; + p->tss.ds = ds & 0xffff; + p->tss.fs = fs & 0xffff; + p->tss.gs = gs & 0xffff; + p->tss.ldt = _LDT(nr); + p->tss.trace_bitmap = 0x80000000; + /* 当前任务使用了协处理器,就保存其上下文 */ + if (last_task_used_math == current) { + __asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387)); + } + /* 复制父进程的内存页表,没有分配物理内存,共享父进程内存 */ + if (copy_mem(nr,p)) { + task[nr] = NULL; + free_page((long) p); + return -EAGAIN; + } + /* 修改打开的文件,当前工作目录,根目录,执行文件,被加载库文件的使用数 */ + for (i = 0; i < NR_OPEN; i++) { + if ((f = p->filp[i])) { + f->f_count ++; + } + } + if (current->pwd) { + current->pwd->i_count++; + } + if (current->root) { + current->root->i_count++; + } + if (current->executable) { + current->executable->i_count++; + } + if (current->library) { + current->library->i_count++; + } + + /* 在GDT表中设置任务状态段描述符TSS和局部表描述符LDT */ + set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY, &(p->tss)); + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, &(p->ldt)); + + /* 设置子进程的进程指针 */ + p->p_pptr = current; + p->p_cptr = 0; + p->p_ysptr = 0; + p->p_osptr = current->p_cptr; + if (p->p_osptr) { + p->p_osptr->p_ysptr = p; + } + current->p_cptr = p; + + p->state = TASK_RUNNING; /* do this last, just in case */ + + return last_pid; +} + +/** + * 取得不重复的进程号last_pid + * 由sys_fork调用为新进程取得不重复的进程号last_pid,并返回第一个空闲的任务结构数组索引号 + * @param[in] void + * @retval 成功返回在任务数组中的任务号(数组项),失败返回错误号 + */ +int find_empty_process(void) +{ + int i; + + repeat: + if ((++last_pid) < 0) { + last_pid = 1; + } + for(i = 0; i < NR_TASKS; i++) { + if (task[i] && ((task[i]->pid == last_pid) || + (task[i]->pgrp == last_pid))) { + goto repeat; + } + } + for(i = 1; i < NR_TASKS; i++) { + if (!task[i]) { + return i; + } + } + return -EAGAIN; +} diff --git a/linux-0.12/kernel/math/Makefile b/linux-0.12/kernel/math/Makefile new file mode 100644 index 0000000..fe07419 --- /dev/null +++ b/linux-0.12/kernel/math/Makefile @@ -0,0 +1,80 @@ +# +# Makefile for the FREAX-kernel character device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +include ../../Rules.make + +LDFLAGS = -x #-s +CFLAGS = -g -Wall -O -fstrength-reduce -fomit-frame-pointer \ + -finline-functions -nostdinc -I../../include +CPP += -E -I../../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = math_emulate.o error.o convert.o ea.o get_put.o \ + add.o mul.o div.o compare.o + +math.a: $(OBJS) + $(AR) rcs math.a $(OBJS) + @sync + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +add.s add.o : add.c ../../include/linux/math_emu.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h +compare.s compare.o : compare.c ../../include/linux/math_emu.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h +convert.s convert.o : convert.c ../../include/linux/math_emu.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h +div.s div.o : div.c ../../include/linux/math_emu.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h +ea.s ea.o : ea.c ../../include/stddef.h ../../include/linux/math_emu.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h \ + ../../include/asm/segment.h +error.s error.o : error.c ../../include/signal.h ../../include/sys/types.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h +get_put.s get_put.o : get_put.c ../../include/signal.h ../../include/sys/types.h \ + ../../include/linux/math_emu.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/linux/mm.h ../../include/linux/kernel.h \ + ../../include/asm/segment.h +math_emulate.s math_emulate.o : math_emulate.c ../../include/signal.h \ + ../../include/sys/types.h ../../include/linux/math_emu.h \ + ../../include/linux/sched.h ../../include/linux/head.h \ + ../../include/linux/fs.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/asm/segment.h +mul.s mul.o : mul.c ../../include/linux/math_emu.h ../../include/linux/sched.h \ + ../../include/linux/head.h ../../include/linux/fs.h \ + ../../include/sys/types.h ../../include/linux/mm.h \ + ../../include/linux/kernel.h ../../include/signal.h diff --git a/linux-0.12/kernel/math/add.c b/linux-0.12/kernel/math/add.c new file mode 100644 index 0000000..5cf84ef --- /dev/null +++ b/linux-0.12/kernel/math/add.c @@ -0,0 +1,92 @@ +/* + * linux/kernel/math/add.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * temporary real addition routine. + * + * NOTE! These aren't exact: they are only 62 bits wide, and don't do + * correct rounding. Fast hack. The reason is that we shift right the + * values by two, in order not to have overflow (1 bit), and to be able + * to move the sign into the mantissa (1 bit). Much simpler algorithms, + * and 62 bits (61 really - no rounding) accuracy is usually enough. The + * only time you should notice anything weird is when adding 64-bit + * integers together. When using doubles (52 bits accuracy), the + * 61-bit accuracy never shows at all. + */ + +#include + +#define NEGINT(a) \ +__asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \ + :"=r" (a->a),"=r" (a->b) \ + :"0" (a->a),"1" (a->b)) + +static void signify(temp_real * a) +{ + a->exponent += 2; + __asm__("shrdl $2,%1,%0 ; shrl $2,%1" + :"=r" (a->a),"=r" (a->b) + :"0" (a->a),"1" (a->b)); + if (a->exponent < 0) + NEGINT(a); + a->exponent &= 0x7fff; +} + +static void unsignify(temp_real * a) +{ + if (!(a->a || a->b)) { + a->exponent = 0; + return; + } + a->exponent &= 0x7fff; + if (a->b < 0) { + NEGINT(a); + a->exponent |= 0x8000; + } + while (a->b >= 0) { + a->exponent--; + __asm__("addl %0,%0 ; adcl %1,%1" + :"=r" (a->a),"=r" (a->b) + :"0" (a->a),"1" (a->b)); + } +} + +void fadd(const temp_real * src1, const temp_real * src2, temp_real * result) +{ + temp_real a,b; + int x1,x2,shift; + + x1 = src1->exponent & 0x7fff; + x2 = src2->exponent & 0x7fff; + if (x1 > x2) { + a = *src1; + b = *src2; + shift = x1-x2; + } else { + a = *src2; + b = *src1; + shift = x2-x1; + } + if (shift >= 64) { + *result = a; + return; + } + if (shift >= 32) { + b.a = b.b; + b.b = 0; + shift -= 32; + } + __asm__("shrdl %4,%1,%0 ; shrl %4,%1" + :"=r" (b.a),"=r" (b.b) + :"0" (b.a),"1" (b.b),"c" ((char) shift)); + signify(&a); + signify(&b); + __asm__("addl %4,%0 ; adcl %5,%1" + :"=r" (a.a),"=r" (a.b) + :"0" (a.a),"1" (a.b),"g" (b.a),"g" (b.b)); + unsignify(&a); + *result = a; +} diff --git a/linux-0.12/kernel/math/compare.c b/linux-0.12/kernel/math/compare.c new file mode 100644 index 0000000..4f1dfac --- /dev/null +++ b/linux-0.12/kernel/math/compare.c @@ -0,0 +1,60 @@ +/* + * linux/kernel/math/compare.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * temporary real comparison routines + */ + +#include + +#define clear_Cx() (I387.swd &= ~0x4500) + +static void normalize(temp_real * a) +{ + int i = a->exponent & 0x7fff; + int sign = a->exponent & 0x8000; + + if (!(a->a || a->b)) { + a->exponent = 0; + return; + } + while (i && a->b >= 0) { + i--; + __asm__("addl %0,%0 ; adcl %1,%1" + :"=r" (a->a),"=r" (a->b) + :"0" (a->a),"1" (a->b)); + } + a->exponent = i | sign; +} + +void ftst(const temp_real * a) +{ + temp_real b; + + clear_Cx(); + b = *a; + normalize(&b); + if (b.a || b.b || b.exponent) { + if (b.exponent < 0) + set_C0(); + } else + set_C3(); +} + +void fcom(const temp_real * src1, const temp_real * src2) +{ + temp_real a; + + a = *src1; + a.exponent ^= 0x8000; + fadd(&a,src2,&a); + ftst(&a); +} + +void fucom(const temp_real * src1, const temp_real * src2) +{ + fcom(src1,src2); +} diff --git a/linux-0.12/kernel/math/convert.c b/linux-0.12/kernel/math/convert.c new file mode 100644 index 0000000..5e3d1a5 --- /dev/null +++ b/linux-0.12/kernel/math/convert.c @@ -0,0 +1,185 @@ +/* + * linux/kernel/math/convert.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +/* + * NOTE!!! There is some "non-obvious" optimisations in the temp_to_long + * and temp_to_short conversion routines: don't touch them if you don't + * know what's going on. They are the adding of one in the rounding: the + * overflow bit is also used for adding one into the exponent. Thus it + * looks like the overflow would be incorrectly handled, but due to the + * way the IEEE numbers work, things are correct. + * + * There is no checking for total overflow in the conversions, though (ie + * if the temp-real number simply won't fit in a short- or long-real.) + */ + +void short_to_temp(const short_real * a, temp_real * b) +{ + if (!(*a & 0x7fffffff)) { + b->a = b->b = 0; + if (*a) + b->exponent = 0x8000; + else + b->exponent = 0; + return; + } + b->exponent = ((*a>>23) & 0xff)-127+16383; + if (*a<0) + b->exponent |= 0x8000; + b->b = (*a<<8) | 0x80000000; + b->a = 0; +} + +void long_to_temp(const long_real * a, temp_real * b) +{ + if (!a->a && !(a->b & 0x7fffffff)) { + b->a = b->b = 0; + if (a->b) + b->exponent = 0x8000; + else + b->exponent = 0; + return; + } + b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383; + if (a->b<0) + b->exponent |= 0x8000; + b->b = 0x80000000 | (a->b<<11) | (((unsigned long)a->a)>>21); + b->a = a->a<<11; +} + +void temp_to_short(const temp_real * a, short_real * b) +{ + if (!(a->exponent & 0x7fff)) { + *b = (a->exponent)?0x80000000:0; + return; + } + *b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000; + if (a->exponent < 0) + *b |= 0x80000000; + *b |= (a->b >> 8) & 0x007fffff; + switch (ROUNDING) { + case ROUND_NEAREST: + if ((a->b & 0xff) > 0x80) + ++*b; + break; + case ROUND_DOWN: + if ((a->exponent & 0x8000) && (a->b & 0xff)) + ++*b; + break; + case ROUND_UP: + if (!(a->exponent & 0x8000) && (a->b & 0xff)) + ++*b; + break; + } +} + +void temp_to_long(const temp_real * a, long_real * b) +{ + if (!(a->exponent & 0x7fff)) { + b->a = 0; + b->b = (a->exponent)?0x80000000:0; + return; + } + b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000; + if (a->exponent < 0) + b->b |= 0x80000000; + b->b |= (a->b >> 11) & 0x000fffff; + b->a = a->b << 21; + b->a |= (a->a >> 11) & 0x001fffff; + switch (ROUNDING) { + case ROUND_NEAREST: + if ((a->a & 0x7ff) > 0x400) + __asm__("addl $1,%0 ; adcl $0,%1" + :"=r" (b->a),"=r" (b->b) + :"0" (b->a),"1" (b->b)); + break; + case ROUND_DOWN: + if ((a->exponent & 0x8000) && (a->b & 0xff)) + __asm__("addl $1,%0 ; adcl $0,%1" + :"=r" (b->a),"=r" (b->b) + :"0" (b->a),"1" (b->b)); + break; + case ROUND_UP: + if (!(a->exponent & 0x8000) && (a->b & 0xff)) + __asm__("addl $1,%0 ; adcl $0,%1" + :"=r" (b->a),"=r" (b->b) + :"0" (b->a),"1" (b->b)); + break; + } +} + +void real_to_int(const temp_real * a, temp_int * b) +{ + int shift = 16383 + 63 - (a->exponent & 0x7fff); + unsigned long underflow; + + b->a = b->b = underflow = 0; + b->sign = (a->exponent < 0); + if (shift < 0) { + set_OE(); + return; + } + if (shift < 32) { + b->b = a->b; b->a = a->a; + } else if (shift < 64) { + b->a = a->b; underflow = a->a; + shift -= 32; + } else if (shift < 96) { + underflow = a->b; + shift -= 64; + } else + return; + __asm__("shrdl %2,%1,%0" + :"=r" (underflow),"=r" (b->a) + :"c" ((char) shift),"0" (underflow),"1" (b->a)); + __asm__("shrdl %2,%1,%0" + :"=r" (b->a),"=r" (b->b) + :"c" ((char) shift),"0" (b->a),"1" (b->b)); + __asm__("shrl %1,%0" + :"=r" (b->b) + :"c" ((char) shift),"0" (b->b)); + switch (ROUNDING) { + case ROUND_NEAREST: + __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1" + :"=r" (b->a),"=r" (b->b) + :"0" (b->a),"1" (b->b) + ,"r" (0x7fffffff + (b->a & 1)) + ,"m" (*&underflow)); + break; + case ROUND_UP: + if (!b->sign && underflow) + __asm__("addl $1,%0 ; adcl $0,%1" + :"=r" (b->a),"=r" (b->b) + :"0" (b->a),"1" (b->b)); + break; + case ROUND_DOWN: + if (b->sign && underflow) + __asm__("addl $1,%0 ; adcl $0,%1" + :"=r" (b->a),"=r" (b->b) + :"0" (b->a),"1" (b->b)); + break; + } +} + +void int_to_real(const temp_int * a, temp_real * b) +{ + b->a = a->a; + b->b = a->b; + if (b->a || b->b) + b->exponent = 16383 + 63 + (a->sign? 0x8000:0); + else { + b->exponent = 0; + return; + } + while (b->b >= 0) { + b->exponent--; + __asm__("addl %0,%0 ; adcl %1,%1" + :"=r" (b->a),"=r" (b->b) + :"0" (b->a),"1" (b->b)); + } +} diff --git a/linux-0.12/kernel/math/div.c b/linux-0.12/kernel/math/div.c new file mode 100644 index 0000000..e485fd9 --- /dev/null +++ b/linux-0.12/kernel/math/div.c @@ -0,0 +1,109 @@ +/* + * linux/kernel/math/div.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * temporary real division routine. + */ + +#include + +static void shift_left(int * c) +{ + __asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t" + "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t" + "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t" + "movl 12(%0),%%eax ; adcl %%eax,12(%0)" + ::"r" ((long) c):"ax"); +} + +static void shift_right(int * c) +{ + __asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)" + ::"r" ((long) c)); +} + +static int try_sub(int * a, int * b) +{ + char ok; + + __asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)\n\t" + "movl 4(%1),%%eax ; sbbl %%eax,4(%2)\n\t" + "movl 8(%1),%%eax ; sbbl %%eax,8(%2)\n\t" + "movl 12(%1),%%eax ; sbbl %%eax,12(%2)\n\t" + "setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b)); + return ok; +} + +static void div64(int * a, int * b, int * c) +{ + int tmp[4]; + int i; + unsigned int mask = 0; + + c += 4; + for (i = 0 ; i<64 ; i++) { + if (!(mask >>= 1)) { + c--; + mask = 0x80000000; + } + tmp[0] = a[0]; tmp[1] = a[1]; + tmp[2] = a[2]; tmp[3] = a[3]; + if (try_sub(b,tmp)) { + *c |= mask; + a[0] = tmp[0]; a[1] = tmp[1]; + a[2] = tmp[2]; a[3] = tmp[3]; + } + shift_right(b); + } +} + +void fdiv(const temp_real * src1, const temp_real * src2, temp_real * result) +{ + int i,sign; + int a[4],b[4],tmp[4] = {0,0,0,0}; + + sign = (src1->exponent ^ src2->exponent) & 0x8000; + if (!(src2->a || src2->b)) { + set_ZE(); + return; + } + i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383; + if (i<0) { + set_UE(); + result->exponent = sign; + result->a = result->b = 0; + return; + } + a[0] = a[1] = 0; + a[2] = src1->a; + a[3] = src1->b; + b[0] = b[1] = 0; + b[2] = src2->a; + b[3] = src2->b; + while (b[3] >= 0) { + i++; + shift_left(b); + } + div64(a,b,tmp); + if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) { + while (i && tmp[3] >= 0) { + i--; + shift_left(tmp); + } + if (tmp[3] >= 0) + set_DE(); + } else + i = 0; + if (i>0x7fff) { + set_OE(); + return; + } + if (tmp[0] || tmp[1]) + set_PE(); + result->exponent = i | sign; + result->a = tmp[2]; + result->b = tmp[3]; +} diff --git a/linux-0.12/kernel/math/ea.c b/linux-0.12/kernel/math/ea.c new file mode 100644 index 0000000..85a1131 --- /dev/null +++ b/linux-0.12/kernel/math/ea.c @@ -0,0 +1,92 @@ +/* + * linux/kernel/math/ea.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * Calculate the effective address. + */ + +#include + +#include +#include + +static int __regoffset[] = { + offsetof(struct info,___eax), + offsetof(struct info,___ecx), + offsetof(struct info,___edx), + offsetof(struct info,___ebx), + offsetof(struct info,___esp), + offsetof(struct info,___ebp), + offsetof(struct info,___esi), + offsetof(struct info,___edi) +}; + +#define REG(x) (*(long *)(__regoffset[(x)]+(char *) info)) + +static char * sib(struct info * info, int mod) +{ + unsigned char ss,index,base; + long offset = 0; + + base = get_fs_byte((char *) EIP); + EIP++; + ss = base >> 6; + index = (base >> 3) & 7; + base &= 7; + if (index == 4) + offset = 0; + else + offset = REG(index); + offset <<= ss; + if (mod || base != 5) + offset += REG(base); + if (mod == 1) { + offset += (signed char) get_fs_byte((char *) EIP); + EIP++; + } else if (mod == 2 || base == 5) { + offset += (signed) get_fs_long((unsigned long *) EIP); + EIP += 4; + } + I387.foo = offset; + I387.fos = 0x17; + return (char *) offset; +} + +char * ea(struct info * info, unsigned short code) +{ + unsigned char mod,rm; + long * tmp = &EAX; + int offset = 0; + + mod = (code >> 6) & 3; + rm = code & 7; + if (rm == 4 && mod != 3) + return sib(info,mod); + if (rm == 5 && !mod) { + offset = get_fs_long((unsigned long *) EIP); + EIP += 4; + I387.foo = offset; + I387.fos = 0x17; + return (char *) offset; + } + tmp = & REG(rm); + switch (mod) { + case 0: offset = 0; break; + case 1: + offset = (signed char) get_fs_byte((char *) EIP); + EIP++; + break; + case 2: + offset = (signed) get_fs_long((unsigned long *) EIP); + EIP += 4; + break; + case 3: + math_abort(info,1<<(SIGILL-1)); + } + I387.foo = offset; + I387.fos = 0x17; + return offset + (char *) *tmp; +} diff --git a/linux-0.12/kernel/math/error.c b/linux-0.12/kernel/math/error.c new file mode 100644 index 0000000..1de404a --- /dev/null +++ b/linux-0.12/kernel/math/error.c @@ -0,0 +1,16 @@ +/* + * linux/kernel/math/error.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +#include + +void math_error(void) +{ + __asm__("fnclex"); + if (last_task_used_math) + last_task_used_math->signal |= 1<<(SIGFPE-1); +} diff --git a/linux-0.12/kernel/math/get_put.c b/linux-0.12/kernel/math/get_put.c new file mode 100644 index 0000000..4adf1e3 --- /dev/null +++ b/linux-0.12/kernel/math/get_put.c @@ -0,0 +1,240 @@ +/* + * linux/kernel/math/get_put.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This file handles all accesses to user memory: getting and putting + * ints/reals/BCD etc. This is the only part that concerns itself with + * other than temporary real format. All other cals are strictly temp_real. + */ +#include + +#include +#include +#include + +void get_short_real(temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + short_real sr; + + addr = ea(info,code); + sr = get_fs_long((unsigned long *) addr); + short_to_temp(&sr,tmp); +} + +void get_long_real(temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + long_real lr; + + addr = ea(info,code); + lr.a = get_fs_long((unsigned long *) addr); + lr.b = get_fs_long(1 + (unsigned long *) addr); + long_to_temp(&lr,tmp); +} + +void get_temp_real(temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + + addr = ea(info,code); + tmp->a = get_fs_long((unsigned long *) addr); + tmp->b = get_fs_long(1 + (unsigned long *) addr); + tmp->exponent = get_fs_word(4 + (unsigned short *) addr); +} + +void get_short_int(temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + temp_int ti; + + addr = ea(info,code); + ti.a = (signed short) get_fs_word((unsigned short *) addr); + ti.b = 0; + if ((ti.sign = (ti.a < 0))) + ti.a = - ti.a; + int_to_real(&ti,tmp); +} + +void get_long_int(temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + temp_int ti; + + addr = ea(info,code); + ti.a = get_fs_long((unsigned long *) addr); + ti.b = 0; + if ((ti.sign = (ti.a < 0))) + ti.a = - ti.a; + int_to_real(&ti,tmp); +} + +void get_longlong_int(temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + temp_int ti; + + addr = ea(info,code); + ti.a = get_fs_long((unsigned long *) addr); + ti.b = get_fs_long(1 + (unsigned long *) addr); + if ((ti.sign = (ti.b < 0))) + __asm__("notl %0 ; notl %1\n\t" + "addl $1,%0 ; adcl $0,%1" + :"=r" (ti.a),"=r" (ti.b) + :"0" (ti.a),"1" (ti.b)); + int_to_real(&ti,tmp); +} + +#define MUL10(low,high) \ +__asm__("addl %0,%0 ; adcl %1,%1\n\t" \ +"movl %0,%%ecx ; movl %1,%%ebx\n\t" \ +"addl %0,%0 ; adcl %1,%1\n\t" \ +"addl %0,%0 ; adcl %1,%1\n\t" \ +"addl %%ecx,%0 ; adcl %%ebx,%1" \ +:"=a" (low),"=d" (high) \ +:"0" (low),"1" (high)) + +#define ADD64(val,low,high) \ +__asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \ +:"0" (low),"1" (high),"r" ((unsigned long) (val))) + +void get_BCD(temp_real * tmp, struct info * info, unsigned short code) +{ + int k; + char * addr; + temp_int i; + unsigned char c; + + addr = ea(info,code); + addr += 9; + i.sign = 0x80 & get_fs_byte(addr--); + i.a = i.b = 0; + for (k = 0; k < 9; k++) { + c = get_fs_byte(addr--); + MUL10(i.a, i.b); + ADD64((c>>4), i.a, i.b); + MUL10(i.a, i.b); + ADD64((c&0xf), i.a, i.b); + } + int_to_real(&i,tmp); +} + +void put_short_real(const temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + short_real sr; + + addr = ea(info,code); + verify_area(addr,4); + temp_to_short(tmp,&sr); + put_fs_long(sr,(unsigned long *) addr); +} + +void put_long_real(const temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + long_real lr; + + addr = ea(info,code); + verify_area(addr,8); + temp_to_long(tmp,&lr); + put_fs_long(lr.a, (unsigned long *) addr); + put_fs_long(lr.b, 1 + (unsigned long *) addr); +} + +void put_temp_real(const temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + + addr = ea(info,code); + verify_area(addr,10); + put_fs_long(tmp->a, (unsigned long *) addr); + put_fs_long(tmp->b, 1 + (unsigned long *) addr); + put_fs_word(tmp->exponent, 4 + (short *) addr); +} + +void put_short_int(const temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + temp_int ti; + + addr = ea(info,code); + real_to_int(tmp,&ti); + verify_area(addr,2); + if (ti.sign) + ti.a = -ti.a; + put_fs_word(ti.a,(short *) addr); +} + +void put_long_int(const temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + temp_int ti; + + addr = ea(info,code); + real_to_int(tmp,&ti); + verify_area(addr,4); + if (ti.sign) + ti.a = -ti.a; + put_fs_long(ti.a,(unsigned long *) addr); +} + +void put_longlong_int(const temp_real * tmp, + struct info * info, unsigned short code) +{ + char * addr; + temp_int ti; + + addr = ea(info,code); + real_to_int(tmp,&ti); + verify_area(addr,8); + if (ti.sign) + __asm__("notl %0 ; notl %1\n\t" + "addl $1,%0 ; adcl $0,%1" + :"=r" (ti.a),"=r" (ti.b) + :"0" (ti.a),"1" (ti.b)); + put_fs_long(ti.a,(unsigned long *) addr); + put_fs_long(ti.b,1 + (unsigned long *) addr); +} + +#define DIV10(low,high,rem) \ +__asm__("divl %6 ; xchgl %1,%2 ; divl %6" \ + :"=d" (rem),"=a" (low),"=b" (high) \ + :"0" (0),"1" (high),"2" (low),"c" (10)) + +void put_BCD(const temp_real * tmp,struct info * info, unsigned short code) +{ + int k,rem; + char * addr; + temp_int i; + unsigned char c; + + addr = ea(info,code); + verify_area(addr,10); + real_to_int(tmp,&i); + if (i.sign) + put_fs_byte(0x80, addr+9); + else + put_fs_byte(0, addr+9); + for (k = 0; k < 9; k++) { + DIV10(i.a,i.b,rem); + c = rem; + DIV10(i.a,i.b,rem); + c += rem<<4; + put_fs_byte(c,addr++); + } +} diff --git a/linux-0.12/kernel/math/math_emulate.c b/linux-0.12/kernel/math/math_emulate.c new file mode 100644 index 0000000..f79fd73 --- /dev/null +++ b/linux-0.12/kernel/math/math_emulate.c @@ -0,0 +1,529 @@ +/* + * linux/kernel/math/math_emulate.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * Limited emulation 27.12.91 - mostly loads/stores, which gcc wants + * even for soft-float, unless you use bruce evans' patches. The patches + * are great, but they have to be re-applied for every version, and the + * library is different for soft-float and 80387. So emulation is more + * practical, even though it's slower. + * + * 28.12.91 - loads/stores work, even BCD. I'll have to start thinking + * about add/sub/mul/div. Urgel. I should find some good source, but I'll + * just fake up something. + * + * 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really + * test every possible combination. + */ + +/* + * This file is full of ugly macros etc: one problem was that gcc simply + * didn't want to make the structures as they should be: it has to try to + * align them. Sickening code, but at least I've hidden the ugly things + * in this one file: the other files don't need to know about these things. + * + * The other files also don't care about ST(x) etc - they just get addresses + * to 80-bit temporary reals, and do with them as they please. I wanted to + * hide most of the 387-specific things here. + */ + +#include + +#define __ALIGNED_TEMP_REAL 1 +#include +#include +#include + +#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x)) +#define ST(x) (*__st((x))) +#define PST(x) ((const temp_real *) __st((x))) + +/* + * We don't want these inlined - it gets too messy in the machine-code. + */ +static void fpop(void); +static void fpush(void); +static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b); +static temp_real_unaligned * __st(int i); + +static void do_emu(struct info * info) +{ + unsigned short code; + temp_real tmp; + char * address; + + if (I387.cwd & I387.swd & 0x3f) + I387.swd |= 0x8000; + else + I387.swd &= 0x7fff; + ORIG_EIP = EIP; +/* 0x0007 means user code space */ + if (CS != 0x000F) { + printk("math_emulate: %04x:%08x\n\r",CS,EIP); + panic("Math emulation needed in kernel"); + } + code = get_fs_word((unsigned short *) EIP); + bswapw(code); + code &= 0x7ff; + I387.fip = EIP; + *(unsigned short *) &I387.fcs = CS; + *(1+(unsigned short *) &I387.fcs) = code; + EIP += 2; + switch (code) { + case 0x1d0: /* fnop */ + return; + case 0x1d1: case 0x1d2: case 0x1d3: + case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7: + math_abort(info,1<<(SIGILL-1)); + case 0x1e0: + ST(0).exponent ^= 0x8000; + return; + case 0x1e1: + ST(0).exponent &= 0x7fff; + return; + case 0x1e2: case 0x1e3: + math_abort(info,1<<(SIGILL-1)); + case 0x1e4: + ftst(PST(0)); + return; + case 0x1e5: + printk("fxam not implemented\n\r"); + math_abort(info,1<<(SIGILL-1)); + case 0x1e6: case 0x1e7: + math_abort(info,1<<(SIGILL-1)); + case 0x1e8: + fpush(); + ST(0) = CONST1; + return; + case 0x1e9: + fpush(); + ST(0) = CONSTL2T; + return; + case 0x1ea: + fpush(); + ST(0) = CONSTL2E; + return; + case 0x1eb: + fpush(); + ST(0) = CONSTPI; + return; + case 0x1ec: + fpush(); + ST(0) = CONSTLG2; + return; + case 0x1ed: + fpush(); + ST(0) = CONSTLN2; + return; + case 0x1ee: + fpush(); + ST(0) = CONSTZ; + return; + case 0x1ef: + math_abort(info,1<<(SIGILL-1)); + case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3: + case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7: + case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb: + case 0x1fc: case 0x1fd: case 0x1fe: case 0x1ff: + printk("%04x fxxx not implemented\n\r",code + 0xc800); + math_abort(info,1<<(SIGILL-1)); + case 0x2e9: + fucom(PST(1),PST(0)); + fpop(); fpop(); + return; + case 0x3d0: case 0x3d1: + return; + case 0x3e2: + I387.swd &= 0x7f00; + return; + case 0x3e3: + I387.cwd = 0x037f; + I387.swd = 0x0000; + I387.twd = 0x0000; + return; + case 0x3e4: + return; + case 0x6d9: + fcom(PST(1),PST(0)); + fpop(); fpop(); + return; + case 0x7e0: + *(short *) &EAX = I387.swd; + return; + } + switch (code >> 3) { + case 0x18: + fadd(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 0x19: + fmul(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 0x1a: + fcom(PST(code & 7),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 0x1b: + fcom(PST(code & 7),&tmp); + real_to_real(&tmp,&ST(0)); + fpop(); + return; + case 0x1c: + real_to_real(&ST(code & 7),&tmp); + tmp.exponent ^= 0x8000; + fadd(PST(0),&tmp,&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 0x1d: + ST(0).exponent ^= 0x8000; + fadd(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 0x1e: + fdiv(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 0x1f: + fdiv(PST(code & 7),PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 0x38: + fpush(); + ST(0) = ST((code & 7)+1); + return; + case 0x39: + fxchg(&ST(0),&ST(code & 7)); + return; + case 0x3b: + ST(code & 7) = ST(0); + fpop(); + return; + case 0x98: + fadd(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(code & 7)); + return; + case 0x99: + fmul(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(code & 7)); + return; + case 0x9a: + fcom(PST(code & 7),PST(0)); + return; + case 0x9b: + fcom(PST(code & 7),PST(0)); + fpop(); + return; + case 0x9c: + ST(code & 7).exponent ^= 0x8000; + fadd(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(code & 7)); + return; + case 0x9d: + real_to_real(&ST(0),&tmp); + tmp.exponent ^= 0x8000; + fadd(PST(code & 7),&tmp,&tmp); + real_to_real(&tmp,&ST(code & 7)); + return; + case 0x9e: + fdiv(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(code & 7)); + return; + case 0x9f: + fdiv(PST(code & 7),PST(0),&tmp); + real_to_real(&tmp,&ST(code & 7)); + return; + case 0xb8: + printk("ffree not implemented\n\r"); + math_abort(info,1<<(SIGILL-1)); + case 0xb9: + fxchg(&ST(0),&ST(code & 7)); + return; + case 0xba: + ST(code & 7) = ST(0); + return; + case 0xbb: + ST(code & 7) = ST(0); + fpop(); + return; + case 0xbc: + fucom(PST(code & 7),PST(0)); + return; + case 0xbd: + fucom(PST(code & 7),PST(0)); + fpop(); + return; + case 0xd8: + fadd(PST(code & 7),PST(0),&tmp); + real_to_real(&tmp,&ST(code & 7)); + fpop(); + return; + case 0xd9: + fmul(PST(code & 7),PST(0),&tmp); + real_to_real(&tmp,&ST(code & 7)); + fpop(); + return; + case 0xda: + fcom(PST(code & 7),PST(0)); + fpop(); + return; + case 0xdc: + ST(code & 7).exponent ^= 0x8000; + fadd(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(code & 7)); + fpop(); + return; + case 0xdd: + real_to_real(&ST(0),&tmp); + tmp.exponent ^= 0x8000; + fadd(PST(code & 7),&tmp,&tmp); + real_to_real(&tmp,&ST(code & 7)); + fpop(); + return; + case 0xde: + fdiv(PST(0),PST(code & 7),&tmp); + real_to_real(&tmp,&ST(code & 7)); + fpop(); + return; + case 0xdf: + fdiv(PST(code & 7),PST(0),&tmp); + real_to_real(&tmp,&ST(code & 7)); + fpop(); + return; + case 0xf8: + printk("ffree not implemented\n\r"); + math_abort(info,1<<(SIGILL-1)); + fpop(); + return; + case 0xf9: + fxchg(&ST(0),&ST(code & 7)); + return; + case 0xfa: + case 0xfb: + ST(code & 7) = ST(0); + fpop(); + return; + } + switch ((code>>3) & 0xe7) { + case 0x22: + put_short_real(PST(0),info,code); + return; + case 0x23: + put_short_real(PST(0),info,code); + fpop(); + return; + case 0x24: + address = ea(info,code); + for (code = 0 ; code < 7 ; code++) { + ((long *) & I387)[code] = + get_fs_long((unsigned long *) address); + address += 4; + } + return; + case 0x25: + address = ea(info,code); + *(unsigned short *) &I387.cwd = + get_fs_word((unsigned short *) address); + return; + case 0x26: + address = ea(info,code); + verify_area(address,28); + for (code = 0 ; code < 7 ; code++) { + put_fs_long( ((long *) & I387)[code], + (unsigned long *) address); + address += 4; + } + return; + case 0x27: + address = ea(info,code); + verify_area(address,2); + put_fs_word(I387.cwd,(short *) address); + return; + case 0x62: + put_long_int(PST(0),info,code); + return; + case 0x63: + put_long_int(PST(0),info,code); + fpop(); + return; + case 0x65: + fpush(); + get_temp_real(&tmp,info,code); + real_to_real(&tmp,&ST(0)); + return; + case 0x67: + put_temp_real(PST(0),info,code); + fpop(); + return; + case 0xa2: + put_long_real(PST(0),info,code); + return; + case 0xa3: + put_long_real(PST(0),info,code); + fpop(); + return; + case 0xa4: + address = ea(info,code); + for (code = 0 ; code < 27 ; code++) { + ((long *) & I387)[code] = + get_fs_long((unsigned long *) address); + address += 4; + } + return; + case 0xa6: + address = ea(info,code); + verify_area(address,108); + for (code = 0 ; code < 27 ; code++) { + put_fs_long( ((long *) & I387)[code], + (unsigned long *) address); + address += 4; + } + I387.cwd = 0x037f; + I387.swd = 0x0000; + I387.twd = 0x0000; + return; + case 0xa7: + address = ea(info,code); + verify_area(address,2); + put_fs_word(I387.swd,(short *) address); + return; + case 0xe2: + put_short_int(PST(0),info,code); + return; + case 0xe3: + put_short_int(PST(0),info,code); + fpop(); + return; + case 0xe4: + fpush(); + get_BCD(&tmp,info,code); + real_to_real(&tmp,&ST(0)); + return; + case 0xe5: + fpush(); + get_longlong_int(&tmp,info,code); + real_to_real(&tmp,&ST(0)); + return; + case 0xe6: + put_BCD(PST(0),info,code); + fpop(); + return; + case 0xe7: + put_longlong_int(PST(0),info,code); + fpop(); + return; + } + switch (code >> 9) { + case 0: + get_short_real(&tmp,info,code); + break; + case 1: + get_long_int(&tmp,info,code); + break; + case 2: + get_long_real(&tmp,info,code); + break; + case 4: + get_short_int(&tmp,info,code); + } + switch ((code>>3) & 0x27) { + case 0: + fadd(&tmp,PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 1: + fmul(&tmp,PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 2: + fcom(&tmp,PST(0)); + return; + case 3: + fcom(&tmp,PST(0)); + fpop(); + return; + case 4: + tmp.exponent ^= 0x8000; + fadd(&tmp,PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 5: + ST(0).exponent ^= 0x8000; + fadd(&tmp,PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 6: + fdiv(PST(0),&tmp,&tmp); + real_to_real(&tmp,&ST(0)); + return; + case 7: + fdiv(&tmp,PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return; + } + if ((code & 0x138) == 0x100) { + fpush(); + real_to_real(&tmp,&ST(0)); + return; + } + printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code); + math_abort(info,1<<(SIGFPE-1)); +} + +void math_emulate(long ___false) +{ + if (!current->used_math) { + current->used_math = 1; + I387.cwd = 0x037f; + I387.swd = 0x0000; + I387.twd = 0x0000; + } +/* &___false points to info->___orig_eip, so subtract 1 to get info */ + do_emu((struct info *) ((&___false) - 1)); +} + +void __math_abort(struct info * info, unsigned int signal) +{ + EIP = ORIG_EIP; + current->signal |= signal; + __asm__("movl %0,%%esp ; ret"::"g" ((long) info)); +} + +static void fpop(void) +{ + unsigned long tmp; + + tmp = I387.swd & 0xffffc7ff; + I387.swd += 0x00000800; + I387.swd &= 0x00003800; + I387.swd |= tmp; +} + +static void fpush(void) +{ + unsigned long tmp; + + tmp = I387.swd & 0xffffc7ff; + I387.swd += 0x00003800; + I387.swd &= 0x00003800; + I387.swd |= tmp; +} + +static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b) +{ + temp_real_unaligned c; + + c = *a; + *a = *b; + *b = c; +} + +static temp_real_unaligned * __st(int i) +{ + i += I387.swd >> 11; + i &= 7; + return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space)); +} diff --git a/linux-0.12/kernel/math/mul.c b/linux-0.12/kernel/math/mul.c new file mode 100644 index 0000000..94f7c52 --- /dev/null +++ b/linux-0.12/kernel/math/mul.c @@ -0,0 +1,73 @@ +/* + * linux/kernel/math/mul.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * temporary real multiplication routine. + */ + +#include + +static void shift(int * c) +{ + __asm__("movl (%0),%%eax ; addl %%eax,(%0)\n\t" + "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t" + "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t" + "movl 12(%0),%%eax ; adcl %%eax,12(%0)" + ::"r" ((long) c):"ax"); +} + +static void mul64(const temp_real * a, const temp_real * b, int * c) +{ + __asm__("movl (%0),%%eax\n\t" + "mull (%1)\n\t" + "movl %%eax,(%2)\n\t" + "movl %%edx,4(%2)\n\t" + "movl 4(%0),%%eax\n\t" + "mull 4(%1)\n\t" + "movl %%eax,8(%2)\n\t" + "movl %%edx,12(%2)\n\t" + "movl (%0),%%eax\n\t" + "mull 4(%1)\n\t" + "addl %%eax,4(%2)\n\t" + "adcl %%edx,8(%2)\n\t" + "adcl $0,12(%2)\n\t" + "movl 4(%0),%%eax\n\t" + "mull (%1)\n\t" + "addl %%eax,4(%2)\n\t" + "adcl %%edx,8(%2)\n\t" + "adcl $0,12(%2)" + ::"b" ((long) a),"c" ((long) b),"D" ((long) c) + :"ax","dx"); +} + +void fmul(const temp_real * src1, const temp_real * src2, temp_real * result) +{ + int i,sign; + int tmp[4] = {0,0,0,0}; + + sign = (src1->exponent ^ src2->exponent) & 0x8000; + i = (src1->exponent & 0x7fff) + (src2->exponent & 0x7fff) - 16383 + 1; + if (i<0) { + result->exponent = sign; + result->a = result->b = 0; + return; + } + if (i>0x7fff) { + set_OE(); + return; + } + mul64(src1,src2,tmp); + if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) + while (i && tmp[3] >= 0) { + i--; + shift(tmp); + } + else + i = 0; + result->exponent = i | sign; + result->a = tmp[2]; + result->b = tmp[3]; +} diff --git a/linux-0.12/kernel/mktime.c b/linux-0.12/kernel/mktime.c new file mode 100644 index 0000000..8b81326 --- /dev/null +++ b/linux-0.12/kernel/mktime.c @@ -0,0 +1,82 @@ +/* + * linux/kernel/mktime.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +/* + * This isn't the library routine, it is only used in the kernel. + * as such, we don't care about years<1970 etc, but assume everything + * is ok. Similarly, TZ etc is happily ignored. We just do everything + * as easily as possible. Let's find something public for the library + * routines (although I think minix times is public). + */ +/* + * PS. I hate whoever though up the year 1970 - couldn't they have gotten + * a leap-year instead? I also hate Gregorius, pope or no. I'm grumpy. + */ +/** + * 这不是库函数,它仅供内核使用。因此我们不关心小于1970年的年份等,但假定一切均很正常。 + * 同样,时间区域TZ问题也先忽略。我们只是尽可能简单地处理问题。最好能找到一些公开的库函数 + * (尽管我认为minix的时间函数是公开的)。 + * 另外,我恨那个设置1970年开始的人 - 难道他们就不能选择从一个闰年开始?我恨格里高利历、 + * 罗马教皇、主教,我什么都不在乎。我是个脾气暴躁的人。 + */ + +#define MINUTE 60 +#define HOUR (60*MINUTE) +#define DAY (24*HOUR) +#define YEAR (365*DAY) + +/* interestingly, we assume leap-years */ +/* 以闰年为基础,每个月开始时的秒数时间 */ +static int month[12] = { + 0, + DAY*(31), + DAY*(31+29), + DAY*(31+29+31), + DAY*(31+29+31+30), + DAY*(31+29+31+30+31), + DAY*(31+29+31+30+31+30), + DAY*(31+29+31+30+31+30+31), + DAY*(31+29+31+30+31+30+31+31), + DAY*(31+29+31+30+31+30+31+31+30), + DAY*(31+29+31+30+31+30+31+31+30+31), + DAY*(31+29+31+30+31+30+31+31+30+31+30) +}; + +/** + * 计算从1970年1月1日0时起到开机当日经过的秒数 + * @param[in] tm 当前时间 + * @return 返回从1970年1月1日0时起到开机当日经过的秒数 + */ +long kernel_mktime(struct tm * tm) +{ + long res; + int year; + + /* tm_year是2位表示方式,处理2000年的问题(例如:2018=>18+100-70) */ + if (tm->tm_year < 70) + tm->tm_year += 100; + + year = tm->tm_year - 70; +/* magic offsets (y+1) needed to get leapyears right.*/ +/* 由于UNIX计年份y是从1970年算起。到1972年就是一个闰年,因此过3年才收到了第1个闰年的影响, + 即从1970年到当前年份经过的闰年数为(y+1)/4 */ + res = YEAR * year + DAY * ((year+1)/4); + + res += month[tm->tm_mon]; +/* and (y+2) here. If it wasn't a leap-year, we have to adjust */ +/* 如果月份大于2月,则需要判断当前年份是否为闰年,计算公式为(year+2)%4。非闰年则需要减去1 + 天,因为month[]这个数组以闰年为假设的 */ + if (tm->tm_mon>1 && ((year+2)%4)) { + res -= DAY; + } + res += DAY * (tm->tm_mday - 1); + res += HOUR * tm->tm_hour; + res += MINUTE * tm->tm_min; + res += tm->tm_sec; + return res; +} diff --git a/linux-0.12/kernel/panic.c b/linux-0.12/kernel/panic.c new file mode 100644 index 0000000..2a5c70e --- /dev/null +++ b/linux-0.12/kernel/panic.c @@ -0,0 +1,30 @@ +/* + * linux/kernel/panic.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This function is used through-out the kernel (includeinh mm and fs) + * to indicate a major problem. + */ +#include +#include + +void sys_sync(void); /* it's really int */ + /* 实际上该函数返回int的 */ + +/** + * 显示内核中出现的重大错误信息,并运行文件系统同步函数,然后进入死循环 + * @note 如果当前进程是任务0的话,还说明是交换任务出错,并且还没有运行文件系统同步函数。 + */ +volatile void panic(const char * s) +{ + printk("Kernel panic: %s\n\r",s); + if (current == task[0]) { + printk("In swapper task - not syncing\n\r"); + } else { + sys_sync(); + } + for(;;); +} diff --git a/linux-0.12/kernel/printk.c b/linux-0.12/kernel/printk.c new file mode 100644 index 0000000..c0793f2 --- /dev/null +++ b/linux-0.12/kernel/printk.c @@ -0,0 +1,32 @@ +/* + * linux/kernel/printk.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * When in kernel-mode, we cannot use printf, as fs is liable to + * point to 'interesting' things. Make a printf with fs-saving, and + * all is well. + */ +#include +#include + +#include + +static char buf[1024]; /* 显示用的临时缓冲区 */ + +extern int vsprintf(char * buf, const char * fmt, va_list args); + +/* 内核使用的显示函数 */ +int printk(const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + console_print(buf); + return i; +} diff --git a/linux-0.12/kernel/sched.c b/linux-0.12/kernel/sched.c new file mode 100755 index 0000000..fc4091e --- /dev/null +++ b/linux-0.12/kernel/sched.c @@ -0,0 +1,579 @@ +/* + * linux/kernel/sched.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 'sched.c' is the main kernel file. It contains scheduling primitives + * (sleep_on, wakeup, schedule etc) as well as a number of simple system + * call functions (type getpid(), which just extracts a field from + * current-task + */ + +/* + * 'sched.c'是主要的内核文件。其中包括有关高度的基本函数(sleep_on,wakeup,schedule等)以及一些 + * 简单的系统调用函数(比如getpid(),仅从当前任务中获取一个字段)。 + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 取信号nr在信号位图中对应位的二进制数值(信号编号1-32) */ +#define _S(nr) (1 << ((nr)-1)) + +/* 除了SIGKILL和SIGSTOP信号以外其他信号都是可阻塞的 */ +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +/** + * 显示任务号nr的进程号,进程状态和内核堆栈空闲字节数(大约)及其相关的子进程和父进程信息 + * @param[in] nr 任务号 + * @param[in] p + * @return void + */ +/* static */ void show_task(int nr, struct task_struct * p) +{ + /* 任务结构的数据和任务的内核态栈在同一内存页面上 */ + int i, j = 4096 - sizeof(struct task_struct); + + printk("%d: pid=%d, state=%d, father=%d, child=%d, ", nr, p->pid, + p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1); + i = 0; + /* 计算1页内存从task_struct结构后0的个数 */ + while (i < j && !((char *)(p+1))[i]) { + i++; + } + printk("%d/%d chars free in kstack\n\r", i, j); + /* 该指针指向任务结构体1019偏移处,应该指的是tts中的EIP(PC指针) */ + printk(" PC=%08X.", *(1019 + (unsigned long *) p)); /* 这么写,有点搞吧... */ + if (p->p_ysptr || p->p_osptr) { + printk(" Younger sib=%d, older sib=%d\n\r", + p->p_ysptr ? p->p_ysptr->pid : -1, + p->p_osptr ? p->p_osptr->pid : -1); + } else { + printk("\n\r"); + } +} + +/* 显示所有进程的进程信息 */ +void show_state(void) +{ + int i; + + printk("\rTask-info:\n\r"); + for (i = 0; i < NR_TASKS; i++) { + if (task[i]) { + show_task(i, task[i]); + } + } +} + +/* PC机8253计数/定时芯片的输入时钟频率约为1.193180MHz。Linux内核希望定时器中断频率 + 是100Hz,也即每10ms发出一次时钟中断 */ +#define LATCH (1193180/HZ) /* LATCH是设置8253芯片的初值 */ + +extern void mem_use(void); + +extern int timer_interrupt(void); +extern int system_call(void); + +union task_union { + struct task_struct task; + char stack[PAGE_SIZE]; +}; + +static union task_union init_task = {INIT_TASK, }; + +unsigned long volatile jiffies = 0; +unsigned long startup_time = 0; +int jiffies_offset = 0; /* # clock ticks to add to get "true time". Should + always be less than 1 second's worth. For time fanaticswho like to syncronize + their machines to WWV :-) */ + /* 为调整时钟而需要增加的时钟嘀嗒数,以获得“精确时间”。这些调整用嘀嗒数的总和不应该超过 + 1秒。这样做是为了那些对时间精确度要求苛刻的人,他们喜欢自己的机器时间与WWV同步 :-) */ + +struct task_struct *current = &(init_task.task); /* 当前任务指针 */ +struct task_struct *last_task_used_math = NULL; /* 上一个使用过协处理器的进程 */ + +struct task_struct * task[NR_TASKS] = {&(init_task.task), }; + +long user_stack [ PAGE_SIZE>>2 ] ; /* 用户堆栈(4 * 1K) */ + +/* Tip: Intel CPU执行堆栈操作时总是先递减堆栈指针ESP值,然后在ESP指针处保存入栈内容 */ +/* 下面结构用于设置堆栈SS:ESP,SS被设置为内核数据段选择符(0x10),ESP被设置为指向user_stack数 + 组最后一项后面。在head.s中被使用,在刚开始内核初始化操作过程中被用作内核栈,初始化操作完成后将被 + 用作任务0的用户态堆栈。在运行任务0之前它是内核栈,以后用作任务0和任务1的用户态栈。*/ +struct { + long * a; + short b; + } stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 }; + +/* + * 'math_state_restore()' saves the current math information in the + * old math state array, and gets the new ones from the current task + */ +/* +* 将当前协处理器内容保存到老协处理器状态数组中,并将当前任务的协处理器内容加载进协处理器。 +*/ +void math_state_restore() +{ + if (last_task_used_math == current) { + return; + } + __asm__("fwait"); + if (last_task_used_math) { + __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); + } + last_task_used_math = current; + if (current->used_math) { + __asm__("frstor %0"::"m" (current->tss.i387)); + } else { + __asm__("fninit"::); + current->used_math = 1; + } +} + +/* + * 'schedule()' is the scheduler function. This is GOOD CODE! There + * probably won't be any reason to change this, as it should work well + * in all circumstances (ie gives IO-bound processes good response etc). + * The one thing you might take a look at is the signal-handler code here. + * + * NOTE!! Task 0 is the 'idle' task, which gets called when no other + * tasks can run. It can not be killed, and it cannot sleep. The 'state' + * information in task[0] is never used. + */ + +/* + * 'schedule()' 是一个调度函数。这是一块很好的代码,没有理由去修改它,因为它可以在所有的环境下工 + * 作(比如能够对IO-边界下得很好的响应等)。只有一件事值得留意,那就是这里的信号处理代码。 + * + * 注意!! 任务0是个闲置('idle')任务,只有当没有其他任务可以运行时才调用它。它不能被杀死,也不睡眠。 + * 任务0中的状态信息'state'是从来不用的。 + * + */ +void schedule(void) +{ + int i, next, c; + struct task_struct ** p; + +/* check alarm, wake up any interruptible tasks that have got a signal */ +/* 检测alarm(进程的报警定时值),唤醒任何已得到信号的可中断任务 */ + + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p) { + /* 如果设置过任务超时定时值timeout,并且已经超时,则复位超时定时值,并且如果任 + 务处于可中断睡眠状态TASK_INTERRUPTIBLE下,将其置为就绪状态(TASK_RUNNING) */ + if ((*p)->timeout && (*p)->timeout < jiffies) { + (*p)->timeout = 0; + if ((*p)->state == TASK_INTERRUPTIBLE) { + (*p)->state = TASK_RUNNING; + } + } + /* 如果设置过任务的SIGALRM信号超时定时器值alarm,并且已经过期(alarmalarm && (*p)->alarm < jiffies) { + (*p)->signal |= (1 << (SIGALRM - 1)); + (*p)->alarm = 0; + } + /* '~(_BLOCKABLE & (*p)->blocked)'用于忽略被阻塞的信号,除被阻塞的信号外还有其 + 他信号,并且任务处于可中断状态,则置任务为就绪状态 */ + if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) && + (*p)->state == TASK_INTERRUPTIBLE) { + (*p)->state = TASK_RUNNING; + } + } + +/* this is the scheduler proper: */ +/* 这里是调度程序的主要部分 */ + + while (1) { + c = -1; + next = 0; + i = NR_TASKS; + p = &task[NR_TASKS]; + /* 找到就绪状态下时间片最大的任务,用next指向该任务 */ + while (--i) { + if (!*--p) { + continue; + } + if ((*p)->state == TASK_RUNNING && (*p)->counter > c) { + c = (*p)->counter, next = i; + } + } + /* c = -1,没有可以运行的任务(此时next=0,会切去任务0);c > 0,找到了可以切换的任务 */ + if (c) { + break; + } + /* 除任务0以外,存在处于就绪状态但时间片都为0的任务,则更新counter值,然后重新寻找 */ + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (*p) { + (*p)->counter = ((*p)->counter >> 1) + (*p)->priority; + } + } + } + switch_to(next); +} + +/** + * 转换当前任务的状态为可中断的等待状态 + * 该系统调用将导致进程进入睡眠状态,直到收到一个信号。该信号用于终止进程或者使进程调用一个信号捕 + * 获函数。只有当捕获了一个信号,并且信号捕获处理函数返回,pause()才会返回。此时pause()返回值应 + * 该是-1,并且errno被置为EINTR。这里还没有完全实现(直到0.95版) + */ +int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return 0; +} + +/** + * 将当前任务置为可中断的或不可中断的睡眠状态 + * @note 该函数存在问题 + * @param p 任务结构指针 + * @param state 任务睡眠使用的状态 + * @return void + */ +static inline void __sleep_on(struct task_struct **p, int state) +{ + struct task_struct *tmp; + + if (!p) { + return; + } + if (current == &(init_task.task)) { + panic("task[0] trying to sleep"); + } + tmp = *p; + *p = current; + current->state = state; +repeat: schedule(); + if (*p && *p != current) { + (**p).state = TASK_RUNNING; + current->state = TASK_UNINTERRUPTIBLE; + goto repeat; + } + if (!*p) { + printk("Warning: *P = NULL\n\r"); + } + if ((*p = tmp)) { + tmp->state = 0; + } +} + +void interruptible_sleep_on(struct task_struct **p) +{ + __sleep_on(p, TASK_INTERRUPTIBLE); +} + +void sleep_on(struct task_struct **p) +{ + __sleep_on(p, TASK_UNINTERRUPTIBLE); +} + +/** + * 唤醒不可中断等待任务 + * @param p 任务结构指针 + * @return void + */ +void wake_up(struct task_struct **p) +{ + if (p && *p) { + if ((**p).state == TASK_STOPPED) { + printk("wake_up: TASK_STOPPED"); + } + if ((**p).state == TASK_ZOMBIE) { + printk("wake_up: TASK_ZOMBIE"); + } + (**p).state = TASK_RUNNING; + } +} + +/* + * OK, here are some floppy things that shouldn't be in the kernel + * proper. They are here because the floppy needs a timer, and this + * was the easiest way of doing it. + */ +static struct task_struct * wait_motor[4] = {NULL, NULL, NULL, NULL}; +static int mon_timer[4] = {0, 0, 0, 0}; +static int moff_timer[4] = {0, 0, 0, 0}; +unsigned char current_DOR = 0x0C; + +int ticks_to_floppy_on(unsigned int nr) +{ + extern unsigned char selected; + unsigned char mask = 0x10 << nr; + + if (nr>3) { + panic("floppy_on: nr>3"); + } + moff_timer[nr] = 10000; /* 100 s = very big :-) */ + cli(); /* use floppy_off to turn it off */ + mask |= current_DOR; + if (!selected) { + mask &= 0xFC; + mask |= nr; + } + if (mask != current_DOR) { + outb(mask, FD_DOR); + if ((mask ^ current_DOR) & 0xf0) { + mon_timer[nr] = HZ / 2; + } else if (mon_timer[nr] < 2) { + mon_timer[nr] = 2; + } + current_DOR = mask; + } + sti(); + return mon_timer[nr]; +} + +void floppy_on(unsigned int nr) +{ + cli(); + while (ticks_to_floppy_on(nr)) { + sleep_on(nr + wait_motor); + } + sti(); +} + +void floppy_off(unsigned int nr) +{ + moff_timer[nr] = 3 * HZ; +} + +void do_floppy_timer(void) +{ + int i; + unsigned char mask = 0x10; + + for (i=0 ; i<4 ; i++,mask <<= 1) { + if (!(mask & current_DOR)) + continue; + if (mon_timer[i]) { + if (!--mon_timer[i]) { + wake_up(i+wait_motor); + } + } else if (!moff_timer[i]) { + current_DOR &= ~mask; + outb(current_DOR, FD_DOR); + } else { + moff_timer[i] --; + } + } +} + +#define TIME_REQUESTS 64 + +static struct timer_list { + long jiffies; + void (*fn)(); + struct timer_list * next; +} timer_list[TIME_REQUESTS], * next_timer = NULL; + +void add_timer(long jiffies, void (*fn)(void)) +{ + struct timer_list * p; + + if (!fn) { + return; + } + cli(); + if (jiffies <= 0) { + (fn)(); + } else { + for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++) { + if (!p->fn) { + break; + } + } + if (p >= timer_list + TIME_REQUESTS) { + panic("No more time requests free"); + } + p->fn = fn; + p->jiffies = jiffies; + p->next = next_timer; + next_timer = p; + while (p->next && p->next->jiffies < p->jiffies) { + p->jiffies -= p->next->jiffies; + fn = p->fn; + p->fn = p->next->fn; + p->next->fn = fn; + jiffies = p->jiffies; + p->jiffies = p->next->jiffies; + p->next->jiffies = jiffies; + p = p->next; + } + } + sti(); +} + +/** + * 时钟中断C函数处理程序 + * 对于一个进程由于执行时间片用完时,则进行任务切换,并执行一个计时更新工作。在sys_call.s中 + * 的timer_interrupt被调用。 + * @param[in] cpl 当前特权级,是时钟中断发生时正被执行的代码选择符中的特权级 + * @retval void + */ +void do_timer(long cpl) +{ + static int blanked = 0; + + if (blankcount || !blankinterval) { + if (blanked) { + unblank_screen(); + } + if (blankcount) { + blankcount--; + } + blanked = 0; + } else if (!blanked) { + blank_screen(); + blanked = 1; + } + if (hd_timeout) { + if (!--hd_timeout) { + hd_times_out(); + } + } + if (beepcount) { + if (!--beepcount) { + sysbeepstop(); + } + } + if (cpl) { + current->utime++; + } else { + current->stime++; + } + if (next_timer) { + next_timer->jiffies--; + while (next_timer && next_timer->jiffies <= 0) { + void (*fn)(void); + + fn = next_timer->fn; + next_timer->fn = NULL; + next_timer = next_timer->next; + (fn)(); + } + } + if (current_DOR & 0xf0) { + do_floppy_timer(); + } + if ((--current->counter)>0) { + return; + } + current->counter=0; + if (!cpl) { + return; + } + schedule(); +} + +/** + * 设置报警定时时间值(秒) + * alarm的单位是系统滴答(1滴答为10毫秒),它是系统开机起到设置定时操作时系统滴答值jiffies和转换成 + * 滴答单位的定时值之和,即'jiffies + HZ*定时秒值' + * @param[in] seconds 新的定时时间值(单位是秒) + * @retval 若参数seconds大于0,则设置新定时值,并返回原定时时刻还剩余的间隔时间;否则返回0 + */ +int sys_alarm(long seconds) +{ + int old = current->alarm; + + if (old) { + old = (old - jiffies) / HZ; + } + current->alarm = (seconds > 0) ? (jiffies + HZ * seconds) : 0; + return (old); +} + +/* 取进程号pid */ +int sys_getpid(void) +{ + return current->pid; +} + +/* 取父进程号ppid */ +int sys_getppid(void) +{ + return current->p_pptr->pid; +} + +/* 取用户id */ +int sys_getuid(void) +{ + return current->uid; +} + +/* 取有效用户id */ +int sys_geteuid(void) +{ + return current->euid; +} + +/* 取组号gid */ +int sys_getgid(void) +{ + return current->gid; +} + +/* 取有效的组号egid */ +int sys_getegid(void) +{ + return current->egid; +} + +/** + * 改变对cpu的使用优先权 + * @param[in] increment + * @retval 0 + */ +int sys_nice(long increment) +{ + if (current->priority-increment > 0) { + current->priority -= increment; + } + return 0; +} + +/* 内核调度程序的初始化子程序 */ +void sched_init(void) +{ + int i; + struct desc_struct * p; /* 描述符表结构指针 */ + + /* 这个判断语句并无必要 */ + if (sizeof(struct sigaction) != 16) { + panic("Struct sigaction MUST be 16 bytes"); + } + set_tss_desc(gdt+FIRST_TSS_ENTRY, &(init_task.task.tss)); + set_ldt_desc(gdt+FIRST_LDT_ENTRY, &(init_task.task.ldt)); + p = gdt + 2 + FIRST_TSS_ENTRY; + for(i = 1; i < NR_TASKS; i++) { + task[i] = NULL; + p->a = p->b = 0; + p++; + p->a = p->b = 0; + p++; + } +/* Clear NT, so that we won't have troubles with that later on */ + __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); + ltr(0); + lldt(0); + outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + set_intr_gate(0x20,&timer_interrupt); + outb(inb_p(0x21)&~0x01,0x21); + + /* 设置系统调用的系统陷阱 */ + set_system_gate(0x80,&system_call); +} diff --git a/linux-0.12/kernel/signal.c b/linux-0.12/kernel/signal.c new file mode 100644 index 0000000..eddec58 --- /dev/null +++ b/linux-0.12/kernel/signal.c @@ -0,0 +1,210 @@ +/* + * linux/kernel/signal.c + * + * (C) 1991 Linus Torvalds + */ + +#include +#include +#include + +#include +#include + +int sys_sgetmask() +{ + return current->blocked; +} + +int sys_ssetmask(int newmask) +{ + int old=current->blocked; + + current->blocked = newmask & ~(1<<(SIGKILL-1)) & ~(1<<(SIGSTOP-1)); + return old; +} + +int sys_sigpending(sigset_t *set) +{ + /* fill in "set" with signals pending but blocked. */ + verify_area(set,4); + put_fs_long(current->blocked & current->signal, (unsigned long *)set); + return 0; +} + +/* atomically swap in the new signal mask, and wait for a signal. + * + * we need to play some games with syscall restarting. We get help + * from the syscall library interface. Note that we need to coordinate + * the calling convention with the libc routine. + * + * "set" is just the sigmask as described in 1003.1-1988, 3.3.7. + * It is assumed that sigset_t can be passed as a 32 bit quantity. + * + * "restart" holds a restart indication. If it's non-zero, then we + * install the old mask, and return normally. If it's zero, we store + * the current mask in old_mask and block until a signal comes in. + */ +int sys_sigsuspend(int restart, unsigned long old_mask, unsigned long set) +{ + extern int sys_pause(void); + + if (restart) { + /* we're restarting */ + current->blocked = old_mask; + return -EINTR; + } + /* we're not restarting. do the work */ + *(&restart) = 1; + *(&old_mask) = current->blocked; + current->blocked = set; + (void) sys_pause(); /* return after a signal arrives */ + return -ERESTARTNOINTR; /* handle the signal, and come back */ +} + +static inline void save_old(char * from,char * to) +{ + int i; + + verify_area(to, sizeof(struct sigaction)); + for (i=0 ; i< sizeof(struct sigaction) ; i++) { + put_fs_byte(*from,to); + from++; + to++; + } +} + +static inline void get_new(char * from,char * to) +{ + int i; + + for (i=0 ; i< sizeof(struct sigaction) ; i++) { + *(to++) = get_fs_byte(from++); + } +} + +int sys_signal(int signum, long handler, long restorer) +{ + struct sigaction tmp; + + if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP) + return -EINVAL; + tmp.sa_handler = (void (*)(int)) handler; + tmp.sa_mask = 0; + tmp.sa_flags = SA_ONESHOT | SA_NOMASK; + tmp.sa_restorer = (void (*)(void)) restorer; + handler = (long) current->sigaction[signum-1].sa_handler; + current->sigaction[signum-1] = tmp; + return handler; +} + +int sys_sigaction(int signum, const struct sigaction * action, + struct sigaction * oldaction) +{ + struct sigaction tmp; + + if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP) + return -EINVAL; + tmp = current->sigaction[signum-1]; + get_new((char *) action, + (char *) (signum-1+current->sigaction)); + if (oldaction) + save_old((char *) &tmp,(char *) oldaction); + if (current->sigaction[signum-1].sa_flags & SA_NOMASK) + current->sigaction[signum-1].sa_mask = 0; + else + current->sigaction[signum-1].sa_mask |= (1<<(signum-1)); + return 0; +} + +/* + * Routine writes a core dump image in the current directory. + * Currently not implemented. + */ +int core_dump(long signr) +{ + return(0); /* We didn't do a dump */ +} + +int do_signal(long signr,long eax,long ebx, long ecx, long edx, long orig_eax, + long fs, long es, long ds, + long eip, long cs, long eflags, + unsigned long * esp, long ss) +{ + unsigned long sa_handler; + long old_eip=eip; + struct sigaction * sa = current->sigaction + signr - 1; + int longs; + + unsigned long * tmp_esp; + +#ifdef notdef + printk("pid: %d, signr: %x, eax=%d, oeax = %d, int=%d\n", + current->pid, signr, eax, orig_eax, + sa->sa_flags & SA_INTERRUPT); +#endif + if ((orig_eax != -1) && + ((eax == -ERESTARTSYS) || (eax == -ERESTARTNOINTR))) { + if ((eax == -ERESTARTSYS) && ((sa->sa_flags & SA_INTERRUPT) || + signr < SIGCONT || signr > SIGTTOU)) + *(&eax) = -EINTR; + else { + *(&eax) = orig_eax; + *(&eip) = old_eip -= 2; + } + } + sa_handler = (unsigned long) sa->sa_handler; + if (sa_handler==1) + return(1); /* Ignore, see if there are more signals... */ + if (!sa_handler) { + switch (signr) { + case SIGCONT: + case SIGCHLD: + return(1); /* Ignore, ... */ + + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & + SA_NOCLDSTOP)) + current->p_pptr->signal |= (1<<(SIGCHLD-1)); + return(1); /* Reschedule another event */ + + case SIGQUIT: + case SIGILL: + case SIGTRAP: + case SIGIOT: + case SIGFPE: + case SIGSEGV: + if (core_dump(signr)) + do_exit(signr|0x80); + /* fall through */ + default: + do_exit(signr); + } + } + /* + * OK, we're invoking a handler + */ + if (sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; + *(&eip) = sa_handler; + longs = (sa->sa_flags & SA_NOMASK)?7:8; + *(&esp) -= longs; + verify_area(esp,longs*4); + tmp_esp=esp; + put_fs_long((long) sa->sa_restorer,tmp_esp++); + put_fs_long(signr,tmp_esp++); + if (!(sa->sa_flags & SA_NOMASK)) + put_fs_long(current->blocked,tmp_esp++); + put_fs_long(eax,tmp_esp++); + put_fs_long(ecx,tmp_esp++); + put_fs_long(edx,tmp_esp++); + put_fs_long(eflags,tmp_esp++); + put_fs_long(old_eip,tmp_esp++); + current->blocked |= sa->sa_mask; + return(0); /* Continue, execute handler */ +} diff --git a/linux-0.12/kernel/sys.c b/linux-0.12/kernel/sys.c new file mode 100644 index 0000000..e3befc1 --- /dev/null +++ b/linux-0.12/kernel/sys.c @@ -0,0 +1,535 @@ +/* + * linux/kernel/sys.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The timezone where the local system is located. Used as a default by some + * programs who obtain this value by using gettimeofday. + */ +struct timezone sys_tz = { 0, 0}; + +extern int session_of_pgrp(int pgrp); + +int sys_ftime() +{ + return -ENOSYS; +} + +int sys_break() +{ + return -ENOSYS; +} + +int sys_ptrace() +{ + return -ENOSYS; +} + +int sys_stty() +{ + return -ENOSYS; +} + +int sys_gtty() +{ + return -ENOSYS; +} + +int sys_rename() +{ + return -ENOSYS; +} + +int sys_prof() +{ + return -ENOSYS; +} + +/* + * This is done BSD-style, with no consideration of the saved gid, except + * that if you set the effective gid, it sets the saved gid too. This + * makes it possible for a setgid program to completely drop its privileges, + * which is often a useful assertion to make when you are doing a security + * audit over a program. + * + * The general idea is that a program which uses just setregid() will be + * 100% compatible with BSD. A program which uses just setgid() will be + * 100% compatible with POSIX w/ Saved ID's. + */ +int sys_setregid(int rgid, int egid) +{ + if (rgid>0) { + if ((current->gid == rgid) || + suser()) + current->gid = rgid; + else + return(-EPERM); + } + if (egid>0) { + if ((current->gid == egid) || + (current->egid == egid) || + suser()) { + current->egid = egid; + current->sgid = egid; + } else + return(-EPERM); + } + return 0; +} + +/* + * setgid() is implemeneted like SysV w/ SAVED_IDS + */ +int sys_setgid(int gid) +{ + if (suser()) + current->gid = current->egid = current->sgid = gid; + else if ((gid == current->gid) || (gid == current->sgid)) + current->egid = gid; + else + return -EPERM; + return 0; +} + +int sys_acct() +{ + return -ENOSYS; +} + +int sys_phys() +{ + return -ENOSYS; +} + +int sys_lock() +{ + return -ENOSYS; +} + +int sys_mpx() +{ + return -ENOSYS; +} + +int sys_ulimit() +{ + return -ENOSYS; +} + +int sys_time(long * tloc) +{ + int i; + + i = CURRENT_TIME; + if (tloc) { + verify_area(tloc,4); + put_fs_long(i,(unsigned long *)tloc); + } + return i; +} + +/* + * Unprivileged users may change the real user id to the effective uid + * or vice versa. (BSD-style) + * + * When you set the effective uid, it sets the saved uid too. This + * makes it possible for a setuid program to completely drop its privileges, + * which is often a useful assertion to make when you are doing a security + * audit over a program. + * + * The general idea is that a program which uses just setreuid() will be + * 100% compatible with BSD. A program which uses just setuid() will be + * 100% compatible with POSIX w/ Saved ID's. + */ +int sys_setreuid(int ruid, int euid) +{ + int old_ruid = current->uid; + + if (ruid>0) { + if ((current->euid==ruid) || + (old_ruid == ruid) || + suser()) + current->uid = ruid; + else + return(-EPERM); + } + if (euid>0) { + if ((old_ruid == euid) || + (current->euid == euid) || + suser()) { + current->euid = euid; + current->suid = euid; + } else { + current->uid = old_ruid; + return(-EPERM); + } + } + return 0; +} + +/* + * setuid() is implemeneted like SysV w/ SAVED_IDS + * + * Note that SAVED_ID's is deficient in that a setuid root program + * like sendmail, for example, cannot set its uid to be a normal + * user and then switch back, because if you're root, setuid() sets + * the saved uid too. If you don't like this, blame the bright people + * in the POSIX commmittee and/or USG. Note that the BSD-style setreuid() + * will allow a root program to temporarily drop privileges and be able to + * regain them by swapping the real and effective uid. + */ +int sys_setuid(int uid) +{ + if (suser()) + current->uid = current->euid = current->suid = uid; + else if ((uid == current->uid) || (uid == current->suid)) + current->euid = uid; + else + return -EPERM; + return(0); +} + +int sys_stime(long * tptr) +{ + if (!suser()) + return -EPERM; + startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ; + jiffies_offset = 0; + return 0; +} + +int sys_times(struct tms * tbuf) +{ + if (tbuf) { + verify_area(tbuf,sizeof *tbuf); + put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime); + put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime); + put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime); + put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime); + } + return jiffies; +} + +int sys_brk(unsigned long end_data_seg) +{ + if (end_data_seg >= current->end_code && + end_data_seg < current->start_stack - 16384) + current->brk = end_data_seg; + return current->brk; +} + +/* + * This needs some heave checking ... + * I just haven't get the stomach for it. I also don't fully + * understand sessions/pgrp etc. Let somebody who does explain it. + * + * OK, I think I have the protection semantics right.... this is really + * only important on a multi-user system anyway, to make sure one user + * can't send a signal to a process owned by another. -TYT, 12/12/91 + */ +int sys_setpgid(int pid, int pgid) +{ + int i; + + if (!pid) + pid = current->pid; + if (!pgid) + pgid = current->pid; + if (pgid < 0) + return -EINVAL; + for (i=0 ; ipid == pid) && + ((task[i]->p_pptr == current) || + (task[i] == current))) { + if (task[i]->leader) + return -EPERM; + if ((task[i]->session != current->session) || + ((pgid != pid) && + (session_of_pgrp(pgid) != current->session))) + return -EPERM; + task[i]->pgrp = pgid; + return 0; + } + return -ESRCH; +} + +int sys_getpgrp(void) +{ + return current->pgrp; +} + +int sys_setsid(void) +{ + if (current->leader && !suser()) + return -EPERM; + current->leader = 1; + current->session = current->pgrp = current->pid; + current->tty = -1; + return current->pgrp; +} + +/* + * Supplementary group ID's + */ +int sys_getgroups(int gidsetsize, gid_t *grouplist) +{ + int i; + + if (gidsetsize) { + verify_area(grouplist, sizeof(gid_t) * gidsetsize); + } + + for (i = 0; (i < NGROUPS) && (current->groups[i] != NOGROUP); + i++, grouplist++) { + if (gidsetsize) { + if (i >= gidsetsize) { + return -EINVAL; + } + put_fs_word(current->groups[i], (short *) grouplist); + } + } + return(i); +} + +int sys_setgroups(int gidsetsize, gid_t *grouplist) +{ + int i; + + if (!suser()) { + return -EPERM; + } + if (gidsetsize > NGROUPS) { + return -EINVAL; + } + for (i = 0; i < gidsetsize; i++, grouplist++) { + current->groups[i] = get_fs_word((unsigned short *) grouplist); + } + if (i < NGROUPS) { + current->groups[i] = NOGROUP; + } + return 0; +} + +/** + * 检查当前进程是否在指定的用户组grp中 + * @param[in] grp 指定的用户组号 + * @return 是则返回1,否则返回0 + */ +int in_group_p(gid_t grp) +{ + int i; + + if (grp == current->egid) { + return 1; + } + + for (i = 0; i < NGROUPS; i++) { + if (current->groups[i] == NOGROUP) { + break; + } + if (current->groups[i] == grp) { + return 1; + } + } + return 0; +} + +static struct utsname thisname = { + UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, UTS_MACHINE +}; + +int sys_uname(struct utsname * name) +{ + int i; + + if (!name) return -ERROR; + verify_area(name,sizeof *name); + for(i=0;i MAXHOSTNAMELEN) + return -EINVAL; + for (i=0; i < len; i++) { + if ((thisname.nodename[i] = get_fs_byte(name+i)) == 0) + break; + } + if (thisname.nodename[i]) { + thisname.nodename[i>MAXHOSTNAMELEN ? MAXHOSTNAMELEN : i] = 0; + } + return 0; +} + +int sys_getrlimit(int resource, struct rlimit *rlim) +{ + if (resource >= RLIM_NLIMITS) + return -EINVAL; + verify_area(rlim,sizeof *rlim); + put_fs_long(current->rlim[resource].rlim_cur, + (unsigned long *) rlim); + put_fs_long(current->rlim[resource].rlim_max, + ((unsigned long *) rlim)+1); + return 0; +} + +int sys_setrlimit(int resource, struct rlimit *rlim) +{ + struct rlimit new, *old; + + if (resource >= RLIM_NLIMITS) + return -EINVAL; + old = current->rlim + resource; + new.rlim_cur = get_fs_long((unsigned long *) rlim); + new.rlim_max = get_fs_long(((unsigned long *) rlim)+1); + if (((new.rlim_cur > old->rlim_max) || + (new.rlim_max > old->rlim_max)) && + !suser()) + return -EPERM; + *old = new; + return 0; +} + +/* + * It would make sense to put struct rusuage in the task_struct, + * except that would make the task_struct be *really big*. After + * task_struct gets moved into malloc'ed memory, it would + * make sense to do this. It will make moving the rest of the information + * a lot simpler! (Which we're not doing right now because we're not + * measuring them yet). + */ +int sys_getrusage(int who, struct rusage *ru) +{ + struct rusage r; + unsigned long *lp, *lpend, *dest; + + if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN) + return -EINVAL; + verify_area(ru, sizeof *ru); + memset((char *) &r, 0, sizeof(r)); + if (who == RUSAGE_SELF) { + r.ru_utime.tv_sec = CT_TO_SECS(current->utime); + r.ru_utime.tv_usec = CT_TO_USECS(current->utime); + r.ru_stime.tv_sec = CT_TO_SECS(current->stime); + r.ru_stime.tv_usec = CT_TO_USECS(current->stime); + } else { + r.ru_utime.tv_sec = CT_TO_SECS(current->cutime); + r.ru_utime.tv_usec = CT_TO_USECS(current->cutime); + r.ru_stime.tv_sec = CT_TO_SECS(current->cstime); + r.ru_stime.tv_usec = CT_TO_USECS(current->cstime); + } + lp = (unsigned long *) &r; + lpend = (unsigned long *) (&r+1); + dest = (unsigned long *) ru; + for (; lp < lpend; lp++, dest++) + put_fs_long(*lp, dest); + return(0); +} + +int sys_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + if (tv) { + verify_area(tv, sizeof *tv); + put_fs_long(startup_time + CT_TO_SECS(jiffies+jiffies_offset), + (unsigned long *) tv); + put_fs_long(CT_TO_USECS(jiffies+jiffies_offset), + ((unsigned long *) tv)+1); + } + if (tz) { + verify_area(tz, sizeof *tz); + put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz); + put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1); + } + return 0; +} + +/* + * The first time we set the timezone, we will warp the clock so that + * it is ticking GMT time instead of local time. Presumably, + * if someone is setting the timezone then we are running in an + * environment where the programs understand about timezones. + * This should be done at boot time in the /etc/rc script, as + * soon as possible, so that the clock can be set right. Otherwise, + * various programs will get confused when the clock gets warped. + */ +int sys_settimeofday(struct timeval *tv, struct timezone *tz) +{ + static int firsttime = 1; + void adjust_clock(); + + if (!suser()) + return -EPERM; + if (tz) { + sys_tz.tz_minuteswest = get_fs_long((unsigned long *) tz); + sys_tz.tz_dsttime = get_fs_long(((unsigned long *) tz)+1); + if (firsttime) { + firsttime = 0; + if (!tv) + adjust_clock(); + } + } + if (tv) { + int sec, usec; + + sec = get_fs_long((unsigned long *)tv); + usec = get_fs_long(((unsigned long *)tv)+1); + + startup_time = sec - jiffies/HZ; + jiffies_offset = usec * HZ / 1000000 - jiffies%HZ; + } + return 0; +} + +/* + * Adjust the time obtained from the CMOS to be GMT time instead of + * local time. + * + * This is ugly, but preferable to the alternatives. Otherwise we + * would either need to write a program to do it in /etc/rc (and risk + * confusion if the program gets run more than once; it would also be + * hard to make the program warp the clock precisely n hours) or + * compile in the timezone information into the kernel. Bad, bad.... + * + * XXX Currently does not adjust for daylight savings time. May not + * need to do anything, depending on how smart (dumb?) the BIOS + * is. Blast it all.... the best thing to do not depend on the CMOS + * clock at all, but get the time via NTP or timed if you're on a + * network.... - TYT, 1/1/92 + */ +void adjust_clock() +{ + startup_time += sys_tz.tz_minuteswest*60; +} + +int sys_umask(int mask) +{ + int old = current->umask; + + current->umask = mask & 0777; + return (old); +} + diff --git a/linux-0.12/kernel/sys_call.s b/linux-0.12/kernel/sys_call.s new file mode 100644 index 0000000..7c31c1e --- /dev/null +++ b/linux-0.12/kernel/sys_call.s @@ -0,0 +1,325 @@ +/* + * linux/kernel/system_call.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * system_call.s contains the system-call low-level handling routines. + * This also contains the timer-interrupt handler, as some of the code is + * the same. The hd- and flopppy-interrupts are also here. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. Ordinary interrupts + * don't handle signal-recognition, as that would clutter them up totally + * unnecessarily. + * + * Stack layout in 'ret_from_system_call': + * + * 0(%esp) - %eax + * 4(%esp) - %ebx + * 8(%esp) - %ecx + * C(%esp) - %edx + * 10(%esp) - original %eax (-1 if not system call) + * 14(%esp) - %fs + * 18(%esp) - %es + * 1C(%esp) - %ds + * 20(%esp) - %eip + * 24(%esp) - %cs + * 28(%esp) - %eflags + * 2C(%esp) - %oldesp + * 30(%esp) - %oldss + */ + +/** + * system_call.s文件包含系统调用(system-call)底层处理子程序。由于有些代码比较类似, + * 所以同时也包括时钟中断处理(timer-interrupt)句柄。硬盘和软盘的中断处理程序也在这里。 + * + * 注意:这段代码处理信号(signal)识别,在每次时钟中断和系统调用之后都会进行识别。一般 + * 中断过程并不处理信号识别,因为会给系统造成混乱。 + * + * 从系统调用返回('ret_from_system_call')时堆栈的内容: + * + * 0(%esp) - %eax + * 4(%esp) - %ebx + * 8(%esp) - %ecx + * C(%esp) - %edx + * 10(%esp) - original %eax (-1 if not system call) + * 14(%esp) - %fs + * 18(%esp) - %es + * 1C(%esp) - %ds + * 20(%esp) - %eip + * 24(%esp) - %cs + * 28(%esp) - %eflags + * 2C(%esp) - %oldesp + * 30(%esp) - %oldss + */ + + +SIG_CHLD = 17 # 定义SIG_CHLD信号(子进程停止或结束 + +EAX = 0x00 # 各寄存器的偏移 +EBX = 0x04 +ECX = 0x08 +EDX = 0x0C +ORIG_EAX = 0x10 +FS = 0x14 +ES = 0x18 +DS = 0x1C +EIP = 0x20 +CS = 0x24 +EFLAGS = 0x28 +OLDESP = 0x2C +OLDSS = 0x30 + +state = 0 # these are offsets into the task-struct. +counter = 4 +priority = 8 +signal = 12 +sigaction = 16 # MUST be 16 (=len of sigaction) +blocked = (33*16) + +# offsets within sigaction +sa_handler = 0 +sa_mask = 4 +sa_flags = 8 +sa_restorer = 12 + +nr_system_calls = 82 + +ENOSYS = 38 + +/* + * Ok, I get parallel printer interrupts while using the floppy for some + * strange reason. Urgel. Now I just ignore them. + */ +.globl system_call, sys_fork, timer_interrupt, sys_execve +.globl hd_interrupt, floppy_interrupt, parallel_interrupt +.globl device_not_available, coprocessor_error + +.align 4 +bad_sys_call: + pushl $-ENOSYS + jmp ret_from_sys_call +.align 4 +reschedule: + pushl $ret_from_sys_call + jmp schedule +.align 4 +system_call: + push %ds + push %es + push %fs + pushl %eax # save the orig_eax + pushl %edx + pushl %ecx # push %ebx,%ecx,%edx as parameters + pushl %ebx # to the system call + movl $0x10,%edx # set up ds,es to kernel space + mov %dx,%ds + mov %dx,%es + movl $0x17,%edx # fs points to local data space + mov %dx,%fs + cmpl NR_syscalls,%eax + jae bad_sys_call + call *sys_call_table(,%eax,4) + pushl %eax +2: + movl current,%eax + cmpl $0,state(%eax) # state + jne reschedule + cmpl $0,counter(%eax) # counter + je reschedule +ret_from_sys_call: + movl current,%eax + cmpl task,%eax # task[0] cannot have signals + je 3f + cmpw $0x0f,CS(%esp) # was old code segment supervisor ? + jne 3f + cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? + jne 3f + movl signal(%eax),%ebx + movl blocked(%eax),%ecx + notl %ecx + andl %ebx,%ecx + bsfl %ecx,%ecx + je 3f + btrl %ecx,%ebx + movl %ebx,signal(%eax) + incl %ecx + pushl %ecx + call do_signal + popl %ecx + testl %eax, %eax + jne 2b # see if we need to switch tasks, or do more signals +3: popl %eax + popl %ebx + popl %ecx + popl %edx + addl $4, %esp # skip orig_eax + pop %fs + pop %es + pop %ds + iret + +.align 4 +coprocessor_error: + push %ds + push %es + push %fs + pushl $-1 # fill in -1 for orig_eax + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + pushl $ret_from_sys_call + jmp math_error + +.align 4 +device_not_available: + push %ds + push %es + push %fs + pushl $-1 # fill in -1 for orig_eax + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + pushl $ret_from_sys_call + clts # clear TS so that we can use math + movl %cr0,%eax + testl $0x4,%eax # EM (math emulation bit) + je math_state_restore + pushl %ebp + pushl %esi + pushl %edi + pushl $0 # temporary storage for ORIG_EIP + call math_emulate + addl $4,%esp + popl %edi + popl %esi + popl %ebp + ret + +.align 4 +timer_interrupt: + push %ds # save ds,es and put kernel data space + push %es # into them. %fs is used by _system_call + push %fs + pushl $-1 # fill in -1 for orig_eax + pushl %edx # we save %eax,%ecx,%edx as gcc doesn't + pushl %ecx # save those across function calls. %ebx + pushl %ebx # is saved as we use that in ret_sys_call + pushl %eax + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + incl jiffies + movb $0x20,%al # EOI to interrupt controller #1 + outb %al,$0x20 + movl CS(%esp),%eax + andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor) + pushl %eax + call do_timer # 'do_timer(long CPL)' does everything from + addl $4,%esp # task switching to accounting ... + jmp ret_from_sys_call + +.align 4 +sys_execve: + lea EIP(%esp),%eax + pushl %eax + call do_execve + addl $4,%esp + ret + +.align 4 +sys_fork: + call find_empty_process + testl %eax,%eax + js 1f + push %gs + pushl %esi + pushl %edi + pushl %ebp + pushl %eax + call copy_process + addl $20,%esp +1: ret + +hd_interrupt: + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + movb $0x20,%al + outb %al,$0xA0 # EOI to interrupt controller #1 + jmp 1f # give port chance to breathe +1: jmp 1f +1: xorl %edx,%edx + movl %edx,hd_timeout + xchgl do_hd,%edx + testl %edx,%edx + jne 1f + movl $unexpected_hd_interrupt,%edx +1: outb %al,$0x20 + call *%edx # "interesting" way of handling intr. + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret + +floppy_interrupt: + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + movb $0x20,%al + outb %al,$0x20 # EOI to interrupt controller #1 + xorl %eax,%eax + xchgl do_floppy,%eax + testl %eax,%eax + jne 1f + movl $unexpected_floppy_interrupt,%eax +1: call *%eax # "interesting" way of handling intr. + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret + +parallel_interrupt: + pushl %eax + movb $0x20,%al + outb %al,$0x20 + popl %eax + iret diff --git a/linux-0.12/kernel/traps.c b/linux-0.12/kernel/traps.c new file mode 100755 index 0000000..38ddfb6 --- /dev/null +++ b/linux-0.12/kernel/traps.c @@ -0,0 +1,243 @@ +/* + * linux/kernel/traps.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. Currently mostly a debugging-aid, will be extended + * to mainly kill the offending process (probably by giving it a signal, + * but possibly by killing it outright if necessary). + */ +/* + * 在程序asm.s中保存了一些状态后,本程序用来处理硬件陷阱和故障。目前主要用于调试目的,以后将扩 + * 展用来杀死遭损坏的进程(主是通过发送一个信号,但如果必要也会直接杀死)。 + */ +#include + +#include +#include +#include +#include +#include +#include + +/** + * 取seg中地址addr处的一个字节 + * @param[in] seg 段选择符 + * @param[in] addr 段内指定地址 + */ +#define get_seg_byte(seg, addr) ({ \ + register char __res; \ + __asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \ + :"=a" (__res):"0" (seg),"m" (*(addr))); \ + __res;}) + +/* 取seg中地址addr处的一个长字(4字节) */ +#define get_seg_long(seg,addr) ({ \ + register unsigned long __res; \ + __asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \ + :"=a" (__res):"0" (seg),"m" (*(addr))); \ + __res;}) + +/* 取fs段寄存器的值(选择符) */ +#define _fs() ({ \ + register unsigned short __res; \ + __asm__("mov %%fs,%%ax":"=a" (__res):); \ + __res;}) + +void page_exception(void); /* 页异常,实际是page_fault(mm/page.s) */ + +void divide_error(void); // int0(kernel/asm.s) +void debug(void); // int1(kernel/asm.s) +void nmi(void); // int2(kernel/asm.s) +void int3(void); // int3(kernel/asm.s) +void overflow(void); // int4(kernel/asm.s) +void bounds(void); // int5(kernel/asm.s) +void invalid_op(void); // int6(kernel/asm.s) +void device_not_available(void); // int7(kernel/sys_call.s) +void double_fault(void); // int8(kernel/asm.s) +void coprocessor_segment_overrun(void); // int9(kernel/asm.s) +void invalid_TSS(void); // int10(kernel/asm.s) +void segment_not_present(void); // int11(kernel/asm.s) +void stack_segment(void); // int12(kernel/asm.s) +void general_protection(void); // int13(kernel/asm.s) +void page_fault(void); // int14(mm/page.s) +void coprocessor_error(void); // int16(kernel/sys_call.s) +void reserved(void); // int15(kernel/asm.s) +void parallel_interrupt(void); // int39(kernel/sys_call.s) +void irq13(void); // int45协处理器中断处理(kernel/asm.s) +void alignment_check(void); // int46(kernel/asm.s) + +/* 该子程序用来打印出错中断的名称,出错号,调用程序的EIP,EFLAGS,ESP,fs段寄存器值,段的基址,段的长度,进程号pid, + 任务号,10字节指令码。如果堆栈在用户数据段,则还打印16字节堆栈内容。这些信息可用于程序调试 */ +static void die(char * str, long esp_ptr, long nr) +{ + long * esp = (long *) esp_ptr; + int i; + + printk("%s: %04x\n\r", str, nr & 0xffff); + // 下行打印语句显示当前调用进程的CS:EIP,EFLAGS和SS:ESP的值. + // (1) EIP:\t%04x:%p\n -- esp[1]是段选择符(cs),esp[0]是eip + // (2) EFLAGS:\t%p -- esp[2]是eflags + // (2) ESP:\t%04x:%p\n -- esp[4]是原ss,esp[3]是原esp + printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n", + esp[1], esp[0], esp[2], esp[4], esp[3]); + printk("fs: %04x\n", _fs()); + printk("base: %p, limit: %p\n", get_base(current->ldt[1]), get_limit(0x17)); + if (esp[4] == 0x17) { // 或原ss值为0x17(用户栈),则还打印出用户栈的4个长字值(16字节). + printk("Stack: "); + for (i = 0; i < 4; i++) + printk("%p ", get_seg_long(0x17, i + (long *)esp[3])); + printk("\n"); + } + str(i); // 取当前运行任务的任务号(include/linux/sched.h). + printk("Pid: %d, process nr: %d\n\r", current->pid, 0xffff & i); + // 进程号,任务号. + for(i = 0; i < 10; i++) + printk("%02x ", 0xff & get_seg_byte(esp[1], (i+(char *)esp[0]))); + printk("\n\r"); + do_exit(11); /* play segment exception */ +} + +/* 以下以do_开头的函数是asm.s中对应中断处理程序调用的C函数 */ +void do_double_fault(long esp, long error_code) +{ + die("double fault", esp, error_code); +} + +void do_general_protection(long esp, long error_code) +{ + die("general protection", esp, error_code); +} + +void do_alignment_check(long esp, long error_code) +{ + die("alignment check", esp, error_code); +} + +void do_divide_error(long esp, long error_code) +{ + die("divide error", esp, error_code); +} + +/* 参数是进入中断后被顺序压入堆栈的寄存器值 */ +void do_int3(long * esp, long error_code, + long fs, long es, long ds, + long ebp, long esi, long edi, + long edx, long ecx, long ebx, long eax) +{ + int tr; + + __asm__("str %%ax":"=a" (tr):"0" (0)); /* 取任务寄存器值->tr */ + printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r", + eax, ebx, ecx, edx); + printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r", + esi, edi, ebp, (long) esp); + printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r", + ds, es, fs, tr); + printk("EIP: %8x CS: %4x EFLAGS: %8x\n\r", esp[0], esp[1], esp[2]); +} + +void do_nmi(long esp, long error_code) +{ + die("nmi", esp, error_code); +} + +void do_debug(long esp, long error_code) +{ + die("debug", esp, error_code); +} + +void do_overflow(long esp, long error_code) +{ + die("overflow", esp, error_code); +} + +void do_bounds(long esp, long error_code) +{ + die("bounds", esp, error_code); +} + +void do_invalid_op(long esp, long error_code) +{ + die("invalid operand", esp, error_code); +} + +void do_device_not_available(long esp, long error_code) +{ + die("device not available", esp, error_code); +} + +void do_coprocessor_segment_overrun(long esp, long error_code) +{ + die("coprocessor segment overrun", esp, error_code); +} + +void do_invalid_TSS(long esp, long error_code) +{ + die("invalid TSS", esp, error_code); +} + +void do_segment_not_present(long esp, long error_code) +{ + die("segment not present", esp, error_code); +} + +void do_stack_segment(long esp, long error_code) +{ + die("stack segment", esp, error_code); +} + +void do_coprocessor_error(long esp, long error_code) +{ + if (last_task_used_math != current) { + return; + } + die("coprocessor error", esp, error_code); +} + +void do_reserved(long esp, long error_code) +{ + die("reserved (15,17-47) error", esp, error_code); +} + +/** + * 异常(陷阱)中断程序初始化 + * 设置它们的中断调用门(中断向量)。set_trap_gate()与set_system_gate()都使用了中断描述符表IDT中 + * 的陷阱门(Trap Gate),它们之间的主要区别在于前者设置的特权级为0,后者是3。因此断点陷阱中断int3, + * 溢出中断overflow和边界出错中断bounds可以由任何程序调用。 + */ +void trap_init(void) +{ + int i; + + set_trap_gate(0, ÷_error); /* 设置除操作出错的中断向量值 */ + set_trap_gate(1, &debug); + set_trap_gate(2, &nmi); + set_system_gate(3, &int3); /* int3-5 can be called from all */ + set_system_gate(4, &overflow); + set_system_gate(5, &bounds); + set_trap_gate(6, &invalid_op); + set_trap_gate(7, &device_not_available); /* 函数未实现 */ + set_trap_gate(8, &double_fault); + set_trap_gate(9, &coprocessor_segment_overrun); + set_trap_gate(10, &invalid_TSS); + set_trap_gate(11, &segment_not_present); + set_trap_gate(12, &stack_segment); + set_trap_gate(13, &general_protection); + set_trap_gate(14, &page_fault); + set_trap_gate(15, &reserved); + set_trap_gate(16, &coprocessor_error); /* 函数未实现 */ + set_trap_gate(17, &alignment_check); + /* 下面把int17-47的陷阱门先均设置为reserved,以后各硬件初始化时会重新设置自己的陷阱门 */ + for (i = 18; i < 48; i++) { + set_trap_gate(i, &reserved); + } + /* 设置协处理器中断0x2d(45)陷阱门描述符,并允许其产生中断请求。设置并行口中断描述符 */ + set_trap_gate(45, &irq13); + outb_p(inb_p(0x21)&0xfb, 0x21); /* 允许8259A主芯片的IRQ2中断请求(连接从芯片) */ + outb(inb_p(0xA1)&0xdf, 0xA1); /* 允许8259A从芯片的IRQ13中断请求(协处理器中断) */ + set_trap_gate(39, ¶llel_interrupt); /* 设置并行口1的中断0x27陷阱门描述符 */ +} diff --git a/linux-0.12/kernel/vsprintf.c b/linux-0.12/kernel/vsprintf.c new file mode 100755 index 0000000..a9bbc94 --- /dev/null +++ b/linux-0.12/kernel/vsprintf.c @@ -0,0 +1,295 @@ +/* + * linux/kernel/vsprintf.c + * + * (C) 1991 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include +#include + +/* we use this so that we can do without the ctype library */ +/* 我们使用下面的定义,这样我们就可以不使用ctype库了 */ +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +/** + * 数字字符串转换成整数 + * @param[in] **s 指向数字字符串指针的指针 + * @retval 结果数值(另外,指针将前移) + */ +static int skip_atoi(const char **s) +{ + int i=0; + + while (is_digit(**s)) { + i = i*10 + *((*s)++) - '0'; + } + return i; +} + +/* 转换类型的各种符号常数 */ +#define ZEROPAD 1 /* pad with zero */ /* 填充零 */ +#define SIGN 2 /* unsigned/signed long */ /* 无符号/符号长整数 */ +#define PLUS 4 /* show plus */ /* 显示加 */ +#define SPACE 8 /* space if plus */ /* 如是加号,则置空格 */ +#define LEFT 16 /* left justified */ /* 左对齐 */ +#define SPECIAL 32 /* 0x */ +#define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */ /* 使用小写字母 */ + +/** + * 除操作 + * @param[in/out] n 被除数 + * @param[in] base 除数 + * @retval 函数返回余数(同时,n为商) */ +#define do_div(n, base) ({ \ + int __res; \ + __asm__("divl %4" \ + :"=a" (n),"=d" (__res) \ + :"0" (n),"1" (0),"r" (base)); \ + __res; }) + + +/** + * 将整数转换为指定进制的字符串 + * @param[out] *str 转换后的字符串 + * @param[in] num 整数 + * @param[in] base 进制 + * @param[in] size 字符串长度 + * @param[in] precision 数字长度(精度) + * @param[in] type 类型选项 + * @retval 数字转换成字符串后指向该字符串末端后面的指针 + */ +static char * number(char * str, int num, int base, int size, int precision + ,int type) +{ + char c, sign, tmp[36]; + const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + if (type & SMALL) { /* 小写字母集 */ + digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + } + if (type & LEFT) { /* 左调整(靠左边界),则屏蔽类型中的填零标志 */ + type &= ~ZEROPAD; + } + if (base < 2 || base > 36) { /* 本程序只能处理基数在2-36之间的数 */ + return 0; + } + c = (type & ZEROPAD) ? '0' : ' '; + if (type&SIGN && num<0) { + sign = '-'; + num = -num; + } else { + sign = (type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0); + } + if (sign){ + size--; + } + if (type&SPECIAL) { + if (base==16) { + size -= 2; + } else if (base==8) { + size--; + } + } + i = 0; + if (num == 0) { + tmp[i++]='0'; + } else { + while (num != 0) { + tmp[i++] = digits[do_div(num, base)]; + } + } + if (i > precision){ + precision = i; + } + size -= precision; + if (!(type & (ZEROPAD+LEFT))) { + while(size-->0) { + *str++ = ' '; + } + } + if (sign) { + *str++ = sign; + } + if (type & SPECIAL) { + if (base==8) { + *str++ = '0'; + } else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type&LEFT)) { + while(size-->0) { + *str++ = c; + } + } + while(i0) { + *str++ = tmp[i]; + } + while(size-->0) { + *str++ = ' '; + } + return str; +} + + +/** + * 送格式化输出到字符串buf中 + * @param[out] buf 输出字符串缓冲区 + * @param[in] fmt 格式字符串 + * @param[in] args 个数变化的值 + * @return 返回字符串buf的长度 + */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + int i; + char * str; + char *s; + int *ip; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + break; + + case 's': + s = va_arg(args, char *); + len = strlen(s); + if (precision < 0) + precision = len; + else if (len > precision) + len = precision; + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + break; + + case 'o': + str = number(str, va_arg(args, unsigned long), 8, + field_width, precision, flags); + break; + + case 'p': + if (field_width == -1) { + field_width = 8; + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + break; + + case 'x': + flags |= SMALL; + case 'X': + str = number(str, va_arg(args, unsigned long), 16, + field_width, precision, flags); + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + str = number(str, va_arg(args, unsigned long), 10, + field_width, precision, flags); + break; + + case 'n': + ip = va_arg(args, int *); + *ip = (str - buf); + break; + + default: + if (*fmt != '%') { + *str++ = '%'; + } + if (*fmt) { + *str++ = *fmt; + } else { + --fmt; + } + break; + } + } + *str = '\0'; + return str-buf; +} diff --git a/linux-0.12/lib/Makefile b/linux-0.12/lib/Makefile new file mode 100644 index 0000000..c389052 --- /dev/null +++ b/linux-0.12/lib/Makefile @@ -0,0 +1,71 @@ +# +# Makefile for some libs needed in the kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +include ../Rules.make + +LDFLAGS = -x +CFLAGS = -g -Wall -fstrength-reduce -fomit-frame-pointer \ + -finline-functions -nostdinc -fno-builtin -I../include +CPP += -E -I../include + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< + +OBJS = ctype.o _exit.o open.o close.o errno.o write.o dup.o setsid.o \ + execve.o wait.o string.o malloc.o log_print.o + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + @sync + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ + $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +_exit.s _exit.o : _exit.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +close.s close.o : close.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +ctype.s ctype.o : ctype.c ../include/ctype.h +dup.s dup.o : dup.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +errno.s errno.o : errno.c +execve.s execve.o : execve.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +malloc.s malloc.o : malloc.c ../include/linux/kernel.h ../include/linux/mm.h \ + ../include/asm/system.h +open.s open.o : open.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h ../include/stdarg.h +setsid.s setsid.o : setsid.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +string.s string.o : string.c ../include/string.h +wait.s wait.o : wait.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h ../include/sys/wait.h +write.s write.o : write.c ../include/unistd.h ../include/sys/stat.h \ + ../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \ + ../include/utime.h +log_print.s log_print.o : log_print.c ../include/stdarg.h ../include/linux/log_print.h diff --git a/linux-0.12/lib/_exit.c b/linux-0.12/lib/_exit.c new file mode 100755 index 0000000..73b7a2e --- /dev/null +++ b/linux-0.12/lib/_exit.c @@ -0,0 +1,18 @@ +/* + * linux/lib/_exit.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +/** + * 内核使用的程序(退出)终止函数 + * @param[in] exit_code 退出码 + * @retval void + */ +volatile void _exit(int exit_code) +{ + __asm__("int $0x80"::"a" (__NR_exit), "b" (exit_code)); +} diff --git a/linux-0.12/lib/close.c b/linux-0.12/lib/close.c new file mode 100755 index 0000000..332036f --- /dev/null +++ b/linux-0.12/lib/close.c @@ -0,0 +1,15 @@ +/* + * linux/lib/close.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +/** + * 关闭文件 + * @param[in] fd 要关闭的文件描述符 + * @retval + */ +_syscall1(int, close, int, fd) diff --git a/linux-0.12/lib/ctype.c b/linux-0.12/lib/ctype.c new file mode 100755 index 0000000..1844a49 --- /dev/null +++ b/linux-0.12/lib/ctype.c @@ -0,0 +1,38 @@ +/* + * linux/lib/ctype.c + * + * (C) 1991 Linus Torvalds + */ + +#include + +char _ctmp; /* 一个临时字符变量,供ctype.h文件中转换字符宏函数使用。*/ + +/* 字符特性数组(表),定义了各个字符对应的属性,这些属性类型(如_C)等在ctype.h中定义,用于判 + 断字符是控制字符(_C),大写字符(_U),小写字符(_L)等所属类型 */ +unsigned char _ctype[] = {0x00, /* EOF */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 160-175 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 176-191 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 192-207 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 208-223 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 224-239 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; /* 240-255 */ + diff --git a/linux-0.12/lib/dup.c b/linux-0.12/lib/dup.c new file mode 100755 index 0000000..446cb97 --- /dev/null +++ b/linux-0.12/lib/dup.c @@ -0,0 +1,15 @@ +/* + * linux/lib/dup.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +/** + * 复制文件描述符 + * @param[in] fd 需要复制的文件描述符 + * @retval 成功返回新文件句柄,失败返回出错码 + */ +_syscall1(int, dup, int, fd) diff --git a/linux-0.12/lib/errno.c b/linux-0.12/lib/errno.c new file mode 100755 index 0000000..00ea497 --- /dev/null +++ b/linux-0.12/lib/errno.c @@ -0,0 +1,7 @@ +/* + * linux/lib/errno.c + * + * (C) 1991 Linus Torvalds + */ +/* 该文件仅定义了一个出错号变量errno,用于在函数调用失败时存放出错号 */ +int errno; diff --git a/linux-0.12/lib/execve.c b/linux-0.12/lib/execve.c new file mode 100755 index 0000000..9f7a732 --- /dev/null +++ b/linux-0.12/lib/execve.c @@ -0,0 +1,18 @@ +/* + * linux/lib/execve.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +/** + * 加载并执行子进程(其他程序) + * int execve(const char *file, char **argv, char **envp) + * @param[in] file 被执行程序文件名 + * @param[in] argv 命令行参数指针数组 + * @param[in] envp 环境变量指针数组 + * @retval 成功不返回;失败设置出错号,并返回-1 + */ +_syscall3(int, execve, const char *, file, char **, argv, char **, envp) diff --git a/linux-0.12/lib/log_print.c b/linux-0.12/lib/log_print.c new file mode 100644 index 0000000..20e0e1b --- /dev/null +++ b/linux-0.12/lib/log_print.c @@ -0,0 +1,23 @@ +/** + * Linux0.12 调试打印 + */ +#include + +#include +#include + +extern int vsprintf(char * buf, const char * fmt, va_list args); + +static char log_buf[1024]; + +/* 打印等级功能目前未使用 */ +void log_print(unsigned short log_level, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsprintf(log_buf, fmt, args); + va_end(args); + + console_print(log_buf); +} diff --git a/linux-0.12/lib/malloc.c b/linux-0.12/lib/malloc.c new file mode 100755 index 0000000..ca6f615 --- /dev/null +++ b/linux-0.12/lib/malloc.c @@ -0,0 +1,322 @@ +/* + * malloc.c --- a general purpose kernel memory allocator for Linux. + * + * Written by Theodore Ts'o (tytso@mit.edu), 11/29/91 + * + * This routine is written to be as fast as possible, so that it + * can be called from the interrupt level. + * + * Limitations: maximum size of memory we can allocate using this routine + * is 4k, the size of a page in Linux. + * + * The general game plan is that each page (called a bucket) will only hold + * objects of a given size. When all of the object on a page are released, + * the page can be returned to the general free pool. When malloc() is + * called, it looks for the smallest bucket size which will fulfill its + * request, and allocate a piece of memory from that bucket pool. + * + * Each bucket has as its control block a bucket descriptor which keeps + * track of how many objects are in use on that page, and the free list + * for that page. Like the buckets themselves, bucket descriptors are + * stored on pages requested from get_free_page(). However, unlike buckets, + * pages devoted to bucket descriptor pages are never released back to the + * system. Fortunately, a system should probably only need 1 or 2 bucket + * descriptor pages, since a page can hold 256 bucket descriptors (which + * corresponds to 1 megabyte worth of bucket pages.) If the kernel is using + * that much allocated memory, it's probably doing something wrong. :-) + * + * Note: malloc() and free() both call get_free_page() and free_page() + * in sections of code where interrupts are turned off, to allow + * malloc() and free() to be safely called from an interrupt routine. + * (We will probably need this functionality when networking code, + * particularily things like NFS, is added to Linux.) However, this + * presumes that get_free_page() and free_page() are interrupt-level + * safe, which they may not be once paging is added. If this is the + * case, we will need to modify malloc() to keep a few unused pages + * "pre-allocated" so that it can safely draw upon those pages if + * it is called from an interrupt routine. + * + * Another concern is that get_free_page() should not sleep; if it + * does, the code is carefully ordered so as to avoid any race + * conditions. The catch is that if malloc() is called re-entrantly, + * there is a chance that unecessary pages will be grabbed from the + * system. Except for the pages for the bucket descriptor page, the + * extra pages will eventually get released back to the system, though, + * so it isn't all that bad. + */ +/* + * malloc.c - Linux的通用内核内存分配函数。 + * + * 由Theodore Ts'o编制(tytso@mit.edu),11/29/91 + * + * 该函数被编写成尽可能地快,从而可以从中断层调用此函数。 + * + * 限制:使用该函数一次所能分配的最大内存是4KB,即Linux中内存页面的大小。 + * + * 编写该函数所遵循的一般规则是每页(被称为一个存储桶)仅分配所要容纳对象的大小。当一页上的 + * 所有对象都释放后,该页就可以返回通用空闲内存池。当malloc()被调用时,它会寻找满足要求的 + * 最小的存储桶,并从该存储桶中分配一块内存。 + * + * 每个存储桶都有一个作为其控制用的存储描述符,其中记录了页面上有多少个对象正被使用以及该页 + * 上空闲内存的列表。就像存储桶自身一样,存储桶描述符也是存储在使用get_free_page()申请到 + * 的页面上的,但是与存储桶不同的是,桶描述符所占用的页面将不再会释放给系统。幸运的是一个系 + * 统大约只需要1到2页的桶描述符页面,因为一个页面可以存放256个桶描述符(对应1MB内存的存储 + * 页面)。如果系统为桶描述符分配了许多内存,那么肯定系统什么地方出了问题。 + * + * 注意!malloc()和free()两者关闭了中断的代码部分都调用了get_free_page()和free_page() + * 函数,以使malloc()和free()可以安全地被从中断程序中调用(当网络代码,尤其是NFS等被加入 + * 到Linux中时就可能需要这种功能)。但前提是假设get_free_page()和free_page()是可以安全 + * 地在中断级程序中使用的,这在一旦加入了分页处理之后就可能不是安全的。如果真是这种情况,那 + * 么我们就需要修改malloc()来“预先分配”几页不用的内存,如果malloc()和free()被从中断程序 + * 中调用时就可以安全地使用这些页面。 + * + * 另外需要考虑到的是get_free_page()不应该睡眠:如果会睡眠的话,则为了防止任何竞争条件,代 + * 码需要仔细地安排顺序。关键在于如果malloc()是可以重入地被调用的话,那么就会存在不必要的页 + * 面被从系统中取走的机会。除了用于桶描述符的页面,这些额外的页面最终会释放给系统,所以并不像 + * 想象的那样不好。 + */ + +#include +#include +#include + +/* 存储桶描述符结构 */ +struct bucket_desc { /* 16 bytes */ + void *page; /* 该桶描述符对应的内存页面指针 */ + struct bucket_desc *next; /* 下一个描述符指针 */ + void *freeptr; /* 指向本桶中空闲内存位置的指针 */ + unsigned short refcnt; /* 引用计数 */ + unsigned short bucket_size; /* 本描述符对应存储桶的大小 */ +}; + +/* 存储桶描述符目录结构 */ +struct _bucket_dir { /* 8 bytes */ + int size; /* 该存储桶的大小(字节数) */ + struct bucket_desc *chain; /* 该存储桶目录项的桶描述符链表指针 */ +}; + +/* + * The following is the where we store a pointer to the first bucket + * descriptor for a given size. + * + * If it turns out that the Linux kernel allocates a lot of objects of a + * specific size, then we may want to add that specific size to this list, + * since that will allow the memory to be allocated more efficiently. + * However, since an entire page must be dedicated to each specific size + * on this list, some amount of temperance must be exercised here. + * + * Note that this list *must* be kept in order. + */ +/* + * 下面是我们存放第一个给定大小存储桶描述符指针的地方。 + * + * 如果Linux内核分配了许多指定大小的对象,那么我们就希望将该指定的大小加到该列表(链表) + * 中,因为这样可以使内存的分配更有效。但是,因为一页完整内存页面必须用于列表中指定大小的 + * 所有对象,所以需要做总数方面的测试操作。 + */ +/* 存储桶目录列表(数组)*/ +struct _bucket_dir bucket_dir[] = { + { 16, (struct bucket_desc *) 0}, /* 16B长度的内存块 */ + { 32, (struct bucket_desc *) 0}, /* 32B长度的内存块 */ + { 64, (struct bucket_desc *) 0}, /* 64B长度的内存块 */ + { 128, (struct bucket_desc *) 0}, /* 128B长度的内存块 */ + { 256, (struct bucket_desc *) 0}, /* 256B长度的内存块 */ + { 512, (struct bucket_desc *) 0}, /* 512B长度的内存块 */ + { 1024, (struct bucket_desc *) 0}, /* 1024B长度的内存块 */ + { 2048, (struct bucket_desc *) 0}, /* 2048B长度的内存块 */ + { 4096, (struct bucket_desc *) 0}, /* 4096B(1页)的内存块 */ + { 0, (struct bucket_desc *) 0}}; /* End of list marker */ + +/* + * This contains a linked list of free bucket descriptor blocks + */ +/* 下面是含有空闲桶描述符内存块的链表 */ +struct bucket_desc *free_bucket_desc = (struct bucket_desc *) 0; + +/* + * This routine initializes a bucket description page. + */ +/* 申请一页内存,存放桶描述符链表 */ +static inline void init_bucket_desc() +{ + struct bucket_desc *bdesc, *first; + int i; + /* 申请一页内存,用于存放桶描述符 */ + first = bdesc = (struct bucket_desc *) get_free_page(); + if (!bdesc) { + panic("Out of memory in init_bucket_desc()"); + } + /* 首先计算一页内存中可存放的桶描述符数量,然后对其建立单向链接指针 */ + for (i = PAGE_SIZE / sizeof(struct bucket_desc); i > 1; i--) { + bdesc->next = bdesc + 1; + bdesc++; + } + /* + * This is done last, to avoid race conditions in case + * get_free_page() sleeps and this routine gets called again.... + */ + /* + * 这是在最后处理的,目的是为了避免在get_free_page()睡眠时该子程序又被调用而引 + * 起的竞争条件。 + */ + /* 将空闲桶描述符指针free_bucket_desc加入链表中 */ + bdesc->next = free_bucket_desc; + free_bucket_desc = first; +} + +/** + * 分配动态内存函数 + * @note 为了不与用户程序使用的malloc()进行区分,0.98版以后就改名为kmalloc() + * @param[in] len 请求的内存块长度 + * @retval 成功返回指向被分配内在的指针,失败返回NULL +*/ +void *malloc(unsigned int len) +{ + struct _bucket_dir *bdir; + struct bucket_desc *bdesc; + void *retval; + + /* + * First we search the bucket_dir to find the right bucket change + * for this request. + */ + /* 首先我们搜索存储桶目录bucket_dir来寻找适合请求的桶大小。*/ + for (bdir = bucket_dir; bdir->size; bdir++) { + if (bdir->size >= len) { + break; + } + } + /* 搜索完整个目录都没有找到合适大小的目录项,则表明所请求的内存块太大,超出了该程序的分配限制(1个页面)*/ + if (!bdir->size) { + printk("malloc called with impossibly large argument (%d)\n", len); + panic("malloc: bad arg"); + } + /* + * Now we search for a bucket descriptor which has free space + */ + cli(); /* Avoid race conditions */ /* 为了避免出现竞争条件,首先关中断 */ + /* 在桶目录项对应的描述符链表查找具有空闲空间的桶描述符 */ + for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) { + if (bdesc->freeptr) { + break; + } + } + /* + * If we didn't find a bucket with free space, then we'll + * allocate a new one. + */ + /* 如果没有找到具有空闲空间的桶描述符,那么我们就要新建立一个该目录项的描述符 */ + if (!bdesc) { + char *cp; + int i; + + if (!free_bucket_desc) { + init_bucket_desc(); + } + /* 取出free_bucket_desc链表头的空闲桶描述符 */ + bdesc = free_bucket_desc; + free_bucket_desc = bdesc->next; + /* 初始化该新的桶描述符 */ + bdesc->refcnt = 0; + bdesc->bucket_size = bdir->size; + bdesc->page = bdesc->freeptr = (void *) (cp = (char *)get_free_page()); + if (!cp) + panic("Out of memory in kernel malloc()"); + /* Set up the chain of free objects */ + /* 在该页空闲内存中建立空闲对象链表 */ + /* 以该桶目录项指定的桶大小对该页内存进行划分,并使每个对象的开始4字节设置成指向下一对象的指针 */ + for (i = PAGE_SIZE / bdir->size; i > 1; i--) { + *((char **) cp) = cp + bdir->size; + cp += bdir->size; + } + *((char **) cp) = 0; + /* 将该描述符插入到描述符链表头处 */ + bdesc->next = bdir->chain; /* OK, link it in! */ + bdir->chain = bdesc; + } + /* 返回该描述符对应页面的当前空闲指针,然后调整该空闲指针指向下一个空闲对象 */ + retval = (void *) bdesc->freeptr; + bdesc->freeptr = *((void **) retval); /* 前4个字节为下一个空闲对象的指针 */ + bdesc->refcnt++; + + sti(); /* OK, we're safe again */ + /* OK,现在我们又安全了 */ + return(retval); +} + +/* + * Here is the free routine. If you know the size of the object that you + * are freeing, then free_s() will use that information to speed up the + * search for the bucket descriptor. + * + * We will #define a macro so that "free(x)" is becomes "free_s(x, 0)" + */ +/* + * 下面是释放子程序。如果你知道释放对象的大小,则free_s()将使用该信息加快搜寻对应桶描述符 + * 的速度。 + * + * 我们将定义一个宏,使得“free(x)”成为“free_s(x, 0)”。 + */ + +/** + * 释放存储桶对象 + * @param[in] obj 对应对象指针 + * @param[in] size 大小 + */ +void free_s(void *obj, int size) +{ + void *page; + struct _bucket_dir *bdir; + struct bucket_desc *bdesc, *prev; + + /* Calculate what page this object lives in */ + /* 计算该对象所在页面 */ + page = (void *) ((unsigned long) obj & 0xfffff000); + /* Now search the buckets looking for that page */ + /* 现在搜索存储桶目录项所链接的桶描述符,寻找该页面 */ + for (bdir = bucket_dir; bdir->size; bdir++) { + prev = 0; + /* If size is zero then this conditional is always false */ + if (bdir->size < size) + continue; + /* 搜索对应目录项中链接的所有描述符,查找对应页面 */ + for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) { + if (bdesc->page == page) + goto found; + prev = bdesc; + } + } + panic("Bad address passed to kernel free_s()"); +found: + cli(); /* To avoid race conditions */ + /* 然后将该对象内存块链入空闲块对象链表中,并使该描述符的对象引用计数减1。*/ + *((void **)obj) = bdesc->freeptr; + bdesc->freeptr = obj; + bdesc->refcnt--; + if (bdesc->refcnt == 0) { /* 引用计数等于0,则需要释放对应的内存页面和该桶描述符 */ + /* + * We need to make sure that prev is still accurate. It + * may not be, if someone rudely interrupted us.... + */ + /* prev已经不是搜索到的描述符的前一个描述符,则重新搜索当前描述符的前一个描述符 */ + if ((prev && (prev->next != bdesc)) || (!prev && (bdir->chain != bdesc))) { + for (prev = bdir->chain; prev; prev = prev->next) + if (prev->next == bdesc) + break; + } + if (prev) + prev->next = bdesc->next; + else { /* 如果prev==NULL,则说明当前描述符是该目录项第1个描述符 */ + if (bdir->chain != bdesc) + panic("malloc bucket chains corrupted"); + bdir->chain = bdesc->next; + } + /* 释放当前描述符所操作的内存页面,并将该描述符插入空闲描述符表开始处 */ + free_page((unsigned long) bdesc->page); + bdesc->next = free_bucket_desc; + free_bucket_desc = bdesc; + } + sti(); + return; +} + diff --git a/linux-0.12/lib/open.c b/linux-0.12/lib/open.c new file mode 100755 index 0000000..938af25 --- /dev/null +++ b/linux-0.12/lib/open.c @@ -0,0 +1,43 @@ +/* + * linux/lib/open.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include +#include + +/** + * 打开(有可能创建)一个文件 + * @param[in] filname 文件名 + * @param[in] flag 文件打开标志 + * @param[in] ... + * @retval 成功返回文件描述符,出错返回-1并且设置出错号 + */ +int open(const char * filename, int flag, ...) +{ + register int res; + va_list arg; + + /* 利用va_start()宏函数,取得flag后面参数的指针,然后调用系统中断int 0x80, + 功能号__NR_open。文件打开操作。 + %0 - eax(返回的描述符或出错码) + %1 - eax(系统中断调用功能号__NR_open) + %2 - ebx(文件名filename) + %3 - ecx(打开文件标志flag) + %4 - edx(后随参数文件属性mode) */ + va_start(arg, flag); + __asm__( + "int $0x80" + :"=a" (res) + :"0" (__NR_open), "b" (filename), "c" (flag), + "d" (va_arg(arg, int))); + /* 系统中断调用返回值大于或等于0,表示是一个文件描述符,则直接返回之 */ + if (res >= 0){ + return res; + } + /* 否则说明返回值小于0,则代表一个出错码,设置该出错码并返回-1 */ + errno = -res; + return -1; +} diff --git a/linux-0.12/lib/setsid.c b/linux-0.12/lib/setsid.c new file mode 100755 index 0000000..8c99f76 --- /dev/null +++ b/linux-0.12/lib/setsid.c @@ -0,0 +1,15 @@ +/* + * linux/lib/setsid.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +/** + * 创建一个会话并设置进程组号 + * pid_t setsid() + * @retval 调用进程的会话标识符(session ID) + */ +_syscall0(pid_t, setsid) diff --git a/linux-0.12/lib/string.c b/linux-0.12/lib/string.c new file mode 100755 index 0000000..264d982 --- /dev/null +++ b/linux-0.12/lib/string.c @@ -0,0 +1,15 @@ +/* + * linux/lib/string.c + * + * (C) 1991 Linus Torvalds + */ + +#ifndef __GNUC__ /* 需要GNU的C编译器编译 */ +#error I want gcc! +#endif + +#define static +#define inline +#define __LIBRARY__ +#include + diff --git a/linux-0.12/lib/wait.c b/linux-0.12/lib/wait.c new file mode 100755 index 0000000..e467970 --- /dev/null +++ b/linux-0.12/lib/wait.c @@ -0,0 +1,31 @@ +/* + * linux/lib/wait.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include +#include + +/** + * 等待进程终止 系统调用 + * pid_t waitpid(pid_t pid, int * wait_stat, int options) + * @param[in] pid 等待被终止进程的进程id,或者是用于指定特殊情况的其他特定数值 + * @param[in] wait_stat 用于存放状态信息 + * @param[in] options WNOHANG 或 WUNTRACED 或 0 + * @retval 成功返回已经结束的子进程的进程号,失败返回-1 + */ +_syscall3(pid_t, waitpid, pid_t, pid, int *, wait_stat, int, options) + + +/** + * wait() 系统调用 + * 直接调用waitpid()函数 + * @param[in] wait_stat 用于存放状态信息 + * @retval 成功返回已经结束的子进程的进程号,失败返回-1 + */ +pid_t wait(int *wait_stat) +{ + return waitpid(-1, wait_stat, 0); +} diff --git a/linux-0.12/lib/write.c b/linux-0.12/lib/write.c new file mode 100755 index 0000000..be4047e --- /dev/null +++ b/linux-0.12/lib/write.c @@ -0,0 +1,19 @@ +/* + * linux/lib/write.c + * + * (C) 1991 Linus Torvalds + */ + +#define __LIBRARY__ +#include + +/** + * 写文件 系统调用 + * int write(int fd, const char * buf, off_t count) + * @param[in] fd 文件描述符 + * @param[in] buf 写缓冲指针 + * @param[in] count 写字节数 + * @retval 成功时返回写入的字节数(0表示写入0字节),出错返回-1,并且设置出错号 + * + */ +_syscall3(int, write, int, fd, const char *, buf, off_t, count) diff --git a/linux-0.12/mm/Makefile b/linux-0.12/mm/Makefile new file mode 100755 index 0000000..9442ca9 --- /dev/null +++ b/linux-0.12/mm/Makefile @@ -0,0 +1,43 @@ +include ../Rules.make + +CFLAGS = -g -Wall -fstrength-reduce -fomit-frame-pointer \ + -finline-functions -nostdinc -fno-builtin -I../include + +CPP += -E -I../include + +.c.o: + $(CC) $(CFLAGS) \ + -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< + +OBJS = memory.o swap.o page.o + +all: mm.o + +mm.o: $(OBJS) + $(LD) -r -o mm.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + cp tmp_make Makefile + +### Dependencies: +memory.o : memory.c ../include/signal.h ../include/sys/types.h \ + ../include/asm/system.h ../include/linux/sched.h ../include/linux/head.h \ + ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h +swap.o : swap.c ../include/string.h ../include/linux/sched.h \ + ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ + ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ + ../include/sys/param.h ../include/sys/time.h ../include/time.h \ + ../include/sys/resource.h diff --git a/linux-0.12/mm/memory.c b/linux-0.12/mm/memory.c new file mode 100755 index 0000000..b38e2b6 --- /dev/null +++ b/linux-0.12/mm/memory.c @@ -0,0 +1,756 @@ +/* + * linux/mm/memory.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * demand-loading started 01.12.91 - seems it is high on the list of + * things wanted, and it should be easy to implement. - Linus + */ +/* + * 需求加载是从91.12.1开始编写 - 在程序编制表中似乎是最重要的程序,并且应该是很容易编制的 - Linus + */ + +/* + * Ok, demand-loading was easy, shared pages a little bit tricker. Shared + * pages started 02.12.91, seems to work. - Linus. + * + * Tested sharing by executing about 30 /bin/sh: under the old kernel it + * would have taken more than the 6M I have free, but it worked well as + * far as I could see. + * + * Also corrected some "invalidate()"s - I wasn't doing enough of them. + */ +/* + * Ok,需求加载是比较容易编写的,而共享页面却需要有点技巧。共享页面程序是91.12.2开始编写的,好 + * 像能够工作 - Linus。 + * + * 通过执行大约30个/bin/sh对共享操作进行了测试:在老内核当中需要占用多于6MB的内在,而目前却不 + * 用。现在看来工作得很好。 + * + * 对“invalidate()”函数也进行了修改--在这方面我还做得不够。 + */ + +/* + * Real VM (paging to/from disk) started 18.12.91. Much more work and + * thought has to go into this. Oh, well.. + * 19.12.91 - works, somewhat. Sometimes I get faults, don't know why. + * Found it. Everything seems to work now. + * 20.12.91 - Ok, making the swap-device changeable like the root. + */ +/* + * 91.12.18开始编写真正的虚拟内存管理VB(交换页面到/从磁盘)。需要对此考虑很多并且需要作很多 + * 工作。呵呵,也只能这样了。 + * 91.12.19 - 在某种程序上可以工作了,但有时会出错,不知道怎么回事。找到错误了,现在好像一切 + 都能工作了。 + * 91.12.20 - OK,把交换设备修改成可更改的了,就像根文件设备那样。 + */ + +#include + +#include + +#include +#include +#include + +/* 用于判断给定线性地址是否位于当前进程的代码段中,“(((addr)+4095)&~4095)”用于取得线性 + 地址addr所在内存页面的末端地址 */ +#define CODE_SPACE(addr) ((((addr) + 4095) & ~4095) < current->start_code + current->end_code) + +/* 存放实际物理内存最高端地址 */ +unsigned long HIGH_MEMORY = 0; + +/* 从from处复制一页内存到to处(4KB) */ +#define copy_page(from, to) __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024)) + +/* 内存映射字节图(1字节代表1页物理内存的使用情况) */ +unsigned char mem_map [ PAGING_PAGES ] = {0, }; + +/* + * Free a page of memory at physical address 'addr'. Used by + * 'free_page_tables()' + */ + +/** + * 释放物理地址addr开始的1页内存 + * @param[in] addr 需要释放的起始物理地址 + * @return void + */ +void free_page(unsigned long addr) +{ + if (addr < LOW_MEM) { + return; + } + if (addr >= HIGH_MEMORY) { + panic("trying to free nonexistent page"); + } + /* 页面号 = (addr-LOW_MEM)/4096 */ + addr -= LOW_MEM; + addr >>= 12; + if (mem_map[addr]--) { + return; + } + /* 执行到此处表示要释放原本已经空闲的页面,内核存在问题 */ + mem_map[addr] = 0; + panic("trying to free free page"); +} + +/* + * This function frees a continuos block of page tables, as needed + * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks. + */ +/* + * 下面函数释放页表连续的内存块,exit()需要该函数。与copy_page_tables()类似,该函数仅处理 + * 4MB长度的内存块。 + */ + +/** + * 根据指定的线性地址和限长(页表个数),释放指定页面 + * @param[in] from 起始线性基地址 + * @param[in] size 释放的字节长度 + * @return 0 + */ +int free_page_tables(unsigned long from, unsigned long size) +{ + unsigned long *pg_table; + unsigned long * dir, nr; + + if (from & 0x3fffff) { /* 参数from给出的线性基地址是否在4MB的边界处 */ + panic("free_page_tables called with wrong alignment"); + } + if (!from) { /* from=0说明试图释放内核和缓冲所在的物理内存空间 */ + panic("Trying to free up swapper memory space"); + } + /* 计算size指定长度所占的页目录数(4MB的进位整数倍,向上取整),例如size=4.01MB则size=2 */ + size = (size + 0x3fffff) >> 22; + /* 页目录项指针 */ + dir = (unsigned long *) ((from >> 20) & 0xffc); /* _pg_dir = 0 */ + /* 遍历需要释放的页目录项,释放对应页表中的页表项 */ + for ( ; size-- > 0 ; dir++) { + if (!(1 & *dir)) { + continue; + } + pg_table = (unsigned long *) (0xfffff000 & *dir); + for (nr = 0 ; nr < 1024 ; nr++) { + if (*pg_table) { + if (1 & *pg_table) { /* 在物理内存中 */ + free_page(0xfffff000 & *pg_table); + } else { /* 在交换设备中 */ + swap_free(*pg_table >> 1); + } + *pg_table = 0; + } + pg_table++; + } + free_page(0xfffff000 & *dir); + *dir = 0; + } + invalidate(); + return 0; +} + +/* + * Well, here is one of the most complicated functions in mm. It + * copies a range of linerar addresses by copying only the pages. + * Let's hope this is bug-free, 'cause this one I don't want to debug :-) + * + * Note! We don't copy just any chunks of memory - addresses have to + * be divisible by 4Mb (one page-directory entry), as this makes the + * function easier. It's used only by fork anyway. + * + * NOTE 2!! When from==0 we are copying kernel space for the first + * fork(). Then we DONT want to copy a full page-directory entry, as + * that would lead to some serious memory waste - we just copy the + * first 160 pages - 640kB. Even that is more than we need, but it + * doesn't take any more memory - we don't copy-on-write in the low + * 1 Mb-range, so the pages can be shared with the kernel. Thus the + * special case for nr=xxxx. + */ +/* + * 好了,下面是内存管理mm中最为复杂的程序之一。它通过只复制内存页面来复制一定范围内线性地址中的内 + * 容。希望代码中没有错误,因为我不想再调试这块代码了:-) + * + * 注意!我们并不复制任何内存块,内存块的地址需要是4MB的倍数(正好一个页目录项对应的内存长度),因 + * 为这样处理可使函数简单。不管怎样,它仅被fork()使用。 + * + * 注意2!!当from==0时,说明是在为第一次fork()调用复制内核空间。此时我们就不想复制整个页目录项 + * 对应的内存,因为这样做会导致内存严重浪费我们只须复制开头160个页面,对应640KB。即使是复制这些页 + * 面也已经超出我们的需求,但这不会占用更多的内存,在低1MB内存范围内不执行写时复制操作,所以这些页面 + * 可以与内核共享。因此这是nr=xxxx的特殊情况(nr在程序中指页面数)。 + */ +/** + * 复制目录表项和页表项(用于写时复制机制) + * 复制指定线性地址和长度内存对应的页目录项和页表项,从而被复制的页目录和页表对应的原物理内存页面区 + * 被两套页表映射而共享使用。复制时,需申请新页面来存放新页表,原物理内存区将被共享。此后两个进程(父 + * 进程和其子进程)将共享内存区,直到有一个进程执行写操作时,内核才会为写操作进程分配新的内存页。 + * @param[in] from 源线性地址 + * @param[in] to 目标线性地址 + * @param[in] size 需要复制的长度(单位是字节) + * @return 0 + */ +int copy_page_tables(unsigned long from, unsigned long to, long size) +{ + unsigned long * from_page_table; + unsigned long * to_page_table; + unsigned long this_page; + unsigned long * from_dir, * to_dir; + unsigned long new_page; + unsigned long nr; + + /* 源地址和目的地址都需要在4MB内存边界地址 */ + if ((from & 0x3fffff) || (to & 0x3fffff)) { + panic("copy_page_tables called with wrong alignment"); + } + /* 源地址的目录项指针,目标地址的目录项指针, 需要复制的目录项数 */ + from_dir = (unsigned long *) ((from >> 20) & 0xffc); /* _pg_dir = 0 */ + to_dir = (unsigned long *) ((to >> 20) & 0xffc); + size = ((unsigned) (size + 0x3fffff)) >> 22; + /* 开始页表项复制操作 */ + for( ; size-- > 0 ; from_dir++, to_dir++) { + if (1 & *to_dir) { + panic("copy_page_tables: already exist"); + } + if (!(1 & *from_dir)) { + continue; + } + from_page_table = (unsigned long *) (0xfffff000 & *from_dir); + if (!(to_page_table = (unsigned long *) get_free_page())) { + return -1; /* Out of memory, see freeing */ + } + *to_dir = ((unsigned long) to_page_table) | 7; + /* 源地址在内核空间,则仅需复制前160页对应的页表项(nr = 160),对应640KB内存 */ + nr = (from == 0) ? 0xA0 : 1024; + /* 循环复制当前页表的nr个内存页面表项 */ + for ( ; nr-- > 0 ; from_page_table++, to_page_table++) { + this_page = *from_page_table; + if (!this_page) { + continue; + } + /* 该页面在交换设备中,申请一页新的内存,然后将交换设备中的数据读取到该页面中 */ + if (!(1 & this_page)) { + if (!(new_page = get_free_page())) { + return -1; + } + read_swap_page(this_page >> 1, (char *) new_page); + *to_page_table = this_page; + *from_page_table = new_page | (PAGE_DIRTY | 7); + continue; + } + this_page &= ~2; /* 让页表项对应的内存页面只读 */ + *to_page_table = this_page; + /* 物理页面的地址在1MB以上,则需在mem_map[]中增加对应页面的引用次数 */ + if (this_page > LOW_MEM) { + *from_page_table = this_page; /* 令源页表项也只读 */ + this_page -= LOW_MEM; + this_page >>= 12; + mem_map[this_page]++; + } + } + } + invalidate(); + return 0; +} + +/* + * This function puts a page in memory at the wanted address. + * It returns the physical address of the page gotten, 0 if + * out of memory (either when trying to access page-table or + * page.) + */ + +/** + * 将一页物理内存页面page映射到指定线性地址address处 + * @param[in] page 物理内存页面的地址 + * @param[in] address 指定线性地址 + * @retval 成功返回页面的物理地址,失败返回0 + */ +static unsigned long put_page(unsigned long page, unsigned long address) +{ + unsigned long tmp, *page_table; + + /* NOTE !!! This uses the fact that _pg_dir=0 */ + /* 注意!!! 这里使用了页目录表基地址pg_dir=0的条件 */ + + if (page < LOW_MEM || page >= HIGH_MEMORY) + printk("Trying to put page %p at %p\n", page, address); + /* page指向的页面未标记为已使用,故不能做映射 */ + if (mem_map[(page - LOW_MEM) >> 12] != 1) + printk("mem_map disagrees with %p at %p\n", page, address); + + /* 根据address从页目录表取出页表地址 */ + page_table = (unsigned long *) ((address >> 20) & 0xffc); + if ((*page_table) & 1) /* 页表存在 */ + page_table = (unsigned long *) (0xfffff000 & *page_table); + else { + if (!(tmp = get_free_page())) + return 0; + *page_table = tmp | 7; /* 置位3个标志(U/S,W/R,P) */ + page_table = (unsigned long *) tmp; + } + /* 在页表中设置页面地址,并置位3个标志(U/S,W/R,P) */ + page_table[(address >> 12) & 0x3ff] = page | 7; + + /* no need for invalidate */ + return page; +} + +/* + * The previous function doesn't work very well if you also want to mark + * the page dirty: exec.c wants this, as it has earlier changed the page, + * and we want the dirty-status to be correct (for VM). Thus the same + * routine, but this time we mark it dirty too. + */ +/* + * 如果你也想设置页面已修改标志,则上一个函数工作得不是很好:exec.c程序需要这种设置。 + * 因为exec.c中函数会在放置页面之前修改过页面内容。为了实现VM,我们需要能正确设置已 + * 修改状态标志。因而下面就有了与上面相同的函数,但是该函数在放置页面时会把页面标志 + * 为已修改状态。 + */ + +/** + * 把一内容已修改过的物理内存页面page映射到指定线性地址address处 + * @note 与上面的put_page函数一样,仅多设置了Dirty位 + * @param[in] page 物理内存页面的地址 + * @param[in] address 指定线性地址 + * @retval 成功返回页面的物理地址,失败返回0 + */ +unsigned long put_dirty_page(unsigned long page, unsigned long address) +{ + unsigned long tmp, *page_table; + + /* NOTE !!! This uses the fact that _pg_dir=0 */ + + if (page < LOW_MEM || page >= HIGH_MEMORY) + printk("Trying to put page %p at %p\n", page, address); + if (mem_map[(page-LOW_MEM)>>12] != 1) + printk("mem_map disagrees with %p at %p\n", page, address); + page_table = (unsigned long *) ((address >> 20) & 0xffc); + if ((*page_table) & 1) + page_table = (unsigned long *) (0xfffff000 & *page_table); + else { + if (!(tmp = get_free_page())) + return 0; + *page_table = tmp | 7; + page_table = (unsigned long *) tmp; + } + page_table[(address >> 12) & 0x3ff] = page | (PAGE_DIRTY | 7); + + /* no need for invalidate */ + return page; +} + +/** + * 取消写保护页面函数 [un_wp_page -- Un-Write Protect Page] + * 用于页异常中断过程中写保护异常的处理(写时复制)。在内核fork创建进程时,copy_mem将父子进程的 + * 页面均被设置成只读页面。而当新进程或原进程需要向内存页面写数据时,CPU就会检测到这个情况并 + * 产生页面写保护异常。于是在这个函数中内核就会首先判断要写的页面是否被共享。若没有则把页面设 + * 置成可写然后退出。若页面处于共享状态,则要重新申请一新页面并复制被写页面内容,以供写进程单 + * 独使用。共享被取消。 + * @param[in] *table_entry 物理页面地址 + * @return void + */ +/*static*/ void un_wp_page(unsigned long * table_entry) +{ + unsigned long old_page, new_page; + + old_page = 0xfffff000 & *table_entry; + + /* 即如果该内存页面此时只被一个进程使用,就直接把属性改为可写即可 */ + if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)] == 1) { + *table_entry |= 2; + invalidate(); + return; + } + + /* 申请一页空闲页面给执行写操作的进程单独使用,取消页面共享。复制原页面的内容至新页面, + 将指定页表项值更新为新页面地址 */ + if (!(new_page = get_free_page())) + oom(); /* 内存不够处理 */ + if (old_page >= LOW_MEM) + mem_map[MAP_NR(old_page)]--; + copy_page(old_page, new_page); + *table_entry = new_page | 7; + invalidate(); +} + +/* + * This routine handles present pages, when users try to write + * to a shared page. It is done by copying the page to a new address + * and decrementing the shared-page counter for the old page. + * + * If it's in code space we exit with a segment error. + */ +/* + * 当用户试图住一共享页面上写时,该函数处理已存在的内存页面(写时复制),它是通过将页面复制到 + * 一个新地址上并且递减原页面的共享计数值实现的。 + * + * 如果它在代码空间,我们就显示段出错信息并退出。 + */ +/** + * 执行写保护页面处理(在page.s程序中被调用) + * 写共享页面时需复制页面(写时复制)。 + * @param[in] error_code 出错类型(没有用到) + * @param[in] address 产生异常的页面的线性地址(CR2寄存器的值) + * @return void + */ +void do_wp_page(unsigned long error_code, unsigned long address) +{ + if (address < TASK_SIZE) + printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r"); + if (address - current->start_code > TASK_SIZE) { + printk("Bad things happen: page error in do_wp_page\n\r"); + do_exit(SIGSEGV); + } +#if 0 + /* we cannot do this yet: the estdio library writes to code space */ + /* stupid, stupid. I really want the libc.a from GNU */ + /* 我们现在还不能这样做:因为estdio库会在代码空间执行写操作 */ + /* 真是太愚蠢了。我真想从GNU得到libca库。 */ + /* 如果线性地址位于进程的代码空间(只读)中,则终止执行程序 */ + if (CODE_SPACE(address)) + do_exit(SIGSEGV); +#endif + /* 根据线性地址计算物理页面地址 */ + un_wp_page((unsigned long *) + (((address >> 10) & 0xffc) + (0xfffff000 & + *((unsigned long *) ((address >> 20) & 0xffc))))); + +} + +/** + * 写页面验证 + * 若页面不可写,则复制页面。在fork.c中被内存验证通用函数verify_area()调用 + * @param[in] address 指定页面在4GB空间中的线性地址 + * @return void + */ +void write_verify(unsigned long address) +{ + unsigned long page; + + /* 指定线性地址对应的页目录项是否存在 */ + if (!( (page = *((unsigned long *) ((address >> 20) & 0xffc)) ) & 1)) { + return; + } + page &= 0xfffff000; + /* 得到页表项的物理地址 */ + page += ((address >> 10) & 0xffc); + /* 然后判断该页表项中位1(R/W),位0(P)标志 */ + if ((3 & *(unsigned long *) page) == 1) { /* non-writeable, present */ + un_wp_page((unsigned long *) page); + } + return; +} + +/** + * 取得一页空闲的物理内存并映射到指定线性地址处 + * @param[in] address 指定页面的线性地址 + * @return void + */ +void get_empty_page(unsigned long address) +{ + unsigned long tmp; + + /* 若不能取得一空闲页面,或者不能将所取页面放置到指定地址处,则显示内存不够的信息 */ + if (!(tmp = get_free_page()) || !put_page(tmp, address)) { + free_page(tmp); /* 0 is ok - ignored */ + oom(); + } +} + +/* + * try_to_share() checks the page at address "address" in the task "p", + * to see if it exists, and if it is clean. If so, share it with the current + * task. + * + * NOTE! This assumes we have checked that p != current, and that they + * share the same executable or library. + */ +/* + * tty_to_share()任务“p”中检查位于地址“address”处的页面,看页面是否存在,是否干净。如果 + * 干净的话,就与当前任务共享。 + * + * 注意!这里我们已假定p!=当前任务,并且它们共享同一个执行程序或库程序。 + */ +/** + * 尝试对当前进程指定地址处的页面进行共享处理 + * 当前进程与进程p是同一执行代码,也可以认为当前进程是由p进程执行fork操作产生的进程,因此它们 + * 的代码内容一样。如果未对数据段内容作过修改那么数据段内容也应一样。 + * @param[in] address 进程空间的逻辑地址 + * @param[in] p 将被共享页面的进程 + * @return 页面共享处理成功返回1,失败返回0 + */ +static int try_to_share(unsigned long address, struct task_struct * p) +{ + unsigned long from; + unsigned long to; + unsigned long from_page; + unsigned long to_page; + unsigned long phys_addr; + + from_page = to_page = ((address >> 20) & 0xffc); /* 逻辑地址的目录项偏移 */ + from_page += ((p->start_code >> 20) & 0xffc); /* p进程目录项 */ + to_page += ((current->start_code >> 20) & 0xffc); /* 当前进程目录项 */ + /* is there a page-directory at from? */ /* 在from处是否存在页目录项? */ + from = *(unsigned long *) from_page; + if (!(from & 1)) + return 0; + from &= 0xfffff000; + from_page = from + ((address >> 10) & 0xffc); /* 页表项指针 */ + phys_addr = *(unsigned long *) from_page; /* 页表项内容 */ + + /* is the page clean and present? */ + if ((phys_addr & 0x41) != 0x01) /* 物理页面干净并且存在吗? */ + return 0; + phys_addr &= 0xfffff000; /* 物理页面地址 */ + if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM) + return 0; + + to = *(unsigned long *) to_page; /* 当前进程目录项内容 */ + if (!(to & 1)) { + if ((to = get_free_page())) { + *(unsigned long *) to_page = to | 7; + } else { + oom(); + } + } + + to &= 0xfffff000; + to_page = to + ((address >> 10) & 0xffc); /* 当前进程的页表项地址 */ + if (1 & *(unsigned long *) to_page) + panic("try_to_share: to_page already exists"); + /* share them: write-protect */ /* 对它们进行共享处理:写保护 */ + *(unsigned long *) from_page &= ~2; + *(unsigned long *) to_page = *(unsigned long *) from_page; + + invalidate(); + /* 将对应物理页面的引用递增1 */ + phys_addr -= LOW_MEM; + phys_addr >>= 12; + mem_map[phys_addr]++; + return 1; +} + +/* + * share_page() tries to find a process that could share a page with + * the current one. Address is the address of the wanted page relative + * to the current data space. + * + * We first check if it is at all feasible by checking executable->i_count. + * It should be >1 if there are other tasks sharing this inode. + */ +/* + * share_page()试图找到一个进程,它可以与当前进程共享页面。参数address是当前进程数据空间中 + * 期望共享的某页面地址。 + * + * 首先我们通过检测executable->i_count来查证是否可行。如果有其他任务已共享该inode,则它应 + * 该大于1。 + */ + +static int share_page(struct m_inode * inode, unsigned long address) +{ + struct task_struct ** p; + + /*如果该内存i节点的引用计数值等于1或者i节点指针空,表示当前系统中只有1个进程在运行该执行文件 + 或者提供的i节点无效,因此无共享可言 */ + if (inode->i_count < 2 || !inode) + return 0; + /* 搜索任务数组中所有任务。寻找与当前进程可共享页面的进程,即运行相同执行文件的另一个进程 */ + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (!*p) /* 该任务项空闲 */ + continue; + if (current == *p) /* 如果是当前任务 */ + continue; + if (address < LIBRARY_OFFSET) { + if (inode != (*p)->executable) + continue; + } else { + if (inode != (*p)->library) + continue; + } + if (try_to_share(address, *p)) /* 尝试共享页面 */ + return 1; + } + return 0; +} + +/** + * 执行缺页处理(在page.s中被调用) + * 函数参数error_code和address是进程在访问页面时由CPU因缺页产生异常而自动生成。 + * 1. 首先查看所缺页是否在交换设备中,若是则交换进来。 + * 2. 否则尝试与已加载的相同文件进行页面共享,或者只是由于进程动态申请内存页面而只需 + * 映射一页物理内存页即可。 + * 3. 若共享操作不成功,那么只能从相应文件中读入所缺的数据页面到指定线性地址处。 + * @param[in] error_code 出错类型(没用到) + * @param[in] address 产生异常的页面线性地址(CR2寄存器的值) + * @return void + */ +void do_no_page(unsigned long error_code, unsigned long address) +{ + int nr[4]; + unsigned long tmp; + unsigned long page; + int block, i; + struct m_inode * inode; + + if (address < TASK_SIZE) + printk("\n\rBAD!! KERNEL PAGE MISSING\n\r"); + + if (address - current->start_code > TASK_SIZE) { + printk("Bad things happen: nonexistent page error in do_no_page\n\r"); + do_exit(SIGSEGV); + } + /* 1.所缺页在交换设备中,从交换设备读页面 */ + page = *(unsigned long *) ((address >> 20) & 0xffc); /* 取目录项内容 */ + if (page & 1) { /* 存在位P */ + page &= 0xfffff000; + page += (address >> 10) & 0xffc; + tmp = *(unsigned long *) page; /* 取页表项内容 */ + if (tmp && !(1 & tmp)) { + swap_in((unsigned long *) page); + return; + } + } + /* 算出address处缺页的页面地址在进程空间中的偏移长度值tmp,根据偏移值判定缺页所在进程空 + 间位置,获取i节点和块号,用于之后从文件中加载页面 */ + address &= 0xfffff000; + tmp = address - current->start_code; + if (tmp >= LIBRARY_OFFSET ) { /* 缺页在库映像文件中 */ + inode = current->library; + block = 1 + (tmp - LIBRARY_OFFSET) / BLOCK_SIZE; + } else if (tmp < current->end_data) { /* 缺页在执行映像文件中 */ + inode = current->executable; + block = 1 + tmp / BLOCK_SIZE; + } else { /* 缺页在动态申请的数据或栈内存页面,无i节点和块号 */ + inode = NULL; + block = 0; + } + + /* 2. 缺页为动态申请的内存页面,则直接申请一页物理内存页面并映射到线性地址address即可 */ + if (!inode) { + get_empty_page(address); + return; + } + + /* 3. 缺页在进程执行文件或库文件范围内,尝试共享页面操作 */ + if (share_page(inode, tmp)) + return; + + /* 4. 共享不成功就只能申请一页物理内存页面page,然后读取执行文件中的相应页面并映射到逻辑地址tmp处 */ + if (!(page = get_free_page())) + oom(); + /* remember that 1 block is used for header */ + /* 记住,程序头占用1个数据块(用于解释上面 block = 1+ ...) */ + for (i = 0 ; i < 4 ; block++, i++) + nr[i] = bmap(inode, block); /* 获取设备逻辑块号 */ + bread_page(page, inode->i_dev, nr); + + /* 读取执行程序最后一页(实际不满一页),把超出end_data后的部分进行清零处理,若该页面离执行程序末端 + 超过1页,说明是从库文件中读取的,因此不用执行清零操作 */ + i = tmp + 4096 - current->end_data; + if (i > 4095) + i = 0; + tmp = page + 4096; + while (i-- > 0) { + tmp--; + *(char *)tmp = 0; + } + /* 把引起缺页异常的一页物理页面映射到指定线性地址address处 */ + if (put_page(page, address)) + return; + /* 否则释放物理页面,显示内存不够 */ + free_page(page); + oom(); +} + + +/** + * 物理内存管理初始化 + * 该函数对1MB以上内存区域以页面为单位进行管理前的初始化设置工作,一个页面长度为4KB,并使用一个页 + * 面映射字节数组mem_map[]来管理所有这些页面。 + * @param[in] start_mem 主内存区的开始地址 + * @param[in] end_mem 主内存区的结束地址 + * @return void + */ +void mem_init(long start_mem, long end_mem) +{ + int i; + + /* 首先,将1MB到16MB范围内的内存页面对应的数组项置为USED(100),即已占用状态 */ + HIGH_MEMORY = end_mem; /* 设置内存最高端 */ + for (i = 0; i < PAGING_PAGES; i++) { + mem_map[i] = USED; + } + + /* 找到主内存区起始位置处页面号i */ + i = MAP_NR(start_mem); + + /* 得到主内存区的页面的数量 */ + end_mem -= start_mem; + end_mem >>= 12; + + /* 将主内存区对应的页面的使用数置0,即未使用 */ + while (end_mem-- > 0) { + mem_map[i++] = 0; + } +} + + + +/* 显示系统内存信息(在chr_drv/keyboard.S中被调用) */ +void show_mem(void) +{ + int i, j, k, free = 0, total = 0; + int shared = 0; + unsigned long * pg_tbl; + + /* 根据mem_map[]统计主内存区页面总数total,以及其中空闲页面数free和被共享的页面数shared */ + printk("Mem-info:\n\r"); + for (i = 0 ; i < PAGING_PAGES ; i++) { + if (mem_map[i] == USED) { /* 不可用页面 */ + continue; + } + total ++; + if (!mem_map[i]) { + free ++; + } + else { + shared += mem_map[i] - 1;/* 共享的页面数(字节值>1) */ + } + } + printk("%d free pages of %d\n\r", free, total); + printk("%d pages shared\n\r", shared); + /* 统计分页管理的逻辑页面数 */ + k = 0; /* 一个进程占用页面统计值 */ + for (i = 4; i < 1024; ) { /*页目录表前4项供内核代码使用,不列为统计范围 */ + if (1 & pg_dir[i]) { + if (pg_dir[i] > HIGH_MEMORY) { /* 目录项内容不正常 */ + printk("page directory[%d]: %08X\n\r", i, pg_dir[i]); + continue; + } + if (pg_dir[i] > LOW_MEM) { + free ++, k ++; /* 统计页表占用页面 */ + } + pg_tbl = (unsigned long *) (0xfffff000 & pg_dir[i]); + for (j = 0 ; j < 1024 ; j++) { + if ((pg_tbl[j]&1) && pg_tbl[j] > LOW_MEM){ + if (pg_tbl[j] > HIGH_MEMORY){ /* 页表项内容不正常 */ + printk("page_dir[%d][%d]: %08X\n\r", i, j, pg_tbl[j]); + } + else{ + k ++, free ++; /* 统计页表项对应页面 */ + } + } + } + } + i++; + /* 每个任务线性空间长度是64MB,每统计了16个目录项就统计了一个任务占用的页表 */ + if (!(i & 15) && k) { /* k=0说明对应的任务没有创建或者已经终止 */ + k ++, free ++; /* one page/process for task_struct */ + printk("Process %d: %d pages\n\r", (i >> 4) - 1, k); + k = 0; + } + } + printk("Memory found: %d (%d)\n\r\n\r", free - shared, total); +} diff --git a/linux-0.12/mm/page.s b/linux-0.12/mm/page.s new file mode 100755 index 0000000..221d4ce --- /dev/null +++ b/linux-0.12/mm/page.s @@ -0,0 +1,42 @@ +/* + * linux/mm/page.s + * + * (C) 1991 Linus Torvalds + */ + +/* + * page.s contains the low-level page-exception code. + * the real work is done in mm.c + */ +/* + * page.s程序包含底层页异常处理代码。实际工作在memory.c中完成。 + */ +.globl page_fault # 声明为全局变量,将在traps.c中用于设置页异常描述符 + +page_fault: + xchgl %eax, (%esp) # 取出错码到eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10, %edx # 置内核数据段选择符 + mov %dx, %ds + mov %dx, %es + mov %dx, %fs + movl %cr2, %edx # 取引起页面异常的线性地址 + pushl %edx # 将该线性地址和出错码压入栈中,作为将调用函数的参数 + pushl %eax + testl $1, %eax # 测试页存在标志P(位0),如果不是缺页引起的异常则跳转 + jne 1f + call do_no_page # 调用缺页处理函数(mm/memory.c) + jmp 2f +1: call do_wp_page # 调用写保护处理函数(mm/memory.c) +2: addl $8, %esp # 丢弃压入栈的两个参数,弹出栈中寄存器并退出中断 + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret diff --git a/linux-0.12/mm/swap.c b/linux-0.12/mm/swap.c new file mode 100755 index 0000000..71d4865 --- /dev/null +++ b/linux-0.12/mm/swap.c @@ -0,0 +1,367 @@ +/* + * linux/mm/swap.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This file should contain most things doing the swapping from/to disk. + * Started 18.12.91 + */ +/* + * 本程序应该包括绝大部分执行内存交换的代码(从内存到磁盘或反之)。从91年12月18日开始编制。 + */ + +#include +#include +#include +#include +#include + +/* 1页(4096B)共有32768个位。最多可管理32768个页面,对应128MB内存容量 */ +#define SWAP_BITS (4096 << 3) + +/** + * 通过给定不同的"op",可定义对指定比特位进行测试,设置或清除三种操作。该宏把给定地 + * 址addr处第nr个比特位的值放入进位标志,设置或复位该比特位并返回进位标志值(即原位值)。 + * @param[in] addr 指定线性地址 + * @param[in] nr 指定地址处开始的比特位偏移位 + * @return 原比特位值 + */ +#define bitop(name, op) \ +static inline int name(char * addr, unsigned int nr) \ +{ \ + int __res; \ + __asm__ __volatile__("bt" op " %1, %2; adcl $0, %0" \ + :"=g" (__res) \ + :"r" (nr),"m" (*(addr)),"0" (0)); \ + return __res; \ +} + +/* 这里根据不同的op字符定义3个内嵌函数: + * 当op=""时,就是指令bt - (Bit Test)测试并返回原值; + * 当op="s"时,就是指令bts - (Bit Test and Set)设置比特位值并返回原值; + * 当op="r"时,就是指令btr - (Bit Test and Reset)复位比特位值并返回原值。 + */ +bitop(bit, "") /* bit(char * addr, unsigned int nr) */ +bitop(setbit, "s") /* setbit(char * addr, unsigned int nr) */ +bitop(clrbit, "r") /* clrbit(char * addr, unsigned int nr) */ + +static char * swap_bitmap = NULL; +int SWAP_DEV = 0; /* 内核初始化时设置的交换设备号 */ + +/* + * We never page the pages in task[0] - kernel memory. + * We page all other pages. + */ +/* + * 我们从不交换任务0(task[0])的页面,即不交换内核页面,我们只对其他页面进行交换操作。 + */ +/* = 64MB/4KB 第1个虚拟内存页面,即从任务0末端(64MB)处开始的虚拟内存页面 */ +#define FIRST_VM_PAGE (TASK_SIZE >> 12) + +/* = 4GB/4KB 4G内存空间对应的页数 */ +#define LAST_VM_PAGE (1024 * 1024) + +#define VM_PAGES (LAST_VM_PAGE - FIRST_VM_PAGE) + +/** + * 申请1页交换页面 + * 扫描整个交换映射位图(除对应位图本身的位0以外),返回值为1的第一个比特位号 + * @param[in] void + * @retval 成功返回交换页面号,失败返回0 + */ +static int get_swap_page(void) +{ + int nr; + + if (!swap_bitmap) { + return 0; + } + for (nr = 1; nr < SWAP_BITS; nr++) { + if (clrbit(swap_bitmap, nr)) { + return nr; + } + } + return 0; +} + +/** + * 释放交换设备中指定的交换页面 + * 在交换位图中设置指定页面号对应的位(置1)。若原来该位就等于1,则表示交换设备中原来该页面就没 + * 有被占用,或者位图出错。于是显示出错信息并返回。 + * @param[in] swap_nr 交换页面号 + * @retval void + */ +void swap_free(int swap_nr) +{ + if (!swap_nr) { + return; + } + if (swap_bitmap && swap_nr < SWAP_BITS) { + if (!setbit(swap_bitmap, swap_nr)) { + return; + } + } + printk("Swap-space bad (swap_free())\n\r"); + return; +} + +/** + * 把指定页面交换进内存中 + * 把指定页表项的对应页面从交换设备中读入到新申请的内存页面中。修改交换位图中对应位(置位),同 + * 时修改页表项内容,让它指向该内存页面,并设置相应标志。 + * @param[in] table_ptr + * @retval void + */ +void swap_in(unsigned long *table_ptr) +{ + int swap_nr; + unsigned long page; + + if (!swap_bitmap) { + printk("Trying to swap in without swap bit-map"); + return; + } + if (1 & *table_ptr) { /* 指定页表项对应的页面已存在于内存中 */ + printk("trying to swap in present page\n\r"); + return; + } + /* 在交换设备中的页面对应的页表项中存放的是交换页面号swap_nr<<1,最低位P=0 */ + swap_nr = *table_ptr >> 1; + if (!swap_nr) { /* 交换页面号为0 */ + printk("No swap page in swap_in\n\r"); + return; + } + if (!(page = get_free_page())) { + oom(); + } + read_swap_page(swap_nr, (char *) page); + if (setbit(swap_bitmap, swap_nr)) { + printk("swapping in multiply from same page\n\r"); + } + /* 让页表指向该物理页面,并设置标志位(Dirty,U/S,R/W,P)。*/ + *table_ptr = page | (PAGE_DIRTY | 7); +} + +/** + * 尝试把页面交换出去(仅在swap_out中被调用) + * 1. 页面未被修改过,则不必换出,直接释放即可,因为对应页面还可以再直接从相应映像文件中读入 + * 2. 页面被修改过,则尝试换出。 + * @param[in] table_ptr 页表项指针 + * @return 页面换或释放成功返回1,失败返回0 + */ +/*static*/ int try_to_swap_out(unsigned long * table_ptr) +{ + unsigned long page; + unsigned long swap_nr; + + page = *table_ptr; + if (!(PAGE_PRESENT & page)) { /* 要换出的页面不存在 */ + return 0; + } + if (page - LOW_MEM > PAGING_MEMORY) { /* 指定物理内存地址高于内存高端或低于LOW_MEM */ + return 0; + } + if (PAGE_DIRTY & page) { /* 内存页面已被修改过 */ + page &= 0xfffff000; + if (mem_map[MAP_NR(page)] != 1) { /* 页面又是被共享的,不宜换出 */ + return 0; + } + if (!(swap_nr = get_swap_page())) { + return 0; + } + /* 换出页面的页表项的内容为(swap_nr << 1)|(P = 0) */ + *table_ptr = swap_nr << 1; + invalidate(); + write_swap_page(swap_nr, (char *) page); + free_page(page); + return 1; + } + /* 执行到这表明页面没有修改过,直接释放即可 */ + *table_ptr = 0; + invalidate(); + free_page(page); + return 1; +} + +/* + * Ok, this has a rather intricate logic - the idea is to make good + * and fast machine code. If we didn't worry about that, things would + * be easier. + */ +/* + * OK,这个函数中有一个非常复杂的逻辑,用于产生逻辑性好并且速度快的机器码。如果我们不对此操心 + * 的话,那么事情可能更容易些。 + */ +/** + * 把内存页面交换到交换设备中(仅在get_free_page被调用) + * 从线性地址64MB对应的目录项(FIRST_VM_PAGE>>10)开始,搜索整个4GB线性空间,对有效页目录 + * 二级页表指定的物理内存页面执行交换到交换设备中去的尝试。 + * @return 成功返回1,失败返回0 + */ +/*static*/ int swap_out(void) +{ + static int dir_entry = FIRST_VM_PAGE >> 10; /* 即任务1的第1个目录项索引 */ + static int page_entry = -1; + int counter = VM_PAGES; /* 表示除去任务0以外的其他任务的所有页数目 */ + int pg_table; + + /* 首先搜索页目录表,查找第一个有效的页目录项pg_table */ + while (counter > 0) { + pg_table = pg_dir[dir_entry]; + if (pg_table & 1) { + break; + } + counter -= 1024; /* 1个页表对应1024个页帧 */ + dir_entry++; + if (dir_entry >= 1024) { + /* 检索完整个页目录表,重新从头开始检索(执行不到) */ + dir_entry = FIRST_VM_PAGE >> 10; + } + } + /* 对取到页目录项对应页表中的页表项开始逐一调用交换函数 */ + pg_table &= 0xfffff000; + while (counter-- > 0) { + page_entry++; + if (page_entry >= 1024) { + /* 页表项索引>=1024,则取下一个有效的页目录项 */ + page_entry = 0; + repeat: + dir_entry++; + if (dir_entry >= 1024) { + /* 检索完整个页目录表,重新从头开始检索(执行不到) */ + dir_entry = FIRST_VM_PAGE >> 10; + } + pg_table = pg_dir[dir_entry]; + if (!(pg_table & 1)) { + if ((counter -= 1024) > 0) { + goto repeat; + } else { + break; + } + } + pg_table &= 0xfffff000; + } + if (try_to_swap_out(page_entry + (unsigned long *) pg_table)) { + /* 成功换出一个页面即退出 */ + return 1; + } + } + printk("Out of swap-memory\n\r"); + return 0; +} + +/* + * Get physical address of first (actually last :-) free page, and mark it + * used. If no free pages left, return 0. + */ +/* + * 获取首个(实际上是最后1个:-)空闲页面,并标志为已使用。如果没有空闲页面,就返回0。 + */ + +/** + * 在主内存区中申请1页空闲物理页面 + * @return 空闲的页面地址 + */ +unsigned long get_free_page(void) +{ + register unsigned long __res; + +/* 在内存映射字节位图中从尾到头地查找值为0的字节项,然后把对应物理内存页面清零 */ +repeat: + __asm__("std ; repne ; scasb\n\t" + "jne 1f\n\t" + "movb $1,1(%%edi)\n\t" + "sall $12,%%ecx\n\t" + "addl %2,%%ecx\n\t" + "movl %%ecx,%%edx\n\t" + "movl $1024,%%ecx\n\t" + "leal 4092(%%edx),%%edi\n\t" + "rep ; stosl\n\t" + "movl %%edx,%%eax\n" + "1:" + :"=a" (__res) + :"0" (0), "i" (LOW_MEM), "c" (PAGING_PAGES), + "D" (mem_map + PAGING_PAGES - 1) + ); + if (__res >= HIGH_MEMORY) { /* 页面地址大于实际内存容量,重新寻找 */ + goto repeat; + } + if (!__res && swap_out()) { /* 没有得到空闲页面则执行交换处理,并重新查找 */ + goto repeat; + } + return __res; +} + +/** + * 内存交换初始化 + * @note 在交换页面位图中,swap_bitmap[0]和swap_bitmap[swap_size ~ SWAP_BITS-1]不可用, + * swap_bitmap[1 ~ swap_size-1]可用 + * @param[in] void + * @retval void + */ +void init_swapping(void) +{ + extern int *blk_size[]; + int swap_size, i, j; + + if (!SWAP_DEV) { + return; + } + if (!blk_size[MAJOR(SWAP_DEV)]) { + printk("Unable to get size of swap device\n\r"); + return; + } + swap_size = blk_size[MAJOR(SWAP_DEV)][MINOR(SWAP_DEV)]; + if (!swap_size) { + return; + } + if (swap_size < 100) { + printk("Swap device too small (%d blocks)\n\r", swap_size); + return; + } + swap_size >>= 2; /* swap_size以1KB为单位,>>2则表示有多少个4KB */ + if (swap_size > SWAP_BITS) { + swap_size = SWAP_BITS; + } + swap_bitmap = (char *) get_free_page(); + if (!swap_bitmap) { + printk("Unable to start swapping: out of memory :-)\n\r"); + return; + } + read_swap_page(0, swap_bitmap); + /* 设备交换分区的页面0的4086起的10个字节应该含有特征字符串“SWAP-SPACE” */ + if (strncmp("SWAP-SPACE", swap_bitmap + 4086, 10)) { + printk("Unable to find swap-space signature\n\r"); + free_page((long) swap_bitmap); + swap_bitmap = NULL; + return; + } + memset(swap_bitmap + 4086, 0, 10); + /* 检查不可用的比特位([0],[swap_size ~ SWAP_BITS-1]) */ + for (i = 0 ; i < SWAP_BITS ; i++) { + if (i == 1) { + i = swap_size; + } + if (bit(swap_bitmap, i)) { + printk("Bad swap-space bit-map\n\r"); + free_page((long) swap_bitmap); + swap_bitmap = NULL; + return; + } + } + j = 0; + /* 统计空闲交换页面(1为空闲) */ + for (i = 1 ; i < swap_size ; i++) { + if (bit(swap_bitmap, i)) { + j++; + } + } + if (!j) { + free_page((long) swap_bitmap); + swap_bitmap = NULL; + return; + } + printk("Swap device ok: %d pages (%d bytes) swap-space\n\r", j, j*4096); +} diff --git a/linux-0.12/tools/build.c b/linux-0.12/tools/build.c new file mode 100755 index 0000000..d46c61b --- /dev/null +++ b/linux-0.12/tools/build.c @@ -0,0 +1,298 @@ +/* + * linux/tools/build.c + * + * (C) 1991 Linus Torvalds + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: max 510 bytes of 8086 machine code, loads the rest + * - setup: max 4 sectors of 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ +/* + * 该程序从三个不同的程序中创建磁盘映像文件: + * + * - bootsect: 该文件的8086机器码最长为510字节,用于加载其他程序。 + * - setup: 该文件的8086机器码最长为4个磁盘扇区,用于设置系统参数。 + * - system: 实际系统的80386代码。 + * + * 该程序首先检查所有程序模块的类型是否正确,并将检查结果在终端上显示出来,然后删除模块头部并扩 + * 充到正确的长度。该程序也会将一些系统数据写到stderr。 + */ +/* + * Changes by tytso to allow root device specification + * + * Added swap-device specification: Linux 20.12.91 + */ +/* + * tytso对该程序作了修改,以允许指定根文件设备。 + * 添加了指定交换设备功能:Linus 20.12.91 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAJOR + #define MAJOR(a) (((unsigned)(a))>>8) +#endif +#ifndef MINOR + #define MINOR(a) ((a)&0xff) +#endif + +#define MINIX_HEADER 32 /* minix二进制目标文件模块头部长度为32B */ +#define GCC_HEADER 1024 /* GCC头部信息长度为1024B */ + +#define SYS_SIZE 0x3000 /* system文件最长节数(SYS_SIZE*16=128KB) */ + +// 默认地把Linux根文件系统所在的设备设置为在第2个硬盘的第1个分区上(即设备号为0x0306),是因为 +// Linus当时开发Linux时,把第1个硬盘用作MINIX系统盘,而第2个硬盘用作Linux的根文件系统盘。 + +#define DEFAULT_MAJOR_ROOT 3 /* 默认根设备主设备号 - 3(硬盘) */ +#define DEFAULT_MINOR_ROOT 1//6 /* 默认根设备次设备号 - 6(第2个硬盘的第1分区) */ + +#define DEFAULT_MAJOR_SWAP 3 /* 默认交换设备主设备号 */ +#define DEFAULT_MINOR_SWAP 4 /* 默认交换设备次设备号 */ + +/* max nr of sectors of setup: don't change unless you also change bootsect etc */ +/* 下面指定setup模块占的最大扇区数:不要改变该值,除非也改变bootsect等相应文件 */ +#define SETUP_SECTS 4 /* setup最大长度4个扇区(2KB) */ + +#define STRINGIFY(x) #x /* 把x转换成字符串类型 */ + +/* 显示出错信息,并终止程序 */ +void die(char * str) +{ + fprintf(stderr, "%s\n", str); + exit(1); +} + +/* 显示程序使用方法,并退出 */ +void usage(void) +{ + die("Usage: build bootsect setup system [rootdev] [> image]"); +} + +/* 主程序 */ +int main(int argc, char ** argv) +{ + int i, c, id; + char buf[1024]; + char major_root, minor_root; + char major_swap, minor_swap; + struct stat sb; + + /* build程序命令行参数不是4-6个(程序名算作1个),则显示程序用法并退出 */ + if ((argc < 4) || (argc > 6)) { + usage(); + } + /* 若argc>4,如果根设备名不是软盘("FLOPPY"),则取该设备文件的状态信息。如果根设备就 + 是FLOPPY设备,则让主设备号取0 */ + if (argc > 4) { + if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = MAJOR(sb.st_rdev); /* 取设备名状态结构中设备号 */ + minor_root = MINOR(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + /* 若argc=4,则取系统默认的根设备号 */ + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + /* 若argc=6,如果交换设备不是"NONE",则取设备名状态结构中的设备号。如果交换设备就是"NONE", + 则让交换设备的主设备号和次设备号取为0,表示交换设备就是当前启动引导设备 */ + if (argc == 6) { + if (strcmp(argv[5], "NONE")) { + if (stat(argv[5], &sb)) { + perror(argv[5]); + die("Couldn't stat root device."); /* root好像应该改成swap */ + } + major_swap = MAJOR(sb.st_rdev); /* 取设备名状态结构中设备号 */ + minor_swap = MINOR(sb.st_rdev); + } else { + major_swap = 0; + minor_swap = 0; + } + } else { + /* 若argc=5,表示命令行上没有交换设备名,则取系统默认的交换设备号 */ + major_swap = DEFAULT_MAJOR_SWAP; + minor_swap = DEFAULT_MINOR_SWAP; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + fprintf(stderr, "Swap device is (%d, %d)\n", major_swap, minor_swap); + /* 如果主设备号不等于2(软盘)或3(硬盘)也不为0(取系统默认设备),则显示出错信息并退出 */ + if ((major_root != 2) && (major_root != 3) && (major_root != 0)) { + fprintf(stderr, "Illegal root device (major = %d)\n", major_root); + die("Bad root device --- major #"); + } + if (major_swap && major_swap != 3) { + fprintf(stderr, "Illegal swap device (major = %d)\n", major_swap); + die("Bad root device --- major #"); + } + /* 首先初始化1KB的复制缓冲区,置全0 */ + for (i = 0; i < sizeof buf; i++) { + buf[i] = 0; + } + +/**** bootsect ****/ + if ((id = open(argv[1], O_RDONLY, 0)) < 0) { + die("Unable to open 'boot'"); + } + /* 从中读取MINIX执行文件头结构内容到缓冲区buf中。bootsect = 32字节的头部+512字 + 节的引导扇区代码和数据 */ + if (read(id, buf, MINIX_HEADER) != MINIX_HEADER) { + die("Unable to read header of 'boot'"); + } + /* 接下来根据MINIX头部结构判断bootsect是否为一个有效的MINIX执行文件。 + 0x0301 - a_magic MINIX头部魔数 + 0x10 - a_flag 可执行 + 0x04 - a_cpu Intel8086机器码 */ + if (((long *) buf)[0] != 0x04100301) { + die("Non-Minix header of 'boot'"); + } + if (((long *) buf)[1] != MINIX_HEADER) { /* 头部长度字段 a_hdrlen */ + die("Non-Minix header of 'boot'"); + } + if (((long *) buf)[3] != 0) { /* 数据段长字段 a_data */ + die("Illegal data segment in 'boot'"); + } + if (((long *) buf)[4] != 0) { /* 堆字段 a_bss */ + die("Illegal bss in 'boot'"); + } + if (((long *) buf)[5] != 0) { /* 执行点字段 a_entry */ + die("Non-Minix header of 'boot'"); + } + if (((long *) buf)[7] != 0) { /* 符号表长字段 a_sym */ + die("Illegal symbol table in 'boot'"); + } + i = read(id, buf, sizeof buf); + fprintf(stderr, "Boot sector is %d bytes.\n", i); + /* 512字节的引导扇区代码和数据 */ + if (i != 512) { + die("Boot block must be exactly 512 bytes"); + } + /* 最后2字节应该是引导标志0xAA55 */ + if ((*(unsigned short *)(buf+510)) != 0xAA55) { + die("Boot block hasn't got boot flag (0xAA55)"); + } + /* 引导扇区的506, 507偏移处需存放交换设备号;508,509偏移处需存放根设备号 */ + buf[506] = (char) minor_swap; + buf[507] = (char) major_swap; + buf[508] = (char) minor_root; + buf[509] = (char) major_root; + + /* 在linux/Makefile中,build程序标准输出被重定向到内核映像文件名Image上,因此引 + 导扇区代码和数据会被写到Image开始的512字节处 */ + i = write(1, buf, 512); + if (i != 512){ + die("Write call failed"); + } + close (id); + +/**** setup模块 ****/ + if ((id = open(argv[2], O_RDONLY, 0)) < 0) { + die("Unable to open 'setup'"); + } + /* 读取32字节的MINIX执行文件头结构内容到缓冲区buf中 */ + if (read(id, buf, MINIX_HEADER) != MINIX_HEADER) { + die("Unable to read header of 'setup'"); + } + /* 接下来根据MINIX头部结构判断setup是否为一个有效的MINIX执行文件 + 0x0301 - a_magic,MINIX头部魔数 + 0x10 - a_flag,可执行 + 0x04 - a_cpu, Intel8086机器码 */ + if (((long *) buf)[0] != 0x04100301) { + die("Non-Minix header of 'setup'"); + } + if (((long *) buf)[1] != MINIX_HEADER) { /* 头部长度字段a_hdrlen */ + die("Non-Minix header of 'setup'"); + } + if (((long *) buf)[3] != 0) { /* 数据段长字段 a_data */ + die("Illegal data segment in 'setup'"); + } + if (((long *) buf)[4] != 0) { /* 堆字段 a_bss */ + die("Illegal bss in 'setup'"); + } + if (((long *) buf)[5] != 0) { /* 执行起始点字段 a_entry */ + die("Non-Minix header of 'setup'"); + } + if (((long *) buf)[7] != 0) { /* 表长字段 a_sym */ + die("Illegal symbol table in 'setup'"); + } + /* 同时统计写的长度(i),该值不能大于(SETUP_SECTS * 512) */ + for (i = 0; (c = read(id, buf, sizeof buf)) > 0; i += c ) { + if (write(1, buf, c) != c) { + die("Write call failed"); + } + } + close (id); + if (i > SETUP_SECTS * 512) { + die("Setup exceeds " STRINGIFY(SETUP_SECTS) \ + " sectors - rewrite build/boot/setup"); + } + fprintf(stderr, "Setup is %d bytes.\n", i); + for (c = 0; c < sizeof(buf); c++) { + buf[c] = '\0'; /* buf数组清零 */ + } + /* 用NULL字符将setup填足为4*512B */ + while (i < SETUP_SECTS * 512) { + c = SETUP_SECTS * 512 - i; + if (c > sizeof(buf)) { + c = sizeof(buf); + } + if (write(1, buf, c) != c) { + die("Write call failed"); + } + i += c; + } + +/**** system模块 ****/ + /* system模块使用gas编译,具有GNU a.out目标文件格式 */ + if ((id = open(argv[3], O_RDONLY, 0)) < 0) { + die("Unable to open 'system'"); + } + #if 0 + /** + * 去掉这里的原因是 + * 1. 去除a.out格式头部的动作已经在主目录Makefile中进行了,故在这里注释掉。 + * 2. 入口位置不应该是((long *) buf)[5],应该为((long *) buf)[6],可以在linux下,通过 + * 命令 readelf -h system 和 od -w4 -N 80 -x system 对比看到入口地址应该在第28~31个 + * 字节处。 + */ + if (read(id, buf, GCC_HEADER) != GCC_HEADER){ + die("Unable to read header of 'system'"); + } + if (((long *) buf)[6/*5*/] != 0){ /* 执行入口点字段 a_entry值应为 0 */ + die("Non-GCC header of 'system'"); + } + #endif + for (i = 0; (c = read(id, buf, sizeof buf)) > 0; i += c ) { + if (write(1, buf, c) != c) { + die("Write call failed"); + } + } + close(id); + fprintf(stderr, "System is %d bytes.\n", i); + /* 若system模块超过SYS_SIZE(即128KB),则显示出错信息并退出 */ + if (i > SYS_SIZE * 16) { + die("System is too big"); + } + return(0); +} diff --git a/oslab/README.txt b/oslab/README.txt new file mode 100644 index 0000000..742692e --- /dev/null +++ b/oslab/README.txt @@ -0,0 +1,34 @@ +# 实验篇 + +1. shell 脚本问题 + +*.bxrc 文件中的变量不要用`{}`包起来,例如要用`$OSLAB_PATH`,不要用`${OSLAB_PATH}`。否则会找不到变量。 + +2. 如何使bochs、gdb调试忽略page fault信号? + +Created a patch "gdbstub.cc.patch" against bochs (version CVS 20080110) + Bochs always tries to find out the reason of an exception, so that it can generate the right signal for gdb. + If it fails to find a reason, bochs assigns a value GDBSTUB_STOP_NO_REASON (see bochs.h), which causes + debug_loop() (see gdbstub.cc) to generate a signal of number 0. + Signal 0 is problematic to gdb, as gdb doesn't allow us to ignore it. + Somehow when we simulate linux, we get tons of signal 0's that seem to be caused by page faults. + This patch makes bochs send SIGSEGV instead of signal 0, so that we can ignore it in gdb. + +gdbstub.cc +debug_loop() + +*** gdbstub.cc.orig Thu Oct 18 18:44:38 2007 +--- gdbstub.cc Sat Jan 12 17:25:22 2008 +*************** static void debug_loop(void) +*** 489,494 **** +--- 489,498 ---- + { + write_signal(&buf[1], SIGTRAP); + } ++ else if (last_stop_reason == GDBSTUB_STOP_NO_REASON) ++ { ++ write_signal(&buf[1], SIGSEGV); ++ } + else + { + write_signal(&buf[1], 0); diff --git a/oslab/bochs/.gdbrc b/oslab/bochs/.gdbrc new file mode 100644 index 0000000..e0b3397 --- /dev/null +++ b/oslab/bochs/.gdbrc @@ -0,0 +1,4 @@ +break main +target remote localhost:1234 +handle SIGSEGV nostop noprint ignore +continue diff --git a/oslab/bochs/BIOS-bochs-latest b/oslab/bochs/BIOS-bochs-latest new file mode 100644 index 0000000..bdfe498 Binary files /dev/null and b/oslab/bochs/BIOS-bochs-latest differ diff --git a/oslab/bochs/VGABIOS-lgpl-latest b/oslab/bochs/VGABIOS-lgpl-latest new file mode 100644 index 0000000..1756065 Binary files /dev/null and b/oslab/bochs/VGABIOS-lgpl-latest differ diff --git a/oslab/bochs/bochs-gdb b/oslab/bochs/bochs-gdb new file mode 100755 index 0000000..bba6326 Binary files /dev/null and b/oslab/bochs/bochs-gdb differ diff --git a/oslab/bochs/bochsrc-gdb.bxrc b/oslab/bochs/bochsrc-gdb.bxrc new file mode 100644 index 0000000..7a7211c --- /dev/null +++ b/oslab/bochs/bochsrc-gdb.bxrc @@ -0,0 +1,10 @@ +romimage: file=$OSLAB_PATH/bochs/BIOS-bochs-latest +megs: 32 +vgaromimage: file=$OSLAB_PATH/bochs/VGABIOS-lgpl-latest +floppya: 1_44="$OSLAB_PATH/Image", status=inserted +ata0-master: type=disk, path="$OSLAB_PATH/hdc.img", mode=flat, cylinders=204, heads=16, spt=38 +boot: floppy +log: $OSLAB_PATH/bochsout.txt +#parport1: enable=0 +cpu: count=1, ips=4000000 +gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0 \ No newline at end of file diff --git a/oslab/bochs/bochsrc.bxrc b/oslab/bochs/bochsrc.bxrc new file mode 100644 index 0000000..19d3337 --- /dev/null +++ b/oslab/bochs/bochsrc.bxrc @@ -0,0 +1,8 @@ +romimage: file=$OSLAB_PATH/bochs/BIOS-bochs-latest +megs: 32 +vgaromimage: file=$OSLAB_PATH/bochs/VGABIOS-lgpl-latest +floppya: 1_44="$OSLAB_PATH/Image", status=inserted +ata0-master: type=disk, path="$OSLAB_PATH/hdc.img", mode=flat, cylinders=204, heads=16, spt=38 +boot: floppy +log: $OSLAB_PATH/bochsout.txt +cpu: count=1, ips=4000000 \ No newline at end of file diff --git a/oslab/hdc.tar.xz b/oslab/hdc.tar.xz new file mode 100644 index 0000000..00390b3 Binary files /dev/null and b/oslab/hdc.tar.xz differ diff --git a/oslab/run.sh b/oslab/run.sh new file mode 100755 index 0000000..533a452 --- /dev/null +++ b/oslab/run.sh @@ -0,0 +1,17 @@ +#!/bin/sh +export OSLAB_PATH=$(dirname `which $0`) + +if [ ! -e "hdc.img" ]; then +tar -xvJf hdc.tar.xz +fi + +if [ "$1" ] && [ "$1" = "-m" ] +then +(cd ../linux-0.12; make clean; make; cp Image ../oslab/Image) +elif [ "$1" ] && [ "$1" = "-g" ] +then +$OSLAB_PATH/bochs/bochs-gdb -q -f $OSLAB_PATH/bochs/bochsrc-gdb.bxrc & \ +gdb -x $OSLAB_PATH/bochs/.gdbrc ../linux-0.12/tools/system +else +bochs -q -f $OSLAB_PATH/bochs/bochsrc.bxrc +fi \ No newline at end of file diff --git a/src/code/dbg.c b/src/code/dbg.c new file mode 100644 index 0000000..d0526a4 --- /dev/null +++ b/src/code/dbg.c @@ -0,0 +1,26 @@ +inline void check_data32(int value, int pos) +{ +__asm__ __volatile__( + "shl $4, %%ebx\n\t" + "addl $0xb8000, %%ebx\n\t" + "movl $0xf0000000, %%eax\n\t" + "movb $28, %%cl\n" + "1:\n\t" + "movl %0,%%edx\n\t" + "andl %%eax, %%edx\n\t" + "shr %%cl, %%edx\n\t" + "add $0x30, %%dx\n\t" + "cmp $0x3a, %%dx\n\t" + "jb 2f\n\t" + "add $0x07, %%dx\n\t" + "2:\n\t" + "add $0x0c00, %%dx\n\t" + "movw %%dx,(%%ebx)\n\t" + "sub $0x04, %%cl\n\t" + "shr $0x04, %%eax\n\t" + "add $0x02, %%ebx\n\t" + "cmpl $0x0,%%eax\n\t" + "jnz 1b\n" + ::"m"(value),"b"(pos) +); +} \ No newline at end of file diff --git a/src/code/linux-0.12.tar.gz b/src/code/linux-0.12.tar.gz new file mode 100755 index 0000000..e6d9969 Binary files /dev/null and b/src/code/linux-0.12.tar.gz differ diff --git a/src/code/setup.sh b/src/code/setup.sh new file mode 100755 index 0000000..ddb2346 --- /dev/null +++ b/src/code/setup.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# 如果在编译bochs时遇到libgtk2.0-dev 依赖问题,请手动执行 +# sudo apt-get install aptitude && sudo aptitude install libgtk2.0-dev +# 若old-releases.ubuntu.com源下载过慢,可以使用国内源http://mirrors.ustc.edu.cn/ubuntu-old-releases/ + +_echo_info(){ + echo -e "[Info]: ${1}" +} + +_echo_err(){ + echo -e "\033[31m[Error]: ${1} \033[0m" +} + +_echo_succ(){ + echo -e "\033[32m[Succ]: ${1} \033[0m" +} + +env_install(){ + + sudo apt-get install -y make bin86 gcc-multilib &> /dev/null \ + && _echo_succ "bin86 is installed." || _echo_err "bin86 is not installed!!!" + + if [ ! -z `which gcc-3.4` ];then + _echo_succ "Gcc-3.4 is installed." + return + fi + + GCC_DIR="gcc-3.4" + + DOWNLOAD_LIST=( + "gcc-3.4-base_3.4.6-6ubuntu3_amd64.deb" + "gcc-3.4_3.4.6-6ubuntu3_amd64.deb" + "cpp-3.4_3.4.6-6ubuntu3_amd64.deb" + "g++-3.4_3.4.6-6ubuntu3_amd64.deb" + "libstdc++6-dev_3.4.6-6ubuntu3_amd64.deb" + ) + + if [ -z `which gcc-3.4` ]; then + _echo_info "Start installing gcc-3.4..." + for deb in ${DOWNLOAD_LIST[*]}; do + if [ ! -e ${GCC_DIR}/${deb} ]; then + wget http://old-releases.ubuntu.com/ubuntu/pool/universe/g/gcc-3.4/${deb} -P ${GCC_DIR} -q --show-progress && \ + _echo_info "Download ${deb} Sucessfully." || ( rm ${deb} & _echo_err "Download ${deb} unsuccessfully!!!" ) + fi + done + sudo dpkg -i ${GCC_DIR}/*.deb &> /dev/null + sudo apt-get install -y -f &> /dev/null + if [ ! -z `which gcc-3.4` ];then + _echo_succ "gcc-3.4 is installed." + fi + rm -rf ${GCC_DIR} + fi +} + +bochs_install(){ + + sudo apt-get install -y build-essential libgtk2.0-dev \ + libx11-dev xserver-xorg-dev xorg-dev g++ \ + pkg-config libxcursor-dev libxrandr-dev \ + libxinerama-dev libxi-dev &> /dev/null + + sudo apt-get install -y build-essential &> /dev/null + sudo apt-get install -y bochs bochs-x bochs-sdl &> /dev/null + + if [ ! -e "bochs-2.6.9.tar.gz" ]; then + wget https://downloads.sourceforge.net/project/bochs/bochs/2.6.9/bochs-2.6.9.tar.gz -q --show-progress && \ + _echo_succ "Download bochs-2.6.9.tar.gz Sucessfully." || (rm bochs-2.6.9.tar.gz & _echo_err "Download bochs-2.6.9.tar.gz unsuccessfully!!!" ) + fi + + if [ ! -d "bochs-2.6.9" ];then + tar zxvf bochs-2.6.9.tar.gz &> /dev/null && \ + _echo_info "tar bochs-2.6.9.tar.gz Sucessfullyhttp://mirrors.ustc.edu.cn/ubuntu-old-releases/." || \ + (rm -rf ../bochs-2.6.9 & _echo_err "tar bochs-2.6.9.tar.gz unsuccessfully!!!" ) + fi + + if [ -d "bochs-2.6.9" ];then + cd bochs-2.6.9 + if [ "$1" ] && [ "$1" = "-d" ];then + sudo apt-get install aptitude && sudo aptitude install libgtk2.0-dev + ./configure --enable-gdb-stub --enable-disasm + # ./configure --enable-debugger --enable-disasm + make && (cp bochs ../bochs-gdb & _echo_succ "make bochs sucessfully.") || _echo_err "make bochs unsucessfully.!!!" + else + ./configure --enable-gdb-stub --enable-disasm &> /dev/null + make &> /dev/null && (cp bochs ../bochs-gdb & _echo_succ "make bochs sucessfully.") || _echo_err "make bochs unsucessfully.!!!" + fi + fi + +} + +trap 'onCtrlC' INT +function onCtrlC () { + _echo_err "[Warning]: stopped by user." + rm -rf ${GCC_DIR} + exit +} +echo " 须知" +echo " 请根据oslab/README.txt中的第2点,修改下载的bochs-2.6.9源码,并重新执行该脚本" +echo "脚本将完成以下两件事:" +echo " 1. 为系统安装相应的编译环境(make,bin86,gcc-3.4,gcc-multilib)" +echo " 2. 在脚本当前目录生成一个bochs-gdb(若没有生成,使用./setup.sh -d 重新执行脚本)" + +env_install +bochs_install $1 + diff --git a/src/docker/Dockerfile b/src/docker/Dockerfile new file mode 100644 index 0000000..af8a1ed --- /dev/null +++ b/src/docker/Dockerfile @@ -0,0 +1,68 @@ +FROM ubuntu:latest + +ENV DEBIAN_FRONTEND=noninteractive + +RUN sed -i "s/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g" /etc/apt/sources.list + +# install tools +RUN apt-get update && apt-get install -y \ + vim \ + wget \ + net-tools \ + locales \ + python-numpy \ + git \ + sudo \ + gdb \ + && apt-get autoclean \ + && locale-gen en_US.UTF-8 + +# install xfce +RUN apt-get update \ + && apt-get install -y supervisor \ + xfce4 \ + xfce4-terminal + +# install tigervnc +RUN wget -qO- https://dl.bintray.com/tigervnc/stable/tigervnc-1.8.0.x86_64.tar.gz | tar xz --strip 1 -C / + +ENV LANG='en_US.UTF-8' \ + LANGUAGE='en_US:en' \ + LC_ALL='en_US.UTF-8' \ + USER=ubuntu \ + PASSWORD=123456 \ + HOME=/home/ubuntu \ + NO_VNC_HOME=/home/ubuntu/.novnc \ + NO_VNC_PASSWORD=123456 + +# change the password of root and add user +RUN (echo "root:123456" | chpasswd) && adduser ${USER} --disabled-password && (echo "${USER}:${PASSWORD}" | chpasswd) && usermod -aG sudo ${USER} + +RUN apt-get update && apt-get install -y \ + build-essential bochs bochs-x bochs-sdl \ + make bin86 gcc-multilib + +RUN wget -q --show-progress http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/pool/universe/g/gcc-3.4/gcc-3.4-base_3.4.6-6ubuntu3_amd64.deb && dpkg -i gcc-3.4-base_3.4.6-6ubuntu3_amd64.deb || echo +RUN wget -q --show-progress http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/pool/universe/g/gcc-3.4/gcc-3.4_3.4.6-6ubuntu3_amd64.deb && dpkg -i gcc-3.4_3.4.6-6ubuntu3_amd64.deb || echo +RUN wget -q --show-progress http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/pool/universe/g/gcc-3.4/cpp-3.4_3.4.6-6ubuntu3_amd64.deb && dpkg -i cpp-3.4_3.4.6-6ubuntu3_amd64.deb || echo +RUN wget -q --show-progress http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/pool/universe/g/gcc-3.4/g++-3.4_3.4.6-6ubuntu3_amd64.deb && dpkg -i g++-3.4_3.4.6-6ubuntu3_amd64.deb || echo +RUN wget -q --show-progress http://mirrors.ustc.edu.cn/ubuntu-old-releases/ubuntu/pool/universe/g/gcc-3.4/libstdc++6-dev_3.4.6-6ubuntu3_amd64.deb && dpkg -i libstdc++6-dev_3.4.6-6ubuntu3_amd64.deb || echo +RUN apt-get install -y -f && rm *.deb + +ADD conf/vnc_startup.sh ${HOME} +RUN chmod +x ${HOME}/vnc_startup.sh + +USER ${USER} +WORKDIR ${HOME} + +# install novnc for ${USER} and change vncpassword +RUN mkdir -p ${NO_VNC_HOME}/utils/websockify && cd ${NO_VNC_HOME}/utils +RUN wget -qO- https://github.com/novnc/noVNC/archive/v1.1.0.tar.gz | tar xz --strip 1 -C ${NO_VNC_HOME} +RUN wget -qO- https://github.com/novnc/websockify/archive/v0.9.0.tar.gz | tar xz --strip 1 -C ${NO_VNC_HOME}/utils/websockify +RUN chmod +x -v $NO_VNC_HOME/utils/*.sh +RUN (echo $NO_VNC_PASSWORD; echo $NO_VNC_PASSWORD; echo n) | vncpasswd +RUN cp ${NO_VNC_HOME}/vnc.html ${NO_VNC_HOME}/index.html + +EXPOSE 6080 + +CMD ["./vnc_startup.sh"] \ No newline at end of file diff --git a/src/docker/conf/vnc_startup.sh b/src/docker/conf/vnc_startup.sh new file mode 100644 index 0000000..019cb4b --- /dev/null +++ b/src/docker/conf/vnc_startup.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +vncserver -kill :1 +vncserver -geometry 1280x1024 &> /dev/null +./.novnc/utils/launch.sh --vnc localhost:5901 &> /dev/null && /bin/sh \ No newline at end of file diff --git a/src/docker/docker-compose.yaml b/src/docker/docker-compose.yaml new file mode 100644 index 0000000..73c283a --- /dev/null +++ b/src/docker/docker-compose.yaml @@ -0,0 +1,15 @@ +version: "2" +services: + ubuntu-novnc-xfce: + image: ultraji/ubuntu-xfce-novnc + container_name: oslab + environment: + - TZ=Asia/Shanghai + ports: + - 6080:6080 + volumes: + # 根据自己的代码路径配置,例如 + - /home/ultraji/workplace/linux-0.12:/home/ubuntu/linux-0.12 + tty: true + stdin_open: true + restart: unless-stopped \ No newline at end of file