This commit is contained in:
alexvoste
2026-05-07 02:22:25 +03:00
commit 1a9fd27a31
226 changed files with 29188 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
demo_iso/
limine/
limine-tools/
limine.conf
OS-TREE.txt
.vscode/
linker-scripts/
bin/
obj/
iso_root/
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
+85
View File
@@ -0,0 +1,85 @@
<p align="center">
<img src="https://github.com/VeoQeo/Cervus/blob/main/wallpapers/cervus_logo.jpg" alt="Cervus OS Logo" width="400px">
</p>
# Cervus x86_64 Operating System
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Platform: x86_64](https://img.shields.io/badge/Platform-x86_64-lightgrey.svg)](https://en.wikipedia.org/wiki/X86-64)
[![Stage: Alpha](https://img.shields.io/badge/Stage-Alpha-orange.svg)]()
**Cervus** - This is a modern 64-bit operating system written for the x86_64 architecture. It is currently under development.
---
## Technical Features
Cervus is currently in the active development phase. The kernel implements core low-level primitives required for a stable execution environment:
### Core Architecture
- *Boot Protocol:* Utilizes the [Limine](https://github.com/limine-bootloader/limine) bootloader (Barebone profile).
- *Memory Management:*
- *PMM:* Bitmap-based Physical Memory Manager.
- *VMM:* Virtual Memory Management with 4-level paging support.
- *CPU Initialization:* Custom GDT (Global Descriptor Table) and IDT (Interrupt Descriptor Table) implementation.
- *Acceleration:* Native support for *SIMD* instructions (SSE/AVX) with proper state saving.
## Roadmap & Progress
| Component | Status | Description |
| :--- | :---: | :--- |
| *Bootloader* | Done | Limine Integration |
| *Graphics/PSF* | Done | Framebuffer & Text Rendering |
| *Memory (PMM/VMM)* | Done | Physical & Virtual Memory Management |
| *Interrupts (IDT)* | Done | Handling exceptions and IRQs |
| *ACPI* | Done(without rebooting) | Table parsing & SDT discovery |
| *APIC / IOAPIC* | Done | Advanced Interrupt Controllers |
| *Timers (HPET/APIC)* | Done | High Precision Event Timers |
| *SMP* | Done | Multicore Initialization |
| *Scheduler* | Done/In parallel development | Preemptive Multitasking |
| *Userspace* | In parallel development | Syscalls & Ring 3 execution |
---
## Build Environment
### Prerequisites
To build Cervus, you need a cross-compilation toolchain and the following utilities:
* *Compiler:* `x86_64-elf-gcc`
* *Assemblers:* `nasm`
* *Emulation:* `qemu-system-x86_64`
* *ISO Tools:* `xorriso`, `mtools`
### Compiling and Running
*1. Clone the repository:*
```bash
bash
git clone https://github.com/VeoQeo/Cervus.git
cd Cervus
```
*2. Compile and launch in QEMU:*
```bash
./build run
```
*3. Deploy to hardware (Flash Drive):*
**WARNING: This will overwrite data on the target device.**
```bash
sudo ./build flash
```
## Contributing
Cervus is an open-source research project. Contributions regarding bug fixes, hardware support, or documentation are welcome. Please feel free to open an Issue or submit a Pull Request.
## License
This project is licensed under the *GPL-3.0 License*. See the [LICENSE](LICENSE) file for details.
---
+23
View File
@@ -0,0 +1,23 @@
- [x] **Bootloader**: Limine Barebone
- [x] **Графика**: Базовая поддержка фреймбуфера
- [x] **Шрифты**: Поддержка PSF шрифтов
- [x] **Порты ввода-вывода**: Базовые операции с портами
- [x] **Последовательный порт**: Отладочный вывод через COM1
- [x] **Минимальная libc**: Базовая стандартная библиотека C
- [x] **GDT**: Глобальная таблица дескрипторов
- [x] **IDT**: Таблица дескрипторов прерываний
- [x] **SIMD**: Поддержка инструкций SSE/AVX
- [x] **PMM**: Менеджер физической памяти
- [x] **VMM**: Менеджер виртуальной памяти
- [x] **PAGING**
- [x] **ACPI**: Парсинг таблиц ACPI
- [x] **APIC**: Расширенный программируемый контроллер прерываний
- [x] **IOAPIC**: Контроллер прерываний ввода-вывода
- [x] **LAPIC**: Локальный APIC
- [x] **HPET**: Таймер высокой точности
- [x] **APIC-TIMER**: Таймер на основе APIC
- [x] **SMP**: Симметричная многопроцессорность
- [x] **Multitasking**: Многозадачность и планировщик
- [x] **Syscalls**: Системные вызовы
- [x] **Userspace**: Пространство пользователя
- [x_x(help me)] **Веселье начинается**: Дальнейшее развитие
Executable
BIN
View File
Binary file not shown.
+15
View File
@@ -0,0 +1,15 @@
CC = gcc
CFLAGS = -Wall -Wextra -O2
TARGET = ../build
all: $(TARGET)
$(TARGET): build.c
$(CC) $(CFLAGS) -o $@ $<
@echo "Build script is ready at: $(TARGET)"
clean:
rm -f $(TARGET)
.PHONY: all clean
+2496
View File
File diff suppressed because it is too large Load Diff
+199
View File
@@ -0,0 +1,199 @@
#ifndef ACPI_H
#define ACPI_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
typedef struct {
char signature[4];
uint32_t length;
uint8_t revision;
uint8_t checksum;
char oem_id[6];
char oem_table_id[8];
uint32_t oem_revision;
uint32_t creator_id;
uint32_t creator_revision;
} __attribute__((packed)) acpi_sdt_header_t;
typedef struct {
char signature[8];
uint8_t checksum;
char oem_id[6];
uint8_t revision;
uint32_t rsdt_address;
} __attribute__((packed)) acpi_rsdp_t;
typedef struct {
acpi_rsdp_t rsdp_v1;
uint32_t length;
uint64_t xsdt_address;
uint8_t extended_checksum;
uint8_t reserved[3];
} __attribute__((packed)) acpi_rsdp2_t;
typedef struct {
acpi_sdt_header_t header;
uint32_t sdt_pointers[];
} __attribute__((packed)) acpi_rsdt_t;
typedef struct {
acpi_sdt_header_t header;
uint64_t sdt_pointers[];
} __attribute__((packed)) acpi_xsdt_t;
typedef struct {
uint8_t address_space_id;
uint8_t register_bit_width;
uint8_t register_bit_offset;
uint8_t access_size;
uint64_t address;
} __attribute__((packed)) acpi_gas_t;
typedef struct {
acpi_sdt_header_t header;
uint32_t firmware_ctrl;
uint32_t dsdt;
uint8_t reserved;
uint8_t preferred_power_management_profile;
uint16_t sci_interrupt;
uint32_t smi_command_port;
uint8_t acpi_enable;
uint8_t acpi_disable;
uint8_t s4bios_req;
uint8_t pstate_control;
uint32_t pm1a_event_block;
uint32_t pm1b_event_block;
uint32_t pm1a_control_block;
uint32_t pm1b_control_block;
uint32_t pm2_control_block;
uint32_t pm_timer_block;
uint32_t gpe0_block;
uint32_t gpe1_block;
uint8_t pm1_event_length;
uint8_t pm1_control_length;
uint8_t pm2_control_length;
uint8_t pm_timer_length;
uint8_t gpe0_length;
uint8_t gpe1_length;
uint8_t gpe1_base;
uint8_t cstate_control;
uint16_t worst_c2_latency;
uint16_t worst_c3_latency;
uint16_t flush_size;
uint16_t flush_stride;
uint8_t duty_offset;
uint8_t duty_width;
uint8_t day_alarm;
uint8_t month_alarm;
uint8_t century;
uint16_t boot_architecture_flags;
uint8_t reserved2;
uint32_t flags;
acpi_gas_t reset_reg;
uint8_t reset_value;
uint16_t arm_boot_architecture_flags;
uint8_t fadt_minor_version;
uint64_t x_firmware_ctrl;
uint64_t x_dsdt;
acpi_gas_t x_pm1a_event_block;
acpi_gas_t x_pm1b_event_block;
acpi_gas_t x_pm1a_control_block;
acpi_gas_t x_pm1b_control_block;
acpi_gas_t x_pm2_control_block;
acpi_gas_t x_pm_timer_block;
acpi_gas_t x_gpe0_block;
acpi_gas_t x_gpe1_block;
} __attribute__((packed)) acpi_fadt_t;
typedef struct {
acpi_sdt_header_t header;
uint32_t local_apic_address;
uint32_t flags;
uint8_t entries[];
} __attribute__((packed)) acpi_madt_t;
#define MADT_ENTRY_LAPIC 0
#define MADT_ENTRY_IOAPIC 1
#define MADT_ENTRY_ISO 2
#define MADT_ENTRY_NMI 4
#define MADT_ENTRY_LAPIC_ADDR 5
#define MADT_ENTRY_IOAPIC_MMIO 6
typedef struct {
uint8_t type;
uint8_t length;
} __attribute__((packed)) madt_entry_header_t;
typedef struct {
madt_entry_header_t header;
uint8_t processor_id;
uint8_t apic_id;
uint32_t flags;
} __attribute__((packed)) madt_lapic_entry_t;
typedef struct {
madt_entry_header_t header;
uint8_t ioapic_id;
uint8_t reserved;
uint32_t ioapic_address;
uint32_t global_system_interrupt_base;
} __attribute__((packed)) madt_ioapic_entry_t;
typedef struct {
madt_entry_header_t header;
uint8_t bus;
uint8_t source;
uint32_t global_system_interrupt;
uint16_t flags;
} __attribute__((packed)) madt_iso_entry_t;
typedef struct {
acpi_sdt_header_t header;
uint8_t hardware_rev_id;
uint8_t comparator_count : 5;
uint8_t counter_size : 1;
uint8_t reserved : 1;
uint8_t legacy_replacement : 1;
uint16_t pci_vendor_id;
uint8_t address_space_id;
uint8_t register_bit_width;
uint8_t register_bit_offset;
uint8_t reserved2;
uint64_t address;
uint8_t hpet_number;
uint16_t minimum_tick;
uint8_t page_protection;
} __attribute__((packed)) acpi_hpet_t;
typedef struct {
acpi_sdt_header_t header;
uint64_t reserved;
} __attribute__((packed)) acpi_mcfg_t;
typedef struct {
uint64_t base_address;
uint16_t pci_segment_group;
uint8_t start_pci_bus;
uint8_t end_pci_bus;
uint32_t reserved;
} __attribute__((packed)) mcfg_entry_t;
typedef struct {
acpi_sdt_header_t header;
uint8_t definition_block[];
} __attribute__((packed)) acpi_ssdt_t;
void acpi_init(void);
bool acpi_is_available(void);
void* acpi_find_table(const char* signature, uint64_t index);
void acpi_print_tables(void);
extern volatile struct limine_rsdp_request rsdp_request;
void acpi_shutdown(void);
void acpi_reboot(void);
#endif
+113
View File
@@ -0,0 +1,113 @@
#ifndef APIC_H
#define APIC_H
#include <stdint.h>
#include <stdbool.h>
#include "../acpi/acpi.h"
#define LAPIC_ID 0x0020
#define LAPIC_VERSION 0x0030
#define LAPIC_TPR 0x0080
#define LAPIC_EOI 0x00B0
#define LAPIC_SIVR 0x00F0
#define LAPIC_ISR0 0x0100
#define LAPIC_TIMER 0x0320
#define LAPIC_THERMAL 0x0330
#define LAPIC_PERFORMANCE 0x0340
#define LAPIC_LINT0 0x0350
#define LAPIC_LINT1 0x0360
#define LAPIC_ERROR 0x0370
#define LAPIC_TIMER_ICR 0x0380
#define LAPIC_TIMER_CCR 0x0390
#define LAPIC_TIMER_DCR 0x03E0
#define LAPIC_ENABLE (1 << 8)
#define LAPIC_SPURIOUS_VECTOR 0xFF
#define LAPIC_TIMER_MASKED (1 << 16)
#define LAPIC_TIMER_PERIODIC (1 << 17)
#define LAPIC_TIMER_DIV1 0x0
#define LAPIC_TIMER_DIV2 0x1
#define LAPIC_TIMER_DIV4 0x2
#define LAPIC_TIMER_DIV8 0x3
#define LAPIC_TIMER_DIV16 0x4
#define LAPIC_TIMER_DIV32 0x5
#define LAPIC_TIMER_DIV64 0x6
#define LAPIC_TIMER_DIV128 0x7
#define IOAPIC_ID 0x00
#define IOAPIC_VERSION 0x01
#define IOAPIC_ARB 0x02
#define IOAPIC_REDIR_START 0x10
#define IOAPIC_INT_MASKED (1 << 16)
#define IOAPIC_TRIGGER_LEVEL (1 << 15)
#define IOAPIC_POLARITY_LOW (1 << 13)
#define IOAPIC_DELIVERY_FIXED 0x0
#define IOAPIC_DELIVERY_NMI 0x4
#define HPET_CAPABILITIES 0x000
#define HPET_PERIOD 0x004
#define HPET_CONFIG 0x010
#define HPET_INTERRUPT_STATUS 0x020
#define HPET_MAIN_COUNTER 0x0F0
#define HPET_TIMER0_CONFIG 0x100
#define HPET_TIMER0_COMPARATOR 0x108
#define HPET_ENABLE_CNF (1ULL << 0)
#define HPET_LEGACY_CNF (1ULL << 1)
#define HPET_TN_INT_ENABLE_CNF (1 << 2)
#define HPET_TN_INT_TYPE_CNF (1 << 3)
#define HPET_TN_PERIODIC_CNF (1 << 4)
#define HPET_TN_32BIT_CNF (1 << 8)
#define HPET_TN_FSB_ENABLE_CNF (1 << 14)
#define HPET_TN_FSB_INT_DEL_CNF (1 << 15)
void apic_init(void);
bool apic_is_available(void);
bool hpet_is_available(void);
bool hpet_init(void);
void lapic_write(uint32_t reg, uint32_t value);
uint32_t lapic_read(uint32_t reg);
void lapic_eoi(void);
void lapic_enable(void);
uint32_t lapic_get_id(void);
void lapic_timer_init(uint32_t vector, uint32_t count, bool periodic, uint8_t divisor);
void lapic_timer_stop(void);
uint32_t lapic_timer_get_current(void);
void lapic_send_ipi(uint32_t target_lapic_id, uint8_t vector);
void lapic_send_ipi_to_all_but_self(uint8_t vector);
void lapic_send_nmi_to_all_but_self(void);
void ioapic_write(uintptr_t base, uint32_t reg, uint32_t value);
uint32_t ioapic_read(uintptr_t base, uint32_t reg);
uint32_t ioapic_get_max_redirects(uintptr_t base);
void ioapic_redirect_irq(uint8_t irq, uint8_t vector, uint32_t flags);
void ioapic_mask_irq(uint8_t irq);
void ioapic_unmask_irq(uint8_t irq);
void apic_setup_irq(uint8_t irq, uint8_t vector, bool mask, uint32_t flags);
void apic_timer_calibrate(void);
uint64_t hpet_read_counter(void);
uint64_t hpet_get_frequency(void);
uint64_t hpet_elapsed_ns(void);
void hpet_sleep_ns(uint64_t nanoseconds);
void hpet_sleep_us(uint64_t microseconds);
void hpet_sleep_ms(uint64_t milliseconds);
extern uintptr_t lapic_base;
extern uintptr_t ioapic_base;
extern uintptr_t hpet_base;
extern uint32_t hpet_period;
extern uint64_t g_hpet_boot_counter;
void ipi_reschedule_all(void);
void ipi_reschedule_cpu(uint32_t lapic_id);
void ipi_reschedule_single(uint32_t target_lapic_id);
void ipi_tlb_shootdown_broadcast(const uintptr_t* addrs, size_t count);
void ipi_tlb_shootdown_single(uint32_t target_lapic_id, uintptr_t addr);
#endif
+77
View File
@@ -0,0 +1,77 @@
#ifndef ATA_H
#define ATA_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define ATA_PRIMARY_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6
#define ATA_SECONDARY_IO 0x170
#define ATA_SECONDARY_CTRL 0x376
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT 0x02
#define ATA_REG_LBA_LO 0x03
#define ATA_REG_LBA_MID 0x04
#define ATA_REG_LBA_HI 0x05
#define ATA_REG_DRIVE 0x06
#define ATA_REG_STATUS 0x07
#define ATA_REG_COMMAND 0x07
#define ATA_REG_ALT_STATUS 0x00
#define ATA_REG_DEV_CTRL 0x00
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_WRITE_PIO_EXT 0x34
#define ATA_CMD_CACHE_FLUSH 0xE7
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_DRIVE_MASTER 0xA0
#define ATA_DRIVE_SLAVE 0xB0
#define ATA_LBA_BIT 0x40
#define ATA_MAX_DRIVES 4
#define ATA_SECTOR_SIZE 512
typedef struct {
bool present;
bool is_atapi;
bool lba48;
uint16_t io_base;
uint16_t ctrl_base;
uint8_t drive_select;
uint8_t irq;
uint64_t sectors;
uint64_t size_bytes;
char model[41];
char serial[21];
char firmware[9];
uint16_t identify[256];
} ata_drive_t;
void ata_init(void);
ata_drive_t *ata_get_drive(int index);
int ata_get_drive_count(void);
int ata_read_sectors(ata_drive_t *drive, uint64_t lba, uint32_t count, void *buffer);
int ata_write_sectors(ata_drive_t *drive, uint64_t lba, uint32_t count, const void *buffer);
int ata_flush(ata_drive_t *drive);
#endif
+38
View File
@@ -0,0 +1,38 @@
#ifndef BLKDEV_H
#define BLKDEV_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define BLKDEV_MAX 8
#define BLKDEV_NAME_MAX 32
#define BLKDEV_SECTOR_SIZE 512
typedef struct blkdev blkdev_t;
typedef struct blkdev_ops {
int (*read_sectors) (blkdev_t *dev, uint64_t lba, uint32_t count, void *buf);
int (*write_sectors)(blkdev_t *dev, uint64_t lba, uint32_t count, const void *buf);
int (*flush) (blkdev_t *dev);
} blkdev_ops_t;
struct blkdev {
char name[BLKDEV_NAME_MAX];
bool present;
uint64_t sector_count;
uint64_t size_bytes;
uint32_t sector_size;
const blkdev_ops_t *ops;
void *priv;
};
int blkdev_register(blkdev_t *dev);
blkdev_t *blkdev_get_by_name(const char *name);
blkdev_t *blkdev_get(int index);
int blkdev_count(void);
void blkdev_init(void);
int blkdev_read(blkdev_t *dev, uint64_t offset, void *buf, size_t len);
int blkdev_write(blkdev_t *dev, uint64_t offset, const void *buf, size_t len);
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef DISK_H
#define DISK_H
#include "../drivers/blkdev.h"
void disk_init(void);
int disk_mount(const char *devname, const char *path);
int disk_umount(const char *path);
int disk_format(const char *devname, const char *label);
#endif
+52
View File
@@ -0,0 +1,52 @@
#ifndef PARTITION_H
#define PARTITION_H
#include <stdint.h>
#include <stdbool.h>
#include "blkdev.h"
#define MBR_SIGNATURE 0xAA55
#define MBR_SIGNATURE_OFF 510
#define MBR_PARTITION_OFF 446
#define MBR_MAX_PARTITIONS 4
#define MBR_TYPE_EMPTY 0x00
#define MBR_TYPE_FAT12 0x01
#define MBR_TYPE_FAT16_S 0x04
#define MBR_TYPE_EXTENDED 0x05
#define MBR_TYPE_FAT16 0x06
#define MBR_TYPE_FAT32_CHS 0x0B
#define MBR_TYPE_FAT32_LBA 0x0C
#define MBR_TYPE_FAT16_LBA 0x0E
#define MBR_TYPE_LINUX 0x83
#define MBR_TYPE_ESP 0xEF
typedef struct __attribute__((packed)) {
uint8_t boot_flag;
uint8_t chs_start[3];
uint8_t type;
uint8_t chs_end[3];
uint32_t lba_start;
uint32_t sector_count;
} mbr_partition_t;
typedef struct __attribute__((packed)) {
uint8_t bootstrap[440];
uint32_t disk_signature;
uint16_t reserved;
mbr_partition_t partitions[4];
uint16_t signature;
} mbr_t;
_Static_assert(sizeof(mbr_t) == 512, "mbr size must be 512");
int partition_scan(blkdev_t *disk);
int partition_write_mbr(blkdev_t *disk, const mbr_partition_t parts[4],
uint32_t disk_signature);
int partition_read_mbr(blkdev_t *disk, mbr_t *out);
blkdev_t *partition_get(const char *name);
#endif
+91
View File
@@ -0,0 +1,91 @@
#ifndef PS2_H
#define PS2_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_PORT 0x64
#define PS2_CMD_PORT 0x64
#define PS2_STATUS_OUTPUT_FULL (1 << 0)
#define PS2_STATUS_INPUT_FULL (1 << 1)
#define PS2_CMD_READ_CONFIG 0x20
#define PS2_CMD_WRITE_CONFIG 0x60
#define PS2_CMD_DISABLE_PORT2 0xA7
#define PS2_CMD_ENABLE_PORT2 0xA8
#define PS2_CMD_TEST_PORT2 0xA9
#define PS2_CMD_SELF_TEST 0xAA
#define PS2_CMD_TEST_PORT1 0xAB
#define PS2_CMD_DISABLE_PORT1 0xAD
#define PS2_CMD_ENABLE_PORT1 0xAE
#define PS2_CMD_WRITE_PORT2 0xD4
#define PS2_CFG_PORT1_IRQ (1 << 0)
#define PS2_CFG_PORT2_IRQ (1 << 1)
#define PS2_CFG_PORT1_XLAT (1 << 6)
#define PS2_KEY_RELEASE_BIT 0x80
#define SC_LSHIFT 0x2A
#define SC_RSHIFT 0x36
#define SC_CAPS 0x3A
#define SC_LCTRL 0x1D
#define SC_LALT 0x38
#define SC_ESCAPE 0x01
#define SC_F1 0x3B
#define SC_F2 0x3C
#define SC_F3 0x3D
#define SC_F4 0x3E
#define SC_F5 0x3F
#define SC_F6 0x40
#define SC_F7 0x41
#define SC_F8 0x42
#define SC_F9 0x43
#define SC_F10 0x44
#define SC_F11 0x57
#define SC_F12 0x58
#define MOUSE_BTN_LEFT (1 << 0)
#define MOUSE_BTN_RIGHT (1 << 1)
#define MOUSE_BTN_MIDDLE (1 << 2)
#define MOUSE_X_SIGN (1 << 4)
#define MOUSE_Y_SIGN (1 << 5)
#define MOUSE_X_OVERFLOW (1 << 6)
#define MOUSE_Y_OVERFLOW (1 << 7)
typedef enum {
MOUSE_SCROLL_NONE = 0,
MOUSE_SCROLL_UP,
MOUSE_SCROLL_DOWN,
} mouse_scroll_t;
typedef struct {
int32_t x, y;
bool btn_left, btn_right, btn_middle;
mouse_scroll_t scroll;
} mouse_state_t;
typedef struct {
bool shift, caps_lock, ctrl, alt;
} kb_state_t;
#define KB_BUF_SIZE 64
typedef struct {
char buf[KB_BUF_SIZE];
uint8_t head, tail;
} kb_buf_t;
bool ps2_init(void);
const kb_state_t* ps2_kb_get_state(void);
const mouse_state_t* ps2_mouse_get_state(void);
bool kb_buf_empty(void);
char kb_buf_getc(void);
bool kb_buf_try_getc(char *out);
bool kb_buf_has_ctrlc(void);
void kb_buf_consume_ctrlc(void);
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef TIMER_H
#define TIMER_H
#include <stdint.h>
#include <stdbool.h>
bool timer_init(void);
uint64_t timer_get_ticks(void);
void timer_sleep_ms(uint64_t milliseconds);
void timer_sleep_us(uint64_t microseconds);
void timer_sleep_ns(uint64_t nanoseconds);
extern volatile uint32_t g_ctrlc_pending;
#endif
+118
View File
@@ -0,0 +1,118 @@
#ifndef ELF_H
#define ELF_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../memory/vmm.h"
#include "../memory/paging.h"
#define ELF_MAGIC 0x464C457F
#define EI_CLASS 4
#define EI_DATA 5
#define EI_VERSION 6
#define EI_OSABI 7
#define EI_NIDENT 16
#define ELFCLASS64 2
#define ELFDATA2LSB 1
#define EV_CURRENT 1
#define ET_EXEC 2
#define ET_DYN 3
#define EM_X86_64 62
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_PHDR 6
#define PT_TLS 7
#define PF_X (1 << 0)
#define PF_W (1 << 1)
#define PF_R (1 << 2)
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_NOBITS 8
typedef struct {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry;
uint64_t e_phoff;
uint64_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} __attribute__((packed)) elf64_ehdr_t;
typedef struct {
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset;
uint64_t p_vaddr;
uint64_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} __attribute__((packed)) elf64_phdr_t;
typedef struct {
uint32_t sh_name;
uint32_t sh_type;
uint64_t sh_flags;
uint64_t sh_addr;
uint64_t sh_offset;
uint64_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint64_t sh_addralign;
uint64_t sh_entsize;
} __attribute__((packed)) elf64_shdr_t;
typedef enum {
ELF_OK = 0,
ELF_ERR_NULL,
ELF_ERR_TOO_SMALL,
ELF_ERR_BAD_MAGIC,
ELF_ERR_NOT_64,
ELF_ERR_NOT_LE,
ELF_ERR_BAD_VERSION,
ELF_ERR_NOT_EXEC,
ELF_ERR_WRONG_ARCH,
ELF_ERR_NO_LOAD,
ELF_ERR_MAP_FAIL,
ELF_ERR_NO_MEM,
} elf_error_t;
typedef struct {
uintptr_t entry;
vmm_pagemap_t* pagemap;
uintptr_t load_base;
uintptr_t stack_top;
size_t stack_size;
elf_error_t error;
uintptr_t load_end;
} elf_load_result_t;
elf_load_result_t elf_load(const void* data, size_t size, size_t stack_sz);
void elf_unload(elf_load_result_t* result);
const char* elf_strerror(elf_error_t err);
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef DEVFS_H
#define DEVFS_H
#include "vfs.h"
vnode_t *devfs_create_root(void);
void devfs_register(const char *name, vnode_t *node);
#endif
+161
View File
@@ -0,0 +1,161 @@
#ifndef EXT2_H
#define EXT2_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../drivers/blkdev.h"
#include "vfs.h"
#define EXT2_SUPER_MAGIC 0xEF53
#define EXT2_SUPER_OFFSET 1024
#define EXT2_ROOT_INO 2
#define EXT2_GOOD_OLD_REV 0
#define EXT2_DYNAMIC_REV 1
#define EXT2_VALID_FS 1
#define EXT2_ERROR_FS 2
#define EXT2_S_IFSOCK 0xC000
#define EXT2_S_IFLNK 0xA000
#define EXT2_S_IFREG 0x8000
#define EXT2_S_IFBLK 0x6000
#define EXT2_S_IFDIR 0x4000
#define EXT2_S_IFCHR 0x2000
#define EXT2_S_IFIFO 0x1000
#define EXT2_FT_UNKNOWN 0
#define EXT2_FT_REG_FILE 1
#define EXT2_FT_DIR 2
#define EXT2_FT_CHRDEV 3
#define EXT2_FT_BLKDEV 4
#define EXT2_FT_FIFO 5
#define EXT2_FT_SOCK 6
#define EXT2_FT_SYMLINK 7
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK 12
#define EXT2_DIND_BLOCK 13
#define EXT2_TIND_BLOCK 14
#define EXT2_N_BLOCKS 15
#define EXT2_NAME_LEN 255
typedef struct __attribute__((packed)) {
uint32_t s_inodes_count;
uint32_t s_blocks_count;
uint32_t s_r_blocks_count;
uint32_t s_free_blocks_count;
uint32_t s_free_inodes_count;
uint32_t s_first_data_block;
uint32_t s_log_block_size;
uint32_t s_log_frag_size;
uint32_t s_blocks_per_group;
uint32_t s_frags_per_group;
uint32_t s_inodes_per_group;
uint32_t s_mtime;
uint32_t s_wtime;
uint16_t s_mnt_count;
uint16_t s_max_mnt_count;
uint16_t s_magic;
uint16_t s_state;
uint16_t s_errors;
uint16_t s_minor_rev_level;
uint32_t s_lastcheck;
uint32_t s_checkinterval;
uint32_t s_creator_os;
uint32_t s_rev_level;
uint16_t s_def_resuid;
uint16_t s_def_resgid;
uint32_t s_first_ino;
uint16_t s_inode_size;
uint16_t s_block_group_nr;
uint32_t s_feature_compat;
uint32_t s_feature_incompat;
uint32_t s_feature_ro_compat;
uint8_t s_uuid[16];
char s_volume_name[16];
char s_last_mounted[64];
uint32_t s_algo_bitmap;
uint8_t s_prealloc_blocks;
uint8_t s_prealloc_dir_blocks;
uint16_t s_padding1;
uint8_t s_journal_uuid[16];
uint32_t s_journal_inum;
uint32_t s_journal_dev;
uint32_t s_last_orphan;
uint32_t s_hash_seed[4];
uint8_t s_def_hash_version;
uint8_t s_reserved_pad[3];
uint32_t s_default_mount_options;
uint32_t s_first_meta_bg;
uint8_t s_reserved[760];
} ext2_superblock_t;
_Static_assert(sizeof(ext2_superblock_t) == 1024, "ext2 superblock size");
typedef struct __attribute__((packed)) {
uint32_t bg_block_bitmap;
uint32_t bg_inode_bitmap;
uint32_t bg_inode_table;
uint16_t bg_free_blocks_count;
uint16_t bg_free_inodes_count;
uint16_t bg_used_dirs_count;
uint16_t bg_pad;
uint8_t bg_reserved[12];
} ext2_group_desc_t;
_Static_assert(sizeof(ext2_group_desc_t) == 32, "ext2 group desc size");
typedef struct __attribute__((packed)) {
uint16_t i_mode;
uint16_t i_uid;
uint32_t i_size;
uint32_t i_atime;
uint32_t i_ctime;
uint32_t i_mtime;
uint32_t i_dtime;
uint16_t i_gid;
uint16_t i_links_count;
uint32_t i_blocks;
uint32_t i_flags;
uint32_t i_osd1;
uint32_t i_block[EXT2_N_BLOCKS];
uint32_t i_generation;
uint32_t i_file_acl;
uint32_t i_dir_acl;
uint32_t i_faddr;
uint8_t i_osd2[12];
} ext2_inode_t;
_Static_assert(sizeof(ext2_inode_t) == 128, "ext2 inode size");
typedef struct __attribute__((packed)) {
uint32_t inode;
uint16_t rec_len;
uint8_t name_len;
uint8_t file_type;
char name[EXT2_NAME_LEN];
} ext2_dir_entry_t;
typedef struct {
blkdev_t *dev;
ext2_superblock_t sb;
ext2_group_desc_t *gdt;
uint32_t block_size;
uint32_t groups_count;
uint32_t inodes_per_block;
uint32_t inode_size;
uint32_t ptrs_per_block;
bool dirty;
} ext2_t;
typedef struct {
ext2_t *fs;
uint32_t ino;
} ext2_vdata_t;
int ext2_format(blkdev_t *dev, const char *label);
vnode_t *ext2_mount(blkdev_t *dev);
void ext2_unmount(ext2_t *fs);
void ext2_sync(ext2_t *fs);
#endif
+146
View File
@@ -0,0 +1,146 @@
#ifndef FAT32_H
#define FAT32_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../drivers/blkdev.h"
#include "vfs.h"
#define FAT32_SECTOR_SIZE 512
#define FAT32_EOC 0x0FFFFFFF
#define FAT32_EOC_MIN 0x0FFFFFF8
#define FAT32_FREE_CLUSTER 0x00000000
#define FAT32_BAD_CLUSTER 0x0FFFFFF7
#define FAT32_CLUSTER_MASK 0x0FFFFFFF
#define FAT32_FSINFO_LEAD_SIG 0x41615252
#define FAT32_FSINFO_STRUCT_SIG 0x61417272
#define FAT32_FSINFO_TRAIL_SIG 0xAA550000
#define FAT_ATTR_READ_ONLY 0x01
#define FAT_ATTR_HIDDEN 0x02
#define FAT_ATTR_SYSTEM 0x04
#define FAT_ATTR_VOLUME_ID 0x08
#define FAT_ATTR_DIRECTORY 0x10
#define FAT_ATTR_ARCHIVE 0x20
#define FAT_ATTR_LONG_NAME 0x0F
typedef struct __attribute__((packed)) {
uint8_t bs_jmp[3];
uint8_t bs_oem[8];
uint16_t bpb_bytes_per_sector;
uint8_t bpb_sectors_per_cluster;
uint16_t bpb_reserved_sectors;
uint8_t bpb_num_fats;
uint16_t bpb_root_entries;
uint16_t bpb_total_sectors_16;
uint8_t bpb_media;
uint16_t bpb_fat_size_16;
uint16_t bpb_sectors_per_track;
uint16_t bpb_num_heads;
uint32_t bpb_hidden_sectors;
uint32_t bpb_total_sectors_32;
uint32_t bpb_fat_size_32;
uint16_t bpb_ext_flags;
uint16_t bpb_fs_version;
uint32_t bpb_root_cluster;
uint16_t bpb_fs_info;
uint16_t bpb_backup_boot;
uint8_t bpb_reserved[12];
uint8_t bs_drive_num;
uint8_t bs_reserved1;
uint8_t bs_boot_sig;
uint32_t bs_vol_id;
uint8_t bs_vol_label[11];
uint8_t bs_fs_type[8];
uint8_t bs_boot_code[420];
uint16_t bs_signature;
} fat32_bpb_t;
_Static_assert(sizeof(fat32_bpb_t) == 512, "fat32 bpb size");
typedef struct __attribute__((packed)) {
uint32_t lead_sig;
uint8_t reserved1[480];
uint32_t struct_sig;
uint32_t free_count;
uint32_t next_free;
uint8_t reserved2[12];
uint32_t trail_sig;
} fat32_fsinfo_t;
_Static_assert(sizeof(fat32_fsinfo_t) == 512, "fat32 fsinfo size");
typedef struct __attribute__((packed)) {
uint8_t name[11];
uint8_t attr;
uint8_t nt_reserved;
uint8_t create_time_tenth;
uint16_t create_time;
uint16_t create_date;
uint16_t access_date;
uint16_t cluster_hi;
uint16_t modify_time;
uint16_t modify_date;
uint16_t cluster_lo;
uint32_t file_size;
} fat32_dirent_t;
_Static_assert(sizeof(fat32_dirent_t) == 32, "fat32 dirent size");
typedef struct __attribute__((packed)) {
uint8_t order;
uint8_t name1[10];
uint8_t attr;
uint8_t type;
uint8_t checksum;
uint8_t name2[12];
uint16_t cluster_lo;
uint8_t name3[4];
} fat32_lfn_t;
_Static_assert(sizeof(fat32_lfn_t) == 32, "fat32 lfn size");
typedef struct {
blkdev_t *dev;
uint32_t bytes_per_sector;
uint32_t sectors_per_cluster;
uint32_t bytes_per_cluster;
uint32_t reserved_sectors;
uint32_t num_fats;
uint32_t fat_size_sectors;
uint32_t total_sectors;
uint32_t root_cluster;
uint32_t first_fat_sector;
uint32_t first_data_sector;
uint32_t total_clusters;
uint32_t fsinfo_sector;
uint32_t next_free_hint;
uint32_t free_count;
bool dirty;
bool readonly;
uint8_t *shared_buf;
} fat32_t;
typedef struct {
fat32_t *fs;
uint32_t first_cluster;
uint32_t dir_cluster;
uint32_t dir_entry_offset;
uint32_t file_size;
uint8_t attr;
uint32_t cached_cluster;
uint32_t cached_index;
bool size_dirty;
uint8_t *io_buf;
} fat32_vdata_t;
int fat32_format(blkdev_t *dev, const char *label);
vnode_t *fat32_mount(blkdev_t *dev);
void fat32_unmount(fat32_t *fs);
int fat32_sync(fat32_t *fs);
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef INITRAMFS_H
#define INITRAMFS_H
#include <stddef.h>
#include <stdint.h>
int initramfs_mount(const void *data, size_t size);
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef RAMFS_H
#define RAMFS_H
#include "vfs.h"
#define RAMFS_MAX_CHILDREN 64
#define RAMFS_MAX_FILE_SIZE (4 * 1024 * 1024)
vnode_t *ramfs_create_root(void);
#endif
+181
View File
@@ -0,0 +1,181 @@
#ifndef VFS_H
#define VFS_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "../syscall/errno.h"
#define VFS_MAX_PATH 512
#define VFS_MAX_NAME 256
#define VFS_MAX_MOUNTS 16
#define VFS_MAX_OPEN_FILES 512
#define O_RDONLY 0x000
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_ACCMODE 0x003
#define O_CREAT 0x040
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_DIRECTORY 0x10000
#define O_NONBLOCK 0x800
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#define FD_CLOEXEC (1 << 0)
#define TASK_MAX_FDS 256
typedef enum {
VFS_NODE_FILE = 0,
VFS_NODE_DIR = 1,
VFS_NODE_CHARDEV = 2,
VFS_NODE_BLKDEV = 3,
VFS_NODE_SYMLINK = 4,
VFS_NODE_PIPE = 5,
} vnode_type_t;
typedef struct {
uint64_t st_ino;
vnode_type_t st_type;
uint32_t st_mode;
uint32_t st_uid;
uint32_t st_gid;
uint64_t st_size;
uint64_t st_blocks;
} vfs_stat_t;
typedef struct {
uint64_t d_ino;
uint8_t d_type;
char d_name[VFS_MAX_NAME];
} vfs_dirent_t;
typedef struct vnode vnode_t;
typedef struct vfs_mount vfs_mount_t;
typedef struct vnode_ops {
int64_t (*read) (vnode_t *node, void *buf, size_t len, uint64_t offset);
int64_t (*write) (vnode_t *node, const void *buf, size_t len, uint64_t offset);
int (*truncate)(vnode_t *node, uint64_t new_size);
int (*lookup) (vnode_t *dir, const char *name, vnode_t **out);
int (*readdir) (vnode_t *dir, uint64_t index, vfs_dirent_t *out);
int (*mkdir) (vnode_t *dir, const char *name, uint32_t mode);
int (*create) (vnode_t *dir, const char *name, uint32_t mode, vnode_t **out);
int (*unlink) (vnode_t *dir, const char *name);
int (*rename) (vnode_t *src_dir, const char *src_name,
vnode_t *dst_dir, const char *dst_name);
int (*stat) (vnode_t *node, vfs_stat_t *out);
void (*ref) (vnode_t *node);
void (*unref) (vnode_t *node);
int64_t (*ioctl) (vnode_t *node, uint64_t req, void *arg);
} vnode_ops_t;
struct vnode {
vnode_type_t type;
uint32_t mode;
uint32_t uid;
uint32_t gid;
uint64_t size;
uint64_t ino;
const vnode_ops_t *ops;
void *fs_data;
volatile int refcount;
vfs_mount_t *mounted;
};
struct vfs_mount {
char path[VFS_MAX_PATH];
char device[32];
char fstype[16];
vnode_t *root;
bool used;
void *fs_priv;
void (*unmount)(void *fs_priv);
void (*sync)(void *fs_priv);
};
typedef struct {
vnode_t *vnode;
uint64_t offset;
int flags;
volatile int refcount;
} vfs_file_t;
typedef struct {
vfs_file_t *file;
int fd_flags;
} fd_entry_t;
typedef struct fd_table fd_table_t;
struct fd_table {
fd_entry_t entries[TASK_MAX_FDS];
};
void vfs_init (void);
int vfs_mount (const char *path, vnode_t *fs_root);
int vfs_mount_fs(const char *path, vnode_t *fs_root,
void *fs_priv, void (*unmount_fn)(void *),
void (*sync_fn)(void *));
int vfs_umount (const char *path);
int vfs_lookup (const char *path, vnode_t **out);
int vfs_open (const char *path, int flags, uint32_t mode, vfs_file_t **out);
void vfs_close (vfs_file_t *file);
int64_t vfs_read (vfs_file_t *file, void *buf, size_t len);
int64_t vfs_write (vfs_file_t *file, const void *buf, size_t len);
int64_t vfs_seek (vfs_file_t *file, int64_t offset, int whence);
int vfs_stat (const char *path, vfs_stat_t *out);
int vfs_fstat (vfs_file_t *file, vfs_stat_t *out);
int64_t vfs_ioctl (vfs_file_t *file, uint64_t req, void *arg);
int vfs_readdir(vfs_file_t *file, vfs_dirent_t *out);
int vfs_mkdir (const char *path, uint32_t mode);
void vnode_ref (vnode_t *node);
void vnode_unref (vnode_t *node);
vfs_file_t *vfs_file_alloc(void);
void vfs_file_free (vfs_file_t *file);
fd_table_t *fd_table_create (void);
fd_table_t *fd_table_clone (const fd_table_t *src);
void fd_table_cloexec(fd_table_t *table);
void fd_table_destroy(fd_table_t *table);
int fd_alloc (fd_table_t *table, vfs_file_t *file, int min_fd);
vfs_file_t *fd_get (const fd_table_t *table, int fd);
int fd_close (fd_table_t *table, int fd);
int fd_dup2 (fd_table_t *table, int oldfd, int newfd);
int fd_set_flags(fd_table_t *table, int fd, int flags);
int fd_get_flags(const fd_table_t *table, int fd);
void vfs_sync_all(void);
int vfs_init_stdio(void *task_ptr);
typedef struct {
char path[VFS_MAX_PATH];
char device[32];
char fstype[16];
uint32_t flags;
} vfs_mount_info_t;
int vfs_set_mount_info(const char *path, const char *device, const char *fstype);
int vfs_list_mounts(vfs_mount_info_t *out, int max);
typedef struct {
uint64_t f_bsize;
uint64_t f_blocks;
uint64_t f_bfree;
uint64_t f_bavail;
uint64_t f_files;
uint64_t f_ffree;
uint32_t f_flag;
uint32_t f_namemax;
} vfs_statvfs_t;
int vfs_statvfs(const char *path, vfs_statvfs_t *out);
#endif
+78
View File
@@ -0,0 +1,78 @@
#ifndef GDT_H
#define GDT_H
#include <stdint.h>
#define GDT_CODE_SEGMENT 0x08
#define GDT_DATA_SEGMENT 0x10
#define GDT_USER_DATA_SEGMENT 0x18
#define GDT_USER_CODE_SEGMENT 0x20
#define GDT_USER_DATA_RPL3 (GDT_USER_DATA_SEGMENT | 3)
#define GDT_USER_CODE_RPL3 (GDT_USER_CODE_SEGMENT | 3)
#define GDT_STAR_SYSRET_BASE GDT_DATA_SEGMENT
#define GDT_STAR_SYSCALL_CS GDT_CODE_SEGMENT
#define TSS_SELECTOR_BASE 0x28
#define KERNEL_STACK_SIZE (4096 * 8)
#define KERNEL_STACK_PAGES (KERNEL_STACK_SIZE / 0x1000)
#define GDT_LIMIT_LOW(limit) (limit & 0xFFFF)
#define GDT_BASE_LOW(base) (base & 0xFFFF)
#define GDT_BASE_MIDDLE(base) ((base >> 16) & 0xFF)
#define GDT_FLAGS_HI_LIMIT(limit, flags) \
(((limit >> 16) & 0xF) | ((flags << 4) & 0xF0))
#define GDT_BASE_HIGH(base) ((base >> 24) & 0xFF)
#define GDT_ENTRY(base, limit, access, flags) \
{GDT_LIMIT_LOW(limit), \
GDT_BASE_LOW(base), \
GDT_BASE_MIDDLE(base), \
access, \
GDT_FLAGS_HI_LIMIT(limit, flags), \
GDT_BASE_HIGH(base)}
typedef struct {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t reserved1;
uint64_t ist[7];
uint64_t reserved2;
uint32_t iobase;
} __attribute__((packed)) tss_t;
typedef struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t limit_high_and_flags;
uint8_t base_high;
} __attribute__((packed)) gdt_entry_t;
typedef struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t limit_high_and_flags;
uint8_t base_high;
uint32_t base_higher;
uint32_t zero;
} __attribute__((packed)) tss_entry_t;
typedef struct {
uint16_t size;
gdt_entry_t *pointer;
} __attribute__((packed)) gdt_pointer_t;
extern gdt_pointer_t gdtr;
extern void load_tss(uint16_t sel);
void gdt_init(void);
void gdt_load(void);
#endif
+58
View File
@@ -0,0 +1,58 @@
#ifndef FB_H
#define FB_H
#include <stdint.h>
#include <stddef.h>
#include <limine.h>
#define RGB(r, g, b) (((r) << 16) | ((g) << 8) | (b))
#define COLOR_BLACK RGB( 0, 0, 0)
#define COLOR_WHITE RGB(255, 255, 255)
#define COLOR_RED RGB(255, 0, 0)
#define COLOR_GREEN RGB( 0, 255, 0)
#define COLOR_BLUE RGB( 0, 0, 255)
#define COLOR_CYAN RGB( 0, 255, 255)
#define COLOR_MAGENTA RGB(255, 0, 255)
#define COLOR_YELLOW RGB(255, 255, 0)
#define COLOR_ORANGE RGB(255, 165, 0)
#define COLOR_GRAY RGB(128, 128, 128)
#define COLOR_DARKGRAY RGB( 64, 64, 64)
#define COLOR_BROWN RGB(165, 42, 42)
struct psf_header {
uint32_t magic;
uint32_t version;
uint32_t headersize;
uint32_t flags;
uint32_t numglyph;
uint32_t bytesperglyph;
uint32_t height;
uint32_t width;
} __attribute__((packed));
extern uint8_t _binary_font_psf_start[];
extern uint8_t _binary_font_psf_end[];
static inline const uint8_t* get_font_data(void) {
return (const uint8_t*)&_binary_font_psf_start;
}
static inline size_t get_font_data_size(void) {
return _binary_font_psf_end - _binary_font_psf_start;
}
static inline const struct psf_header* get_psf_header(void) {
return (const struct psf_header*)&_binary_font_psf_start;
}
int psf_validate(void);
void fb_draw_pixel(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t color);
void fb_fill_rect(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color);
void fb_clear(struct limine_framebuffer *fb, uint32_t color);
void fb_draw_char(struct limine_framebuffer *fb, char c, uint32_t x, uint32_t y, uint32_t color);
void fb_draw_string(struct limine_framebuffer *fb, const char *str, uint32_t x, uint32_t y, uint32_t color);
void fb_init_backbuffer(struct limine_framebuffer *fb);
void fb_flush(struct limine_framebuffer *fb);
void fb_flush_lines(struct limine_framebuffer *fb, uint32_t y_start, uint32_t y_end);
#endif
+47
View File
@@ -0,0 +1,47 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
#include <stdbool.h>
#define IDT_MAX_DESCRIPTORS 256
#define IS_FLAG_SETTED(x, flag) ((x) | (flag))
#define FLAG_SET(x, flag) ((x) |= (flag))
#define FLAG_UNSET(x, flag) ((x) &= ~(flag))
typedef enum {
IDT_FLAG_GATE_TASK = 0x5,
IDT_FLAG_GATE_16BIT_INT = 0x6,
IDT_FLAG_GATE_16BIT_TRAP = 0x7,
IDT_FLAG_GATE_32BIT_INT = 0xE,
IDT_FLAG_GATE_32BIT_TRAP = 0xF,
IDT_FLAG_RING0 = (0 << 5),
IDT_FLAG_RING1 = (1 << 5),
IDT_FLAG_RING2 = (2 << 5),
IDT_FLAG_RING3 = (3 << 5),
IDT_FLAG_PRESENT = 0x80,
} IDT_FLAGS;
typedef struct {
uint16_t base_low;
uint16_t kernel_cs;
uint8_t ist;
uint8_t attributes;
uint16_t base_mid;
uint32_t base_high;
uint32_t reserved;
}__attribute__((packed)) idt_entry_t;
typedef struct {
uint16_t limit;
idt_entry_t *base;
}__attribute__((packed)) idtr_t;
extern idtr_t idtr;
void setup_interrupt_descriptor_table(uint64_t kernel_code_segment);
void idt_load(void);
#endif
+77
View File
@@ -0,0 +1,77 @@
#ifndef INTERRUPTS_H
#define INTERRUPTS_H
#include <stdint.h>
struct int_frame_t {
uint64_t ds;
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rbp;
uint64_t rdi;
uint64_t rsi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
uint64_t interrupt;
uint64_t error;
uint64_t rip;
uint64_t cs;
uint64_t rflags;
uint64_t rsp;
uint64_t ss;
} __attribute__((packed));
typedef void (*int_handler_f)(struct int_frame_t* frame);
typedef struct {
uint64_t vector;
int_handler_f handler;
} int_desc_t;
#define __CHECK_HANDLER(fn) \
_Static_assert( \
__builtin_types_compatible_p( \
typeof(fn), void (*)(struct int_frame_t *)), \
"Invalid interrupt handler signature")
#define __CONCAT(a, b) a##b
#define __UNIQUE_NAME(base) __CONCAT(base, __COUNTER__)
#define DEFINE_ISR(_vector, _name) \
static void _name(struct int_frame_t *frame); \
static const int_desc_t __UNIQUE_NAME(__isr_desc_##_name) \
__attribute__((used, section(".isr_handlers"))) = { \
.vector = (_vector), \
.handler = _name, \
}; \
static void _name(struct int_frame_t *frame)
#define DEFINE_IRQ(_vector, _name) \
static void _name(struct int_frame_t *frame); \
static const int_desc_t __UNIQUE_NAME(__irq_desc_##_name) \
__attribute__((used, section(".irq_handlers"))) = { \
.vector = (_vector), \
.handler = _name, \
}; \
static void _name(struct int_frame_t *frame)
#define IPI_RESCHEDULE_VECTOR 0x40
#define IPI_TLB_SHOOTDOWN 0x41
void init_interrupt_system();
#endif
+31
View File
@@ -0,0 +1,31 @@
#ifndef IRQ_H
#define IRQ_H
#include <stdint.h>
#include "interrupts.h"
#define IRQ_INTERRUPTS_COUNT 224
static const char* irq_default_names[] __attribute__((unused)) = {
"IRQ0 timer",
"IRQ1 keyboard",
"IRQ2 cascade",
"IRQ3 COM2",
"IRQ4 COM1",
"IRQ5 LPT2",
"IRQ6 floppy",
"IRQ7 LPT1",
"IRQ8 RTC",
"IRQ9 ACPI",
"IRQ10 reserved",
"IRQ11 reserved",
"IRQ12 mouse",
"IRQ13 FPU",
"IRQ14 ATA1",
"IRQ15 ATA2"
};
void irq_common_handler(struct int_frame_t* regs);
void setup_defined_irq_handlers(void);
#endif
+81
View File
@@ -0,0 +1,81 @@
#ifndef ISR_H
#define ISR_H
#include "interrupts.h"
#define ISR_EXCEPTION_COUNT 32
enum {
EXCEPTION_DIVIDE_ERROR = 0,
EXCEPTION_DEBUG,
EXCEPTION_NMI,
EXCEPTION_BREAKPOINT,
EXCEPTION_OVERFLOW,
EXCEPTION_BOUND_RANGE,
EXCEPTION_INVALID_OPCODE,
EXCEPTION_DEVICE_NOT_AVAILABLE,
EXCEPTION_DOUBLE_FAULT,
EXCEPTION_COPROCESSOR_SEGMENT_OVERRUN,
EXCEPTION_INVALID_TSS,
EXCEPTION_SEGMENT_NOT_PRESENT,
EXCEPTION_STACK_SEGMENT_FAULT,
EXCEPTION_GENERAL_PROTECTION_FAULT,
EXCEPTION_PAGE_FAULT,
EXCEPTION_RESERVED15,
EXCEPTION_X87_FPU_ERROR,
EXCEPTION_ALIGNMENT_CHECK,
EXCEPTION_MACHINE_CHECK,
EXCEPTION_SIMD_FPU_EXCEPTION,
EXCEPTION_VIRTUALIZATION_EXCEPTION,
EXCEPTION_RESERVED21,
EXCEPTION_RESERVED22,
EXCEPTION_RESERVED23,
EXCEPTION_RESERVED24,
EXCEPTION_RESERVED25,
EXCEPTION_RESERVED26,
EXCEPTION_RESERVED27,
EXCEPTION_RESERVED28,
EXCEPTION_RESERVED29,
EXCEPTION_SECURITY_EXCEPTION = 30,
EXCEPTION_RESERVED31
};
static const char* exception_names[] __attribute__((unused)) = {
"Divide Error",
"Debug",
"Non-Maskable Interrupt",
"Breakpoint",
"Overflow",
"Bound Range Exceeded",
"Invalid Opcode",
"Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack Segment Fault",
"General Protection Fault",
"Page Fault",
"Reserved",
"x87 Floating-Point Exception",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Security Exception",
"Reserved"
};
void isr_common_handler(struct int_frame_t* regs);
void setup_defined_isr_handlers(void);
#endif
+40
View File
@@ -0,0 +1,40 @@
#ifndef PORTS_H
#define PORTS_H
#include <stdint.h>
static inline uint8_t inb(uint16_t port) {
uint8_t result;
asm volatile ("inb %1, %0" : "=a"(result) : "Nd"(port));
return result;
}
static inline void outb(uint16_t port, uint8_t data) {
asm volatile ("outb %0, %1" : : "a"(data), "Nd"(port));
}
static inline uint16_t inw(uint16_t port) {
uint16_t result;
asm volatile ("inw %1, %0" : "=a"(result) : "Nd"(port));
return result;
}
static inline void outw(uint16_t port, uint16_t data) {
asm volatile ("outw %0, %1" : : "a"(data), "Nd"(port));
}
static inline uint32_t inl(uint16_t port) {
uint32_t result;
asm volatile ("inl %1, %0" : "=a"(result) : "Nd"(port));
return result;
}
static inline void outl(uint16_t port, uint32_t data) {
asm volatile ("outl %0, %1" : : "a"(data), "Nd"(port));
}
static inline void io_wait(void) {
outb(0x80, 0);
}
#endif
+51
View File
@@ -0,0 +1,51 @@
#ifndef SERIAL_H
#define SERIAL_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdarg.h>
#define COM1 0x3F8
#define COM2 0x2F8
#define COM3 0x3E8
#define COM4 0x2E8
#define SERIAL_DATA_PORT(base) (base)
#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2)
#define SERIAL_LINE_COMMAND_PORT(base) (base + 3)
#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4)
#define SERIAL_LINE_STATUS_PORT(base) (base + 5)
#define SERIAL_LSR_DATA_READY 0x01
#define SERIAL_LSR_OVERRUN_ERROR 0x02
#define SERIAL_LSR_PARITY_ERROR 0x04
#define SERIAL_LSR_FRAMING_ERROR 0x08
#define SERIAL_LSR_BREAK_INDICATOR 0x10
#define SERIAL_LSR_TRANSMIT_HOLDING_EMPTY 0x20
#define SERIAL_LSR_TRANSMIT_EMPTY 0x40
#define SERIAL_LSR_FIFO_ERROR 0x80
void serial_initialize(uint16_t port, uint32_t baud_rate);
int serial_received_port(uint16_t port);
char serial_read_port(uint16_t port);
int serial_is_transmit_empty_port(uint16_t port);
void serial_write_port(uint16_t port, char c);
void serial_writestring_port(uint16_t port, const char* str);
void serial_printf_port(uint16_t port, const char* format, ...);
int serial_received(void);
char serial_read(void);
int serial_is_transmit_empty(void);
void serial_write(char c);
void serial_writestring(const char* str);
void serial_writebuf(const char* buf, size_t len);
void serial_printf(const char* format, ...);
uint16_t serial_get_default_port(void);
void serial_set_default_port(uint16_t port);
void serial_force_unlock(void);
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef PAGING_H
#define PAGING_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "vmm.h"
#include "../include/interrupts/interrupts.h"
#define PAGING_PRESENT VMM_PRESENT
#define PAGING_WRITE VMM_WRITE
#define PAGING_USER VMM_USER
#define PAGING_NOEXEC VMM_NOEXEC
#define PAGING_KERNEL (PAGING_PRESENT | PAGING_WRITE)
#define PAGING_USER_RW (PAGING_PRESENT | PAGING_WRITE | PAGING_USER)
#define PAGING_USER_RO (PAGING_PRESENT | PAGING_USER)
#define PAGING_USER_NOEXEC (PAGING_PRESENT | PAGING_USER | PAGING_NOEXEC)
#define PAGING_LARGE_PAGE_SIZE 0x200000
#define PAGING_HUGE_PAGE_SIZE 0x40000000
typedef struct {
uintptr_t virtual_start;
uintptr_t virtual_end;
uintptr_t physical_start;
uint64_t flags;
size_t page_count;
bool allocated;
} paging_region_t;
void paging_init(void);
bool paging_map_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t phys_start, size_t page_count, uint64_t flags);
bool paging_unmap_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t page_count);
bool paging_change_flags(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t page_count, uint64_t new_flags);
paging_region_t* paging_create_region(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t size, uint64_t flags);
bool paging_destroy_region(vmm_pagemap_t* pagemap, paging_region_t* region);
void* paging_alloc_pages(vmm_pagemap_t* pagemap, size_t page_count, uint64_t flags, uintptr_t preferred_virt);
void paging_free_pages(vmm_pagemap_t* pagemap, void* virt_addr, size_t page_count);
bool paging_reserve_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end);
bool paging_is_range_free(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end);
void paging_print_stats(vmm_pagemap_t* pagemap);
void paging_dump_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end);
#endif
+89
View File
@@ -0,0 +1,89 @@
#ifndef PMM_H
#define PMM_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <limine.h>
#define PAGE_SIZE 4096UL
#define PAGE_SHIFT 12
#define PMM_PAGE_ALIGN(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
#define PMM_MAX_ORDER 10
#define PMM_MAX_ORDER_NR (PMM_MAX_ORDER + 1)
#define SLAB_MIN_SIZE 8
#define SLAB_MAX_SIZE 4096
#define SLAB_NUM_CACHES 10
#define PMM_FREE_MIN_PHYS 0x100000ULL
typedef struct pmm_block {
struct pmm_block *next;
struct pmm_block *prev;
int order;
} pmm_block_t;
typedef struct {
pmm_block_t head;
size_t count;
} pmm_free_list_t;
typedef struct {
uintptr_t hhdm_offset;
uintptr_t mem_start;
uintptr_t mem_end;
size_t total_pages;
size_t usable_pages;
size_t free_pages;
pmm_free_list_t orders[PMM_MAX_ORDER_NR];
} pmm_buddy_state_t;
typedef struct slab {
struct slab *next;
struct slab *prev;
void *freelist;
uint16_t obj_size;
uint16_t total;
uint16_t used;
uint16_t _pad;
} slab_t;
typedef struct {
size_t obj_size;
slab_t *partial;
slab_t *full;
size_t total_allocs;
size_t total_frees;
} slab_cache_t;
void pmm_init(struct limine_memmap_response *memmap,
struct limine_hhdm_response *hhdm);
void *pmm_alloc(size_t pages);
void *pmm_alloc_zero(size_t pages);
void *pmm_alloc_aligned(size_t pages, size_t alignment);
void pmm_free(void *addr, size_t pages);
void pmm_free_single(void *addr);
void *pmm_phys_to_virt(uintptr_t phys);
uintptr_t pmm_virt_to_phys(void *vaddr);
uint64_t pmm_get_hhdm_offset(void);
size_t pmm_get_total_pages(void);
size_t pmm_get_usable_pages(void);
size_t pmm_get_free_pages(void);
size_t pmm_get_used_pages(void);
void pmm_print_stats(void);
void slab_init(void);
void *kmalloc(size_t size);
void *kzalloc(size_t size);
void *krealloc(void *ptr, size_t new_size);
void kfree(void *ptr);
void slab_print_stats(void);
extern slab_cache_t g_caches[SLAB_NUM_CACHES];
#endif
+41
View File
@@ -0,0 +1,41 @@
#ifndef VMM_H
#define VMM_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define VMM_PRESENT (1ULL << 0)
#define VMM_WRITE (1ULL << 1)
#define VMM_USER (1ULL << 2)
#define VMM_PWT (1ULL << 3)
#define VMM_PCD (1ULL << 4)
#define VMM_ACCESSED (1ULL << 5)
#define VMM_DIRTY (1ULL << 6)
#define VMM_PSE (1ULL << 7)
#define VMM_GLOBAL (1ULL << 8)
#define VMM_NOEXEC (1ULL << 63)
typedef uint64_t vmm_pte_t;
typedef struct {
vmm_pte_t* pml4;
} vmm_pagemap_t;
extern uintptr_t kernel_pml4_phys;
void vmm_init(void);
vmm_pagemap_t* vmm_create_pagemap(void);
void vmm_switch_pagemap(vmm_pagemap_t* map);
bool vmm_map_page(vmm_pagemap_t* map, uintptr_t virt, uintptr_t phys, uint64_t flags);
void vmm_unmap_page(vmm_pagemap_t* map, uintptr_t virt);
void vmm_unmap_page_noflush(vmm_pagemap_t* map, uintptr_t virt);
bool vmm_virt_to_phys(vmm_pagemap_t* map, uintptr_t virt, uintptr_t* phys_out);
bool vmm_get_page_flags(vmm_pagemap_t* map, uintptr_t virt, uint64_t* flags_out);
vmm_pagemap_t* vmm_get_kernel_pagemap(void);
vmm_pagemap_t* vmm_clone_pagemap(vmm_pagemap_t* src);
void vmm_free_pagemap(vmm_pagemap_t* map);
void vmm_sync_kernel_mappings(vmm_pagemap_t* map);
void vmm_test(void);
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef PANIC_H
#define PANIC_H
#include <stdint.h>
#include "../interrupts/interrupts.h"
__attribute__((noreturn))
void kernel_panic(const char *msg);
__attribute__((noreturn))
void kernel_panic_regs(const char *msg, struct int_frame_t *regs);
#define KPANIC(msg) \
kernel_panic("[" __FILE__ ":" STRINGIFY(__LINE__) "] " msg)
#define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x)
#endif
+49
View File
@@ -0,0 +1,49 @@
#ifndef CAPABILITIES_H
#define CAPABILITIES_H
#include <stdint.h>
#include <stdbool.h>
#define UID_ROOT 0
#define GID_ROOT 0
#define UID_NOBODY 65534
#define GID_NOBODY 65534
#define CAP_IOPORT (1ULL << 0)
#define CAP_RAWMEM (1ULL << 1)
#define CAP_DMA (1ULL << 2)
#define CAP_IRQ (1ULL << 3)
#define CAP_KILL_ANY (1ULL << 4)
#define CAP_SET_PRIO (1ULL << 5)
#define CAP_TASK_SPAWN (1ULL << 6)
#define CAP_TASK_INFO (1ULL << 7)
#define CAP_MMAP_EXEC (1ULL << 8)
#define CAP_MMAP_PHYS (1ULL << 9)
#define CAP_FS_ROOT (1ULL << 10)
#define CAP_FS_OWNER (1ULL << 11)
#define CAP_NET_RAW (1ULL << 12)
#define CAP_NET_BIND (1ULL << 13)
#define CAP_SYSADMIN (1ULL << 14)
#define CAP_REBOOT (1ULL << 15)
#define CAP_MODULE (1ULL << 16)
#define CAP_SETUID (1ULL << 17)
#define CAP_AUDIT (1ULL << 18)
#define CAP_PTRACE (1ULL << 19)
#define CAP_DBG_SERIAL (1ULL << 20)
#define CAP_ALL (~0ULL)
#define CAP_BASIC_SET (CAP_MMAP_EXEC | CAP_TASK_SPAWN)
#define CAP_SERVICE_SET (CAP_BASIC_SET | CAP_TASK_INFO | CAP_SET_PRIO | CAP_NET_BIND)
static inline bool cap_has(uint64_t caps, uint64_t cap) {
return (caps & cap) == cap;
}
static inline uint64_t cap_drop(uint64_t caps, uint64_t cap) {
return caps & ~cap;
}
static inline uint64_t cap_initial(uint32_t uid) {
return (uid == UID_ROOT) ? CAP_ALL : CAP_BASIC_SET;
}
#endif
+183
View File
@@ -0,0 +1,183 @@
#ifndef TASK_H
#define TASK_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "capabilities.h"
#include "../memory/vmm.h"
#include "../smp/smp.h"
#ifndef __ATOMIC_RELAXED
#define __ATOMIC_RELAXED 0
#define __ATOMIC_ACQUIRE 2
#define __ATOMIC_RELEASE 3
#endif
typedef struct { volatile bool _val; } atomic_bool;
static inline void atomic_init_bool(atomic_bool *a, bool v) {
__atomic_store_n(&a->_val, v, __ATOMIC_RELAXED);
}
static inline bool atomic_load_bool_acq(const atomic_bool *a) {
return __atomic_load_n(&a->_val, __ATOMIC_ACQUIRE);
}
static inline void atomic_store_bool_rel(atomic_bool *a, bool v) {
__atomic_store_n(&a->_val, v, __ATOMIC_RELEASE);
}
static inline bool atomic_cas_bool(atomic_bool *a, bool *expected, bool desired) {
return __atomic_compare_exchange_n(
&a->_val, expected, desired,
false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
typedef struct fd_table fd_table_t;
typedef struct {
uint8_t data[512] __attribute__((aligned(16)));
} fpu_state_t;
typedef enum {
TASK_RUNNING = 0,
TASK_READY,
TASK_BLOCKED,
TASK_ZOMBIE,
TASK_DEAD,
} task_state_t;
typedef enum {
TASK_TYPE_KERNEL = 0,
TASK_TYPE_USER = 1,
} task_type_t;
#define MAX_PRIORITY 31
#define DEFAULT_PRIORITY 16
#define TASK_DEFAULT_TIMESLICE 10
typedef struct task {
uint64_t rsp;
uint64_t rip;
uint64_t rbp_save;
uint64_t cr3;
int priority;
bool runnable;
uint8_t is_userspace;
uint8_t _pad0[2];
uint32_t cpu_id;
char name[32];
uint32_t time_slice;
uint32_t time_slice_init;
uint8_t _pad1[4];
uint64_t total_runtime;
fpu_state_t* fpu_state;
bool fpu_used;
uint8_t _pad2[3];
uint32_t last_cpu;
uint64_t cpu_affinity;
void (*entry)(void*);
void *arg;
uintptr_t stack_base;
uint64_t user_rsp;
task_state_t state;
uint8_t _pad3[4];
struct task* next;
uint32_t pid;
uint32_t ppid;
uint32_t uid;
uint32_t gid;
uint64_t capabilities;
struct task* parent;
struct task* children;
struct task* sibling;
int exit_code;
volatile bool pending_kill;
uint8_t _pad4[3];
uintptr_t brk_start;
uintptr_t brk_current;
uintptr_t brk_max;
vmm_pagemap_t* pagemap;
uint32_t flags;
uint32_t wait_for_pid;
uint64_t wakeup_time_ns;
uint64_t user_saved_rip;
uint64_t user_saved_rbp;
uint64_t user_saved_rbx;
uint64_t user_saved_r12;
uint64_t user_saved_r13;
uint64_t user_saved_r14;
uint64_t user_saved_r15;
uint64_t user_saved_r11;
fd_table_t *fd_table;
atomic_bool on_cpu;
} task_t;
#define TASK_FLAG_TRACE (1 << 0)
#define TASK_FLAG_VFORK (1 << 1)
#define TASK_FLAG_FORK (1 << 2)
#define TASK_FLAG_STARTED (1 << 3)
#define TASK_FLAG_OWN_PAGEMAP (1 << 4)
#define TASK_FLAG_STACK_DEFERRED (1 << 5)
#define TASK_FLAG_DESTROYED (1U << 31)
_Static_assert(offsetof(task_t, rsp) == 0, "task_t: rsp");
_Static_assert(offsetof(task_t, entry) == 120, "task_t: entry — update TASK_ENTRY_OFFSET");
_Static_assert(offsetof(task_t, arg) == 128, "task_t: arg — update TASK_ARG_OFFSET");
_Static_assert(offsetof(task_t, stack_base) == 136, "task_t: stack_base");
_Static_assert(offsetof(task_t, user_rsp) == 144, "task_t: user_rsp — update TASK_USER_RSP_OFFSET");
_Static_assert(offsetof(task_t, user_saved_rip) == 272, "task_t: user_saved_rip");
_Static_assert(offsetof(task_t, user_saved_rbp) == 280, "task_t: user_saved_rbp — update TASK_USER_SAVED_RBP_OFFSET");
extern task_t* ready_queues[MAX_PRIORITY + 1];
extern task_t* current_task[MAX_CPUS];
void sched_init(void);
void sched_reschedule(void);
void sched_print_stats(void);
void task_yield(void);
task_t* task_create(const char* name, void (*entry)(void*), void* arg, int priority);
task_t* task_create_user(const char* name, uintptr_t entry, uintptr_t user_rsp, uint64_t cr3, int priority, vmm_pagemap_t* pagemap, uint32_t uid, uint32_t gid);
__attribute__((noreturn)) void task_exit(void);
void task_kill(task_t* task);
void task_destroy(task_t* task);
task_t* task_fork(task_t* parent);
task_t* task_find_by_pid(uint32_t pid);
uint32_t task_alloc_pid(void);
void task_reparent(task_t* child, task_t* new_parent);
#include "spinlock.h"
extern spinlock_t children_lock;
void task_wakeup_waiters(uint32_t pid);
void task_unblock(task_t* t);
void sched_wakeup_sleepers(uint64_t now_ns);
task_t* task_find_foreground(void);
extern volatile uint32_t g_foreground_pid;
void task_set_foreground(uint32_t pid);
extern void context_switch(task_t* old, task_t* next, task_t** current_task_slot, uint64_t new_cr3);
extern void first_task_start(task_t* task);
extern void task_trampoline(void);
extern void task_trampoline_user(void);
extern void task_trampoline_fork(void);
extern void fpu_save(fpu_state_t* state);
extern void fpu_restore(fpu_state_t* state);
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef SPINLOCK_H
#define SPINLOCK_H
#include <stdint.h>
typedef struct {
volatile uint32_t ticket;
volatile uint32_t serving;
} spinlock_t;
#define SPINLOCK_INIT { .ticket = 0, .serving = 0 }
static inline void spinlock_acquire(spinlock_t* lock) {
uint32_t my_ticket = __atomic_fetch_add(&lock->ticket, 1, __ATOMIC_RELAXED);
while (__atomic_load_n(&lock->serving, __ATOMIC_ACQUIRE) != my_ticket)
asm volatile("pause");
}
static inline void spinlock_release(spinlock_t* lock) {
__atomic_fetch_add(&lock->serving, 1, __ATOMIC_RELEASE);
}
static inline int spinlock_try_acquire(spinlock_t* lock) {
uint32_t serving = __atomic_load_n(&lock->serving, __ATOMIC_RELAXED);
uint32_t ticket = __atomic_load_n(&lock->ticket, __ATOMIC_RELAXED);
if (serving != ticket) return 0;
uint32_t expected = serving;
return __atomic_compare_exchange_n(&lock->ticket, &expected, serving + 1,
0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
}
static inline uint64_t spinlock_acquire_irqsave(spinlock_t* lock) {
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(lock);
return flags;
}
static inline void spinlock_release_irqrestore(spinlock_t* lock, uint64_t flags) {
spinlock_release(lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
#endif
+55
View File
@@ -0,0 +1,55 @@
#ifndef PERCPU_H
#define PERCPU_H
#include <stdint.h>
#include "../include/smp/smp.h"
#define PERCPU_SECTION __attribute__((section(".percpu")))
typedef struct {
uint64_t syscall_kernel_rsp;
uint64_t syscall_user_rsp;
uint32_t cpu_id;
uint32_t _pad;
void* current_task;
uint64_t some_counter;
bool need_resched;
uint8_t _pad2[7];
uint64_t user_saved_rbp;
uint64_t user_saved_rbx;
uint64_t user_saved_r12;
uint64_t user_saved_r13;
uint64_t user_saved_r14;
uint64_t user_saved_r15;
uint64_t user_saved_r11;
uint64_t user_saved_rip;
uint8_t _pad3[8];
void* deferred_free_task;
uint64_t sched_stack_top;
} __attribute__((aligned(64))) percpu_t;
_Static_assert(__builtin_offsetof(percpu_t, syscall_kernel_rsp) == 0, "percpu: kernel_rsp");
_Static_assert(__builtin_offsetof(percpu_t, syscall_user_rsp) == 8, "percpu: user_rsp");
_Static_assert(__builtin_offsetof(percpu_t, current_task) == 24, "percpu: current_task");
_Static_assert(__builtin_offsetof(percpu_t, need_resched) == 40, "percpu: need_resched");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_rbp) == 48, "percpu: saved_rbp");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_rbx) == 56, "percpu: saved_rbx");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r12) == 64, "percpu: saved_r12");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r13) == 72, "percpu: saved_r13");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r14) == 80, "percpu: saved_r14");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r15) == 88, "percpu: saved_r15");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_r11) == 96, "percpu: saved_r11");
_Static_assert(__builtin_offsetof(percpu_t, user_saved_rip) == 104, "percpu: saved_rip");
extern percpu_t percpu;
extern percpu_t* percpu_regions[MAX_CPUS];
extern bool g_has_fsgsbase;
percpu_t* get_percpu(void);
percpu_t* get_percpu_mut(void);
void init_percpu_regions(void);
void set_percpu_base(percpu_t* base);
#define current_cpu_id() (get_percpu()->cpu_id)
#endif
+60
View File
@@ -0,0 +1,60 @@
#ifndef SMP_H
#define SMP_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <limine.h>
#define AP_STACK_SIZE 16384
#define MAX_CPUS 256
#define MAX_TLB_ADDRESSES 32
typedef struct {
volatile bool pending;
uintptr_t addresses[MAX_TLB_ADDRESSES];
size_t count;
} tlb_shootdown_t;
extern tlb_shootdown_t tlb_shootdown_queue[MAX_CPUS];
typedef enum {
CPU_UNINITIALIZED = 0,
CPU_BOOTED,
CPU_ONLINE,
CPU_OFFLINE,
CPU_FAULTED
} cpu_state_t;
typedef struct {
uint32_t lapic_id;
uint32_t processor_id;
uint32_t acpi_id;
cpu_state_t state;
bool is_bsp;
uint64_t stack_top;
uint32_t cpu_index;
uint16_t tss_selector;
} cpu_info_t;
typedef struct {
uint32_t cpu_count;
uint32_t online_count;
uint32_t bsp_lapic_id;
uint64_t lapic_base;
cpu_info_t cpus[256];
} smp_info_t;
void smp_init(struct limine_mp_response* mp_response);
void smp_boot_aps(struct limine_mp_response* mp_response);
smp_info_t* smp_get_info(void);
cpu_info_t* smp_get_current_cpu(void);
uint32_t smp_get_cpu_count(void);
uint32_t smp_get_online_count(void);
bool smp_is_bsp(void);
void smp_print_info(void);
void smp_print_info_fb(void);
void smp_wait_for_ready(void);
void ap_entry_point(struct limine_mp_info* cpu_info);
void sched_notify_ready(void);
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef FPU_H
#define FPU_H
#include <stdint.h>
#include <stdbool.h>
void fpu_init(void);
bool fpu_detect(void);
void fpu_set_control_word(uint16_t cw);
uint16_t fpu_get_control_word(void);
void fpu_set_status_word(uint16_t sw);
uint16_t fpu_get_status_word(void);
void fpu_set_tag_word(uint16_t tw);
uint16_t fpu_get_tag_word(void);
void fpu_reset(void);
#endif
+31
View File
@@ -0,0 +1,31 @@
#ifndef SSE_H
#define SSE_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define MXCSR_DEFAULT 0x1F80
#define MXCSR_FLUSH_TO_ZERO (1 << 15)
#define MXCSR_DENORMALS_ARE_ZERO (1 << 6)
bool sse_supported(void);
bool sse2_supported(void);
bool sse3_supported(void);
bool ssse3_supported(void);
bool sse4_1_supported(void);
bool sse4_2_supported(void);
bool avx_supported(void);
bool avx2_supported(void);
void sse_init(void);
void sse_set_mxcsr(uint32_t mxcsr);
uint32_t sse_get_mxcsr(void);
void sse_memcpy_fast(void* dest, const void* src, size_t n);
void sse_memset_fast(void* dest, int value, size_t n);
bool mmx_supported(void);
void mmx_enter(void);
void mmx_exit(void);
void print_simd_cpuid(void);
void enable_fsgsbase(void);
#endif
+66
View File
@@ -0,0 +1,66 @@
#ifndef ERRNO_H
#define ERRNO_H
#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 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
#define EFBIG 27
#define ENOSPC 28
#define ESPIPE 29
#define EROFS 30
#define EMLINK 31
#define EPIPE 32
#define ERANGE 34
#define EDEADLK 35
#define ENAMETOOLONG 36
#define ENOLCK 37
#define ENOSYS 38
#define ENOTEMPTY 39
#define ELOOP 40
#define ENOMSG 42
#define EIDRM 43
#define ENOSTR 60
#define ENODATA 61
#define ETIME 62
#define ENOSR 63
#define EREMOTE 66
#define ENOLINK 67
#define EPROTO 71
#define EMULTIHOP 72
#define EBADMSG 74
#define EOVERFLOW 75
#define EILSEQ 84
#define EUSERS 87
#define ENOTSOCK 88
#define EOPNOTSUPP 95
#define EADDRINUSE 98
#define ECONNREFUSED 111
#define ETIMEDOUT 110
#define ECERVUS_BASE 200
#define ECAPABILITY 200
#define ETASKDEAD 201
#define EBADCR3 202
#endif
+12
View File
@@ -0,0 +1,12 @@
#ifndef SYSCALL_H
#define SYSCALL_H
#include <stdint.h>
#include "syscall_nums.h"
#include "../sched/capabilities.h"
void syscall_init(void);
int64_t syscall_handler_c(uint64_t nr, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t user_rip);
#endif
+142
View File
@@ -0,0 +1,142 @@
#ifndef SYSCALL_NUMS_H
#define SYSCALL_NUMS_H
#define SYS_EXIT 0
#define SYS_EXIT_GROUP 1
#define SYS_GETPID 2
#define SYS_GETPPID 3
#define SYS_FORK 4
#define SYS_WAIT 5
#define SYS_EXECVE 14
#define SYS_YIELD 6
#define SYS_GETUID 7
#define SYS_GETGID 8
#define SYS_SETUID 9
#define SYS_SETGID 10
#define SYS_CAP_GET 11
#define SYS_CAP_DROP 12
#define SYS_TASK_INFO 13
#define SYS_READ 20
#define SYS_WRITE 21
#define SYS_OPEN 22
#define SYS_CLOSE 23
#define SYS_SEEK 24
#define SYS_STAT 25
#define SYS_FSTAT 26
#define SYS_IOCTL 27
#define SYS_DUP 28
#define SYS_DUP2 29
#define SYS_PIPE 30
#define SYS_FCNTL 31
#define SYS_READDIR 32
#define SYS_MMAP 40
#define SYS_MUNMAP 41
#define SYS_MPROTECT 42
#define SYS_BRK 43
#define SYS_CLOCK_GET 60
#define SYS_SLEEP_NS 61
#define SYS_UPTIME 62
#define SYS_MEMINFO 63
#define SYS_FUTEX_WAIT 80
#define SYS_FUTEX_WAKE 81
#define SYS_CERVUS_BASE 512
#define SYS_DBG_PRINT 512
#define SYS_DBG_DUMP 513
#define SYS_TASK_SPAWN 514
#define SYS_TASK_KILL 515
#define SYS_SHMEM_CREATE 516
#define SYS_SHMEM_MAP 517
#define SYS_SHMEM_UNMAP 518
#define SYS_IPC_SEND 519
#define SYS_IPC_RECV 520
#define SYS_IOPORT_READ 521
#define SYS_IOPORT_WRITE 522
#define SYS_SHUTDOWN 523
#define SYS_REBOOT 524
#define SYS_DISK_MOUNT 530
#define SYS_DISK_UMOUNT 531
#define SYS_DISK_FORMAT 532
#define SYS_DISK_INFO 533
#define SYS_UNLINK 534
#define SYS_RMDIR 535
#define SYS_MKDIR 536
#define SYS_RENAME 537
#define SYS_DISK_READ_RAW 540
#define SYS_DISK_WRITE_RAW 541
#define SYS_DISK_PARTITION 542
#define SYS_DISK_MKFS_FAT32 543
#define SYS_DISK_LIST_PARTS 544
#define SYS_DISK_BIOS_INSTALL 545
#define SYS_LIST_MOUNTS 546
#define SYS_STATVFS 547
#define SYSCALL_TABLE_SIZE 548
#define PROT_NONE 0x0
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_FIXED 0x10
#define MAP_FAILED ((void*)-1)
#define WNOHANG 0x1
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
typedef struct {
uint32_t pid;
uint32_t ppid;
uint32_t uid;
uint32_t gid;
uint64_t capabilities;
char name[32];
uint32_t state;
uint32_t priority;
uint64_t total_runtime_ns;
} cervus_task_info_t;
typedef struct {
int64_t tv_sec;
int64_t tv_nsec;
} cervus_timespec_t;
typedef struct {
uint64_t total_bytes;
uint64_t free_bytes;
uint64_t used_bytes;
uint64_t usable_bytes;
uint64_t page_size;
} cervus_meminfo_t;
typedef struct __attribute__((packed)) {
uint8_t boot_flag;
uint8_t type;
uint32_t lba_start;
uint32_t sector_count;
} cervus_mbr_part_t;
typedef struct __attribute__((packed)) {
char disk_name[32];
char part_name[32];
uint32_t part_num;
uint8_t type;
uint8_t bootable;
uint64_t lba_start;
uint64_t sector_count;
uint64_t size_bytes;
} cervus_part_info_t;
#endif
+397
View File
@@ -0,0 +1,397 @@
#include "../../include/acpi/acpi.h"
#include "../../include/io/serial.h"
#include "../../include/io/ports.h"
#include "../../include/memory/pmm.h"
#include <string.h>
static acpi_rsdp2_t *rsdp;
static acpi_xsdt_t *xsdt;
static acpi_rsdt_t *rsdt;
static uint8_t s5_slp_typa = 0;
static uint8_t s5_slp_typb = 0;
static bool s5_parsed = false;
static inline void *phys_to_virt(uintptr_t phys) {
return (void *)(phys + pmm_get_hhdm_offset());
}
static bool checksum(void *base, size_t len) {
uint8_t sum = 0;
uint8_t *b = base;
for (size_t i = 0; i < len; i++)
sum += b[i];
return sum == 0;
}
static bool parse_s5_from_aml(const uint8_t *aml, size_t aml_len) {
if (!aml || aml_len < 40) return false;
for (size_t i = 0; i + 20 < aml_len; i++) {
if (aml[i] == '_' && aml[i+1] == 'S' &&
aml[i+2] == '5' && aml[i+3] == '_') {
size_t pos = i + 4;
for (size_t j = pos; j < pos + 4 && j < aml_len; j++) {
if (aml[j] == 0x12) {
size_t pkg = j + 1;
if (pkg >= aml_len) continue;
uint8_t lead = aml[pkg];
size_t pkg_len_bytes = 1;
if (lead & 0xC0) {
pkg_len_bytes = (lead >> 6) + 1;
}
pkg += pkg_len_bytes;
if (pkg >= aml_len) continue;
pkg++;
if (pkg >= aml_len) continue;
if (aml[pkg] == 0x0A) {
pkg++;
if (pkg >= aml_len) continue;
s5_slp_typa = aml[pkg];
pkg++;
} else if (aml[pkg] == 0x00) {
s5_slp_typa = 0;
pkg++;
} else if (aml[pkg] == 0x01) {
s5_slp_typa = 1;
pkg++;
} else {
s5_slp_typa = aml[pkg];
pkg++;
}
if (pkg >= aml_len) continue;
if (aml[pkg] == 0x0A) {
pkg++;
if (pkg >= aml_len) continue;
s5_slp_typb = aml[pkg];
} else if (aml[pkg] == 0x00) {
s5_slp_typb = 0;
} else if (aml[pkg] == 0x01) {
s5_slp_typb = 1;
} else {
s5_slp_typb = aml[pkg];
}
serial_printf("ACPI: parsed S5 from AML: SLP_TYPa=%u SLP_TYPb=%u\n",
s5_slp_typa, s5_slp_typb);
return true;
}
}
}
}
return false;
}
static void parse_s5(void) {
if (s5_parsed) return;
s5_parsed = true;
acpi_fadt_t *fadt = (acpi_fadt_t *)acpi_find_table("FACP", 0);
if (!fadt) {
serial_writestring("ACPI: no FADT, cannot parse S5\n");
return;
}
uintptr_t dsdt_phys = 0;
if (fadt->header.length >= 148 && fadt->x_dsdt)
dsdt_phys = (uintptr_t)fadt->x_dsdt;
if (!dsdt_phys)
dsdt_phys = (uintptr_t)fadt->dsdt;
if (!dsdt_phys) {
serial_writestring("ACPI: no DSDT address in FADT\n");
return;
}
acpi_sdt_header_t *dsdt = phys_to_virt(dsdt_phys);
if (memcmp(dsdt->signature, "DSDT", 4) != 0) {
serial_writestring("ACPI: invalid DSDT signature\n");
return;
}
serial_printf("ACPI: DSDT at phys=0x%llx length=%u\n",
(unsigned long long)dsdt_phys, dsdt->length);
const uint8_t *aml = (const uint8_t *)dsdt + sizeof(acpi_sdt_header_t);
size_t aml_len = dsdt->length - sizeof(acpi_sdt_header_t);
if (parse_s5_from_aml(aml, aml_len))
return;
for (uint64_t idx = 0; ; idx++) {
acpi_sdt_header_t *ssdt = acpi_find_table("SSDT", idx);
if (!ssdt) break;
const uint8_t *s_aml = (const uint8_t *)ssdt + sizeof(acpi_sdt_header_t);
size_t s_len = ssdt->length - sizeof(acpi_sdt_header_t);
if (parse_s5_from_aml(s_aml, s_len))
return;
}
serial_writestring("ACPI: S5 object not found in DSDT/SSDT, using defaults\n");
s5_slp_typa = 5;
s5_slp_typb = 0;
}
static void acpi_enable_mode(acpi_fadt_t *fadt) {
if (!fadt->smi_command_port || !fadt->acpi_enable)
return;
uint16_t pm1a_val = inw(fadt->pm1a_control_block);
if (pm1a_val & 1) {
serial_writestring("ACPI: already in ACPI mode\n");
return;
}
serial_writestring("ACPI: enabling ACPI mode...\n");
outb((uint16_t)fadt->smi_command_port, fadt->acpi_enable);
for (int i = 0; i < 3000; i++) {
pm1a_val = inw(fadt->pm1a_control_block);
if (pm1a_val & 1) {
serial_writestring("ACPI: ACPI mode enabled\n");
return;
}
for (volatile int j = 0; j < 10000; j++)
asm volatile("pause");
}
serial_writestring("ACPI: WARNING - timeout enabling ACPI mode\n");
}
void acpi_init(void) {
if (!rsdp_request.response) {
serial_writestring("ACPI: no RSDP\n");
return;
}
rsdp = (acpi_rsdp2_t *)rsdp_request.response->address;
if (!checksum(&rsdp->rsdp_v1, sizeof(acpi_rsdp_t))) {
serial_writestring("ACPI: bad RSDP checksum\n");
return;
}
if (rsdp->rsdp_v1.revision >= 2 && rsdp->xsdt_address) {
xsdt = phys_to_virt(rsdp->xsdt_address);
if (!checksum(xsdt, xsdt->header.length))
xsdt = NULL;
}
if (!xsdt && rsdp->rsdp_v1.rsdt_address) {
rsdt = phys_to_virt(rsdp->rsdp_v1.rsdt_address);
if (!checksum(rsdt, rsdt->header.length))
rsdt = NULL;
}
if (!xsdt && !rsdt)
return;
parse_s5();
}
void *acpi_find_table(const char *sig, uint64_t index) {
uint64_t count = 0;
if (xsdt) {
size_t n = (xsdt->header.length - sizeof(acpi_sdt_header_t)) / 8;
for (size_t i = 0; i < n; i++) {
acpi_sdt_header_t *h = phys_to_virt(xsdt->sdt_pointers[i]);
if (!memcmp(h->signature, sig, 4)) {
if (count++ == index)
return h;
}
}
} else if (rsdt) {
size_t n = (rsdt->header.length - sizeof(acpi_sdt_header_t)) / 4;
for (size_t i = 0; i < n; i++) {
acpi_sdt_header_t *h = phys_to_virt(rsdt->sdt_pointers[i]);
if (!memcmp(h->signature, sig, 4)) {
if (count++ == index)
return h;
}
}
}
return NULL;
}
void acpi_print_tables(void) {
serial_writestring("ACPI tables:\n");
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("APIC", i);
if (!h) break;
serial_writestring(" - APIC (MADT)\n");
}
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("HPET", i);
if (!h) break;
serial_writestring(" - HPET\n");
}
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("MCFG", i);
if (!h) break;
serial_writestring(" - MCFG (PCIe)\n");
}
for (uint64_t i = 0;; i++) {
acpi_sdt_header_t *h = acpi_find_table("FACP", i);
if (!h) break;
serial_writestring(" - FACP (FADT)\n");
}
}
static void acpi_write_gas(const acpi_gas_t *gas, uint64_t value) {
if (gas->address == 0) return;
switch (gas->address_space_id) {
case 0x00:
{
volatile uint8_t *mmio = phys_to_virt(gas->address);
switch (gas->register_bit_width) {
case 8: *(volatile uint8_t *)mmio = (uint8_t)value; break;
case 16: *(volatile uint16_t *)mmio = (uint16_t)value; break;
case 32: *(volatile uint32_t *)mmio = (uint32_t)value; break;
case 64: *(volatile uint64_t *)mmio = value; break;
}
break;
}
case 0x01:
{
uint16_t port = (uint16_t)gas->address;
switch (gas->register_bit_width) {
case 8: outb(port, (uint8_t)value); break;
case 16: outw(port, (uint16_t)value); break;
case 32: outl(port, (uint32_t)value); break;
}
break;
}
}
}
void acpi_shutdown(void) {
acpi_fadt_t *fadt = (acpi_fadt_t *)acpi_find_table("FACP", 0);
if (!fadt) {
serial_writestring("ACPI shutdown: FADT not found\n");
goto fallback;
}
acpi_enable_mode(fadt);
parse_s5();
serial_printf("ACPI shutdown: SLP_TYPa=%u SLP_TYPb=%u\n",
s5_slp_typa, s5_slp_typb);
serial_printf("ACPI shutdown: PM1a_CNT=0x%x PM1b_CNT=0x%x\n",
fadt->pm1a_control_block, fadt->pm1b_control_block);
asm volatile("cli");
uint16_t pm1a_value = (s5_slp_typa << 10) | (1 << 13);
outw(fadt->pm1a_control_block, pm1a_value);
if (fadt->pm1b_control_block) {
uint16_t pm1b_value = (s5_slp_typb << 10) | (1 << 13);
outw(fadt->pm1b_control_block, pm1b_value);
}
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
if (fadt->header.length >= 244) {
if (fadt->x_pm1a_control_block.address) {
serial_writestring("ACPI shutdown: trying extended PM1a...\n");
acpi_write_gas(&fadt->x_pm1a_control_block, pm1a_value);
}
if (fadt->x_pm1b_control_block.address) {
uint16_t pm1b_value = (s5_slp_typb << 10) | (1 << 13);
acpi_write_gas(&fadt->x_pm1b_control_block, pm1b_value);
}
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
}
fallback:
serial_writestring("ACPI shutdown: trying QEMU/Bochs port 0x604...\n");
outw(0x604, 0x2000);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
serial_writestring("ACPI shutdown: trying VirtualBox port 0x4004...\n");
outw(0x4004, 0x3400);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
serial_writestring("ACPI shutdown: trying legacy APM...\n");
outw(0xB004, 0x2000);
serial_writestring("ACPI shutdown: all methods failed, halting CPU\n");
for (;;)
asm volatile("cli; hlt");
}
void acpi_reboot(void) {
acpi_fadt_t *fadt = (acpi_fadt_t *)acpi_find_table("FACP", 0);
asm volatile("cli");
if (fadt && fadt->header.length >= 129) {
if ((fadt->flags & (1 << 10)) && fadt->reset_reg.address) {
serial_printf("ACPI reboot: using FADT reset register "
"(space=%u addr=0x%llx val=0x%x)\n",
fadt->reset_reg.address_space_id,
(unsigned long long)fadt->reset_reg.address,
fadt->reset_value);
acpi_write_gas(&fadt->reset_reg, fadt->reset_value);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
}
}
serial_writestring("ACPI reboot: trying 8042 keyboard controller reset...\n");
for (int i = 0; i < 10000; i++) {
if (!(inb(0x64) & 0x02))
break;
for (volatile int j = 0; j < 100; j++)
asm volatile("pause");
}
outb(0x64, 0xFE);
for (volatile int i = 0; i < 100000; i++)
asm volatile("pause");
serial_writestring("ACPI reboot: triggering triple fault...\n");
struct {
uint16_t limit;
uint64_t base;
} __attribute__((packed)) null_idt = { 0, 0 };
asm volatile(
"lidt %0\n"
"int $3\n"
:: "m"(null_idt)
);
for (;;)
asm volatile("cli; hlt");
}
+212
View File
@@ -0,0 +1,212 @@
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/io/ports.h"
#include "../../include/acpi/acpi.h"
#include "../../include/memory/pmm.h"
#include "../../include/memory/vmm.h"
#include <string.h>
#include <stddef.h>
uintptr_t lapic_base = 0;
uintptr_t ioapic_base = 0;
uintptr_t hpet_base = 0;
uint32_t hpet_period = 0;
static acpi_madt_t* madt = NULL;
static acpi_hpet_t* hpet_table = NULL;
uint64_t g_hpet_boot_counter = 0;
static inline uintptr_t phys_to_virt(uintptr_t phys) {
return phys + pmm_get_hhdm_offset();
}
static bool map_mmio_region(uintptr_t phys_base, size_t size) {
vmm_pagemap_t* kernel_pagemap = vmm_get_kernel_pagemap();
if (!kernel_pagemap) return false;
for (uintptr_t offset = 0; offset < size; offset += 0x1000) {
uintptr_t phys_addr = phys_base + offset;
uintptr_t virt_addr = phys_to_virt(phys_addr);
uintptr_t mapped_phys;
if (vmm_virt_to_phys(kernel_pagemap, virt_addr, &mapped_phys)) {
if (mapped_phys != phys_addr) return false;
continue;
}
if (!vmm_map_page(kernel_pagemap, virt_addr, phys_addr,
VMM_PRESENT | VMM_WRITE | VMM_NOEXEC)) {
return false;
}
}
return true;
}
bool hpet_init(void) {
hpet_table = (acpi_hpet_t*)acpi_find_table("HPET", 0);
if (!hpet_table) return false;
uintptr_t phys_base = hpet_table->address;
if (!map_mmio_region(phys_base, 0x1000)) return false;
hpet_base = phys_to_virt(phys_base);
volatile uint32_t* hpet_regs = (volatile uint32_t*)hpet_base;
hpet_period = hpet_regs[HPET_PERIOD / 4];
if (hpet_period == 0) return false;
uint64_t config = *(volatile uint64_t*)(hpet_base + HPET_CONFIG);
config |= HPET_ENABLE_CNF;
if (hpet_table->legacy_replacement) config |= HPET_LEGACY_CNF;
*(volatile uint64_t*)(hpet_base + HPET_CONFIG) = config;
for (volatile int _i = 0; _i < 1000; _i++) asm volatile("pause");
g_hpet_boot_counter = hpet_read_counter();
return true;
}
bool hpet_is_available(void) {
return hpet_base != 0 && hpet_period != 0;
}
uint64_t hpet_read_counter(void) {
if (!hpet_base) return 0;
if (hpet_table->counter_size) {
return *(volatile uint64_t*)(hpet_base + HPET_MAIN_COUNTER);
} else {
return *(volatile uint32_t*)(hpet_base + HPET_MAIN_COUNTER);
}
}
uint64_t hpet_get_frequency(void) {
if (!hpet_period) return 0;
return 1000000000000000ULL / hpet_period;
}
uint64_t hpet_elapsed_ns(void) {
if (!hpet_is_available()) return 0;
uint64_t delta = hpet_read_counter() - g_hpet_boot_counter;
return (delta * (uint64_t)hpet_period) / 1000000ULL;
}
void hpet_sleep_ns(uint64_t nanoseconds) {
if (!hpet_base || !hpet_period) return;
uint64_t ticks_needed = (nanoseconds * 1000000ULL) / hpet_period;
if (ticks_needed == 0) ticks_needed = 1;
uint64_t start = hpet_read_counter();
uint64_t target = start + ticks_needed;
if (target > start) {
while (hpet_read_counter() < target) asm volatile("pause");
} else {
while (hpet_read_counter() > start) asm volatile("pause");
while (hpet_read_counter() < (target - 0xFFFFFFFFFFFFFFFFULL)) asm volatile("pause");
}
}
void hpet_sleep_us(uint64_t microseconds) {
hpet_sleep_ns(microseconds * 1000ULL);
}
void hpet_sleep_ms(uint64_t milliseconds) {
hpet_sleep_ns(milliseconds * 1000000ULL);
}
static void parse_madt(void) {
madt = (acpi_madt_t*)acpi_find_table("APIC", 0);
if (!madt) return;
if (!map_mmio_region(madt->local_apic_address, 0x1000)) return;
lapic_base = phys_to_virt(madt->local_apic_address);
uint8_t* entries = madt->entries;
uint32_t length = madt->header.length;
uint8_t* end = (uint8_t*)madt + length;
while (entries < end) {
madt_entry_header_t* header = (madt_entry_header_t*)entries;
switch (header->type) {
case MADT_ENTRY_LAPIC: {
madt_lapic_entry_t* lapic_entry = (madt_lapic_entry_t*)entries;
(void)lapic_entry;
break;
}
case MADT_ENTRY_IOAPIC: {
madt_ioapic_entry_t* ioapic_entry = (madt_ioapic_entry_t*)entries;
if (!map_mmio_region(ioapic_entry->ioapic_address, 0x1000)) break;
ioapic_base = phys_to_virt(ioapic_entry->ioapic_address);
break;
}
default:
break;
}
entries += header->length;
}
}
bool apic_is_available(void) {
return madt != NULL;
}
void apic_init(void) {
parse_madt();
if (!madt) return;
if (!lapic_base) return;
hpet_init();
lapic_enable();
if (ioapic_base) {
uint32_t max_redirects = ioapic_get_max_redirects(ioapic_base);
for (uint32_t i = 0; i < max_redirects; i++) {
ioapic_mask_irq(i);
}
}
}
void apic_setup_irq(uint8_t irq, uint8_t vector, bool mask, uint32_t flags) {
if (!ioapic_base) return;
uint32_t redir_flags = IOAPIC_DELIVERY_FIXED | flags;
if (mask) redir_flags |= IOAPIC_INT_MASKED;
ioapic_redirect_irq(irq, vector, redir_flags);
}
void apic_timer_calibrate(void) {
if (!hpet_is_available()) return;
uint64_t measurement_time_ns = 10000000ULL;
uint64_t hpet_ticks_needed = (measurement_time_ns * 1000000ULL) / hpet_period;
lapic_write(LAPIC_TIMER_DCR, 0x3);
lapic_write(LAPIC_TIMER_ICR, 0xFFFFFFFF);
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED | 0xFF);
lapic_write(LAPIC_TIMER, 0xFF);
uint64_t hpet_start = hpet_read_counter();
uint64_t hpet_target = hpet_start + hpet_ticks_needed;
while (hpet_read_counter() < hpet_target) asm volatile("pause");
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED | 0xFF);
uint32_t remaining = lapic_read(LAPIC_TIMER_CCR);
if (remaining == 0 || remaining == 0xFFFFFFFF) return;
uint32_t ticks_elapsed = 0xFFFFFFFF - remaining;
uint32_t ticks_per_10ms = (uint32_t)((ticks_elapsed * 10000000ULL) / measurement_time_ns);
if (ticks_per_10ms == 0) return;
lapic_timer_init(0x20, ticks_per_10ms, true, 0x3);
}
+79
View File
@@ -0,0 +1,79 @@
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include <stddef.h>
static void ioapic_write_internal(uintptr_t base, uint32_t reg, uint32_t value) {
if (!base) {
serial_printf("IOAPIC: Attempt to write to unmapped IOAPIC (reg: 0x%x)\n", reg);
return;
}
volatile uint32_t* ioregsel = (volatile uint32_t*)base;
volatile uint32_t* iowin = (volatile uint32_t*)(base + 0x10);
*ioregsel = reg;
*iowin = value;
}
static uint32_t ioapic_read_internal(uintptr_t base, uint32_t reg) {
if (!base) {
serial_printf("IOAPIC: Attempt to read from unmapped IOAPIC (reg: 0x%x)\n", reg);
return 0;
}
volatile uint32_t* ioregsel = (volatile uint32_t*)base;
volatile uint32_t* iowin = (volatile uint32_t*)(base + 0x10);
*ioregsel = reg;
return *iowin;
}
void ioapic_write(uintptr_t base, uint32_t reg, uint32_t value) {
ioapic_write_internal(base, reg, value);
}
uint32_t ioapic_read(uintptr_t base, uint32_t reg) {
return ioapic_read_internal(base, reg);
}
uint32_t ioapic_get_max_redirects(uintptr_t base) {
uint32_t version = ioapic_read(base, IOAPIC_VERSION);
return ((version >> 16) & 0xFF) + 1;
}
void ioapic_redirect_irq(uint8_t irq, uint8_t vector, uint32_t flags) {
if (!ioapic_base) return;
uint32_t max_redirects = ioapic_get_max_redirects(ioapic_base);
if (irq >= max_redirects) {
serial_printf("IOAPIC: IRQ %u out of range (max %u)\n", irq, max_redirects);
return;
}
uint32_t low = vector | flags;
uint32_t high = 0;
uint32_t redir_reg = IOAPIC_REDIR_START + irq * 2;
ioapic_write(ioapic_base, redir_reg, low);
ioapic_write(ioapic_base, redir_reg + 1, high);
serial_printf("IOAPIC: IRQ %u redirected to vector 0x%x\n", irq, vector);
}
void ioapic_mask_irq(uint8_t irq) {
if (!ioapic_base) return;
uint32_t redir_reg = IOAPIC_REDIR_START + irq * 2;
uint32_t current = ioapic_read(ioapic_base, redir_reg);
ioapic_write(ioapic_base, redir_reg, current | IOAPIC_INT_MASKED);
}
void ioapic_unmask_irq(uint8_t irq) {
if (!ioapic_base) return;
uint32_t redir_reg = IOAPIC_REDIR_START + irq * 2;
uint32_t current = ioapic_read(ioapic_base, redir_reg);
ioapic_write(ioapic_base, redir_reg, current & ~IOAPIC_INT_MASKED);
}
+207
View File
@@ -0,0 +1,207 @@
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/io/ports.h"
#include "../../include/memory/pmm.h"
#include "../../include/smp/smp.h"
#include "../../include/interrupts/interrupts.h"
#include <stddef.h>
static inline uintptr_t phys_to_virt(uintptr_t phys) {
return phys + pmm_get_hhdm_offset();
}
void lapic_write(uint32_t reg, uint32_t value) {
if (!lapic_base) {
serial_printf("LAPIC: Attempt to write to unmapped LAPIC (reg: 0x%x)\n", reg);
return;
}
if (reg & 0x3) {
serial_printf("LAPIC: Unaligned register access: 0x%x\n", reg);
return;
}
volatile uint32_t* addr = (volatile uint32_t*)(lapic_base + reg);
*addr = value;
(void)*addr;
}
uint32_t lapic_read(uint32_t reg) {
if (!lapic_base) {
serial_printf("LAPIC: Attempt to read from unmapped LAPIC (reg: 0x%x)\n", reg);
return 0;
}
if (reg & 0x3) {
serial_printf("LAPIC: Unaligned register access: 0x%x\n", reg);
return 0;
}
volatile uint32_t* addr = (volatile uint32_t*)(lapic_base + reg);
return *addr;
}
void lapic_enable(void) {
if (!lapic_base) return;
lapic_write(LAPIC_SIVR, lapic_read(LAPIC_SIVR) | LAPIC_ENABLE | LAPIC_SPURIOUS_VECTOR);
serial_printf("LAPIC enabled, ID: 0x%x\n", lapic_get_id());
}
uint32_t lapic_get_id(void) {
return (lapic_read(LAPIC_ID) >> 24) & 0xFF;
}
void lapic_eoi(void) {
lapic_write(LAPIC_EOI, 0);
}
void lapic_timer_init(uint32_t vector, uint32_t count, bool periodic, uint8_t divisor) {
serial_printf("Initializing LAPIC timer: vector=0x%x, count=%u, periodic=%d, divisor=%u\n",
vector, count, periodic, divisor);
if (count == 0) {
serial_writestring("Warning: APIC timer count is 0, timer will not generate interrupts\n");
}
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED);
lapic_write(LAPIC_TIMER_DCR, divisor & 0x7);
lapic_write(LAPIC_TIMER_ICR, count);
uint32_t timer_config = vector & 0xFF;
if (periodic) {
timer_config |= LAPIC_TIMER_PERIODIC;
}
lapic_write(LAPIC_TIMER, timer_config);
uint32_t current = lapic_read(LAPIC_TIMER_CCR);
serial_printf("LAPIC timer started: current count: %u\n", current);
}
void lapic_timer_stop(void) {
lapic_write(LAPIC_TIMER, LAPIC_TIMER_MASKED);
}
uint32_t lapic_timer_get_current(void) {
return lapic_read(LAPIC_TIMER_CCR);
}
void lapic_send_ipi(uint32_t target_lapic_id, uint8_t vector)
{
uint32_t icr_low = vector | (0 << 8) | (1 << 14);
uint32_t icr_high = target_lapic_id << 24;
lapic_write(0x310, icr_high);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("Sent IPI vector 0x%02x to LAPIC %u\n", vector, target_lapic_id);
}
void lapic_send_ipi_to_all_but_self(uint8_t vector)
{
uint32_t icr_low = vector | (0 << 8) | (1 << 14) | (3 << 18);
lapic_write(0x310, 0);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("Broadcast IPI (all but self) vector 0x%02x sent\n", vector);
}
void lapic_send_nmi_to_all_but_self(void)
{
uint32_t icr_low = 0x02 | (0x4 << 8) | (1 << 14) | (3 << 18);
lapic_write(0x310, 0);
lapic_write(0x300, icr_low);
for (int _w = 0; _w < 10000 && (lapic_read(0x300) & (1 << 12)); _w++)
asm volatile ("pause");
serial_printf("NMI broadcast (all but self) sent\n");
}
void ipi_reschedule_all(void) {
lapic_send_ipi_to_all_but_self(IPI_RESCHEDULE_VECTOR);
}
void ipi_reschedule_cpu(uint32_t lapic_id) {
lapic_send_ipi(lapic_id, IPI_RESCHEDULE_VECTOR);
}
void ipi_reschedule_single(uint32_t target_lapic_id) {
uint32_t icr_high = target_lapic_id << 24;
uint32_t icr_low = IPI_RESCHEDULE_VECTOR | (0 << 8) | (1 << 14);
lapic_write(0x310, icr_high);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("Reschedule IPI sent to LAPIC %u\n", target_lapic_id);
}
void ipi_tlb_shootdown_broadcast(const uintptr_t* addrs, size_t count) {
if (count > MAX_TLB_ADDRESSES) count = MAX_TLB_ADDRESSES;
uint32_t my_lapic = lapic_get_id();
smp_info_t* info = smp_get_info();
for (uint32_t i = 0; i < info->cpu_count; i++) {
uint32_t target_lapic = info->cpus[i].lapic_id;
if (target_lapic == my_lapic) continue;
tlb_shootdown_t* q = &tlb_shootdown_queue[target_lapic];
q->count = count;
for (size_t j = 0; j < count; j++)
q->addresses[j] = addrs[j];
__atomic_store_n(&q->pending, true, __ATOMIC_RELEASE);
}
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
uint32_t icr_low = IPI_TLB_SHOOTDOWN
| (0 << 8)
| (1 << 14)
| (3 << 18);
lapic_write(0x310, 0);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("TLB shootdown broadcast sent for %zu addresses\n", count);
}
void ipi_tlb_shootdown_single(uint32_t target_lapic_id, uintptr_t addr) {
tlb_shootdown_t* q = &tlb_shootdown_queue[target_lapic_id];
q->addresses[0] = addr;
q->count = 1;
__atomic_store_n(&q->pending, true, __ATOMIC_RELEASE);
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
uint32_t icr_high = target_lapic_id << 24;
uint32_t icr_low = IPI_TLB_SHOOTDOWN | (0 << 8) | (1 << 14);
lapic_write(0x310, icr_high);
lapic_write(0x300, icr_low);
while (lapic_read(0x300) & (1 << 12))
asm volatile ("pause");
serial_printf("TLB shootdown sent to LAPIC %u for virt 0x%llx\n",
target_lapic_id, addr);
}
+371
View File
@@ -0,0 +1,371 @@
#include "../../include/drivers/ata.h"
#include "../../include/io/ports.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/sched/spinlock.h"
#include "../../include/syscall/errno.h"
#include <string.h>
static ata_drive_t g_drives[ATA_MAX_DRIVES];
static int g_drive_count = 0;
static spinlock_t g_ata_lock = SPINLOCK_INIT;
static void ata_io_wait(uint16_t ctrl) {
inb(ctrl); inb(ctrl); inb(ctrl); inb(ctrl);
}
static inline void ata_cpu_relax(void) {
asm volatile("pause" ::: "memory");
}
static void ata_soft_reset(uint16_t ctrl) {
outb(ctrl, 0x04);
ata_io_wait(ctrl);
outb(ctrl, 0x00);
ata_io_wait(ctrl);
for (int i = 0; i < 2000000; i++) {
uint8_t s = inb(ctrl);
if (!(s & ATA_SR_BSY)) return;
ata_io_wait(ctrl);
ata_cpu_relax();
}
}
static int ata_wait_ready(uint16_t io, uint16_t ctrl, int timeout_us) {
(void)io;
ata_io_wait(ctrl);
for (int i = 0; i < timeout_us; i++) {
uint8_t s = inb(ctrl + ATA_REG_ALT_STATUS);
if (!(s & ATA_SR_BSY)) {
if (s & ATA_SR_ERR) return -EIO;
if (s & ATA_SR_DF) return -EIO;
return 0;
}
ata_io_wait(ctrl);
ata_cpu_relax();
}
return -ETIMEDOUT;
}
static int ata_wait_drq(uint16_t io, uint16_t ctrl, int timeout_us) {
(void)io;
ata_io_wait(ctrl);
for (int i = 0; i < timeout_us; i++) {
uint8_t s = inb(ctrl + ATA_REG_ALT_STATUS);
if (s & ATA_SR_ERR) return -EIO;
if (s & ATA_SR_DF) return -EIO;
if (!(s & ATA_SR_BSY) && (s & ATA_SR_DRQ)) return 0;
ata_io_wait(ctrl);
ata_cpu_relax();
}
return -ETIMEDOUT;
}
static void ata_fix_string(char *dst, const uint16_t *src, int words) {
for (int i = 0; i < words; i++) {
dst[i * 2 + 0] = (char)(src[i] >> 8);
dst[i * 2 + 1] = (char)(src[i] & 0xFF);
}
dst[words * 2] = '\0';
for (int i = words * 2 - 1; i >= 0 && dst[i] == ' '; i--)
dst[i] = '\0';
}
static bool ata_identify_drive(uint16_t io, uint16_t ctrl, uint8_t drv_sel, ata_drive_t *out) {
memset(out, 0, sizeof(*out));
out->io_base = io;
out->ctrl_base = ctrl;
out->drive_select = drv_sel;
outb(io + ATA_REG_DRIVE, drv_sel);
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, 0);
outb(io + ATA_REG_LBA_LO, 0);
outb(io + ATA_REG_LBA_MID, 0);
outb(io + ATA_REG_LBA_HI, 0);
outb(io + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
ata_io_wait(ctrl);
uint8_t status = inb(io + ATA_REG_STATUS);
if (status == 0) return false;
for (int i = 0; i < 1000000; i++) {
status = inb(io + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY)) break;
ata_cpu_relax();
}
if (status & ATA_SR_BSY) return false;
uint8_t lm = inb(io + ATA_REG_LBA_MID);
uint8_t lh = inb(io + ATA_REG_LBA_HI);
if (lm == 0x14 && lh == 0xEB) {
out->is_atapi = true;
return false;
}
if (lm == 0x3C && lh == 0xC3) {
}
if (lm != 0 || lh != 0) {
return false;
}
for (int i = 0; i < 1000000; i++) {
status = inb(io + ATA_REG_STATUS);
if (status & ATA_SR_ERR) return false;
if (status & ATA_SR_DRQ) break;
ata_cpu_relax();
}
if (!(status & ATA_SR_DRQ)) return false;
for (int i = 0; i < 256; i++)
out->identify[i] = inw(io + ATA_REG_DATA);
ata_fix_string(out->model, &out->identify[27], 20);
ata_fix_string(out->serial, &out->identify[10], 10);
ata_fix_string(out->firmware, &out->identify[23], 4);
if (out->identify[83] & (1 << 10)) {
out->lba48 = true;
out->sectors = (uint64_t)out->identify[100]
| ((uint64_t)out->identify[101] << 16)
| ((uint64_t)out->identify[102] << 32)
| ((uint64_t)out->identify[103] << 48);
} else {
out->lba48 = false;
out->sectors = (uint64_t)out->identify[60]
| ((uint64_t)out->identify[61] << 16);
}
out->size_bytes = out->sectors * ATA_SECTOR_SIZE;
out->present = true;
return true;
}
void ata_init(void) {
serial_writestring("[ATA] probing drives...\n");
g_drive_count = 0;
struct { uint16_t io; uint16_t ctrl; uint8_t irq; } channels[] = {
{ ATA_PRIMARY_IO, ATA_PRIMARY_CTRL, 14 },
{ ATA_SECONDARY_IO, ATA_SECONDARY_CTRL, 15 },
};
for (int ch = 0; ch < 2; ch++) {
outb(channels[ch].ctrl, 0x02);
ata_soft_reset(channels[ch].ctrl);
uint8_t drvs[] = { ATA_DRIVE_MASTER, ATA_DRIVE_SLAVE };
for (int d = 0; d < 2; d++) {
int idx = ch * 2 + d;
ata_drive_t *drv = &g_drives[idx];
if (ata_identify_drive(channels[ch].io, channels[ch].ctrl,
drvs[d], drv))
{
drv->irq = channels[ch].irq;
g_drive_count++;
serial_printf("[ATA] drive %d: '%s' %llu sectors (%llu MB) %s\n",
idx, drv->model, drv->sectors,
drv->size_bytes / (1024 * 1024),
drv->lba48 ? "LBA48" : "LBA28");
}
}
}
if (g_drive_count == 0)
serial_writestring("[ATA] no drives found\n");
else
serial_printf("[ATA] %d drive(s) detected\n", g_drive_count);
}
ata_drive_t *ata_get_drive(int index) {
if (index < 0 || index >= ATA_MAX_DRIVES) return NULL;
if (!g_drives[index].present) return NULL;
return &g_drives[index];
}
int ata_get_drive_count(void) {
return g_drive_count;
}
#define ATA_IO_TIMEOUT 20000000
#define ATA_WRITE_TIMEOUT 40000000
#define ATA_RETRY_COUNT 3
static int ata_read_sectors_once(ata_drive_t *drive, uint64_t lba,
uint32_t count, void *buffer)
{
uint16_t io = drive->io_base;
uint16_t ctrl = drive->ctrl_base;
uint16_t *buf = (uint16_t *)buffer;
int ret = 0;
outb(io + ATA_REG_DRIVE, drive->drive_select);
ata_io_wait(ctrl);
(void)inb(io + ATA_REG_STATUS);
ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
if (drive->lba48 && (lba > 0x0FFFFFFF || count > 256)) {
outb(io + ATA_REG_DRIVE, (drive->drive_select & 0xF0) | ATA_LBA_BIT);
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count >> 8));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba >> 24));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 32));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 40));
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_READ_PIO_EXT);
} else {
outb(io + ATA_REG_DRIVE,
(drive->drive_select & 0xF0) | ATA_LBA_BIT | ((lba >> 24) & 0x0F));
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)count);
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
}
ata_io_wait(ctrl);
for (uint32_t s = 0; s < count; s++) {
ret = ata_wait_drq(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
for (int i = 0; i < 256; i++)
*buf++ = inw(io + ATA_REG_DATA);
ata_io_wait(ctrl);
}
return 0;
}
int ata_read_sectors(ata_drive_t *drive, uint64_t lba,
uint32_t count, void *buffer)
{
if (!drive || !drive->present || !buffer) return -EINVAL;
if (count == 0) return 0;
if (lba + count > drive->sectors) return -EINVAL;
uint64_t flags = spinlock_acquire_irqsave(&g_ata_lock);
int ret = -EIO;
for (int attempt = 0; attempt < ATA_RETRY_COUNT; attempt++) {
ret = ata_read_sectors_once(drive, lba, count, buffer);
if (ret == 0) break;
serial_printf("[ATA] read lba=%llu count=%u attempt %d failed: %d\n",
lba, count, attempt + 1, ret);
ata_soft_reset(drive->ctrl_base);
for (volatile int k = 0; k < 100000; k++) ata_cpu_relax();
}
spinlock_release_irqrestore(&g_ata_lock, flags);
return ret;
}
static int ata_write_sectors_once(ata_drive_t *drive, uint64_t lba,
uint32_t count, const void *buffer)
{
uint16_t io = drive->io_base;
uint16_t ctrl = drive->ctrl_base;
const uint16_t *buf = (const uint16_t *)buffer;
int ret = 0;
outb(io + ATA_REG_DRIVE, drive->drive_select);
ata_io_wait(ctrl);
(void)inb(io + ATA_REG_STATUS);
ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
if (drive->lba48 && (lba > 0x0FFFFFFF || count > 256)) {
outb(io + ATA_REG_DRIVE, (drive->drive_select & 0xF0) | ATA_LBA_BIT);
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count >> 8));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba >> 24));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 32));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 40));
outb(io + ATA_REG_SECCOUNT, (uint8_t)(count));
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO_EXT);
} else {
outb(io + ATA_REG_DRIVE,
(drive->drive_select & 0xF0) | ATA_LBA_BIT | ((lba >> 24) & 0x0F));
ata_io_wait(ctrl);
outb(io + ATA_REG_SECCOUNT, (uint8_t)count);
outb(io + ATA_REG_LBA_LO, (uint8_t)(lba));
outb(io + ATA_REG_LBA_MID, (uint8_t)(lba >> 8));
outb(io + ATA_REG_LBA_HI, (uint8_t)(lba >> 16));
outb(io + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
}
ata_io_wait(ctrl);
for (uint32_t s = 0; s < count; s++) {
ret = ata_wait_drq(io, ctrl, ATA_WRITE_TIMEOUT);
if (ret < 0) return ret;
for (int i = 0; i < 256; i++)
outw(io + ATA_REG_DATA, *buf++);
ata_io_wait(ctrl);
}
ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
if (ret < 0) return ret;
return 0;
}
int ata_write_sectors(ata_drive_t *drive, uint64_t lba,
uint32_t count, const void *buffer)
{
if (!drive || !drive->present || !buffer) return -EINVAL;
if (count == 0) return 0;
if (lba + count > drive->sectors) return -EINVAL;
uint64_t flags = spinlock_acquire_irqsave(&g_ata_lock);
int ret = -EIO;
for (int attempt = 0; attempt < ATA_RETRY_COUNT; attempt++) {
ret = ata_write_sectors_once(drive, lba, count, buffer);
if (ret == 0) break;
serial_printf("[ATA] write lba=%llu count=%u attempt %d failed: %d\n",
lba, count, attempt + 1, ret);
ata_soft_reset(drive->ctrl_base);
for (volatile int k = 0; k < 100000; k++) ata_cpu_relax();
}
spinlock_release_irqrestore(&g_ata_lock, flags);
return ret;
}
int ata_flush(ata_drive_t *drive) {
if (!drive || !drive->present) return -EINVAL;
uint64_t flags = spinlock_acquire_irqsave(&g_ata_lock);
uint16_t io = drive->io_base;
uint16_t ctrl = drive->ctrl_base;
outb(io + ATA_REG_DRIVE, drive->drive_select);
outb(io + ATA_REG_COMMAND,
drive->lba48 ? ATA_CMD_CACHE_FLUSH_EXT : ATA_CMD_CACHE_FLUSH);
int ret = ata_wait_ready(io, ctrl, ATA_IO_TIMEOUT);
spinlock_release_irqrestore(&g_ata_lock, flags);
return ret;
}
+95
View File
@@ -0,0 +1,95 @@
#include "../../include/drivers/blkdev.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/syscall/errno.h"
#include <string.h>
static blkdev_t *g_blkdevs[BLKDEV_MAX];
static int g_blkdev_count = 0;
void blkdev_init(void) {
memset(g_blkdevs, 0, sizeof(g_blkdevs));
g_blkdev_count = 0;
serial_writestring("[blkdev] initialized\n");
}
int blkdev_register(blkdev_t *dev) {
if (!dev || g_blkdev_count >= BLKDEV_MAX) return -ENOMEM;
int idx = g_blkdev_count;
g_blkdevs[idx] = dev;
g_blkdev_count++;
serial_printf("[blkdev] registered '%s' (%llu sectors, %llu MB)\n",
dev->name, dev->sector_count,
dev->size_bytes / (1024 * 1024));
return idx;
}
blkdev_t *blkdev_get_by_name(const char *name) {
for (int i = 0; i < g_blkdev_count; i++) {
if (g_blkdevs[i] && strcmp(g_blkdevs[i]->name, name) == 0)
return g_blkdevs[i];
}
return NULL;
}
blkdev_t *blkdev_get(int index) {
if (index < 0 || index >= g_blkdev_count) return NULL;
return g_blkdevs[index];
}
int blkdev_count(void) {
return g_blkdev_count;
}
int blkdev_read(blkdev_t *dev, uint64_t offset, void *buf, size_t len) {
if (!dev || !dev->ops || !dev->ops->read_sectors) return -EIO;
if (len == 0) return 0;
uint32_t sec_size = dev->sector_size ? dev->sector_size : BLKDEV_SECTOR_SIZE;
uint64_t start_lba = offset / sec_size;
uint64_t end_byte = offset + len;
uint64_t end_lba = (end_byte + sec_size - 1) / sec_size;
uint32_t nsectors = (uint32_t)(end_lba - start_lba);
uint8_t *tmp = kmalloc((size_t)nsectors * sec_size);
if (!tmp) return -ENOMEM;
int ret = dev->ops->read_sectors(dev, start_lba, nsectors, tmp);
if (ret < 0) {
kfree(tmp);
return ret;
}
uint64_t off_in_buf = offset - start_lba * sec_size;
memcpy(buf, tmp + off_in_buf, len);
kfree(tmp);
return 0;
}
int blkdev_write(blkdev_t *dev, uint64_t offset, const void *buf, size_t len) {
if (!dev || !dev->ops || !dev->ops->write_sectors) return -EIO;
if (len == 0) return 0;
uint32_t sec_size = dev->sector_size ? dev->sector_size : BLKDEV_SECTOR_SIZE;
uint64_t start_lba = offset / sec_size;
uint64_t end_byte = offset + len;
uint64_t end_lba = (end_byte + sec_size - 1) / sec_size;
uint32_t nsectors = (uint32_t)(end_lba - start_lba);
uint8_t *tmp = kmalloc((size_t)nsectors * sec_size);
if (!tmp) return -ENOMEM;
int ret = dev->ops->read_sectors(dev, start_lba, nsectors, tmp);
if (ret < 0) {
kfree(tmp);
return ret;
}
uint64_t off_in_buf = offset - start_lba * sec_size;
memcpy(tmp + off_in_buf, buf, len);
ret = dev->ops->write_sectors(dev, start_lba, nsectors, tmp);
kfree(tmp);
return ret;
}
+164
View File
@@ -0,0 +1,164 @@
#include "../../include/drivers/disk.h"
#include "../../include/drivers/ata.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/drivers/partition.h"
#include "../../include/fs/ext2.h"
#include "../../include/fs/fat32.h"
#include "../../include/fs/vfs.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/syscall/errno.h"
#include <stdio.h>
#include <string.h>
static int ata_blk_read(blkdev_t *dev, uint64_t lba, uint32_t count, void *buf) {
ata_drive_t *drv = (ata_drive_t *)dev->priv;
return ata_read_sectors(drv, lba, count, buf);
}
static int ata_blk_write(blkdev_t *dev, uint64_t lba, uint32_t count, const void *buf) {
ata_drive_t *drv = (ata_drive_t *)dev->priv;
return ata_write_sectors(drv, lba, count, buf);
}
static int ata_blk_flush(blkdev_t *dev) {
ata_drive_t *drv = (ata_drive_t *)dev->priv;
return ata_flush(drv);
}
static const blkdev_ops_t ata_blkdev_ops = {
.read_sectors = ata_blk_read,
.write_sectors = ata_blk_write,
.flush = ata_blk_flush,
};
static blkdev_t g_ata_blkdevs[ATA_MAX_DRIVES];
extern void devfs_register(const char *name, vnode_t *node);
static int64_t blk_vnode_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_read(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int64_t blk_vnode_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_write(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int blk_vnode_stat(vnode_t *node, vfs_stat_t *out) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = VFS_NODE_BLKDEV;
out->st_mode = 0660;
out->st_size = dev ? dev->size_bytes : 0;
return 0;
}
static void blk_vnode_ref(vnode_t *n) { (void)n; }
static void blk_vnode_unref(vnode_t *n) { (void)n; }
static const vnode_ops_t blk_vnode_ops = {
.read = blk_vnode_read, .write = blk_vnode_write,
.stat = blk_vnode_stat, .ref = blk_vnode_ref, .unref = blk_vnode_unref,
};
static vnode_t g_blk_vnodes[ATA_MAX_DRIVES];
static uint64_t g_blk_ino_base = 200;
void disk_init(void) {
serial_writestring("[disk] initializing...\n");
blkdev_init();
ata_init();
int count = 0;
const char *names[] = { "hda", "hdb", "hdc", "hdd" };
for (int i = 0; i < ATA_MAX_DRIVES; i++) {
ata_drive_t *drv = ata_get_drive(i);
if (!drv) continue;
blkdev_t *bdev = &g_ata_blkdevs[count];
memset(bdev, 0, sizeof(*bdev));
strncpy(bdev->name, names[i], BLKDEV_NAME_MAX - 1);
bdev->present = true;
bdev->sector_count = drv->sectors;
bdev->size_bytes = drv->size_bytes;
bdev->sector_size = ATA_SECTOR_SIZE;
bdev->ops = &ata_blkdev_ops;
bdev->priv = drv;
blkdev_register(bdev);
vnode_t *vn = &g_blk_vnodes[count];
memset(vn, 0, sizeof(*vn));
vn->type = VFS_NODE_BLKDEV;
vn->mode = 0660;
vn->ino = g_blk_ino_base + (uint64_t)count;
vn->ops = &blk_vnode_ops;
vn->fs_data = bdev;
vn->size = drv->size_bytes;
vn->refcount = 1;
devfs_register(names[i], vn);
serial_printf("[disk] /dev/%s -> %s (%llu MB)\n",
names[i], drv->model, drv->size_bytes / (1024 * 1024));
printf("[disk] /dev/%s -> %s (%llu MB)\n",
names[i], drv->model, drv->size_bytes / (1024 * 1024));
partition_scan(bdev);
count++;
}
if (count == 0) serial_writestring("[disk] no disks available\n");
else { serial_printf("[disk] %d disk(s) ready\n", count); printf("[disk] %d disk(s) ready\n", count); }
}
static const char *strip_dev_prefix(const char *name) {
if (strncmp(name, "/dev/", 5) == 0) return name + 5;
return name;
}
int disk_format(const char *devname, const char *label) {
const char *raw = strip_dev_prefix(devname);
blkdev_t *dev = blkdev_get_by_name(raw);
if (!dev) return -ENODEV;
return ext2_format(dev, label ? label : raw);
}
static int detect_fs_type(blkdev_t *dev) {
uint8_t sec[512];
if (dev->ops->read_sectors(dev, 0, 1, sec) < 0) return -1;
if (sec[510] == 0x55 && sec[511] == (uint8_t)0xAA) {
if (memcmp(sec + 82, "FAT32", 5) == 0) return 1;
if (memcmp(sec + 54, "FAT", 3) == 0) return 1;
}
uint16_t magic = 0;
if (blkdev_read(dev, EXT2_SUPER_OFFSET + 56, &magic, sizeof(magic)) == 0
&& magic == EXT2_SUPER_MAGIC) return 2;
return 0;
}
int disk_mount(const char *devname, const char *path) {
const char *raw = strip_dev_prefix(devname);
blkdev_t *dev = blkdev_get_by_name(raw);
if (!dev) return -ENODEV;
int t = detect_fs_type(dev);
vnode_t *root = NULL;
if (t == 1) {
root = fat32_mount(dev);
serial_printf("[disk] mounting FAT32 %s -> %s\n", raw, path);
} else if (t == 2) {
root = ext2_mount(dev);
serial_printf("[disk] mounting ext2 %s -> %s\n", raw, path);
} else {
serial_printf("[disk] %s: no recognizable FS\n", raw);
return -EINVAL;
}
if (!root) return -EIO;
int r = vfs_mount(path, root);
if (r < 0) { vnode_unref(root); return r; }
const char *fsname = (t == 1) ? "fat32" : "ext2";
int si = vfs_set_mount_info(path, raw, fsname);
serial_printf("[disk_mount] set_mount_info path='%s' dev='%s' fs='%s' -> %d\n",
path, raw, fsname, si);
return 0;
}
int disk_umount(const char *path) {
return vfs_umount(path);
}
+204
View File
@@ -0,0 +1,204 @@
#include "../../include/drivers/partition.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/fs/vfs.h"
#include "../../include/io/serial.h"
#include "../../include/syscall/errno.h"
#include "../../include/memory/pmm.h"
#include <string.h>
#include <stdio.h>
#define MAX_PARTITIONS 16
typedef struct {
blkdev_t base;
blkdev_t *parent;
uint64_t offset_sectors;
uint64_t count_sectors;
uint8_t type;
uint8_t bootable;
uint32_t partnum;
} partition_blkdev_t;
static partition_blkdev_t g_partitions[MAX_PARTITIONS];
static int g_partition_count = 0;
extern void devfs_register(const char *name, vnode_t *node);
static int64_t part_vnode_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_read(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int64_t part_vnode_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
if (!dev) return -EIO;
int r = blkdev_write(dev, offset, buf, len);
return (r < 0) ? r : (int64_t)len;
}
static int part_vnode_stat(vnode_t *node, vfs_stat_t *out) {
blkdev_t *dev = (blkdev_t *)node->fs_data;
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = VFS_NODE_BLKDEV;
out->st_mode = 0660;
out->st_size = dev ? dev->size_bytes : 0;
return 0;
}
static void part_vnode_ref(vnode_t *n) { (void)n; }
static void part_vnode_unref(vnode_t *n) { (void)n; }
static const vnode_ops_t part_vnode_ops = {
.read = part_vnode_read, .write = part_vnode_write,
.stat = part_vnode_stat, .ref = part_vnode_ref, .unref = part_vnode_unref,
};
static vnode_t g_part_vnodes[MAX_PARTITIONS];
static uint64_t g_part_ino_base = 300;
static int part_read_sectors(blkdev_t *dev, uint64_t lba, uint32_t count, void *buf) {
partition_blkdev_t *p = (partition_blkdev_t *)dev->priv;
if (!p || !p->parent) return -EIO;
if (lba + count > p->count_sectors) return -EINVAL;
return p->parent->ops->read_sectors(p->parent, p->offset_sectors + lba, count, buf);
}
static int part_write_sectors(blkdev_t *dev, uint64_t lba, uint32_t count, const void *buf) {
partition_blkdev_t *p = (partition_blkdev_t *)dev->priv;
if (!p || !p->parent) return -EIO;
if (lba + count > p->count_sectors) return -EINVAL;
return p->parent->ops->write_sectors(p->parent, p->offset_sectors + lba, count, buf);
}
static int part_flush(blkdev_t *dev) {
partition_blkdev_t *p = (partition_blkdev_t *)dev->priv;
if (!p || !p->parent) return -EIO;
return p->parent->ops->flush ? p->parent->ops->flush(p->parent) : 0;
}
static const blkdev_ops_t part_blkdev_ops = {
.read_sectors = part_read_sectors,
.write_sectors = part_write_sectors,
.flush = part_flush,
};
int partition_read_mbr(blkdev_t *disk, mbr_t *out) {
if (!disk || !out) return -EINVAL;
uint8_t sector[512];
int r = disk->ops->read_sectors(disk, 0, 1, sector);
if (r < 0) return r;
memcpy(out, sector, 512);
return 0;
}
int partition_write_mbr(blkdev_t *disk, const mbr_partition_t parts[4],
uint32_t disk_signature)
{
if (!disk || !parts) return -EINVAL;
uint8_t sector[512];
int r = disk->ops->read_sectors(disk, 0, 1, sector);
if (r < 0) return r;
mbr_t *mbr = (mbr_t *)sector;
mbr->disk_signature = disk_signature;
mbr->reserved = 0;
for (int i = 0; i < 4; i++) mbr->partitions[i] = parts[i];
mbr->signature = MBR_SIGNATURE;
return disk->ops->write_sectors(disk, 0, 1, sector);
}
static const char *part_type_name(uint8_t t) {
switch (t) {
case MBR_TYPE_EMPTY: return "empty";
case MBR_TYPE_FAT12: return "FAT12";
case MBR_TYPE_FAT16_S: return "FAT16 <32M";
case MBR_TYPE_FAT16: return "FAT16";
case MBR_TYPE_EXTENDED: return "Extended";
case MBR_TYPE_FAT32_CHS: return "FAT32 CHS";
case MBR_TYPE_FAT32_LBA: return "FAT32 LBA";
case MBR_TYPE_FAT16_LBA: return "FAT16 LBA";
case MBR_TYPE_LINUX: return "Linux";
case MBR_TYPE_ESP: return "EFI System";
default: return "unknown";
}
}
int partition_scan(blkdev_t *disk) {
if (!disk || !disk->present) return -ENODEV;
mbr_t mbr;
int r = partition_read_mbr(disk, &mbr);
if (r < 0) {
serial_printf("[part] %s: cannot read MBR: %d\n", disk->name, r);
return r;
}
if (mbr.signature != MBR_SIGNATURE) {
serial_printf("[part] %s: no MBR signature (raw disk / unpartitioned)\n",
disk->name);
return 0;
}
int found = 0;
for (int i = 0; i < 4; i++) {
mbr_partition_t *p = &mbr.partitions[i];
if (p->type == 0 || p->sector_count == 0) continue;
if (g_partition_count >= MAX_PARTITIONS) break;
partition_blkdev_t *pb = &g_partitions[g_partition_count];
memset(pb, 0, sizeof(*pb));
pb->parent = disk;
pb->offset_sectors = p->lba_start;
pb->count_sectors = p->sector_count;
pb->type = p->type;
pb->bootable = (p->boot_flag == 0x80) ? 1 : 0;
pb->partnum = (uint32_t)(i + 1);
snprintf(pb->base.name, BLKDEV_NAME_MAX, "%s%u", disk->name, pb->partnum);
pb->base.present = true;
pb->base.sector_count = pb->count_sectors;
pb->base.size_bytes = pb->count_sectors * (uint64_t)disk->sector_size;
pb->base.sector_size = disk->sector_size;
pb->base.ops = &part_blkdev_ops;
pb->base.priv = pb;
blkdev_register(&pb->base);
vnode_t *vn = &g_part_vnodes[g_partition_count];
memset(vn, 0, sizeof(*vn));
vn->type = VFS_NODE_BLKDEV;
vn->mode = 0660;
vn->ino = g_part_ino_base + (uint64_t)g_partition_count;
vn->ops = &part_vnode_ops;
vn->fs_data = &pb->base;
vn->size = pb->base.size_bytes;
vn->refcount = 1;
devfs_register(pb->base.name, vn);
serial_printf("[part] /dev/%s: type=0x%02x (%s) lba=%u sectors=%u %s\n",
pb->base.name, pb->type, part_type_name(pb->type),
p->lba_start, p->sector_count,
pb->bootable ? "[bootable]" : "");
printf("[part] /dev/%s: 0x%02x (%s) %u MB %s\n",
pb->base.name, pb->type, part_type_name(pb->type),
(unsigned)(pb->base.size_bytes / (1024 * 1024)),
pb->bootable ? "*" : "");
g_partition_count++;
found++;
}
if (found == 0) {
serial_printf("[part] %s: MBR present but no valid partitions\n", disk->name);
}
return found;
}
blkdev_t *partition_get(const char *name) {
for (int i = 0; i < g_partition_count; i++) {
if (strcmp(g_partitions[i].base.name, name) == 0) return &g_partitions[i].base;
}
return NULL;
}
+505
View File
@@ -0,0 +1,505 @@
#include "../../include/drivers/ps2.h"
#include "../../include/sched/sched.h"
#include "../../include/fs/devfs.h"
#include "../../include/drivers/timer.h"
#include "../../include/interrupts/interrupts.h"
#include "../../include/apic/apic.h"
#include "../../include/io/ports.h"
#include "../../include/io/serial.h"
#include "../../include/graphics/fb/fb.h"
#include <stdio.h>
#include <stddef.h>
#define KB_IRQ_VECTOR 0x21
#define MOUSE_IRQ_VECTOR 0x2C
#define KB_IRQ_LINE 1
#define MOUSE_IRQ_LINE 12
typedef enum { MOUSE_MODE_RELATIVE = 0, MOUSE_MODE_ABSOLUTE } mouse_mode_t;
static volatile kb_state_t kb_state;
static volatile mouse_state_t mouse_state;
static volatile mouse_mode_t mouse_mode = MOUSE_MODE_RELATIVE;
static volatile int32_t mouse_screen_w = 1024;
static volatile int32_t mouse_screen_h = 768;
static volatile uint8_t mouse_packet[4];
static volatile uint8_t mouse_packet_idx = 0;
static volatile bool mouse_has_scroll = false;
static volatile uint32_t mouse_lost_sync = 0;
static volatile kb_buf_t kb_buf;
static const char sc_lower[89] = {
0, '\x1b','1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', '\b', '\t',
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
'o', 'p', '[', ']', '\n', 0, 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
'\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', 0, '*',
0, ' ', 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
};
static const char sc_upper[89] = {
0, '\x1b','!', '@', '#', '$', '%', '^',
'&', '*', '(', ')', '_', '+', '\b', '\t',
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
'O', 'P', '{', '}', '\n', 0, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
'"', '~', 0, '|', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?', 0, '*',
0, ' ', 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
};
static void ps2_wait_read(void) {
for (int i = 0; i < 100000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL) return;
io_wait();
}
}
static void ps2_wait_write(void) {
for (int i = 0; i < 100000; i++) {
if (!(inb(PS2_STATUS_PORT) & PS2_STATUS_INPUT_FULL)) return;
io_wait();
}
}
static void ps2_send_cmd(uint8_t cmd) { ps2_wait_write(); outb(PS2_CMD_PORT, cmd); }
static void ps2_send_data(uint8_t data) { ps2_wait_write(); outb(PS2_DATA_PORT, data); }
static uint8_t ps2_recv_data(void) { ps2_wait_read(); return inb(PS2_DATA_PORT); }
static void ps2_flush_output(void) {
for (int i = 0; i < 32; i++) {
if (!(inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL)) break;
inb(PS2_DATA_PORT);
io_wait();
}
}
static bool ps2_mouse_wait_ack(void) {
for (int i = 0; i < 100000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL) {
uint8_t r = inb(PS2_DATA_PORT);
if (r == 0xFA) return true;
if (r == 0xFE || r == 0xFC) return false;
}
io_wait();
}
serial_writestring("[PS2] mouse_wait_ack: TIMEOUT\n");
return false;
}
static bool ps2_mouse_cmd(uint8_t cmd) {
ps2_send_cmd(PS2_CMD_WRITE_PORT2);
ps2_send_data(cmd);
return ps2_mouse_wait_ack();
}
static bool ps2_mouse_cmd_param(uint8_t cmd, uint8_t param) {
return ps2_mouse_cmd(cmd) && ps2_mouse_cmd(param);
}
static uint8_t ps2_mouse_read_byte(void) {
for (int i = 0; i < 100000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL)
return inb(PS2_DATA_PORT);
io_wait();
}
return 0xFF;
}
static uint8_t ps2_mouse_get_id(void) {
ps2_mouse_cmd(0xF2);
return ps2_mouse_read_byte();
}
static bool ps2_mouse_init_scroll(void) {
ps2_mouse_cmd_param(0xF3, 200);
ps2_mouse_cmd_param(0xF3, 100);
ps2_mouse_cmd_param(0xF3, 80);
uint8_t id = ps2_mouse_get_id();
serial_printf("[PS2] Mouse ID after IntelliMouse knock: 0x%02x\n", id);
return (id == 0x03);
}
static bool ps2_detect_absolute(void) {
uint8_t id = ps2_mouse_get_id();
serial_printf("[PS2] Mouse base device ID: 0x%02x\n", id);
if (!ps2_mouse_cmd(0xE9)) {
serial_writestring("[PS2] Status request failed\n");
return false;
}
uint8_t status = ps2_mouse_read_byte();
uint8_t resolution = ps2_mouse_read_byte();
uint8_t sample_rate = ps2_mouse_read_byte();
serial_printf("[PS2] Mouse status: flags=0x%02x res=0x%02x rate=%d\n",
status, resolution, sample_rate);
if (resolution == 0x08) {
serial_writestring("[PS2] Detected: QEMU absolute tablet mode\n");
return true;
}
ps2_mouse_cmd_param(0xF3, 200); ps2_mouse_cmd_param(0xF3, 100); ps2_mouse_cmd_param(0xF3, 80);
ps2_mouse_cmd_param(0xF3, 200); ps2_mouse_cmd_param(0xF3, 100); ps2_mouse_cmd_param(0xF3, 80);
id = ps2_mouse_get_id();
serial_printf("[PS2] Mouse ID after abs knock: 0x%02x\n", id);
if (id == 0x08) {
serial_writestring("[PS2] Detected: QEMU absolute mode (ID=0x08)\n");
return true;
}
return false;
}
static char scancode_to_char(uint8_t sc) {
if (sc >= sizeof(sc_lower) / sizeof(sc_lower[0])) return 0;
bool use_upper = kb_state.shift;
if ((sc >= 0x10 && sc <= 0x19) ||
(sc >= 0x1E && sc <= 0x26) ||
(sc >= 0x2C && sc <= 0x32))
use_upper = kb_state.caps_lock ^ kb_state.shift;
return use_upper ? sc_upper[sc] : sc_lower[sc];
}
void kb_buf_push(char c) {
uint8_t next = (kb_buf.tail + 1) % KB_BUF_SIZE;
if (next != kb_buf.head) {
kb_buf.buf[kb_buf.tail] = c;
kb_buf.tail = next;
}
}
extern uint32_t cursor_x;
extern uint32_t cursor_y;
extern uint32_t bg_color;
extern struct limine_framebuffer *global_framebuffer;
extern void get_cursor_position(uint32_t *x, uint32_t *y);
extern uint32_t get_screen_width(void);
/*static void mouse_print_status(void) {
const mouse_state_t *m = (const mouse_state_t *)&mouse_state;
const char *scroll_str = "NONE";
if (m->scroll == MOUSE_SCROLL_UP) scroll_str = "UP ";
if (m->scroll == MOUSE_SCROLL_DOWN) scroll_str = "DOWN";
serial_printf("[MOUSE] X:%5d Y:%5d L:%d M:%d R:%d Scroll:%s\n",
m->x, m->y,
(int)m->btn_left, (int)m->btn_middle, (int)m->btn_right,
scroll_str);
uint32_t saved_x, saved_y;
get_cursor_position(&saved_x, &saved_y);
if (global_framebuffer)
fb_fill_rect(global_framebuffer, 0, 0, global_framebuffer->width, 16, bg_color);
cursor_x = 0; cursor_y = 0;
printf("[MOUSE] X:%-5d Y:%-5d [L:%d M:%d R:%d] Wheel:%s",
m->x, m->y,
(int)m->btn_left, (int)m->btn_middle, (int)m->btn_right,
scroll_str);
cursor_x = saved_x;
cursor_y = saved_y;
}*/
DEFINE_IRQ(KB_IRQ_VECTOR, ps2_kb_handler)
{
(void)frame;
static bool e0_prefix = false;
uint8_t sc = inb(PS2_DATA_PORT);
bool released = (sc & PS2_KEY_RELEASE_BIT) != 0;
uint8_t key = sc & ~PS2_KEY_RELEASE_BIT;
if (sc == 0xE0) { e0_prefix = true; lapic_eoi(); return; }
if (e0_prefix) {
e0_prefix = false;
if (!released) {
switch (key) {
case 0x48: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('A'); break;
case 0x50: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('B'); break;
case 0x4D: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('C'); break;
case 0x4B: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('D'); break;
case 0x47: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('H'); break;
case 0x4F: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('F'); break;
case 0x53: kb_buf_push('\x1b'); kb_buf_push('['); kb_buf_push('3'); kb_buf_push('~'); break;
default: break;
}
}
lapic_eoi();
return;
}
if (key == SC_LSHIFT || key == SC_RSHIFT) { kb_state.shift = !released; lapic_eoi(); return; }
if (key == SC_LCTRL) { kb_state.ctrl = !released; lapic_eoi(); return; }
if (key == SC_LALT) { kb_state.alt = !released; lapic_eoi(); return; }
if (key == SC_CAPS && !released) { kb_state.caps_lock = !kb_state.caps_lock; lapic_eoi(); return; }
if (released) { lapic_eoi(); return; }
if (kb_state.ctrl) {
char base = scancode_to_char(key);
serial_printf("[PS2] ctrl+key sc=0x%02x base='%c'(0x%02x)\n", (unsigned)key, (base>=0x20&&base<0x7f)?base:'?', (unsigned)(uint8_t)base);
if (base >= 'a' && base <= 'z') {
uint8_t ctrl_char = (uint8_t)(base - 'a' + 1);
serial_printf("[PS2] ctrl char generated: 0x%02x\n", ctrl_char);
if (ctrl_char == 0x03) {
g_ctrlc_pending = 1;
lapic_eoi();
return;
}
kb_buf_push((char)ctrl_char);
lapic_eoi();
return;
}
if (base >= 'A' && base <= 'Z') {
kb_buf_push((char)(base - 'A' + 1));
lapic_eoi();
return;
}
}
char c = scancode_to_char(key);
if (c != 0)
kb_buf_push(c);
lapic_eoi();
}
DEFINE_IRQ(MOUSE_IRQ_VECTOR, ps2_mouse_handler)
{
(void)frame;
uint8_t data = inb(PS2_DATA_PORT);
uint8_t idx = mouse_packet_idx;
if (idx == 0 && !(data & 0x08)) {
mouse_lost_sync++;
lapic_eoi();
return;
}
mouse_packet[idx] = data;
mouse_packet_idx = (uint8_t)(idx + 1);
uint8_t pkt_size = mouse_has_scroll ? 4 : 3;
if (mouse_packet_idx < pkt_size) { lapic_eoi(); return; }
mouse_packet_idx = 0;
uint8_t flags = mouse_packet[0];
if ((flags & MOUSE_X_OVERFLOW) || (flags & MOUSE_Y_OVERFLOW)) { lapic_eoi(); return; }
mouse_state.btn_left = (flags & MOUSE_BTN_LEFT) != 0;
mouse_state.btn_right = (flags & MOUSE_BTN_RIGHT) != 0;
mouse_state.btn_middle = (flags & MOUSE_BTN_MIDDLE) != 0;
if (mouse_mode == MOUSE_MODE_ABSOLUTE) {
uint16_t abs_x = (uint16_t)mouse_packet[1] | ((flags & 0x10) ? 0x100 : 0);
uint16_t abs_y = (uint16_t)mouse_packet[2] | ((flags & 0x20) ? 0x100 : 0);
mouse_state.x = (int32_t)((uint32_t)abs_x * (uint32_t)mouse_screen_w / 0x1FF);
mouse_state.y = (int32_t)((uint32_t)abs_y * (uint32_t)mouse_screen_h / 0x1FF);
} else {
int32_t dx = mouse_packet[1];
int32_t dy = mouse_packet[2];
if (flags & MOUSE_X_SIGN) dx |= 0xFFFFFF00;
if (flags & MOUSE_Y_SIGN) dy |= 0xFFFFFF00;
mouse_state.x += dx;
mouse_state.y -= dy;
}
if (mouse_state.x < 0) mouse_state.x = 0;
if (mouse_state.y < 0) mouse_state.y = 0;
if (mouse_state.x >= mouse_screen_w) mouse_state.x = mouse_screen_w - 1;
if (mouse_state.y >= mouse_screen_h) mouse_state.y = mouse_screen_h - 1;
if (mouse_has_scroll) {
uint8_t z_raw = mouse_packet[3] & 0x0F;
int8_t z = (z_raw & 0x08) ? (int8_t)(z_raw | 0xF0) : (int8_t)z_raw;
if (z > 0) mouse_state.scroll = MOUSE_SCROLL_DOWN;
else if (z < 0) mouse_state.scroll = MOUSE_SCROLL_UP;
else mouse_state.scroll = MOUSE_SCROLL_NONE;
}
//mouse_print_status();
lapic_eoi();
}
bool ps2_init(void) {
serial_writestring("[PS2] Initializing PS/2 driver...\n");
if (!apic_is_available()) {
serial_writestring("[PS2] ERROR: APIC not available\n");
return false;
}
serial_writestring("[PS2] Initializing PS/2 controller...\n");
ps2_send_cmd(PS2_CMD_DISABLE_PORT1);
ps2_send_cmd(PS2_CMD_DISABLE_PORT2);
ps2_flush_output();
ps2_send_cmd(PS2_CMD_READ_CONFIG);
uint8_t cfg = ps2_recv_data();
cfg &= ~(PS2_CFG_PORT1_IRQ | PS2_CFG_PORT2_IRQ | PS2_CFG_PORT1_XLAT);
ps2_send_cmd(PS2_CMD_WRITE_CONFIG);
ps2_send_data(cfg);
ps2_send_cmd(PS2_CMD_SELF_TEST);
uint8_t result = ps2_recv_data();
if (result != 0x55) { serial_printf("[PS2] Self-test FAILED (0x%02x)\n", result); return false; }
serial_writestring("[PS2] Self-test PASSED\n");
ps2_send_cmd(PS2_CMD_WRITE_CONFIG);
ps2_send_data(cfg);
ps2_send_cmd(PS2_CMD_TEST_PORT1);
bool kb_ok = (ps2_recv_data() == 0x00);
serial_printf("[PS2] Port 1 (keyboard): %s\n", kb_ok ? "OK" : "FAIL");
ps2_send_cmd(PS2_CMD_TEST_PORT2);
bool mouse_ok = (ps2_recv_data() == 0x00);
serial_printf("[PS2] Port 2 (mouse): %s\n", mouse_ok ? "OK" : "FAIL");
if (kb_ok) ps2_send_cmd(PS2_CMD_ENABLE_PORT1);
if (mouse_ok) ps2_send_cmd(PS2_CMD_ENABLE_PORT2);
if (kb_ok) {
ps2_send_data(0xFF);
for (int i = 0; i < 200000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL)
if (inb(PS2_DATA_PORT) == 0xAA) break;
io_wait();
}
ps2_flush_output();
ps2_send_data(0xF0); ps2_flush_output();
ps2_send_data(0x01); ps2_flush_output();
ps2_send_data(0xF4); ps2_flush_output();
serial_writestring("[PS2] Keyboard ready (scancode set 1)\n");
}
if (mouse_ok) {
ps2_mouse_cmd(0xFF);
for (int i = 0; i < 200000; i++) {
if (inb(PS2_STATUS_PORT) & PS2_STATUS_OUTPUT_FULL) inb(PS2_DATA_PORT);
io_wait();
}
ps2_flush_output();
ps2_mouse_cmd(0xF6);
bool is_abs = ps2_detect_absolute();
if (is_abs) {
mouse_mode = MOUSE_MODE_ABSOLUTE;
mouse_has_scroll = false;
serial_writestring("[PS2] Mouse mode: ABSOLUTE (QEMU tablet)\n");
} else {
mouse_mode = MOUSE_MODE_RELATIVE;
mouse_has_scroll = ps2_mouse_init_scroll();
serial_printf("[PS2] Mouse mode: RELATIVE, scroll=%s\n",
mouse_has_scroll ? "YES" : "NO");
}
ps2_mouse_cmd_param(0xF3, 100);
ps2_mouse_cmd(0xF4);
serial_writestring("[PS2] Mouse: data reporting enabled\n");
}
ps2_send_cmd(PS2_CMD_READ_CONFIG);
cfg = ps2_recv_data();
if (kb_ok) cfg |= PS2_CFG_PORT1_IRQ;
if (mouse_ok) cfg |= PS2_CFG_PORT2_IRQ;
ps2_send_cmd(PS2_CMD_WRITE_CONFIG);
ps2_send_data(cfg);
if (kb_ok) {
apic_setup_irq(KB_IRQ_LINE, KB_IRQ_VECTOR, false, 0);
serial_printf("[PS2] Keyboard IRQ%d -> vector 0x%02x\n", KB_IRQ_LINE, KB_IRQ_VECTOR);
}
if (mouse_ok) {
apic_setup_irq(MOUSE_IRQ_LINE, MOUSE_IRQ_VECTOR, false, 0);
serial_printf("[PS2] Mouse IRQ%d -> vector 0x%02x\n", MOUSE_IRQ_LINE, MOUSE_IRQ_VECTOR);
}
kb_state = (kb_state_t){0};
kb_buf = (kb_buf_t){0};
mouse_state = (mouse_state_t){0};
mouse_packet_idx = 0;
mouse_lost_sync = 0;
if (global_framebuffer) {
mouse_screen_w = (int32_t)global_framebuffer->width;
mouse_screen_h = (int32_t)global_framebuffer->height;
mouse_state.x = mouse_screen_w / 2;
mouse_state.y = mouse_screen_h / 2;
}
serial_printf("[PS2] Screen: %dx%d, cursor start: %d,%d\n",
(int)mouse_screen_w, (int)mouse_screen_h,
(int)mouse_state.x, (int)mouse_state.y);
serial_writestring("[PS2] Driver ready.\n");
return true;
}
const kb_state_t* ps2_kb_get_state(void) {
return (const kb_state_t*)&kb_state;
}
const mouse_state_t* ps2_mouse_get_state(void) {
return (const mouse_state_t*)&mouse_state;
}
bool kb_buf_empty(void) {
return kb_buf.head == kb_buf.tail;
}
bool kb_buf_try_getc(char *out) {
if (kb_buf_empty()) return false;
*out = kb_buf.buf[kb_buf.head];
kb_buf.head = (kb_buf.head + 1) % KB_BUF_SIZE;
return true;
}
bool kb_buf_has_ctrlc(void) {
uint8_t h = kb_buf.head;
while (h != kb_buf.tail) {
if (kb_buf.buf[h] == 0x03) return true;
h = (h + 1) % KB_BUF_SIZE;
}
return false;
}
void kb_buf_consume_ctrlc(void) {
uint8_t h = kb_buf.head, t = kb_buf.tail;
uint8_t pos = h;
bool found = false;
while (pos != t) {
if (kb_buf.buf[pos] == 0x03) { found = true; break; }
pos = (pos + 1) % KB_BUF_SIZE;
}
if (!found) return;
uint8_t cur = pos;
uint8_t nxt = (cur + 1) % KB_BUF_SIZE;
while (nxt != t) {
kb_buf.buf[cur] = kb_buf.buf[nxt];
cur = nxt;
nxt = (nxt + 1) % KB_BUF_SIZE;
}
kb_buf.tail = cur;
}
char kb_buf_getc(void) {
char c;
while (!kb_buf_try_getc(&c)) {
//wait
}
return c;
}
+145
View File
@@ -0,0 +1,145 @@
#include "../include/drivers/timer.h"
#include "../include/apic/apic.h"
#include "../include/io/serial.h"
#include "../include/interrupts/interrupts.h"
#include "../include/io/ports.h"
#include "../include/sched/sched.h"
#include "../include/smp/percpu.h"
#include "../include/memory/pmm.h"
#include "../include/gdt/gdt.h"
#include <stdlib.h>
static volatile uint64_t ticks = 0;
volatile uint32_t g_ctrlc_pending = 0;
extern task_t* task_find_foreground(void);
extern void task_kill(task_t* target);
extern void sched_wakeup_sleepers(uint64_t now_ns);
DEFINE_IRQ(0x20, timer_handler)
{
ticks++;
lapic_eoi();
uint32_t cpu = lapic_get_id();
task_t* current = current_task[cpu];
percpu_t* pc = get_percpu();
if (cpu == 0 && g_ctrlc_pending) {
g_ctrlc_pending = 0;
task_t *fg = task_find_foreground();
if (fg) {
task_kill(fg);
} else {
extern void kb_buf_push(char c);
extern bool kb_buf_has_ctrlc(void);
if (!kb_buf_has_ctrlc())
kb_buf_push(0x03);
}
}
if (cpu == 0 && hpet_is_available()) {
sched_wakeup_sleepers(hpet_elapsed_ns());
}
if (!current) return;
if (current->pending_kill && pc) {
current->time_slice = 0;
pc->need_resched = true;
(void)frame;
return;
}
if (current->time_slice > 0)
current->time_slice--;
if (current->time_slice == 0 && pc)
pc->need_resched = true;
(void)frame;
}
bool timer_init(void) {
serial_writestring("Initializing timer subsystem...\n");
if (!apic_is_available()) {
serial_writestring("ERROR: APIC not available\n");
return false;
}
apic_timer_calibrate();
outb(0x21, 0xFF);
outb(0xA1, 0xFF);
serial_writestring("Timer subsystem initialized\n");
if (hpet_is_available()) {
serial_printf("HPET frequency: %llu Hz\n", hpet_get_frequency());
} else {
serial_writestring("HPET not available\n");
}
serial_writestring("Checking APIC Timer Status\n");
uint32_t timer_config = lapic_read(LAPIC_TIMER);
uint32_t initial_count = lapic_read(LAPIC_TIMER_ICR);
uint32_t current_count = lapic_read(LAPIC_TIMER_CCR);
serial_printf("APIC Timer Config: 0x%x\n", timer_config);
serial_printf(" Vector: 0x%x\n", timer_config & 0xFF);
serial_printf(" Masked: %s\n", (timer_config & LAPIC_TIMER_MASKED) ? "YES" : "NO");
serial_printf(" Periodic: %s\n", (timer_config & LAPIC_TIMER_PERIODIC) ? "YES" : "NO");
serial_printf("Initial Count: %u\n", initial_count);
serial_printf("Current Count: %u\n", current_count);
if (timer_config & LAPIC_TIMER_MASKED) {
serial_writestring("WARNING: APIC timer is masked! Unmasking...\n");
lapic_write(LAPIC_TIMER, (timer_config & ~LAPIC_TIMER_MASKED));
}
uint64_t rflags;
asm volatile("pushfq; pop %0" : "=r"(rflags));
serial_printf("RFLAGS: 0x%llx (IF=%s)\n", rflags, (rflags & 0x200) ? "ON" : "OFF");
if (!(rflags & 0x200)) {
serial_writestring("WARNING: Interrupts are DISABLED! Enabling...\n");
asm volatile("sti");
}
return true;
}
uint64_t timer_get_ticks(void) {
return ticks;
}
void timer_sleep_ms(uint64_t milliseconds) {
if (hpet_is_available()) {
hpet_sleep_ms(milliseconds);
} else {
volatile uint64_t i;
for (i = 0; i < milliseconds * 1000; i++) {
asm volatile("pause");
}
}
}
void timer_sleep_us(uint64_t microseconds) {
if (hpet_is_available()) {
hpet_sleep_us(microseconds);
} else {
volatile uint64_t i;
for (i = 0; i < microseconds; i++) {
asm volatile("pause");
}
}
}
void timer_sleep_ns(uint64_t nanoseconds) {
if (hpet_is_available()) {
hpet_sleep_ns(nanoseconds);
} else {
timer_sleep_us(nanoseconds / 1000);
}
}
+374
View File
@@ -0,0 +1,374 @@
#include "../../include/elf/elf.h"
#include "../../include/memory/vmm.h"
#include "../../include/memory/pmm.h"
#include "../../include/memory/paging.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stdlib.h>
#define ELF_PIE_BASE 0x0000000000400000ULL
#define ELF_USER_STACK_TOP 0x00007FFFFFFFE000ULL
#define ELF_DEFAULT_STACK (64 * 1024)
#define PML4_KERNEL_START 256
#define PML4_ENTRIES 512
#ifndef SHF_WRITE
#define SHF_WRITE 0x1
#endif
#ifndef SHF_ALLOC
#define SHF_ALLOC 0x2
#endif
#ifndef SHF_EXECINSTR
#define SHF_EXECINSTR 0x4
#endif
static inline uintptr_t page_align_down(uintptr_t addr) {
return addr & ~(uintptr_t)(PAGE_SIZE - 1);
}
static inline uintptr_t page_align_up(uintptr_t addr) {
return (addr + PAGE_SIZE - 1) & ~(uintptr_t)(PAGE_SIZE - 1);
}
static uint64_t phdr_flags_to_vmm(uint32_t pf) {
uint64_t flags = VMM_PRESENT | VMM_USER;
if (pf & PF_W) flags |= VMM_WRITE;
if (!(pf & PF_X)) flags |= VMM_NOEXEC;
return flags;
}
static elf_error_t elf_validate(const elf64_ehdr_t* hdr, size_t size) {
if (size < sizeof(elf64_ehdr_t)) return ELF_ERR_TOO_SMALL;
uint32_t magic;
memcpy(&magic, hdr->e_ident, 4);
if (magic != ELF_MAGIC) return ELF_ERR_BAD_MAGIC;
if (hdr->e_ident[EI_CLASS] != ELFCLASS64) return ELF_ERR_NOT_64;
if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) return ELF_ERR_NOT_LE;
if (hdr->e_ident[EI_VERSION] != EV_CURRENT ||
hdr->e_version != EV_CURRENT) return ELF_ERR_BAD_VERSION;
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) return ELF_ERR_NOT_EXEC;
if (hdr->e_machine != EM_X86_64) return ELF_ERR_WRONG_ARCH;
if (hdr->e_phnum == 0) return ELF_ERR_NO_LOAD;
uint64_t pht_end = (uint64_t)hdr->e_phoff +
(uint64_t)hdr->e_phentsize * hdr->e_phnum;
if (pht_end > size) return ELF_ERR_TOO_SMALL;
if (hdr->e_shnum && hdr->e_shoff) {
uint64_t sht_end = (uint64_t)hdr->e_shoff +
(uint64_t)hdr->e_shentsize * hdr->e_shnum;
if (sht_end > size) return ELF_ERR_TOO_SMALL;
}
return ELF_OK;
}
static bool ensure_page_mapped(vmm_pagemap_t* map, uintptr_t virt, uint64_t flags) {
uint64_t pf = 0;
if (vmm_get_page_flags(map, virt, &pf) && (pf & VMM_PRESENT)) {
return true;
}
void* page = pmm_alloc_zero(1);
if (!page) return false;
if (!vmm_map_page(map, virt, pmm_virt_to_phys(page), flags)) {
pmm_free(page, 1);
return false;
}
return true;
}
static elf_error_t load_segment(vmm_pagemap_t* map,
const uint8_t* data,
size_t file_size,
const elf64_phdr_t* phdr,
uintptr_t load_bias)
{
if (phdr->p_memsz == 0) return ELF_OK;
uintptr_t virt_start = phdr->p_vaddr + load_bias;
uintptr_t virt_end = virt_start + phdr->p_memsz;
uintptr_t page_start = page_align_down(virt_start);
uintptr_t page_end = page_align_up(virt_end);
size_t page_count = (page_end - page_start) / PAGE_SIZE;
if (phdr->p_filesz > 0 && phdr->p_offset + phdr->p_filesz > file_size) {
serial_printf("[ELF] Segment data out of file bounds\n");
return ELF_ERR_TOO_SMALL;
}
uint64_t vmm_flags = phdr_flags_to_vmm(phdr->p_flags);
for (size_t i = 0; i < page_count; i++) {
uintptr_t virt = page_start + i * PAGE_SIZE;
uint64_t cur_flags = 0;
bool already_mapped =
vmm_get_page_flags(map, virt, &cur_flags) && (cur_flags & VMM_PRESENT);
if (!already_mapped) {
void* page = pmm_alloc_zero(1);
if (!page) {
serial_printf("[ELF] pmm_alloc_zero(1) failed at page %zu for vaddr 0x%llx\n",
i, virt_start);
return ELF_ERR_NO_MEM;
}
if (!vmm_map_page(map, virt, pmm_virt_to_phys(page), vmm_flags)) {
serial_printf("[ELF] vmm_map_page failed: virt=0x%llx\n", virt);
pmm_free(page, 1);
return ELF_ERR_MAP_FAIL;
}
}
if (phdr->p_filesz > 0) {
uintptr_t pv_start = virt;
uintptr_t pv_end = pv_start + PAGE_SIZE;
uintptr_t data_end = virt_start + phdr->p_filesz;
uintptr_t cp_start = (pv_start > virt_start) ? pv_start : virt_start;
uintptr_t cp_end = (pv_end < data_end) ? pv_end : data_end;
if (cp_start < cp_end) {
uintptr_t phys = 0;
if (!vmm_virt_to_phys(map, cp_start, &phys)) {
return ELF_ERR_MAP_FAIL;
}
phys &= ~(uintptr_t)0xFFF;
size_t dst_off = cp_start - pv_start;
size_t src_off = cp_start - virt_start;
memcpy((uint8_t*)pmm_phys_to_virt(phys) + dst_off,
data + phdr->p_offset + src_off,
cp_end - cp_start);
}
}
}
serial_printf("[ELF] Segment loaded: virt=0x%llx-0x%llx flags=%s%s%s "
"phys=<per-page> pages=%zu\n",
virt_start, virt_end,
(phdr->p_flags & PF_R) ? "R" : "-",
(phdr->p_flags & PF_W) ? "W" : "-",
(phdr->p_flags & PF_X) ? "X" : "-",
page_count);
return ELF_OK;
}
static uintptr_t cover_orphan_sections(vmm_pagemap_t* map,
const elf64_ehdr_t* ehdr,
const uint8_t* bytes,
size_t file_size,
uintptr_t load_bias,
uintptr_t cur_load_end)
{
if (!ehdr->e_shnum || !ehdr->e_shoff) {
serial_printf("[ELF] no section headers; skipping orphan-section pass\n");
return cur_load_end;
}
if (ehdr->e_shentsize < sizeof(elf64_shdr_t)) {
serial_printf("[ELF] e_shentsize=%u too small; skipping orphan-section pass\n",
(unsigned)ehdr->e_shentsize);
return cur_load_end;
}
uint64_t sht_end = (uint64_t)ehdr->e_shoff +
(uint64_t)ehdr->e_shentsize * ehdr->e_shnum;
if (sht_end > file_size) {
serial_printf("[ELF] section table out of file bounds; skip\n");
return cur_load_end;
}
serial_printf("[ELF] section scan: e_shnum=%u\n", (unsigned)ehdr->e_shnum);
uintptr_t new_end = cur_load_end;
for (uint16_t i = 0; i < ehdr->e_shnum; i++) {
const elf64_shdr_t* sh = (const elf64_shdr_t*)
(bytes + ehdr->e_shoff + (uint64_t)ehdr->e_shentsize * i);
if (!(sh->sh_flags & SHF_ALLOC)) continue;
if (sh->sh_size == 0) continue;
if (sh->sh_addr == 0) continue;
uintptr_t s_start = (uintptr_t)sh->sh_addr + load_bias;
uintptr_t s_end = s_start + sh->sh_size;
uintptr_t p_start = page_align_down(s_start);
uintptr_t p_end = page_align_up(s_end);
uint64_t flags = VMM_PRESENT | VMM_USER;
if (sh->sh_flags & SHF_WRITE) flags |= VMM_WRITE;
if (!(sh->sh_flags & SHF_EXECINSTR)) flags |= VMM_NOEXEC;
size_t added_pages = 0;
for (uintptr_t p = p_start; p < p_end; p += PAGE_SIZE) {
uint64_t pf = 0;
if (vmm_get_page_flags(map, p, &pf) && (pf & VMM_PRESENT))
continue;
if (!ensure_page_mapped(map, p, flags)) {
serial_printf("[ELF] orphan-section: alloc/map failed at 0x%llx\n",
(unsigned long long)p);
return new_end;
}
added_pages++;
}
if (added_pages > 0) {
serial_printf("[ELF] ORPHAN section #%u type=%u: virt=0x%llx-0x%llx "
"added=%zu pages flags=%s%s%s\n",
(unsigned)i, (unsigned)sh->sh_type,
(unsigned long long)s_start,
(unsigned long long)s_end,
added_pages,
(sh->sh_flags & SHF_ALLOC) ? "A" : "-",
(sh->sh_flags & SHF_WRITE) ? "W" : "-",
(sh->sh_flags & SHF_EXECINSTR) ? "X" : "-");
if (sh->sh_type != SHT_NOBITS && sh->sh_size > 0) {
if (sh->sh_offset + sh->sh_size <= file_size) {
for (uintptr_t p = p_start; p < p_end; p += PAGE_SIZE) {
uintptr_t pv_end = p + PAGE_SIZE;
uintptr_t cp_start = (p > s_start) ? p : s_start;
uintptr_t cp_end = (pv_end < s_end) ? pv_end : s_end;
if (cp_start >= cp_end) continue;
uintptr_t phys = 0;
if (!vmm_virt_to_phys(map, cp_start, &phys)) continue;
phys &= ~(uintptr_t)0xFFF;
size_t dst_off = cp_start - p;
size_t src_off = cp_start - s_start;
memcpy((uint8_t*)pmm_phys_to_virt(phys) + dst_off,
bytes + sh->sh_offset + src_off,
cp_end - cp_start);
}
}
}
}
if (p_end > new_end) new_end = p_end;
}
return new_end;
}
static uintptr_t alloc_user_stack(vmm_pagemap_t* map, size_t stack_size) {
size_t page_count = page_align_up(stack_size) / PAGE_SIZE;
uintptr_t stack_bottom = ELF_USER_STACK_TOP - page_count * PAGE_SIZE;
uint64_t flags = VMM_PRESENT | VMM_WRITE | VMM_USER | VMM_NOEXEC;
for (size_t i = 0; i < page_count; i++) {
void* page = pmm_alloc_zero(1);
if (!page) {
serial_printf("[ELF] Stack alloc failed at page %zu\n", i);
for (size_t j = 0; j < i; j++)
vmm_unmap_page_noflush(map, stack_bottom + j * PAGE_SIZE);
return 0;
}
if (!vmm_map_page(map, stack_bottom + i * PAGE_SIZE,
pmm_virt_to_phys(page), flags)) {
serial_printf("[ELF] Stack map failed at page %zu\n", i);
pmm_free(page, 1);
for (size_t j = 0; j < i; j++)
vmm_unmap_page_noflush(map, stack_bottom + j * PAGE_SIZE);
return 0;
}
}
serial_printf("[ELF] Stack: virt=0x%llx-0x%llx (%zu KiB)\n",
stack_bottom, ELF_USER_STACK_TOP,
(page_count * PAGE_SIZE) / 1024);
return (ELF_USER_STACK_TOP - 8) & ~(uintptr_t)0xF;
}
elf_load_result_t elf_load(const void* data, size_t size, size_t stack_sz) {
elf_load_result_t result = {0};
if (!data) { result.error = ELF_ERR_NULL; return result; }
const elf64_ehdr_t* ehdr = (const elf64_ehdr_t*)data;
result.error = elf_validate(ehdr, size);
if (result.error != ELF_OK) {
serial_printf("[ELF] Validation failed: %s\n", elf_strerror(result.error));
return result;
}
uintptr_t load_bias = (ehdr->e_type == ET_DYN) ? ELF_PIE_BASE : 0;
if (load_bias) serial_printf("[ELF] PIE binary, bias=0x%llx\n", load_bias);
vmm_pagemap_t* map = vmm_create_pagemap();
if (!map) {
serial_printf("[ELF] vmm_create_pagemap() failed\n");
result.error = ELF_ERR_NO_MEM;
return result;
}
result.pagemap = map;
serial_printf("[ELF] Kernel PML4[256..511] inherited via vmm_create_pagemap\n");
const uint8_t* bytes = (const uint8_t*)data;
const elf64_phdr_t* phdrs = (const elf64_phdr_t*)(bytes + ehdr->e_phoff);
bool has_load = false;
uintptr_t max_vaddr = 0;
for (uint16_t i = 0; i < ehdr->e_phnum; i++) {
const elf64_phdr_t* ph = &phdrs[i];
if (ph->p_type != PT_LOAD) continue;
has_load = true;
serial_printf("[ELF] PT_LOAD[%u]: off=0x%llx vaddr=0x%llx "
"filesz=0x%llx memsz=0x%llx flags=0x%x\n",
i, ph->p_offset, ph->p_vaddr,
ph->p_filesz, ph->p_memsz, ph->p_flags);
elf_error_t err = load_segment(map, bytes, size, ph, load_bias);
if (err != ELF_OK) {
result.error = err;
return result;
}
uintptr_t seg_end = ph->p_vaddr + load_bias + ph->p_memsz;
if (seg_end > max_vaddr) max_vaddr = seg_end;
}
if (!has_load) { result.error = ELF_ERR_NO_LOAD; return result; }
uintptr_t load_end = page_align_up(max_vaddr);
load_end = cover_orphan_sections(map, ehdr, bytes, size, load_bias, load_end);
result.entry = ehdr->e_entry + load_bias;
result.load_base = load_bias;
result.load_end = load_end;
serial_printf("[ELF] Entry point: 0x%llx load_end (brk_start): 0x%llx\n",
result.entry, result.load_end);
if (stack_sz == 0) stack_sz = ELF_DEFAULT_STACK;
result.stack_size = stack_sz;
result.stack_top = alloc_user_stack(map, stack_sz);
if (result.stack_top == 0) { result.error = ELF_ERR_NO_MEM; return result; }
serial_printf("[ELF] Load complete. entry=0x%llx stack_top=0x%llx\n",
result.entry, result.stack_top);
return result;
}
void elf_unload(elf_load_result_t* result) {
if (!result) return;
if (result->pagemap) {
vmm_free_pagemap(result->pagemap);
result->pagemap = NULL;
}
serial_printf("[ELF] Process unloaded.\n");
}
const char* elf_strerror(elf_error_t err) {
switch (err) {
case ELF_OK: return "OK";
case ELF_ERR_NULL: return "NULL pointer";
case ELF_ERR_TOO_SMALL: return "file too small / data out of bounds";
case ELF_ERR_BAD_MAGIC: return "not an ELF file (bad magic)";
case ELF_ERR_NOT_64: return "not ELF64";
case ELF_ERR_NOT_LE: return "not little-endian";
case ELF_ERR_BAD_VERSION: return "unsupported ELF version";
case ELF_ERR_NOT_EXEC: return "not an executable or shared object";
case ELF_ERR_WRONG_ARCH: return "not x86_64";
case ELF_ERR_NO_LOAD: return "no PT_LOAD segments";
case ELF_ERR_MAP_FAIL: return "vmm_map_page failed";
case ELF_ERR_NO_MEM: return "out of memory";
default: return "unknown error";
}
}
Binary file not shown.
+352
View File
@@ -0,0 +1,352 @@
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "../../include/fs/devfs.h"
#include "../../include/fs/vfs.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include "../../include/drivers/ps2.h"
#include "../../include/sched/sched.h"
#include "../../include/apic/apic.h"
#include "../../include/smp/percpu.h"
extern void draw_cursor(void);
extern void erase_cursor(void);
extern uint32_t get_screen_width(void);
extern uint32_t get_screen_height(void);
extern uint32_t get_cursor_row(void);
extern uint32_t get_cursor_col(void);
#define TIOCGWINSZ 0x5413
#define TIOCGCURSOR 0x5480
#define TCGETS 0x5401
#define TCSETS 0x5402
#define TCSETSW 0x5403
#define TCSETSF 0x5404
#define TIOCSNONBLOCK 0x5481
static int tty_nonblock = 0;
#define T_ICANON 0x0002
#define T_ECHO 0x0008
#define T_ISIG 0x0001
struct cervus_winsize {
uint16_t ws_row;
uint16_t ws_col;
uint16_t ws_xpixel;
uint16_t ws_ypixel;
};
struct cervus_cursor_pos {
uint32_t row;
uint32_t col;
};
struct cervus_termios {
uint32_t c_iflag;
uint32_t c_oflag;
uint32_t c_cflag;
uint32_t c_lflag;
uint8_t c_cc[32];
};
static struct cervus_termios g_tty_termios = {
.c_iflag = 0,
.c_oflag = 0,
.c_cflag = 0,
.c_lflag = T_ICANON | T_ECHO | T_ISIG,
.c_cc = {0},
};
static inline int tty_is_canonical(void) {
return (g_tty_termios.c_lflag & T_ICANON) != 0;
}
static inline int tty_has_isig(void) {
return (g_tty_termios.c_lflag & T_ISIG) != 0;
}
static inline task_t* devfs_cur_task(void) {
percpu_t* pc = get_percpu();
return pc ? (task_t*)pc->current_task : NULL;
}
static int64_t tty_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
(void)node; (void)offset;
if (len == 0) return 0;
char *dst = buf;
uint64_t freq = hpet_is_available() ? hpet_get_frequency() : 0;
uint64_t half_tick = freq ? freq / 2 : 0;
uint64_t next_blink = half_tick ? (hpet_read_counter() + half_tick) : 0;
int cursor_on = 1;
int canonical = tty_is_canonical();
int isig = tty_has_isig();
if (tty_nonblock && kb_buf_empty()) return -EAGAIN;
draw_cursor();
while (kb_buf_empty()) {
task_t *me = devfs_cur_task();
if (me && me->pending_kill) {
erase_cursor();
return -EINTR;
}
if (half_tick) {
uint64_t now = hpet_read_counter();
if (now >= next_blink) {
if (cursor_on) { erase_cursor(); cursor_on = 0; }
else { draw_cursor(); cursor_on = 1; }
next_blink = now + half_tick;
}
}
task_yield();
}
erase_cursor();
task_t *me = devfs_cur_task();
if (me && me->pending_kill) {
kb_buf_consume_ctrlc();
return -EINTR;
}
char first = kb_buf_getc();
if (isig && canonical && first == 0x03) {
return -EINTR;
}
dst[0] = first;
size_t got = 1;
if (!canonical) {
while (got < len) {
char c;
if (!kb_buf_try_getc(&c)) break;
dst[got++] = c;
}
return (int64_t)got;
}
while (got < len) {
char c;
if (!kb_buf_try_getc(&c)) break;
if (isig && c == 0x03) break;
dst[got++] = c;
if (c == '\n') break;
}
return (int64_t)got;
}
static int64_t tty_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
(void)node; (void)offset;
serial_writebuf((const char *)buf, len);
const unsigned char *p = (const unsigned char *)buf;
for (size_t i = 0; i < len; i++) putchar((int)p[i]);
return (int64_t)len;
}
static int64_t tty_ioctl(vnode_t *node, uint64_t req, void *arg) {
(void)node;
if (req == TIOCGWINSZ) {
if (!arg) return -EFAULT;
struct cervus_winsize *ws = (struct cervus_winsize *)arg;
uint32_t w = get_screen_width();
uint32_t h = get_screen_height();
ws->ws_col = (uint16_t)(w / 8);
ws->ws_row = (uint16_t)(h / 16);
ws->ws_xpixel = (uint16_t)w;
ws->ws_ypixel = (uint16_t)h;
return 0;
}
if (req == TIOCGCURSOR) {
if (!arg) return -EFAULT;
struct cervus_cursor_pos *cp = (struct cervus_cursor_pos *)arg;
cp->row = get_cursor_row();
cp->col = get_cursor_col();
return 0;
}
if (req == TIOCSNONBLOCK) {
if (!arg) return -EFAULT;
tty_nonblock = *((int *)arg) ? 1 : 0;
return 0;
}
if (req == TCGETS) {
if (!arg) return -EFAULT;
memcpy(arg, &g_tty_termios, sizeof(g_tty_termios));
return 0;
}
if (req == TCSETS || req == TCSETSW || req == TCSETSF) {
if (!arg) return -EFAULT;
memcpy(&g_tty_termios, arg, sizeof(g_tty_termios));
return 0;
}
return -ENOTTY;
}
static void devfs_ref(vnode_t *node) {
(void)node;
}
static void devfs_unref(vnode_t *node) {
(void)node;
}
static int devfs_stat(vnode_t *node, vfs_stat_t *out) {
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
return 0;
}
static const vnode_ops_t tty_ops = {
.read = tty_read,
.write = tty_write,
.ioctl = tty_ioctl,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
static int64_t null_read(vnode_t *n, void *buf, size_t len, uint64_t off) {
(void)n; (void)buf; (void)len; (void)off;
return 0;
}
static int64_t null_write(vnode_t *n, const void *buf, size_t len, uint64_t off) {
(void)n; (void)buf; (void)off;
return (int64_t)len;
}
static const vnode_ops_t null_ops = {
.read = null_read,
.write = null_write,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
static int64_t zero_read(vnode_t *n, void *buf, size_t len, uint64_t off) {
(void)n; (void)off;
memset(buf, 0, len);
return (int64_t)len;
}
static const vnode_ops_t zero_ops = {
.read = zero_read,
.write = null_write,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
#define DEVFS_MAX_ENTRIES 32
typedef struct {
char name[VFS_MAX_NAME];
vnode_t *node;
} devfs_entry_t;
typedef struct {
devfs_entry_t entries[DEVFS_MAX_ENTRIES];
int count;
} devfs_dir_data_t;
static devfs_dir_data_t g_devdir;
static vnode_t g_devroot;
static vnode_t g_tty_node;
static vnode_t g_null_node;
static vnode_t g_zero_node;
static uint64_t g_devfs_ino = 100;
static int devfs_dir_lookup(vnode_t *dir, const char *name, vnode_t **out) {
(void)dir;
for (int i = 0; i < g_devdir.count; i++) {
if (strcmp(g_devdir.entries[i].name, name) == 0) {
vnode_ref(g_devdir.entries[i].node);
*out = g_devdir.entries[i].node;
return 0;
}
}
return -ENOENT;
}
static int devfs_dir_readdir(vnode_t *dir, uint64_t index, vfs_dirent_t *out) {
(void)dir;
if ((int64_t)index >= g_devdir.count) return -ENOENT;
devfs_entry_t *e = &g_devdir.entries[index];
out->d_ino = e->node->ino;
out->d_type = (uint8_t)e->node->type;
strncpy(out->d_name, e->name, VFS_MAX_NAME - 1);
out->d_name[VFS_MAX_NAME - 1] = '\0';
return 0;
}
static const vnode_ops_t devfs_dir_ops = {
.lookup = devfs_dir_lookup,
.readdir = devfs_dir_readdir,
.stat = devfs_stat,
.ref = devfs_ref,
.unref = devfs_unref,
};
void devfs_register(const char *name, vnode_t *node) {
if (g_devdir.count >= DEVFS_MAX_ENTRIES) return;
devfs_entry_t *e = &g_devdir.entries[g_devdir.count++];
strncpy(e->name, name, VFS_MAX_NAME - 1);
e->name[VFS_MAX_NAME - 1] = '\0';
e->node = node;
}
vnode_t *devfs_create_root(void) {
memset(&g_devdir, 0, sizeof(g_devdir));
memset(&g_devroot, 0, sizeof(g_devroot));
memset(&g_tty_node, 0, sizeof(g_tty_node));
memset(&g_null_node, 0, sizeof(g_null_node));
memset(&g_zero_node, 0, sizeof(g_zero_node));
g_devroot.type = VFS_NODE_DIR;
g_devroot.mode = 0755;
g_devroot.ino = g_devfs_ino++;
g_devroot.ops = &devfs_dir_ops;
g_devroot.fs_data = &g_devdir;
g_devroot.refcount = 1;
g_tty_node.type = VFS_NODE_CHARDEV;
g_tty_node.mode = 0666;
g_tty_node.ino = g_devfs_ino++;
g_tty_node.ops = &tty_ops;
g_tty_node.refcount = 1;
g_null_node.type = VFS_NODE_CHARDEV;
g_null_node.mode = 0666;
g_null_node.ino = g_devfs_ino++;
g_null_node.ops = &null_ops;
g_null_node.refcount = 1;
g_zero_node.type = VFS_NODE_CHARDEV;
g_zero_node.mode = 0666;
g_zero_node.ino = g_devfs_ino++;
g_zero_node.ops = &zero_ops;
g_zero_node.refcount = 1;
devfs_register("tty", &g_tty_node);
devfs_register("null", &g_null_node);
devfs_register("zero", &g_zero_node);
serial_writestring("[devfs] /dev/tty, /dev/null, /dev/zero registered\n");
return &g_devroot;
}
+935
View File
@@ -0,0 +1,935 @@
#include "../../include/fs/ext2.h"
#include "../../include/fs/vfs.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include "../../include/syscall/errno.h"
#include <string.h>
#include <stdio.h>
static int block_read(ext2_t *fs, uint32_t block, void *buf) {
return blkdev_read(fs->dev, (uint64_t)block * fs->block_size, buf, fs->block_size);
}
static int block_write(ext2_t *fs, uint32_t block, const void *buf) {
return blkdev_write(fs->dev, (uint64_t)block * fs->block_size, buf, fs->block_size);
}
static int sb_flush(ext2_t *fs) {
return blkdev_write(fs->dev, EXT2_SUPER_OFFSET, &fs->sb, sizeof(fs->sb));
}
static int gdt_flush(ext2_t *fs) {
uint32_t gdt_block = (fs->block_size == 1024) ? 2 : 1;
return blkdev_write(fs->dev, (uint64_t)gdt_block * fs->block_size,
fs->gdt, fs->groups_count * sizeof(ext2_group_desc_t));
}
static int inode_read(ext2_t *fs, uint32_t ino, ext2_inode_t *out) {
if (ino == 0) return -EINVAL;
uint32_t idx = ino - 1;
uint32_t group = idx / fs->sb.s_inodes_per_group;
uint32_t local = idx % fs->sb.s_inodes_per_group;
if (group >= fs->groups_count) return -EINVAL;
uint64_t off = (uint64_t)fs->gdt[group].bg_inode_table * fs->block_size
+ (uint64_t)local * fs->inode_size;
return blkdev_read(fs->dev, off, out, sizeof(*out));
}
static int inode_write(ext2_t *fs, uint32_t ino, const ext2_inode_t *in) {
if (ino == 0) return -EINVAL;
uint32_t idx = ino - 1;
uint32_t group = idx / fs->sb.s_inodes_per_group;
uint32_t local = idx % fs->sb.s_inodes_per_group;
if (group >= fs->groups_count) return -EINVAL;
uint64_t off = (uint64_t)fs->gdt[group].bg_inode_table * fs->block_size
+ (uint64_t)local * fs->inode_size;
return blkdev_write(fs->dev, off, in, sizeof(*in));
}
static bool bmp_test(const uint8_t *bmp, uint32_t bit) {
return (bmp[bit / 8] >> (bit % 8)) & 1;
}
static void bmp_set(uint8_t *bmp, uint32_t bit) {
bmp[bit / 8] |= (uint8_t)(1 << (bit % 8));
}
static void bmp_clear(uint8_t *bmp, uint32_t bit) {
bmp[bit / 8] &= (uint8_t)~(1 << (bit % 8));
}
static int32_t alloc_inode(ext2_t *fs) {
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return -ENOMEM;
for (uint32_t g = 0; g < fs->groups_count; g++) {
if (fs->gdt[g].bg_free_inodes_count == 0) continue;
block_read(fs, fs->gdt[g].bg_inode_bitmap, bmp);
for (uint32_t i = 0; i < fs->sb.s_inodes_per_group; i++) {
if (!bmp_test(bmp, i)) {
bmp_set(bmp, i);
block_write(fs, fs->gdt[g].bg_inode_bitmap, bmp);
fs->gdt[g].bg_free_inodes_count--;
fs->sb.s_free_inodes_count--;
fs->dirty = true;
kfree(bmp);
return (int32_t)(g * fs->sb.s_inodes_per_group + i + 1);
}
}
}
kfree(bmp);
return -ENOSPC;
}
static void free_inode(ext2_t *fs, uint32_t ino) {
uint32_t idx = ino - 1;
uint32_t group = idx / fs->sb.s_inodes_per_group;
uint32_t local = idx % fs->sb.s_inodes_per_group;
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return;
block_read(fs, fs->gdt[group].bg_inode_bitmap, bmp);
bmp_clear(bmp, local);
block_write(fs, fs->gdt[group].bg_inode_bitmap, bmp);
fs->gdt[group].bg_free_inodes_count++;
fs->sb.s_free_inodes_count++;
fs->dirty = true;
kfree(bmp);
}
static int32_t alloc_block(ext2_t *fs) {
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return -ENOMEM;
for (uint32_t g = 0; g < fs->groups_count; g++) {
if (fs->gdt[g].bg_free_blocks_count == 0) continue;
block_read(fs, fs->gdt[g].bg_block_bitmap, bmp);
for (uint32_t i = 0; i < fs->sb.s_blocks_per_group; i++) {
uint32_t abs_block = g * fs->sb.s_blocks_per_group + i + fs->sb.s_first_data_block;
if (abs_block >= fs->sb.s_blocks_count) break;
if (!bmp_test(bmp, i)) {
bmp_set(bmp, i);
block_write(fs, fs->gdt[g].bg_block_bitmap, bmp);
fs->gdt[g].bg_free_blocks_count--;
fs->sb.s_free_blocks_count--;
fs->dirty = true;
kfree(bmp);
return (int32_t)abs_block;
}
}
}
kfree(bmp);
return -ENOSPC;
}
static void free_block(ext2_t *fs, uint32_t blk) {
if (blk < fs->sb.s_first_data_block) return;
uint32_t adj = blk - fs->sb.s_first_data_block;
uint32_t group = adj / fs->sb.s_blocks_per_group;
uint32_t local = adj % fs->sb.s_blocks_per_group;
if (group >= fs->groups_count) return;
uint8_t *bmp = kmalloc(fs->block_size);
if (!bmp) return;
block_read(fs, fs->gdt[group].bg_block_bitmap, bmp);
bmp_clear(bmp, local);
block_write(fs, fs->gdt[group].bg_block_bitmap, bmp);
fs->gdt[group].bg_free_blocks_count++;
fs->sb.s_free_blocks_count++;
fs->dirty = true;
kfree(bmp);
}
static int32_t get_block_num(ext2_t *fs, ext2_inode_t *di, uint32_t file_block) {
if (file_block < EXT2_NDIR_BLOCKS)
return (int32_t)di->i_block[file_block];
uint32_t ppb = fs->ptrs_per_block;
file_block -= EXT2_NDIR_BLOCKS;
if (file_block < ppb) {
if (di->i_block[EXT2_IND_BLOCK] == 0) return 0;
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
int32_t ret = (int32_t)ind[file_block];
kfree(ind);
return ret;
}
file_block -= ppb;
if (file_block < ppb * ppb) {
if (di->i_block[EXT2_DIND_BLOCK] == 0) return 0;
uint32_t *dind = kmalloc(fs->block_size);
if (!dind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
uint32_t i1 = file_block / ppb;
uint32_t i2 = file_block % ppb;
if (dind[i1] == 0) { kfree(dind); return 0; }
uint32_t ib = dind[i1];
kfree(dind);
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, ib, ind);
int32_t ret = (int32_t)ind[i2];
kfree(ind);
return ret;
}
return -EFBIG;
}
static int set_block_num(ext2_t *fs, ext2_inode_t *di, uint32_t file_block, uint32_t disk_block) {
if (file_block < EXT2_NDIR_BLOCKS) {
di->i_block[file_block] = disk_block;
return 0;
}
uint32_t ppb = fs->ptrs_per_block;
file_block -= EXT2_NDIR_BLOCKS;
if (file_block < ppb) {
if (di->i_block[EXT2_IND_BLOCK] == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) return nb;
di->i_block[EXT2_IND_BLOCK] = (uint32_t)nb;
uint8_t *z = kzalloc(fs->block_size);
block_write(fs, (uint32_t)nb, z);
kfree(z);
}
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
ind[file_block] = disk_block;
block_write(fs, di->i_block[EXT2_IND_BLOCK], ind);
kfree(ind);
return 0;
}
file_block -= ppb;
if (file_block < ppb * ppb) {
if (di->i_block[EXT2_DIND_BLOCK] == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) return nb;
di->i_block[EXT2_DIND_BLOCK] = (uint32_t)nb;
uint8_t *z = kzalloc(fs->block_size);
block_write(fs, (uint32_t)nb, z);
kfree(z);
}
uint32_t *dind = kmalloc(fs->block_size);
if (!dind) return -ENOMEM;
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
uint32_t i1 = file_block / ppb;
uint32_t i2 = file_block % ppb;
if (dind[i1] == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) { kfree(dind); return nb; }
dind[i1] = (uint32_t)nb;
block_write(fs, di->i_block[EXT2_DIND_BLOCK], dind);
uint8_t *z = kzalloc(fs->block_size);
block_write(fs, (uint32_t)nb, z);
kfree(z);
}
uint32_t ib = dind[i1];
kfree(dind);
uint32_t *ind = kmalloc(fs->block_size);
if (!ind) return -ENOMEM;
block_read(fs, ib, ind);
ind[i2] = disk_block;
block_write(fs, ib, ind);
kfree(ind);
return 0;
}
return -EFBIG;
}
static const vnode_ops_t ext2_file_ops;
static const vnode_ops_t ext2_dir_ops;
static vnode_t *ext2_make_vnode(ext2_t *fs, uint32_t ino, ext2_inode_t *di) {
vnode_t *v = kzalloc(sizeof(vnode_t));
if (!v) return NULL;
ext2_vdata_t *vd = kzalloc(sizeof(ext2_vdata_t));
if (!vd) { kfree(v); return NULL; }
vd->fs = fs;
vd->ino = ino;
if ((di->i_mode & 0xF000) == EXT2_S_IFDIR) {
v->type = VFS_NODE_DIR;
v->ops = &ext2_dir_ops;
} else {
v->type = VFS_NODE_FILE;
v->ops = &ext2_file_ops;
}
v->mode = di->i_mode & 0x0FFF;
v->uid = di->i_uid;
v->gid = di->i_gid;
v->ino = ino;
v->size = di->i_size;
v->fs_data = vd;
v->refcount = 1;
return v;
}
static int64_t ext2_file_read(vnode_t *node, void *buf, size_t len, uint64_t offset) {
ext2_vdata_t *vd = node->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
if (offset >= di.i_size) return 0;
if (offset + len > di.i_size) len = di.i_size - (size_t)offset;
if (len == 0) return 0;
uint8_t *dst = (uint8_t *)buf;
size_t done = 0;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
while (done < len) {
uint32_t co = (uint32_t)(offset + done);
uint32_t fb = co / fs->block_size;
uint32_t bo = co % fs->block_size;
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) {
if (db < 0) { kfree(bb); return db; }
memset(bb, 0, fs->block_size);
} else {
r = block_read(fs, (uint32_t)db, bb);
if (r < 0) { kfree(bb); return r; }
}
size_t ch = fs->block_size - bo;
if (ch > len - done) ch = len - done;
memcpy(dst + done, bb + bo, ch);
done += ch;
}
kfree(bb);
return (int64_t)done;
}
static int64_t ext2_file_write(vnode_t *node, const void *buf, size_t len, uint64_t offset) {
ext2_vdata_t *vd = node->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
const uint8_t *src = (const uint8_t *)buf;
size_t done = 0;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
while (done < len) {
uint32_t co = (uint32_t)(offset + done);
uint32_t fb = co / fs->block_size;
uint32_t bo = co % fs->block_size;
int32_t db = get_block_num(fs, &di, fb);
if (db == 0) {
int32_t nb = alloc_block(fs);
if (nb < 0) { kfree(bb); return (done > 0) ? (int64_t)done : nb; }
set_block_num(fs, &di, fb, (uint32_t)nb);
db = nb;
memset(bb, 0, fs->block_size);
di.i_blocks += fs->block_size / 512;
} else if (db < 0) {
kfree(bb); return db;
} else {
if (bo != 0 || (len - done) < fs->block_size)
block_read(fs, (uint32_t)db, bb);
}
size_t ch = fs->block_size - bo;
if (ch > len - done) ch = len - done;
memcpy(bb + bo, src + done, ch);
block_write(fs, (uint32_t)db, bb);
done += ch;
}
uint32_t ne = (uint32_t)(offset + done);
if (ne > di.i_size) { di.i_size = ne; node->size = ne; }
inode_write(fs, vd->ino, &di);
fs->dirty = true;
kfree(bb);
return (int64_t)done;
}
static void free_all_blocks(ext2_t *fs, ext2_inode_t *di) {
for (int i = 0; i < EXT2_NDIR_BLOCKS; i++) {
if (di->i_block[i]) { free_block(fs, di->i_block[i]); di->i_block[i] = 0; }
}
if (di->i_block[EXT2_IND_BLOCK]) {
uint32_t *ind = kmalloc(fs->block_size);
if (ind) {
block_read(fs, di->i_block[EXT2_IND_BLOCK], ind);
for (uint32_t i = 0; i < fs->ptrs_per_block; i++)
if (ind[i]) free_block(fs, ind[i]);
kfree(ind);
}
free_block(fs, di->i_block[EXT2_IND_BLOCK]);
di->i_block[EXT2_IND_BLOCK] = 0;
}
if (di->i_block[EXT2_DIND_BLOCK]) {
uint32_t *dind = kmalloc(fs->block_size);
if (dind) {
block_read(fs, di->i_block[EXT2_DIND_BLOCK], dind);
for (uint32_t i = 0; i < fs->ptrs_per_block; i++) {
if (dind[i]) {
uint32_t *ind = kmalloc(fs->block_size);
if (ind) {
block_read(fs, dind[i], ind);
for (uint32_t j = 0; j < fs->ptrs_per_block; j++)
if (ind[j]) free_block(fs, ind[j]);
kfree(ind);
}
free_block(fs, dind[i]);
}
}
kfree(dind);
}
free_block(fs, di->i_block[EXT2_DIND_BLOCK]);
di->i_block[EXT2_DIND_BLOCK] = 0;
}
di->i_blocks = 0;
}
static int ext2_file_truncate(vnode_t *node, uint64_t new_size) {
ext2_vdata_t *vd = node->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
if (new_size == 0) free_all_blocks(fs, &di);
di.i_size = (uint32_t)new_size;
node->size = new_size;
inode_write(fs, vd->ino, &di);
fs->dirty = true;
return 0;
}
static int ext2_stat(vnode_t *node, vfs_stat_t *out) {
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
out->st_blocks = (node->size + 511) / 512;
return 0;
}
static void ext2_vnode_ref(vnode_t *node) { (void)node; }
static void ext2_vnode_unref(vnode_t *node) {
if (node->fs_data) kfree(node->fs_data);
kfree(node);
}
static const vnode_ops_t ext2_file_ops = {
.read = ext2_file_read,
.write = ext2_file_write,
.truncate = ext2_file_truncate,
.stat = ext2_stat,
.ref = ext2_vnode_ref,
.unref = ext2_vnode_unref,
};
static int ext2_dir_lookup(vnode_t *dir, const char *name, vnode_t **out) {
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
size_t nl = strlen(name);
uint32_t ds = di.i_size;
uint32_t pos = 0, fb = 0;
while (pos < ds) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) { fb++; pos += fs->block_size; continue; }
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
while (off < fs->block_size && (pos + off) < ds) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode != 0 && de->name_len == nl && memcmp(de->name, name, nl) == 0) {
ext2_inode_t ci;
inode_read(fs, de->inode, &ci);
*out = ext2_make_vnode(fs, de->inode, &ci);
kfree(bb);
return *out ? 0 : -ENOMEM;
}
off += de->rec_len;
}
fb++; pos += fs->block_size;
}
kfree(bb);
return -ENOENT;
}
static int ext2_dir_readdir(vnode_t *dir, uint64_t index, vfs_dirent_t *out) {
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
uint32_t ds = di.i_size, pos = 0, fb = 0;
uint64_t cur = 0;
while (pos < ds) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) { fb++; pos += fs->block_size; continue; }
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
while (off < fs->block_size && (pos + off) < ds) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode != 0 && de->name_len > 0) {
bool skip = (de->name_len == 1 && de->name[0] == '.') ||
(de->name_len == 2 && de->name[0] == '.' && de->name[1] == '.');
if (!skip) {
if (cur == index) {
out->d_ino = de->inode;
out->d_type = (de->file_type == EXT2_FT_DIR) ? VFS_NODE_DIR : VFS_NODE_FILE;
size_t n = de->name_len;
if (n >= VFS_MAX_NAME) n = VFS_MAX_NAME - 1;
memcpy(out->d_name, de->name, n);
out->d_name[n] = '\0';
kfree(bb);
return 0;
}
cur++;
}
}
off += de->rec_len;
}
fb++; pos += fs->block_size;
}
kfree(bb);
return -ENOENT;
}
static int ext2_dir_add_entry(ext2_t *fs, uint32_t dir_ino, uint32_t child_ino,
uint8_t file_type, const char *name) {
if (!name || !name[0]) return -EINVAL;
ext2_inode_t di;
int r = inode_read(fs, dir_ino, &di);
if (r < 0) return r;
uint8_t nl = (uint8_t)strlen(name);
uint16_t need = (uint16_t)((8 + nl + 3) & ~3);
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
uint32_t nb = (di.i_size + fs->block_size - 1) / fs->block_size;
for (uint32_t fb = 0; fb < nb; fb++) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) continue;
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
while (off < fs->block_size) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode == 0 && de->rec_len >= need) {
de->inode = child_ino;
de->name_len = nl;
de->file_type = file_type;
memcpy(de->name, name, nl);
block_write(fs, (uint32_t)db, bb);
kfree(bb);
return 0;
}
uint16_t actual = (uint16_t)((8 + de->name_len + 3) & ~3);
uint16_t slack = de->rec_len - actual;
if (slack >= need) {
de->rec_len = actual;
ext2_dir_entry_t *ne = (ext2_dir_entry_t *)(bb + off + actual);
ne->inode = child_ino;
ne->rec_len = slack;
ne->name_len = nl;
ne->file_type = file_type;
memcpy(ne->name, name, nl);
block_write(fs, (uint32_t)db, bb);
kfree(bb);
return 0;
}
off += de->rec_len;
}
}
int32_t new_blk = alloc_block(fs);
if (new_blk < 0) { kfree(bb); return (int)new_blk; }
set_block_num(fs, &di, nb, (uint32_t)new_blk);
di.i_size += fs->block_size;
di.i_blocks += fs->block_size / 512;
memset(bb, 0, fs->block_size);
ext2_dir_entry_t *de = (ext2_dir_entry_t *)bb;
de->inode = child_ino;
de->rec_len = (uint16_t)fs->block_size;
de->name_len = nl;
de->file_type = file_type;
memcpy(de->name, name, nl);
block_write(fs, (uint32_t)new_blk, bb);
inode_write(fs, dir_ino, &di);
kfree(bb);
return 0;
}
static int ext2_dir_mkdir(vnode_t *dir, const char *name, uint32_t mode) {
if (!name || !name[0]) return -EINVAL;
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
vnode_t *ex = NULL;
if (ext2_dir_lookup(dir, name, &ex) == 0) { vnode_unref(ex); return -EEXIST; }
int32_t ino = alloc_inode(fs);
if (ino < 0) return (int)ino;
ext2_inode_t ndi;
memset(&ndi, 0, sizeof(ndi));
ndi.i_mode = EXT2_S_IFDIR | (uint16_t)(mode ? mode : 0755);
ndi.i_links_count = 2;
int32_t blk = alloc_block(fs);
if (blk < 0) { free_inode(fs, (uint32_t)ino); return (int)blk; }
ndi.i_block[0] = (uint32_t)blk;
ndi.i_size = fs->block_size;
ndi.i_blocks = fs->block_size / 512;
uint8_t *bb = kzalloc(fs->block_size);
if (!bb) { free_block(fs, (uint32_t)blk); free_inode(fs, (uint32_t)ino); return -ENOMEM; }
ext2_dir_entry_t *dot = (ext2_dir_entry_t *)bb;
dot->inode = (uint32_t)ino; dot->rec_len = 12; dot->name_len = 1;
dot->file_type = EXT2_FT_DIR; dot->name[0] = '.';
ext2_dir_entry_t *dotdot = (ext2_dir_entry_t *)(bb + 12);
dotdot->inode = vd->ino; dotdot->rec_len = (uint16_t)(fs->block_size - 12);
dotdot->name_len = 2; dotdot->file_type = EXT2_FT_DIR;
dotdot->name[0] = '.'; dotdot->name[1] = '.';
block_write(fs, (uint32_t)blk, bb);
kfree(bb);
inode_write(fs, (uint32_t)ino, &ndi);
int r = ext2_dir_add_entry(fs, vd->ino, (uint32_t)ino, EXT2_FT_DIR, name);
if (r < 0) { free_block(fs, (uint32_t)blk); free_inode(fs, (uint32_t)ino); return r; }
ext2_inode_t pdi;
inode_read(fs, vd->ino, &pdi);
pdi.i_links_count++;
inode_write(fs, vd->ino, &pdi);
uint32_t grp = ((uint32_t)ino - 1) / fs->sb.s_inodes_per_group;
fs->gdt[grp].bg_used_dirs_count++;
fs->dirty = true;
dir->size++;
return 0;
}
static int ext2_dir_create(vnode_t *dir, const char *name, uint32_t mode, vnode_t **out) {
if (!name || !name[0]) return -EINVAL;
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
if (ext2_dir_lookup(dir, name, out) == 0) return 0;
int32_t ino = alloc_inode(fs);
if (ino < 0) return (int)ino;
ext2_inode_t ndi;
memset(&ndi, 0, sizeof(ndi));
ndi.i_mode = EXT2_S_IFREG | (uint16_t)(mode ? mode : 0644);
ndi.i_links_count = 1;
inode_write(fs, (uint32_t)ino, &ndi);
int r = ext2_dir_add_entry(fs, vd->ino, (uint32_t)ino, EXT2_FT_REG_FILE, name);
if (r < 0) { free_inode(fs, (uint32_t)ino); return r; }
*out = ext2_make_vnode(fs, (uint32_t)ino, &ndi);
if (!*out) return -ENOMEM;
fs->dirty = true;
dir->size++;
return 0;
}
static int ext2_dir_unlink(vnode_t *dir, const char *name) {
ext2_vdata_t *vd = dir->fs_data;
ext2_t *fs = vd->fs;
ext2_inode_t di;
int r = inode_read(fs, vd->ino, &di);
if (r < 0) return r;
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
size_t nl = strlen(name);
uint32_t nblocks = (di.i_size + fs->block_size - 1) / fs->block_size;
for (uint32_t fb = 0; fb < nblocks; fb++) {
int32_t db = get_block_num(fs, &di, fb);
if (db <= 0) continue;
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
ext2_dir_entry_t *prev = NULL;
while (off < fs->block_size) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode != 0 && de->name_len == nl && memcmp(de->name, name, nl) == 0) {
uint32_t cino = de->inode;
ext2_inode_t ci;
inode_read(fs, cino, &ci);
bool is_dir = ((ci.i_mode & 0xF000) == EXT2_S_IFDIR);
free_all_blocks(fs, &ci);
free_inode(fs, cino);
if (prev) prev->rec_len += de->rec_len;
else de->inode = 0;
block_write(fs, (uint32_t)db, bb);
if (is_dir) {
ext2_inode_t pdi;
inode_read(fs, vd->ino, &pdi);
if (pdi.i_links_count > 1) pdi.i_links_count--;
inode_write(fs, vd->ino, &pdi);
uint32_t grp = (cino - 1) / fs->sb.s_inodes_per_group;
if (fs->gdt[grp].bg_used_dirs_count > 0)
fs->gdt[grp].bg_used_dirs_count--;
}
dir->size--;
fs->dirty = true;
kfree(bb);
return 0;
}
prev = de;
off += de->rec_len;
}
}
kfree(bb);
return -ENOENT;
}
static int ext2_dir_rename(vnode_t *src_dir, const char *src_name,
vnode_t *dst_dir, const char *dst_name) {
ext2_vdata_t *svd = src_dir->fs_data;
ext2_t *fs = svd->fs;
vnode_t *child = NULL;
int r = ext2_dir_lookup(src_dir, src_name, &child);
if (r < 0) return r;
uint8_t ft = (child->type == VFS_NODE_DIR) ? EXT2_FT_DIR : EXT2_FT_REG_FILE;
uint32_t cino = (uint32_t)child->ino;
vnode_unref(child);
vnode_t *ex = NULL;
if (ext2_dir_lookup(dst_dir, dst_name, &ex) == 0) {
vnode_unref(ex);
ext2_dir_unlink(dst_dir, dst_name);
}
ext2_vdata_t *dvd = dst_dir->fs_data;
r = ext2_dir_add_entry(fs, dvd->ino, cino, ft, dst_name);
if (r < 0) return r;
ext2_inode_t sdi;
inode_read(fs, svd->ino, &sdi);
uint8_t *bb = kmalloc(fs->block_size);
if (!bb) return -ENOMEM;
size_t snl = strlen(src_name);
uint32_t nblocks = (sdi.i_size + fs->block_size - 1) / fs->block_size;
for (uint32_t fb = 0; fb < nblocks; fb++) {
int32_t db = get_block_num(fs, &sdi, fb);
if (db <= 0) continue;
block_read(fs, (uint32_t)db, bb);
uint32_t off = 0;
ext2_dir_entry_t *prev = NULL;
while (off < fs->block_size) {
ext2_dir_entry_t *de = (ext2_dir_entry_t *)(bb + off);
if (de->rec_len == 0) break;
if (de->inode == cino && de->name_len == snl &&
memcmp(de->name, src_name, snl) == 0) {
if (prev) prev->rec_len += de->rec_len;
else de->inode = 0;
block_write(fs, (uint32_t)db, bb);
kfree(bb);
fs->dirty = true;
return 0;
}
prev = de;
off += de->rec_len;
}
}
kfree(bb);
fs->dirty = true;
return 0;
}
static const vnode_ops_t ext2_dir_ops = {
.lookup = ext2_dir_lookup,
.readdir = ext2_dir_readdir,
.mkdir = ext2_dir_mkdir,
.create = ext2_dir_create,
.unlink = ext2_dir_unlink,
.rename = ext2_dir_rename,
.stat = ext2_stat,
.ref = ext2_vnode_ref,
.unref = ext2_vnode_unref,
};
int ext2_format(blkdev_t *dev, const char *label) {
if (!dev) return -EINVAL;
uint64_t disk_bytes = dev->size_bytes;
if (disk_bytes < 64 * 1024) return -ENOSPC;
uint32_t block_size = 1024;
uint32_t total_blocks = (uint32_t)(disk_bytes / block_size);
uint32_t blocks_per_group = block_size * 8;
uint32_t inodes_per_group = (total_blocks < 8192) ? 128 : 256;
uint32_t groups_count = (total_blocks + blocks_per_group - 1) / blocks_per_group;
if (groups_count == 0) groups_count = 1;
uint32_t total_inodes = inodes_per_group * groups_count;
uint32_t inode_size = 128;
uint32_t it_blocks_pg = (inodes_per_group * inode_size + block_size - 1) / block_size;
uint32_t first_data_block = 1;
uint8_t *zb = kzalloc(block_size);
if (!zb) return -ENOMEM;
for (uint32_t b = 0; b < total_blocks && b < 16; b++)
blkdev_write(dev, (uint64_t)b * block_size, zb, block_size);
ext2_superblock_t sb;
memset(&sb, 0, sizeof(sb));
sb.s_inodes_count = total_inodes;
sb.s_blocks_count = total_blocks;
sb.s_r_blocks_count = total_blocks / 20;
sb.s_free_blocks_count = total_blocks;
sb.s_free_inodes_count = total_inodes;
sb.s_first_data_block = first_data_block;
sb.s_log_block_size = 0;
sb.s_blocks_per_group = blocks_per_group;
sb.s_frags_per_group = blocks_per_group;
sb.s_inodes_per_group = inodes_per_group;
sb.s_magic = EXT2_SUPER_MAGIC;
sb.s_state = EXT2_VALID_FS;
sb.s_errors = 1;
sb.s_rev_level = EXT2_DYNAMIC_REV;
sb.s_first_ino = 11;
sb.s_inode_size = (uint16_t)inode_size;
sb.s_max_mnt_count = 20;
if (label) strncpy(sb.s_volume_name, label, 15);
uint32_t gdt_block = first_data_block + 1;
uint32_t gdt_blocks = (groups_count * sizeof(ext2_group_desc_t) + block_size - 1) / block_size;
ext2_group_desc_t *gdt = kzalloc(gdt_blocks * block_size);
if (!gdt) { kfree(zb); return -ENOMEM; }
uint32_t used_total = first_data_block;
printf(" ext2: ");
uint32_t last_pct_ext2 = 999;
uint32_t spinner_ext2 = 0;
for (uint32_t g = 0; g < groups_count; g++) {
uint32_t gs = g * blocks_per_group + first_data_block;
uint32_t cb = (g == 0) ? (gdt_block + gdt_blocks) : gs;
gdt[g].bg_block_bitmap = cb++;
gdt[g].bg_inode_bitmap = cb++;
gdt[g].bg_inode_table = cb;
cb += it_blocks_pg;
uint32_t meta = cb - gs;
if (g == 0) meta = cb - first_data_block;
uint32_t gb = blocks_per_group;
if (g == groups_count - 1) gb = total_blocks - gs;
gdt[g].bg_free_blocks_count = (gb > meta) ? (uint16_t)(gb - meta) : 0;
gdt[g].bg_free_inodes_count = (uint16_t)inodes_per_group;
used_total += meta;
for (uint32_t t = 0; t < it_blocks_pg; t++)
blkdev_write(dev, (uint64_t)(gdt[g].bg_inode_table + t) * block_size, zb, block_size);
uint8_t *bbmp = kzalloc(block_size);
for (uint32_t i = 0; i < meta; i++) bmp_set(bbmp, i);
if (g == 0)
for (uint32_t i = 0; i < (gdt_block + gdt_blocks - first_data_block); i++)
bmp_set(bbmp, i);
for (uint32_t i = gb; i < blocks_per_group; i++) bmp_set(bbmp, i);
blkdev_write(dev, (uint64_t)gdt[g].bg_block_bitmap * block_size, bbmp, block_size);
kfree(bbmp);
uint8_t *ibmp = kzalloc(block_size);
if (g == 0) {
for (uint32_t i = 0; i < 11; i++) { bmp_set(ibmp, i); gdt[g].bg_free_inodes_count--; }
}
for (uint32_t i = inodes_per_group; i < block_size * 8; i++) bmp_set(ibmp, i);
blkdev_write(dev, (uint64_t)gdt[g].bg_inode_bitmap * block_size, ibmp, block_size);
kfree(ibmp);
uint32_t pct = ((g + 1) * 100) / groups_count;
if (pct != last_pct_ext2) {
static const char glyphs[4] = { '|', '/', '-', '\\' };
printf("\r \r %c ext2: %u%% (group %u/%u)",
glyphs[spinner_ext2 & 3], pct, g + 1, groups_count);
spinner_ext2++;
last_pct_ext2 = pct;
}
}
printf("\r \r ext2: done\n");
sb.s_free_blocks_count = total_blocks - used_total;
sb.s_free_inodes_count = total_inodes - 11;
int32_t root_blk = -1;
{
uint8_t *bbmp = kmalloc(block_size);
blkdev_read(dev, (uint64_t)gdt[0].bg_block_bitmap * block_size, bbmp, block_size);
for (uint32_t i = 0; i < blocks_per_group; i++) {
if (!bmp_test(bbmp, i)) {
bmp_set(bbmp, i);
blkdev_write(dev, (uint64_t)gdt[0].bg_block_bitmap * block_size, bbmp, block_size);
root_blk = (int32_t)(i + first_data_block);
gdt[0].bg_free_blocks_count--;
sb.s_free_blocks_count--;
break;
}
}
kfree(bbmp);
}
if (root_blk < 0) { kfree(gdt); kfree(zb); return -ENOSPC; }
ext2_inode_t ri;
memset(&ri, 0, sizeof(ri));
ri.i_mode = EXT2_S_IFDIR | 0755;
ri.i_links_count = 2;
ri.i_block[0] = (uint32_t)root_blk;
ri.i_size = block_size;
ri.i_blocks = block_size / 512;
uint64_t roff = (uint64_t)gdt[0].bg_inode_table * block_size + (uint64_t)(EXT2_ROOT_INO - 1) * inode_size;
blkdev_write(dev, roff, &ri, sizeof(ri));
uint8_t *rdb = kzalloc(block_size);
ext2_dir_entry_t *dot = (ext2_dir_entry_t *)rdb;
dot->inode = EXT2_ROOT_INO; dot->rec_len = 12; dot->name_len = 1;
dot->file_type = EXT2_FT_DIR; dot->name[0] = '.';
ext2_dir_entry_t *dotdot = (ext2_dir_entry_t *)(rdb + 12);
dotdot->inode = EXT2_ROOT_INO; dotdot->rec_len = (uint16_t)(block_size - 12);
dotdot->name_len = 2; dotdot->file_type = EXT2_FT_DIR;
dotdot->name[0] = '.'; dotdot->name[1] = '.';
blkdev_write(dev, (uint64_t)root_blk * block_size, rdb, block_size);
kfree(rdb);
gdt[0].bg_used_dirs_count = 1;
blkdev_write(dev, EXT2_SUPER_OFFSET, &sb, sizeof(sb));
blkdev_write(dev, (uint64_t)gdt_block * block_size, gdt, gdt_blocks * block_size);
kfree(gdt);
kfree(zb);
serial_printf("[ext2] formatted: %u blocks (%u KiB), %u inodes, bs=%u, label='%s'\n",
total_blocks, total_blocks * block_size / 1024, total_inodes, block_size,
label ? label : "");
return 0;
}
vnode_t *ext2_mount(blkdev_t *dev) {
if (!dev) return NULL;
ext2_t *fs = kzalloc(sizeof(ext2_t));
if (!fs) return NULL;
fs->dev = dev;
if (blkdev_read(dev, EXT2_SUPER_OFFSET, &fs->sb, sizeof(fs->sb)) < 0) { kfree(fs); return NULL; }
if (fs->sb.s_magic != EXT2_SUPER_MAGIC) {
serial_printf("[ext2] bad magic: 0x%x (expected 0x%x)\n", fs->sb.s_magic, EXT2_SUPER_MAGIC);
kfree(fs);
return NULL;
}
fs->block_size = 1024 << fs->sb.s_log_block_size;
fs->inode_size = (fs->sb.s_rev_level >= EXT2_DYNAMIC_REV) ? fs->sb.s_inode_size : 128;
fs->groups_count = (fs->sb.s_blocks_count + fs->sb.s_blocks_per_group - 1) / fs->sb.s_blocks_per_group;
fs->inodes_per_block = fs->block_size / fs->inode_size;
fs->ptrs_per_block = fs->block_size / 4;
uint32_t gdt_block = (fs->block_size == 1024) ? 2 : 1;
uint32_t gdt_sz = fs->groups_count * sizeof(ext2_group_desc_t);
uint32_t gdt_blocks = (gdt_sz + fs->block_size - 1) / fs->block_size;
fs->gdt = kmalloc(gdt_blocks * fs->block_size);
if (!fs->gdt) { kfree(fs); return NULL; }
if (blkdev_read(dev, (uint64_t)gdt_block * fs->block_size, fs->gdt, gdt_blocks * fs->block_size) < 0) {
kfree(fs->gdt); kfree(fs); return NULL;
}
ext2_inode_t root_di;
if (inode_read(fs, EXT2_ROOT_INO, &root_di) < 0) { kfree(fs->gdt); kfree(fs); return NULL; }
vnode_t *root = ext2_make_vnode(fs, EXT2_ROOT_INO, &root_di);
if (!root) { kfree(fs->gdt); kfree(fs); return NULL; }
serial_printf("[ext2] mounted '%s': %u blocks (%u free), %u inodes (%u free), bs=%u\n",
fs->sb.s_volume_name, fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
fs->sb.s_inodes_count, fs->sb.s_free_inodes_count, fs->block_size);
return root;
}
void ext2_sync(ext2_t *fs) {
if (!fs || !fs->dirty) return;
sb_flush(fs);
gdt_flush(fs);
if (fs->dev->ops && fs->dev->ops->flush) fs->dev->ops->flush(fs->dev);
fs->dirty = false;
}
void ext2_unmount(ext2_t *fs) {
if (!fs) return;
ext2_sync(fs);
if (fs->gdt) kfree(fs->gdt);
kfree(fs);
}
int ext2_statvfs(vnode_t *root, vfs_statvfs_t *out) {
if (!root || !root->fs_data || !out) return -EINVAL;
ext2_vdata_t *vd = (ext2_vdata_t *)root->fs_data;
ext2_t *fs = vd->fs;
if (!fs) return -EINVAL;
out->f_bsize = fs->block_size;
out->f_blocks = fs->sb.s_blocks_count;
out->f_bfree = fs->sb.s_free_blocks_count;
out->f_bavail = fs->sb.s_free_blocks_count;
out->f_files = fs->sb.s_inodes_count;
out->f_ffree = fs->sb.s_free_inodes_count;
out->f_flag = 0;
out->f_namemax = 255;
return 0;
}
File diff suppressed because it is too large Load Diff
+311
View File
@@ -0,0 +1,311 @@
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include "../../include/fs/initramfs.h"
#include "../../include/fs/vfs.h"
#include "../../include/io/serial.h"
typedef struct {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char checksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
char _pad[12];
} __attribute__((packed)) ustar_header_t;
#define USTAR_BLOCK 512
static uint64_t octal_parse(const char *s, size_t n) {
uint64_t v = 0;
for (size_t i = 0; i < n && s[i] >= '0' && s[i] <= '7'; i++)
v = v * 8 + (uint64_t)(s[i] - '0');
return v;
}
static size_t align_block(size_t n) {
return (n + USTAR_BLOCK - 1) & ~(size_t)(USTAR_BLOCK - 1);
}
static void path_normalize(const char *in, char *out, size_t maxlen) {
if (maxlen == 0) return;
const char *p = in;
while (p[0] == '.' && p[1] == '/') p += 2;
while (*p == '/') p++;
if (*p == '\0' || (p[0] == '.' && p[1] == '\0')) {
out[0] = '/';
out[1] = '\0';
return;
}
char tmp[VFS_MAX_PATH];
size_t j = 0;
tmp[j++] = '/';
while (*p && j < sizeof(tmp) - 1) {
const char *start = p;
while (*p && *p != '/') p++;
size_t len = (size_t)(p - start);
if (len == 1 && start[0] == '.') {
while (*p == '/') p++;
continue;
}
if (j > 1) {
if (j < sizeof(tmp) - 1)
tmp[j++] = '/';
}
size_t avail = sizeof(tmp) - 1 - j;
size_t copy = len < avail ? len : avail;
memcpy(tmp + j, start, copy);
j += copy;
while (*p == '/') p++;
}
tmp[j] = '\0';
strncpy(out, tmp, maxlen - 1);
out[maxlen - 1] = '\0';
}
static int mkdir_p(const char *abspath) {
if (!abspath || strcmp(abspath, "/") == 0) return 0;
char buf[VFS_MAX_PATH];
strncpy(buf, abspath, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
for (char *p = buf + 1; *p; p++) {
if (*p == '/') {
*p = '\0';
int r = vfs_mkdir(buf, 0755);
if (r < 0 && r != -EEXIST) {
serial_printf("[initramfs] mkdir_p: mkdir '%s' failed: %d\n", buf, r);
*p = '/';
return r;
}
*p = '/';
}
}
int r = vfs_mkdir(buf, 0755);
if (r < 0 && r != -EEXIST) {
serial_printf("[initramfs] mkdir_p: mkdir '%s' failed: %d\n", buf, r);
return r;
}
return 0;
}
static int write_file(const char *abspath, const void *data,
size_t size, uint32_t mode)
{
serial_printf("[initramfs] write_file '%s' size=%zu\n", abspath, size);
char parent[VFS_MAX_PATH];
strncpy(parent, abspath, sizeof(parent) - 1);
parent[sizeof(parent) - 1] = '\0';
char *last_slash = NULL;
for (int i = (int)strlen(parent) - 1; i > 0; i--) {
if (parent[i] == '/') { last_slash = &parent[i]; break; }
}
if (last_slash) {
*last_slash = '\0';
if (strlen(parent) > 0) {
serial_printf("[initramfs] ensuring parent dir '%s'\n", parent);
vnode_t *check = NULL;
int lr = vfs_lookup(parent, &check);
serial_printf("[initramfs] vfs_lookup('%s') before mkdir_p = %d\n", parent, lr);
if (lr == 0) {
serial_printf("[initramfs] parent exists, type=%d\n", check->type);
vnode_unref(check);
} else {
serial_printf("[initramfs] parent not found, calling mkdir_p\n");
int mr = mkdir_p(parent);
if (mr < 0) {
serial_printf("[initramfs] mkdir_p('%s') failed: %d\n", parent, mr);
*last_slash = '/';
return mr;
}
lr = vfs_lookup(parent, &check);
serial_printf("[initramfs] vfs_lookup('%s') after mkdir_p = %d\n", parent, lr);
if (lr == 0) {
serial_printf("[initramfs] parent now exists, type=%d\n", check->type);
vnode_unref(check);
} else {
serial_printf("[initramfs] ERROR: parent still not found after mkdir_p!\n");
*last_slash = '/';
return -ENOENT;
}
}
}
*last_slash = '/';
}
vfs_file_t *file = NULL;
int ret = vfs_open(abspath, O_WRONLY | O_CREAT | O_TRUNC, mode ? mode : 0644, &file);
serial_printf("[initramfs] vfs_open('%s') = %d\n", abspath, ret);
if (ret < 0) {
serial_printf("[initramfs] open '%s' failed: %d\n", abspath, ret);
return ret;
}
if (size > 0 && data != NULL) {
int64_t w = vfs_write(file, data, size);
vfs_close(file);
if (w < 0) {
serial_printf("[initramfs] write '%s' failed: %lld\n",
abspath, (long long)w);
return (int)w;
}
serial_printf("[initramfs] wrote %lld bytes\n", (long long)w);
} else {
vfs_close(file);
}
return 0;
}
int initramfs_mount(const void *data, size_t size) {
if (!data || size < USTAR_BLOCK) return -EINVAL;
const uint8_t *ptr = data;
const uint8_t *end = ptr + size;
int files_ok = 0, dirs_ok = 0, skipped = 0, errors = 0;
serial_printf("[initramfs] parsing TAR @ %p, size=%zu\n", data, size);
{
vnode_t *root_check = NULL;
int r = vfs_lookup("/", &root_check);
serial_printf("[initramfs] pre-parse: vfs_lookup('/') = %d\n", r);
if (r == 0) {
serial_printf("[initramfs] pre-parse: root type=%d refcnt=%d\n",
root_check->type, root_check->refcount);
vnode_unref(root_check);
}
const char *test_dirs[] = { "/bin", "/etc", "/dev", "/tmp", "/proc", NULL };
for (int i = 0; test_dirs[i]; i++) {
vnode_t *n = NULL;
int rv = vfs_lookup(test_dirs[i], &n);
serial_printf("[initramfs] pre-parse: vfs_lookup('%s') = %d\n", test_dirs[i], rv);
if (rv == 0) vnode_unref(n);
}
}
while (ptr + USTAR_BLOCK <= end) {
const ustar_header_t *hdr = (const ustar_header_t *)ptr;
if (hdr->name[0] == '\0') {
ptr += USTAR_BLOCK;
if (ptr + USTAR_BLOCK <= end &&
((const ustar_header_t *)ptr)->name[0] == '\0')
break;
continue;
}
if (memcmp(hdr->magic, "ustar", 5) != 0) {
serial_writestring("[initramfs] bad magic, stopping\n");
break;
}
uint64_t file_size = octal_parse(hdr->size, sizeof(hdr->size));
uint32_t mode = (uint32_t)octal_parse(hdr->mode, sizeof(hdr->mode));
char raw[VFS_MAX_PATH];
if (hdr->prefix[0]) {
size_t pl = strnlen(hdr->prefix, sizeof(hdr->prefix));
size_t nl = strnlen(hdr->name, sizeof(hdr->name));
if (pl + nl + 2 < sizeof(raw)) {
memcpy(raw, hdr->prefix, pl);
raw[pl] = '/';
memcpy(raw + pl + 1, hdr->name, nl);
raw[pl + 1 + nl] = '\0';
} else {
strncpy(raw, hdr->name, sizeof(raw) - 1);
raw[sizeof(raw) - 1] = '\0';
}
} else {
strncpy(raw, hdr->name, sizeof(raw) - 1);
raw[sizeof(raw) - 1] = '\0';
}
char abspath[VFS_MAX_PATH];
path_normalize(raw, abspath, sizeof(abspath));
serial_printf("[initramfs] entry: raw='%s' -> abs='%s' type='%c' size=%llu\n",
raw, abspath, hdr->typeflag ? hdr->typeflag : '0',
(unsigned long long)file_size);
ptr += USTAR_BLOCK;
switch (hdr->typeflag) {
case '5':
if (strcmp(abspath, "/") != 0) {
int r = mkdir_p(abspath);
if (r == 0) {
serial_printf("[initramfs] dir %s\n", abspath);
dirs_ok++;
} else {
serial_printf("[initramfs] dir %s FAILED: %d\n", abspath, r);
errors++;
}
}
break;
case '0':
case '\0':
{
const void *filedata = (file_size > 0) ? (const void *)ptr : NULL;
int r = write_file(abspath, filedata, (size_t)file_size,
mode ? mode : 0644);
if (r == 0) {
serial_printf("[initramfs] file %s (%llu bytes)\n",
abspath, (unsigned long long)file_size);
files_ok++;
} else {
errors++;
}
break;
}
case '2':
serial_printf("[initramfs] skip symlink %s\n", abspath);
skipped++;
break;
default:
serial_printf("[initramfs] skip type='%c' %s\n",
hdr->typeflag ? hdr->typeflag : '0', abspath);
skipped++;
break;
}
ptr += align_block(file_size);
}
serial_printf("[initramfs] done: %d files, %d dirs, %d skipped, %d errors\n",
files_ok, dirs_ok, skipped, errors);
return (errors > 0 && files_ok == 0 && dirs_ok == 0) ? -EIO : 0;
}
+283
View File
@@ -0,0 +1,283 @@
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include "../../include/fs/ramfs.h"
#include "../../include/fs/vfs.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
typedef struct {
char *name;
vnode_t *node;
} ramfs_child_t;
typedef struct {
uint8_t *data;
size_t capacity;
ramfs_child_t *children;
int child_count;
int child_cap;
uint64_t ino;
} ramfs_node_t;
static uint64_t g_next_ino = 1;
static const vnode_ops_t ramfs_file_ops;
static const vnode_ops_t ramfs_dir_ops;
static vnode_t *ramfs_alloc_vnode(vnode_type_t type, uint32_t mode) {
vnode_t *v = kzalloc(sizeof(vnode_t));
if (!v) return NULL;
ramfs_node_t *rn = kzalloc(sizeof(ramfs_node_t));
if (!rn) { kfree(v); return NULL; }
rn->ino = g_next_ino++;
v->type = type;
v->mode = mode;
v->ino = rn->ino;
v->fs_data = rn;
return v;
}
static void ramfs_ref(vnode_t *node) {
(void)node;
}
static void ramfs_unref(vnode_t *node) {
ramfs_node_t *rn = node->fs_data;
if (rn->data) kfree(rn->data);
if (rn->children) {
for (int i = 0; i < rn->child_count; i++) {
if (rn->children[i].name) kfree(rn->children[i].name);
if (rn->children[i].node) vnode_unref(rn->children[i].node);
}
kfree(rn->children);
}
kfree(rn);
kfree(node);
}
static int64_t ramfs_file_read(vnode_t *node, void *buf,
size_t len, uint64_t offset)
{
ramfs_node_t *rn = node->fs_data;
if (offset >= node->size) return 0;
size_t avail = node->size - (size_t)offset;
if (len > avail) len = avail;
if (len == 0) return 0;
memcpy(buf, rn->data + offset, len);
return (int64_t)len;
}
static int64_t ramfs_file_write(vnode_t *node, const void *buf,
size_t len, uint64_t offset)
{
ramfs_node_t *rn = node->fs_data;
size_t end = (size_t)offset + len;
if (end > RAMFS_MAX_FILE_SIZE) return -EFBIG;
if (end > rn->capacity) {
size_t newcap = (end + 4095) & ~(size_t)4095;
if (newcap > RAMFS_MAX_FILE_SIZE) newcap = RAMFS_MAX_FILE_SIZE;
uint8_t *newdata = kmalloc(newcap);
if (!newdata) return -ENOMEM;
size_t copy_size = rn->data ? node->size : 0;
if (copy_size > 0)
memcpy(newdata, rn->data, copy_size);
memset(newdata + copy_size, 0, newcap - copy_size);
if (rn->data) kfree(rn->data);
rn->data = newdata;
rn->capacity = newcap;
}
memcpy(rn->data + offset, buf, len);
if (end > node->size) node->size = end;
return (int64_t)len;
}
static int ramfs_file_truncate(vnode_t *node, uint64_t new_size) {
ramfs_node_t *rn = node->fs_data;
if (new_size == 0) {
if (rn->data) {
kfree(rn->data);
rn->data = NULL;
rn->capacity = 0;
}
node->size = 0;
} else if (new_size < node->size) {
node->size = new_size;
}
return 0;
}
static int ramfs_stat(vnode_t *node, vfs_stat_t *out) {
ramfs_node_t *rn = node->fs_data;
out->st_ino = rn->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
out->st_blocks = (node->size + 511) / 512;
return 0;
}
static const vnode_ops_t ramfs_file_ops = {
.read = ramfs_file_read,
.write = ramfs_file_write,
.truncate = ramfs_file_truncate,
.stat = ramfs_stat,
.ref = ramfs_ref,
.unref = ramfs_unref,
};
static int ramfs_dir_grow(ramfs_node_t *rn) {
int newcap = rn->child_cap == 0 ? 8 : rn->child_cap * 2;
if (newcap > RAMFS_MAX_CHILDREN) return -ENOSPC;
ramfs_child_t *nb = kmalloc((size_t)newcap * sizeof(ramfs_child_t));
if (!nb) return -ENOMEM;
if (rn->children && rn->child_count > 0)
memcpy(nb, rn->children, (size_t)rn->child_count * sizeof(ramfs_child_t));
if (rn->children) kfree(rn->children);
rn->children = nb;
rn->child_cap = newcap;
return 0;
}
static int ramfs_dir_lookup(vnode_t *dir, const char *name, vnode_t **out) {
ramfs_node_t *rn = dir->fs_data;
for (int i = 0; i < rn->child_count; i++) {
if (strcmp(rn->children[i].name, name) == 0) {
vnode_ref(rn->children[i].node);
*out = rn->children[i].node;
return 0;
}
}
return -ENOENT;
}
static int ramfs_dir_readdir(vnode_t *dir, uint64_t index, vfs_dirent_t *out) {
ramfs_node_t *rn = dir->fs_data;
if ((int64_t)index >= rn->child_count) return -ENOENT;
ramfs_child_t *ch = &rn->children[index];
out->d_ino = ch->node->ino;
out->d_type = (uint8_t)ch->node->type;
strncpy(out->d_name, ch->name, VFS_MAX_NAME - 1);
out->d_name[VFS_MAX_NAME - 1] = '\0';
return 0;
}
static int ramfs_dir_add_child(vnode_t *dir, const char *name, vnode_t *child) {
ramfs_node_t *rn = dir->fs_data;
if (rn->child_count >= rn->child_cap) {
int r = ramfs_dir_grow(rn);
if (r < 0) return r;
}
char *dup = kmalloc(strlen(name) + 1);
if (!dup) return -ENOMEM;
strcpy(dup, name);
rn->children[rn->child_count].name = dup;
rn->children[rn->child_count].node = child;
rn->child_count++;
dir->size = (uint64_t)rn->child_count;
vnode_ref(child);
return 0;
}
static int ramfs_dir_mkdir(vnode_t *dir, const char *name, uint32_t mode) {
vnode_t *existing = NULL;
if (ramfs_dir_lookup(dir, name, &existing) == 0) {
vnode_unref(existing);
return -EEXIST;
}
vnode_t *child = ramfs_alloc_vnode(VFS_NODE_DIR, mode ? mode : 0755);
if (!child) return -ENOMEM;
child->ops = &ramfs_dir_ops;
child->refcount = 1;
int ret = ramfs_dir_add_child(dir, name, child);
if (ret < 0) {
kfree(child->fs_data);
kfree(child);
return ret;
}
vnode_unref(child);
return 0;
}
static int ramfs_dir_create(vnode_t *dir, const char *name, uint32_t mode, vnode_t **out) {
vnode_t *existing = NULL;
if (ramfs_dir_lookup(dir, name, &existing) == 0) {
*out = existing;
return 0;
}
vnode_t *child = ramfs_alloc_vnode(VFS_NODE_FILE, mode ? mode : 0644);
if (!child) return -ENOMEM;
child->ops = &ramfs_file_ops;
child->refcount = 1;
int ret = ramfs_dir_add_child(dir, name, child);
if (ret < 0) {
kfree(child->fs_data);
kfree(child);
return ret;
}
*out = child;
return 0;
}
static int ramfs_dir_unlink(vnode_t *dir, const char *name) {
ramfs_node_t *rn = dir->fs_data;
for (int i = 0; i < rn->child_count; i++) {
if (strcmp(rn->children[i].name, name) == 0) {
kfree(rn->children[i].name);
vnode_unref(rn->children[i].node);
for (int j = i; j < rn->child_count - 1; j++)
rn->children[j] = rn->children[j + 1];
rn->child_count--;
dir->size = (uint64_t)rn->child_count;
return 0;
}
}
return -ENOENT;
}
static const vnode_ops_t ramfs_dir_ops = {
.lookup = ramfs_dir_lookup,
.readdir = ramfs_dir_readdir,
.mkdir = ramfs_dir_mkdir,
.create = ramfs_dir_create,
.unlink = ramfs_dir_unlink,
.stat = ramfs_stat,
.ref = ramfs_ref,
.unref = ramfs_unref,
};
vnode_t *ramfs_create_root(void) {
vnode_t *root = ramfs_alloc_vnode(VFS_NODE_DIR, 0755);
if (!root) return NULL;
root->ops = &ramfs_dir_ops;
root->refcount = 1;
serial_writestring("[ramfs] root created\n");
return root;
}
+617
View File
@@ -0,0 +1,617 @@
#include "../../include/fs/vfs.h"
#include "../../include/sched/sched.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include <string.h>
static vfs_mount_t g_mounts[VFS_MAX_MOUNTS];
static vfs_file_t g_open_files[VFS_MAX_OPEN_FILES];
static bool g_vfs_ready = false;
void vfs_init(void) {
memset(g_mounts, 0, sizeof(g_mounts));
memset(g_open_files, 0, sizeof(g_open_files));
g_vfs_ready = true;
serial_writestring("[VFS] initialized\n");
}
void vnode_ref(vnode_t *node) {
if (!node) return;
__atomic_fetch_add(&node->refcount, 1, __ATOMIC_RELAXED);
}
void vnode_unref(vnode_t *node) {
if (!node) return;
int old = __atomic_fetch_sub(&node->refcount, 1, __ATOMIC_ACQ_REL);
if (old <= 1) {
if (node->ops && node->ops->unref)
node->ops->unref(node);
}
}
int vfs_mount(const char *path, vnode_t *fs_root) {
if (!path || !fs_root) return -EINVAL;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
serial_printf("[VFS] mount: '%s' already mounted\n", path);
return -EBUSY;
}
}
int slot = -1;
if (strcmp(path, "/") == 0) {
slot = 0;
} else {
for (int i = 1; i < VFS_MAX_MOUNTS; i++) {
if (!g_mounts[i].used) { slot = i; break; }
}
}
if (slot < 0) return -ENOMEM;
strncpy(g_mounts[slot].path, path, VFS_MAX_PATH - 1);
g_mounts[slot].root = fs_root;
g_mounts[slot].used = true;
g_mounts[slot].fs_priv = NULL;
g_mounts[slot].unmount = NULL;
vnode_ref(fs_root);
serial_printf("[VFS] mounted '%s' at slot %d\n", path, slot);
return 0;
}
int vfs_mount_fs(const char *path, vnode_t *fs_root,
void *fs_priv, void (*unmount_fn)(void *),
void (*sync_fn)(void *)) {
int ret = vfs_mount(path, fs_root);
if (ret < 0) return ret;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
g_mounts[i].fs_priv = fs_priv;
g_mounts[i].unmount = unmount_fn;
g_mounts[i].sync = sync_fn;
break;
}
}
return 0;
}
void vfs_sync_all(void) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && g_mounts[i].fs_priv) {
serial_printf("[VFS] sync_all: flushing mount '%s'\n", g_mounts[i].path);
if (g_mounts[i].sync)
g_mounts[i].sync(g_mounts[i].fs_priv);
}
}
}
int vfs_umount(const char *path) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
if (g_mounts[i].unmount && g_mounts[i].fs_priv)
g_mounts[i].unmount(g_mounts[i].fs_priv);
vnode_unref(g_mounts[i].root);
memset(&g_mounts[i], 0, sizeof(vfs_mount_t));
return 0;
}
}
return -ENOENT;
}
static vfs_mount_t *find_mount(const char *path, const char **rel_out) {
vfs_mount_t *best = NULL;
size_t best_len = 0;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (!g_mounts[i].used) continue;
size_t mlen = strlen(g_mounts[i].path);
if (strncmp(path, g_mounts[i].path, mlen) == 0) {
if (path[mlen] == '/' || path[mlen] == '\0' || mlen == 1) {
if (mlen > best_len) {
best = &g_mounts[i];
best_len = mlen;
}
}
}
}
if (best && rel_out) {
const char *rel = path + best_len;
while (*rel == '/') rel++;
*rel_out = rel;
}
return best;
}
static const char *path_next_component(const char *src, char *dst, size_t maxlen) {
while (*src == '/') src++;
size_t i = 0;
while (*src && *src != '/' && i < maxlen - 1)
dst[i++] = *src++;
dst[i] = '\0';
return src;
}
int vfs_lookup(const char *path, vnode_t **out) {
if (!path || !out) return -EINVAL;
if (!g_vfs_ready) return -EIO;
if (path[0] != '/') return -EINVAL;
const char *rel;
vfs_mount_t *mnt = find_mount(path, &rel);
if (!mnt) {
serial_printf("[VFS] lookup '%s': no mount found\n", path);
return -ENOENT;
}
vnode_t *cur = mnt->root;
if (!cur) {
serial_printf("[VFS] lookup '%s': mount root is NULL!\n", path);
return -EIO;
}
vnode_ref(cur);
if (*rel == '\0') {
*out = cur;
return 0;
}
char comp[VFS_MAX_NAME];
const char *p = rel;
while (*p) {
p = path_next_component(p, comp, sizeof(comp));
if (comp[0] == '\0') continue;
if (cur->type != VFS_NODE_DIR) {
serial_printf("[VFS] lookup '%s': '%s' is not a dir (type=%d)\n",
path, comp, cur->type);
vnode_unref(cur);
return -ENOTDIR;
}
if (!cur->ops || !cur->ops->lookup) {
serial_printf("[VFS] lookup '%s': no lookup op for comp '%s'\n", path, comp);
vnode_unref(cur);
return -EIO;
}
vnode_t *next = NULL;
int ret = cur->ops->lookup(cur, comp, &next);
if (ret < 0) {
serial_printf("[VFS] lookup '%s': ops->lookup('%s') = %d\n", path, comp, ret);
vnode_unref(cur);
return ret;
}
vnode_unref(cur);
cur = next;
if (cur->mounted) {
vnode_t *mroot = cur->mounted->root;
vnode_ref(mroot);
vnode_unref(cur);
cur = mroot;
}
}
*out = cur;
return 0;
}
vfs_file_t *vfs_file_alloc(void) {
for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) {
if (g_open_files[i].refcount == 0) {
memset(&g_open_files[i], 0, sizeof(vfs_file_t));
g_open_files[i].refcount = 1;
return &g_open_files[i];
}
}
return NULL;
}
void vfs_file_free(vfs_file_t *file) {
if (!file) return;
int old = __atomic_fetch_sub(&file->refcount, 1, __ATOMIC_ACQ_REL);
if (old == 1) {
vnode_unref(file->vnode);
file->vnode = NULL;
}
}
int vfs_open(const char *path, int flags, uint32_t mode, vfs_file_t **out) {
if (!path || !out) return -EINVAL;
vnode_t *node = NULL;
int ret = vfs_lookup(path, &node);
if (ret == -ENOENT && (flags & O_CREAT)) {
char dirpath[VFS_MAX_PATH];
strncpy(dirpath, path, VFS_MAX_PATH - 1);
dirpath[VFS_MAX_PATH - 1] = '\0';
char *slash = NULL;
for (int i = (int)strlen(dirpath) - 1; i >= 0; i--) {
if (dirpath[i] == '/') { slash = &dirpath[i]; break; }
}
if (!slash) return -EINVAL;
char filename[VFS_MAX_NAME];
strncpy(filename, slash + 1, VFS_MAX_NAME - 1);
filename[VFS_MAX_NAME - 1] = '\0';
if (filename[0] == '\0') return -EINVAL;
if (slash == dirpath)
dirpath[1] = '\0';
else
*slash = '\0';
serial_printf("[VFS] open O_CREAT: parent='%s' name='%s'\n", dirpath, filename);
vnode_t *dir = NULL;
ret = vfs_lookup(dirpath, &dir);
if (ret < 0) {
serial_printf("[VFS] open O_CREAT: lookup parent '%s' failed: %d\n", dirpath, ret);
return ret;
}
if (!dir->ops || !dir->ops->create) {
serial_printf("[VFS] open O_CREAT: parent has no create op\n");
vnode_unref(dir);
return -ENOSYS;
}
ret = dir->ops->create(dir, filename, mode, &node);
vnode_unref(dir);
if (ret < 0) {
serial_printf("[VFS] open O_CREAT: create '%s' failed: %d\n", filename, ret);
return ret;
}
} else if (ret < 0) {
return ret;
}
int acc = flags & O_ACCMODE;
if ((acc == O_WRONLY || acc == O_RDWR) && node->type == VFS_NODE_DIR) {
vnode_unref(node);
return -EISDIR;
}
vfs_file_t *file = vfs_file_alloc();
if (!file) {
vnode_unref(node);
return -ENFILE;
}
file->vnode = node;
file->flags = flags;
file->offset = (flags & O_APPEND) ? node->size : 0;
if ((flags & O_TRUNC) && (acc == O_WRONLY || acc == O_RDWR)) {
if (node->ops && node->ops->truncate) {
node->ops->truncate(node, 0);
} else {
node->size = 0;
}
file->offset = 0;
}
*out = file;
return 0;
}
void vfs_close(vfs_file_t *file) {
if (file) {
int acc = file->flags & O_ACCMODE;
if (acc == O_WRONLY || acc == O_RDWR) {
vfs_sync_all();
}
}
vfs_file_free(file);
}
int64_t vfs_read(vfs_file_t *file, void *buf, size_t len) {
if (!file || !file->vnode) return -EBADF;
if (len == 0) return 0;
if ((file->flags & O_ACCMODE) == O_WRONLY) return -EBADF;
if (!file->vnode->ops || !file->vnode->ops->read) return -EIO;
int64_t n = file->vnode->ops->read(file->vnode, buf, len, file->offset);
if (n > 0) file->offset += (uint64_t)n;
return n;
}
int64_t vfs_write(vfs_file_t *file, const void *buf, size_t len) {
if (!file || !file->vnode) return -EBADF;
if (len == 0) return 0;
if ((file->flags & O_ACCMODE) == O_RDONLY) return -EBADF;
if (!file->vnode->ops || !file->vnode->ops->write) return -EIO;
if (file->flags & O_APPEND) file->offset = file->vnode->size;
int64_t n = file->vnode->ops->write(file->vnode, buf, len, file->offset);
if (n > 0) file->offset += (uint64_t)n;
return n;
}
int64_t vfs_seek(vfs_file_t *file, int64_t offset, int whence) {
if (!file || !file->vnode) return -EBADF;
vnode_type_t t = file->vnode->type;
if (t == VFS_NODE_CHARDEV || t == VFS_NODE_PIPE) return -ESPIPE;
int64_t new_off;
switch (whence) {
case SEEK_SET: new_off = offset; break;
case SEEK_CUR: new_off = (int64_t)file->offset + offset; break;
case SEEK_END: new_off = (int64_t)file->vnode->size + offset; break;
default: return -EINVAL;
}
if (new_off < 0) return -EINVAL;
file->offset = (uint64_t)new_off;
return new_off;
}
int vfs_stat(const char *path, vfs_stat_t *out) {
if (!path || !out) return -EINVAL;
vnode_t *node = NULL;
int ret = vfs_lookup(path, &node);
if (ret < 0) return ret;
if (node->ops && node->ops->stat) {
ret = node->ops->stat(node, out);
} else {
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
ret = 0;
}
vnode_unref(node);
return ret;
}
int vfs_fstat(vfs_file_t *file, vfs_stat_t *out) {
if (!file || !file->vnode || !out) return -EBADF;
vnode_t *node = file->vnode;
if (node->ops && node->ops->stat) return node->ops->stat(node, out);
memset(out, 0, sizeof(*out));
out->st_ino = node->ino;
out->st_type = node->type;
out->st_mode = node->mode;
out->st_uid = node->uid;
out->st_gid = node->gid;
out->st_size = node->size;
return 0;
}
int64_t vfs_ioctl(vfs_file_t *file, uint64_t req, void *arg) {
if (!file || !file->vnode) return -EBADF;
if (!file->vnode->ops || !file->vnode->ops->ioctl) return -ENOTTY;
return file->vnode->ops->ioctl(file->vnode, req, arg);
}
int vfs_readdir(vfs_file_t *file, vfs_dirent_t *out) {
if (!file || !file->vnode || !out) return -EBADF;
if (file->vnode->type != VFS_NODE_DIR) return -ENOTDIR;
if (!file->vnode->ops || !file->vnode->ops->readdir) return -EIO;
int ret = file->vnode->ops->readdir(file->vnode, file->offset, out);
if (ret == 0) file->offset++;
return ret;
}
int vfs_mkdir(const char *path, uint32_t mode) {
if (!path) return -EINVAL;
char dirpath[VFS_MAX_PATH];
strncpy(dirpath, path, VFS_MAX_PATH - 1);
dirpath[VFS_MAX_PATH - 1] = '\0';
char *slash = NULL;
for (int i = (int)strlen(dirpath) - 1; i >= 0; i--) {
if (dirpath[i] == '/') { slash = &dirpath[i]; break; }
}
if (!slash) return -EINVAL;
char dirname[VFS_MAX_NAME];
strncpy(dirname, slash + 1, VFS_MAX_NAME - 1);
dirname[VFS_MAX_NAME - 1] = '\0';
if (dirname[0] == '\0') return -EINVAL;
if (slash == dirpath) dirpath[1] = '\0';
else *slash = '\0';
vnode_t *dir = NULL;
int ret = vfs_lookup(dirpath, &dir);
if (ret < 0) return ret;
if (!dir->ops || !dir->ops->mkdir) { vnode_unref(dir); return -ENOSYS; }
ret = dir->ops->mkdir(dir, dirname, mode);
vnode_unref(dir);
return ret;
}
fd_table_t *fd_table_create(void) {
fd_table_t *t = kzalloc(sizeof(fd_table_t));
return t;
}
fd_table_t *fd_table_clone(const fd_table_t *src) {
if (!src) return NULL;
fd_table_t *dst = kzalloc(sizeof(fd_table_t));
if (!dst) return NULL;
for (int i = 0; i < TASK_MAX_FDS; i++) {
if (src->entries[i].file) {
dst->entries[i] = src->entries[i];
__atomic_fetch_add(&dst->entries[i].file->refcount, 1, __ATOMIC_RELAXED);
}
}
return dst;
}
void fd_table_cloexec(fd_table_t *table) {
if (!table) return;
for (int i = 0; i < TASK_MAX_FDS; i++) {
if (table->entries[i].file && (table->entries[i].fd_flags & FD_CLOEXEC)) {
vfs_file_free(table->entries[i].file);
table->entries[i].file = NULL;
table->entries[i].fd_flags = 0;
}
}
}
void fd_table_destroy(fd_table_t *table) {
if (!table) return;
for (int i = 0; i < TASK_MAX_FDS; i++) {
if (table->entries[i].file) {
vfs_file_free(table->entries[i].file);
table->entries[i].file = NULL;
}
}
kfree(table);
}
int fd_alloc(fd_table_t *table, vfs_file_t *file, int min_fd) {
if (!table || !file) return -EINVAL;
if (min_fd < 0 || min_fd >= TASK_MAX_FDS) return -EINVAL;
for (int i = min_fd; i < TASK_MAX_FDS; i++) {
if (!table->entries[i].file) {
table->entries[i].file = file;
table->entries[i].fd_flags = 0;
return i;
}
}
return -EMFILE;
}
vfs_file_t *fd_get(const fd_table_t *table, int fd) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return NULL;
return table->entries[fd].file;
}
int fd_close(fd_table_t *table, int fd) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return -EBADF;
vfs_file_t *file = table->entries[fd].file;
if (!file) return -EBADF;
vfs_file_free(file);
table->entries[fd].file = NULL;
table->entries[fd].fd_flags = 0;
return 0;
}
int fd_dup2(fd_table_t *table, int oldfd, int newfd) {
if (!table) return -EBADF;
if (oldfd < 0 || oldfd >= TASK_MAX_FDS) return -EBADF;
if (newfd < 0 || newfd >= TASK_MAX_FDS) return -EBADF;
if (oldfd == newfd) return newfd;
vfs_file_t *src = table->entries[oldfd].file;
if (!src) return -EBADF;
if (table->entries[newfd].file)
vfs_file_free(table->entries[newfd].file);
table->entries[newfd].file = src;
table->entries[newfd].fd_flags = 0;
__atomic_fetch_add(&src->refcount, 1, __ATOMIC_RELAXED);
return newfd;
}
int fd_set_flags(fd_table_t *table, int fd, int flags) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return -EBADF;
if (!table->entries[fd].file) return -EBADF;
table->entries[fd].fd_flags = flags;
return 0;
}
int fd_get_flags(const fd_table_t *table, int fd) {
if (!table || fd < 0 || fd >= TASK_MAX_FDS) return -EBADF;
if (!table->entries[fd].file) return -EBADF;
return table->entries[fd].fd_flags;
}
int vfs_init_stdio(void *task_ptr) {
task_t *t = (task_t *)task_ptr;
if (!t || !t->fd_table) return -EINVAL;
vfs_file_t *in = NULL;
int ret = vfs_open("/dev/tty", O_RDONLY, 0, &in);
if (ret < 0) return ret;
fd_alloc(t->fd_table, in, 0);
vfs_file_t *out = NULL;
ret = vfs_open("/dev/tty", O_WRONLY, 0, &out);
if (ret < 0) return ret;
fd_alloc(t->fd_table, out, 1);
vfs_file_t *err = NULL;
ret = vfs_open("/dev/tty", O_WRONLY, 0, &err);
if (ret < 0) return ret;
fd_alloc(t->fd_table, err, 2);
return 0;
}
int vfs_set_mount_info(const char *path, const char *device, const char *fstype) {
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (g_mounts[i].used && strcmp(g_mounts[i].path, path) == 0) {
if (device) {
strncpy(g_mounts[i].device, device, sizeof(g_mounts[i].device) - 1);
g_mounts[i].device[sizeof(g_mounts[i].device) - 1] = '\0';
}
if (fstype) {
strncpy(g_mounts[i].fstype, fstype, sizeof(g_mounts[i].fstype) - 1);
g_mounts[i].fstype[sizeof(g_mounts[i].fstype) - 1] = '\0';
}
return 0;
}
}
return -ENOENT;
}
int vfs_list_mounts(vfs_mount_info_t *out, int max) {
int n = 0;
for (int i = 0; i < VFS_MAX_MOUNTS && n < max; i++) {
if (!g_mounts[i].used) continue;
strncpy(out[n].path, g_mounts[i].path, VFS_MAX_PATH - 1);
out[n].path[VFS_MAX_PATH - 1] = '\0';
strncpy(out[n].device, g_mounts[i].device, sizeof(out[n].device) - 1);
out[n].device[sizeof(out[n].device) - 1] = '\0';
strncpy(out[n].fstype, g_mounts[i].fstype, sizeof(out[n].fstype) - 1);
out[n].fstype[sizeof(out[n].fstype) - 1] = '\0';
out[n].flags = 0;
n++;
}
return n;
}
extern int ext2_statvfs(vnode_t *root, vfs_statvfs_t *out);
extern int fat32_statvfs(vnode_t *root, vfs_statvfs_t *out);
int vfs_statvfs(const char *path, vfs_statvfs_t *out) {
if (!path || !out) return -EINVAL;
memset(out, 0, sizeof(*out));
vfs_mount_t *best = NULL;
size_t best_len = 0;
for (int i = 0; i < VFS_MAX_MOUNTS; i++) {
if (!g_mounts[i].used) continue;
size_t mlen = strlen(g_mounts[i].path);
if (strncmp(path, g_mounts[i].path, mlen) == 0) {
if (path[mlen] == '/' || path[mlen] == '\0' || mlen == 1) {
if (mlen > best_len) { best = &g_mounts[i]; best_len = mlen; }
}
}
}
if (!best) return -ENOENT;
if (strcmp(best->fstype, "ext2") == 0) {
return ext2_statvfs(best->root, out);
}
if (strcmp(best->fstype, "fat32") == 0) {
return fat32_statvfs(best->root, out);
}
out->f_bsize = 4096;
out->f_namemax = 255;
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
#include "../../include/gdt/gdt.h"
#include "../../include/smp/smp.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stddef.h>
gdt_pointer_t gdtr;
struct {
gdt_entry_t gdt_entries[5 + (MAX_CPUS * 2)];
} __attribute__((packed)) gdt;
tss_t *tss[MAX_CPUS] = {0};
__attribute__((aligned(16))) char kernel_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char def_ist_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char df_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char nmi_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
__attribute__((aligned(16))) char pf_stacks[MAX_CPUS][KERNEL_STACK_SIZE];
extern void _load_gdt(gdt_pointer_t *descriptor);
extern void _reload_segments(uint64_t cs, uint64_t ds);
void gdt_init(void) {
memset(&gdt, 0, sizeof(gdt));
gdt.gdt_entries[0] = (gdt_entry_t)GDT_ENTRY(0, 0, 0x00, 0x0);
gdt.gdt_entries[1] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0x9A, 0xA);
gdt.gdt_entries[2] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0x92, 0xC);
gdt.gdt_entries[3] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0xF2, 0x8);
gdt.gdt_entries[4] = (gdt_entry_t)GDT_ENTRY(0, 0xFFFFF, 0xFA, 0xA);
gdtr.size = (5 * sizeof(gdt_entry_t)) - 1;
gdtr.pointer = &gdt.gdt_entries[0];
serial_printf("Installing temporary GDT for BSP...\n");
gdt_load();
serial_printf("Temporary GDT installed\n");
}
void gdt_load(void) {
_load_gdt(&gdtr);
_reload_segments(GDT_CODE_SEGMENT, GDT_DATA_SEGMENT);
}
+28
View File
@@ -0,0 +1,28 @@
section .text
global _reload_segments
global _load_gdt
global load_tss
_load_gdt:
lgdt [rdi]
ret
load_tss:
ltr di
ret
_reload_segments:
push rdi
lea rax, [rel .reload_cs]
push rax
retfq
.reload_cs:
mov ax, si
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
ret
+136
View File
@@ -0,0 +1,136 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <limine.h>
#include "../../../include/graphics/fb/fb.h"
#include "../../../include/io/serial.h"
uint32_t *g_backbuf = NULL;
uint32_t g_bb_pitch = 0;
static uint32_t g_bb_w = 0;
static uint32_t g_bb_h = 0;
void fb_init_backbuffer(struct limine_framebuffer *fb) {
if (!fb) return;
g_bb_w = fb->width;
g_bb_h = fb->height;
g_bb_pitch = fb->pitch / 4;
size_t sz = (size_t)g_bb_pitch * g_bb_h * sizeof(uint32_t);
g_backbuf = (uint32_t *)malloc(sz);
if (g_backbuf) {
memcpy(g_backbuf, fb->address, sz);
serial_printf("[FB] Backbuffer allocated: %ux%u (%zu KB)\n",
g_bb_w, g_bb_h, sz / 1024);
} else {
serial_printf("[FB] WARNING: backbuffer alloc failed, using direct VRAM\n");
}
}
static inline uint32_t *fb_get_buf(struct limine_framebuffer *fb) {
return g_backbuf ? g_backbuf : (uint32_t *)fb->address;
}
static inline uint32_t fb_get_pitch(struct limine_framebuffer *fb) {
return g_backbuf ? g_bb_pitch : (fb->pitch / 4);
}
void fb_flush(struct limine_framebuffer *fb) {
if (!fb || !g_backbuf) return;
memcpy(fb->address, g_backbuf, (size_t)g_bb_pitch * g_bb_h * sizeof(uint32_t));
}
void fb_flush_lines(struct limine_framebuffer *fb, uint32_t y_start, uint32_t y_end) {
if (!fb || !g_backbuf) return;
if (y_start >= g_bb_h) return;
if (y_end > g_bb_h) y_end = g_bb_h;
if (y_start >= y_end) return;
uint32_t *dst = (uint32_t *)fb->address + y_start * g_bb_pitch;
uint32_t *src = g_backbuf + y_start * g_bb_pitch;
size_t bytes = (size_t)(y_end - y_start) * g_bb_pitch * sizeof(uint32_t);
memcpy(dst, src, bytes);
}
void fb_draw_pixel(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t color) {
if (x >= fb->width || y >= fb->height) return;
uint32_t *buf = fb_get_buf(fb);
uint32_t pitch = fb_get_pitch(fb);
buf[y * pitch + x] = color;
}
void fb_fill_rect(struct limine_framebuffer *fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color) {
if (!fb) return;
uint32_t *buf = fb_get_buf(fb);
uint32_t pitch = fb_get_pitch(fb);
if (x >= fb->width || y >= fb->height) return;
if (x + w > fb->width) w = fb->width - x;
if (y + h > fb->height) h = fb->height - y;
for (uint32_t py = 0; py < h; py++) {
uint32_t *row = buf + (y + py) * pitch + x;
if (color == 0) {
memset(row, 0, w * sizeof(uint32_t));
} else {
for (uint32_t px = 0; px < w; px++)
row[px] = color;
}
}
}
void fb_clear(struct limine_framebuffer *fb, uint32_t color) {
if (!fb) return;
uint32_t *buf = fb_get_buf(fb);
size_t total = (size_t)fb_get_pitch(fb) * fb->height;
if (color == 0) {
memset(buf, 0, total * sizeof(uint32_t));
} else {
for (size_t i = 0; i < total; i++)
buf[i] = color;
}
}
int psf_validate(void) {
const uint8_t *raw = get_font_data();
if (raw[0] == 0x72 && raw[1] == 0xb5 &&
raw[2] == 0x4a && raw[3] == 0x86)
return 2;
if (raw[0] == 0x36 && raw[1] == 0x04)
return 1;
return 0;
}
void fb_draw_char(struct limine_framebuffer *fb, char c, uint32_t x, uint32_t y, uint32_t color) {
const uint8_t *raw = get_font_data();
uint32_t headersize = 32;
uint32_t charsiz = *(uint32_t*)(raw + 20);
uint8_t *glyphs = (uint8_t*)raw + headersize;
uint32_t glyph_index = ((uint8_t)c < 128) ? (uint8_t)c : '?';
if (glyph_index >= 512) glyph_index = '?';
uint8_t *glyph = &glyphs[glyph_index * charsiz];
uint32_t *buf = fb_get_buf(fb);
uint32_t pitch = fb_get_pitch(fb);
if (x + 8 > fb->width || y + 16 > fb->height) return;
for (uint32_t row = 0; row < 16; row++) {
uint8_t byte = glyph[row];
uint32_t *line = buf + (y + row) * pitch + x;
for (uint32_t col = 0; col < 8; col++) {
if (byte & (0x80 >> col))
line[col] = color;
}
}
}
void fb_draw_string(struct limine_framebuffer *fb, const char *str, uint32_t x, uint32_t y, uint32_t color) {
uint32_t orig_x = x;
if (!psf_validate()) return;
while (*str) {
if (*str == '\n') { x = orig_x; y += 16; }
else { fb_draw_char(fb, *str, x, y, color); x += 8; }
str++;
}
}
+79
View File
@@ -0,0 +1,79 @@
#include "../../include/interrupts/idt.h"
#include "../../include/interrupts/isr.h"
#include "../../include/interrupts/irq.h"
#include "../../include/io/serial.h"
#include <stddef.h>
#include <string.h>
extern void *interrupts_stub_table[];
__attribute__((
aligned(0x10))) static idt_entry_t idt_entries[IDT_MAX_DESCRIPTORS];
idtr_t idtr;
void idt_set_gate(uint8_t index, void *base, uint16_t selector, uint8_t flags, uint8_t ist) {
idt_entries[index].base_low = (uint64_t)base & 0xFFFF;
idt_entries[index].kernel_cs = selector;
idt_entries[index].ist = ist;
idt_entries[index].attributes = flags;
idt_entries[index].base_mid = ((uint64_t)base >> 16) & 0xFFFF;
idt_entries[index].base_high = ((uint64_t)base >> 32) & 0xFFFFFFFF;
idt_entries[index].reserved = 0;
}
void idt_gate_enable(int interrupt) {
FLAG_SET(idt_entries[interrupt].attributes, IDT_FLAG_PRESENT);
}
void idt_gate_disable(int interrupt) {
FLAG_UNSET(idt_entries[interrupt].attributes, IDT_FLAG_PRESENT);
}
bool setup_specific_vectors(uint64_t kernel_code_segment, uint64_t vector) {
if(vector == EXCEPTION_DOUBLE_FAULT) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 1);
return true;
}
if(vector == EXCEPTION_NMI) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 2);
return true;
}
if(vector == EXCEPTION_PAGE_FAULT) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 3);
return true;
}
if(vector == 0x40) {
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 4);
return true;
}
return false;
}
void idt_load(void) {
__asm__ volatile("lidt %0" : : "m"(idtr));
}
void setup_interrupt_descriptor_table(uint64_t kernel_code_segment) {
serial_printf("[IDT] Initializing IDT...\n");
idtr.base = (idt_entry_t *)&idt_entries[0];
idtr.limit = (uint16_t)sizeof(idt_entry_t) * IDT_MAX_DESCRIPTORS - 1;
for (uint16_t vector = 0; vector < IDT_MAX_DESCRIPTORS; vector++) {
if(setup_specific_vectors(kernel_code_segment, vector)) {
continue;
}
idt_set_gate(vector, interrupts_stub_table[vector], kernel_code_segment, 0x8E, 0);
}
idt_load();
serial_printf("[IDT] IDT initialized successfully\n");
}
+133
View File
@@ -0,0 +1,133 @@
section .text
extern base_trap
extern sched_reschedule
extern get_percpu
PERCPU_NEED_RESCHED equ 40
common_stub:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rax, ds
push rax
mov rax, [rsp + 19*8]
and rax, 3
jz .kernel_entry
swapgs
.kernel_entry:
mov ax, 0x10
mov ds, ax
mov es, ax
mov rdi, rsp
call base_trap
pop rax
mov ds, ax
mov es, ax
mov rax, [rsp + 18*8]
and rax, 3
jz .kernel_resched
.check_resched:
call get_percpu
test rax, rax
jz .do_swapgs
cmp byte [rax + PERCPU_NEED_RESCHED], 0
je .do_swapgs
mov byte [rax + PERCPU_NEED_RESCHED], 0
call sched_reschedule
jmp .check_resched
.do_swapgs:
swapgs
jmp .kernel_exit
.kernel_resched:
call get_percpu
test rax, rax
jz .kernel_check_cs
cmp byte [rax + PERCPU_NEED_RESCHED], 0
je .kernel_check_cs
mov byte [rax + PERCPU_NEED_RESCHED], 0
call sched_reschedule
jmp .kernel_resched
.kernel_check_cs:
mov rax, [rsp + 18*8]
and rax, 3
jz .kernel_exit
swapgs
jmp .kernel_exit
.kernel_exit:
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
add rsp, 16
iretq
%macro INTERRUPT_ERR_STUB 1
interrupt_stub_%1:
push qword %1
jmp common_stub
%endmacro
%macro INTERRUPT_NO_ERR_STUB 1
interrupt_stub_%1:
push qword 0
push qword %1
jmp common_stub
%endmacro
%assign i 0
%rep 32
%if i = 8 || i = 10 || i = 11 || i = 12 || i = 13 || i = 14 || i = 17 || i = 21 || i = 29 || i = 30
INTERRUPT_ERR_STUB i
%else
INTERRUPT_NO_ERR_STUB i
%endif
%assign i i+1
%endrep
%rep 224
INTERRUPT_NO_ERR_STUB i
%assign i i+1
%endrep
section .data
global interrupts_stub_table
interrupts_stub_table:
%assign i 0
%rep 256
dq interrupt_stub_%+i
%assign i i+1
%endrep
+32
View File
@@ -0,0 +1,32 @@
#include "../../include/interrupts/interrupts.h"
#include "../../include/io/serial.h"
#include "../../include/interrupts/isr.h"
#include "../../include/interrupts/irq.h"
#include "../../include/interrupts/idt.h"
#include "../../include/gdt/gdt.h"
#include "../../include/io/ports.h"
extern const int_desc_t __start_int_handlers[];
extern const int_desc_t __stop_int_handlers[];
void init_interrupt_system(void) {
asm volatile("cli");
setup_interrupt_descriptor_table(GDT_CODE_SEGMENT);
setup_defined_isr_handlers();
setup_defined_irq_handlers();
asm volatile("sti");
}
void base_trap(void *ctx) {
struct int_frame_t *regs = (struct int_frame_t*)ctx;
if(regs->interrupt < ISR_EXCEPTION_COUNT) {
return isr_common_handler(regs);
}
return irq_common_handler(regs);
}
+82
View File
@@ -0,0 +1,82 @@
#include "../../../include/interrupts/interrupts.h"
#include "../../../include/io/serial.h"
#include "../../../include/interrupts/irq.h"
#include "../../../include/interrupts/idt.h"
#include "../../../include/io/ports.h"
#include "../../../include/smp/percpu.h"
#include "../../../include/apic/apic.h"
extern const int_desc_t __start_irq_handlers[];
extern const int_desc_t __stop_irq_handlers[];
static int_handler_f registered_irq_interrupts[IRQ_INTERRUPTS_COUNT]__attribute__((aligned(64)));
void irq_common_handler(struct int_frame_t* regs) {
uint64_t vec = regs->interrupt;
if (vec >= IRQ_INTERRUPTS_COUNT || vec < (IDT_MAX_DESCRIPTORS - IRQ_INTERRUPTS_COUNT)) {
serial_printf("IRQ vector out of range: %d\n", vec);
while (1)
{
asm volatile ("hlt");
}
}
if(registered_irq_interrupts[vec]) {
return registered_irq_interrupts[vec](regs);
}
serial_printf("IRQ interrupt handler\n");
while (1)
{
asm volatile ("hlt");
}
}
void setup_defined_irq_handlers(void) {
const int_desc_t* desc;
for (desc = __start_irq_handlers; desc < __stop_irq_handlers; desc++) {
if(desc->vector >= IRQ_INTERRUPTS_COUNT) {
serial_printf("Invalid IRQ vector number! Must be < %d\n", IRQ_INTERRUPTS_COUNT);
continue;
}
registered_irq_interrupts[desc->vector] = desc->handler;
serial_printf("Registered IRQ vector 0x%d\n", desc->vector);
}
}
extern percpu_t* percpu_regions[MAX_CPUS];
DEFINE_IRQ(IPI_RESCHEDULE_VECTOR, ipi_reschedule_handler)
{
(void)frame;
uint32_t id = lapic_get_id();
if (id < MAX_CPUS && percpu_regions[id] != NULL) {
percpu_regions[id]->need_resched = true;
}
lapic_eoi();
}
DEFINE_IRQ(IPI_TLB_SHOOTDOWN, ipi_tlb_shootdown_handler)
{
(void)frame;
uint32_t id = lapic_get_id();
tlb_shootdown_t* q = &tlb_shootdown_queue[id];
if (q->pending) {
for (size_t i = 0; i < q->count; i++) {
uintptr_t addr = q->addresses[i];
if (addr != 0) {
asm volatile ("invlpg (%0)" :: "r"(addr) : "memory");
}
}
q->pending = false;
q->count = 0;
}
lapic_eoi();
}
+271
View File
@@ -0,0 +1,271 @@
#include "../../../include/interrupts/interrupts.h"
#include "../../../include/io/serial.h"
#include "../../../include/interrupts/isr.h"
#include "../../../include/sched/sched.h"
#include "../../../include/smp/percpu.h"
#include "../../../include/apic/apic.h"
#include "../../../include/memory/vmm.h"
#include "../../../include/memory/pmm.h"
#include "../../../include/panic/panic.h"
#include <stdio.h>
extern const int_desc_t __start_isr_handlers[];
extern const int_desc_t __stop_isr_handlers[];
static int_handler_f registered_isr_interrupts[ISR_EXCEPTION_COUNT] __attribute__((aligned(64)));
extern volatile int g_panic_owner;
void registers_dump(struct int_frame_t *regs) {
uint64_t cr2 = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
serial_printf("\nRegisters Dump:\n");
serial_printf("\tRAX:0x%llx\n\tRBX:0x%llx\n\tRCX:0x%llx\n\tRDX:0x%llx\n",
regs->rax, regs->rbx, regs->rcx, regs->rdx);
serial_printf("\tRSI:0x%llx\n\tRDI:0x%llx\n\tRBP:0x%llx\n\tRSP:0x%llx\n",
regs->rsi, regs->rdi, regs->rbp, regs->rsp);
serial_printf("\tRIP:0x%llx\n\tRFL:0x%llx\n\tCS:0x%llx\n\tERR:0x%llx\n",
regs->rip, regs->rflags, regs->cs, regs->error);
serial_printf("\tCR2:0x%llx\n", cr2);
serial_printf("\nInt:%d (%s)\n", regs->interrupt, exception_names[regs->interrupt]);
}
static void dump_rip_bytes_safe(uint64_t rip) {
uintptr_t hhdm = (uintptr_t)pmm_phys_to_virt(0);
uint64_t vpage = rip & ~0xFFFULL;
uintptr_t off = rip & 0xFFF;
if (off > PAGE_SIZE - 8) {
serial_printf("[ISR-UD] RIP=0x%llx near page boundary, skipping byte dump\n", rip);
return;
}
uint64_t cr3_val = 0;
asm volatile("mov %%cr3, %0" : "=r"(cr3_val));
volatile uint64_t *pml4v = (volatile uint64_t*)(hhdm + (cr3_val & ~0xFFFULL));
uint64_t e4 = pml4v[(vpage >> 39) & 0x1FF];
if (!(e4 & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PML4 not present\n", rip);
return;
}
volatile uint64_t *pdpt = (volatile uint64_t*)(hhdm + (e4 & ~0xFFFULL));
uint64_t e3 = pdpt[(vpage >> 30) & 0x1FF];
if (!(e3 & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PDPT not present\n", rip);
return;
}
volatile uint64_t *pd = (volatile uint64_t*)(hhdm + (e3 & ~0xFFFULL));
uint64_t e2 = pd[(vpage >> 21) & 0x1FF];
if (!(e2 & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PD not present\n", rip);
return;
}
if (e2 & (1ULL << 7)) {
uintptr_t hp_phys = (e2 & ~0x1FFFFFULL) + (vpage & 0x1FFFFFULL);
volatile uint8_t *bytes = (volatile uint8_t*)(hhdm + hp_phys + off);
serial_printf("[ISR-UD] RIP=0x%llx (huge page) bytes: "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
rip,
(unsigned)bytes[0], (unsigned)bytes[1],
(unsigned)bytes[2], (unsigned)bytes[3],
(unsigned)bytes[4], (unsigned)bytes[5],
(unsigned)bytes[6], (unsigned)bytes[7]);
return;
}
volatile uint64_t *pt = (volatile uint64_t*)(hhdm + (e2 & ~0xFFFULL));
uint64_t pte = pt[(vpage >> 12) & 0x1FF];
if (!(pte & 1)) {
serial_printf("[ISR-UD] RIP=0x%llx: PT not present (pte=0x%llx)\n", rip, pte);
return;
}
uintptr_t phys_page = pte & ~0xFFFULL;
volatile uint8_t *bytes = (volatile uint8_t*)(hhdm + phys_page + off);
serial_printf("[ISR-UD] RIP=0x%llx phys=0x%llx flags=0x%03llx bytes: "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
rip, phys_page, pte & 0xFFFULL,
(unsigned)bytes[0], (unsigned)bytes[1],
(unsigned)bytes[2], (unsigned)bytes[3],
(unsigned)bytes[4], (unsigned)bytes[5],
(unsigned)bytes[6], (unsigned)bytes[7]);
if (off >= 16) {
volatile uint8_t *prev = (volatile uint8_t*)(hhdm + phys_page + off - 16);
serial_printf("[ISR-UD] RIP-16..RIP-1: "
"%02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
(unsigned)prev[0], (unsigned)prev[1],
(unsigned)prev[2], (unsigned)prev[3],
(unsigned)prev[4], (unsigned)prev[5],
(unsigned)prev[6], (unsigned)prev[7],
(unsigned)prev[8], (unsigned)prev[9],
(unsigned)prev[10], (unsigned)prev[11],
(unsigned)prev[12], (unsigned)prev[13],
(unsigned)prev[14], (unsigned)prev[15]);
}
}
static void dump_user_qword_via_task(uint64_t uaddr, const char *label) {
percpu_t *pc = get_percpu();
task_t *me = pc ? (task_t *)pc->current_task : NULL;
if (!me) { uint32_t cpu = lapic_get_id(); me = current_task[cpu]; }
if (!me || !me->pagemap) {
serial_printf("[ISR-PF] %s: no task/pagemap\n", label);
return;
}
if ((uaddr & 0x7) != 0) {
serial_printf("[ISR-PF] %s addr 0x%llx not 8-aligned\n", label, uaddr);
return;
}
uint64_t flags = 0;
if (!vmm_get_page_flags(me->pagemap, uaddr, &flags) || !(flags & 1)) {
serial_printf("[ISR-PF] %s addr 0x%llx not mapped (flags=0x%llx)\n",
label, uaddr, flags);
return;
}
uintptr_t phys = 0;
if (!vmm_virt_to_phys(me->pagemap, uaddr, &phys)) {
serial_printf("[ISR-PF] %s virt_to_phys failed for 0x%llx\n", label, uaddr);
return;
}
volatile uint64_t *p = (volatile uint64_t *)pmm_phys_to_virt(phys & ~7ULL);
uint64_t v = *p;
serial_printf("[ISR-PF] %s [0x%llx] = 0x%llx\n", label, uaddr, v);
}
static __attribute__((noreturn)) void kill_current_task(int exit_code) {
percpu_t *pc = get_percpu();
task_t *me = pc ? (task_t *)pc->current_task : NULL;
if (!me) { uint32_t cpu = lapic_get_id(); me = current_task[cpu]; }
if (me) me->exit_code = exit_code;
vmm_switch_pagemap(vmm_get_kernel_pagemap());
task_exit();
}
void handle_intercpu_interrupt(struct int_frame_t *regs)
{
if (regs->interrupt == 2) {
if (__atomic_load_n(&g_panic_owner, __ATOMIC_ACQUIRE) != 0) {
for (;;) asm volatile("cli; hlt");
}
kernel_panic_regs("Non-Maskable Interrupt (hardware)", regs);
}
serial_force_unlock();
registers_dump(regs);
switch (regs->interrupt) {
case EXCEPTION_DIVIDE_ERROR:
case EXCEPTION_OVERFLOW:
case EXCEPTION_BOUND_RANGE:
case EXCEPTION_INVALID_OPCODE:
case EXCEPTION_DEVICE_NOT_AVAILABLE:
case EXCEPTION_X87_FPU_ERROR:
if ((regs->cs & 3) == 3) {
if (regs->interrupt == EXCEPTION_INVALID_OPCODE) {
dump_rip_bytes_safe(regs->rip);
serial_printf("[ISR-UD] RBP=0x%llx RSP=0x%llx\n",
regs->rbp, regs->rsp);
}
serial_printf("[ISR] %s in userspace at RIP=0x%llx — killing task\n",
exception_names[regs->interrupt], regs->rip);
kill_current_task(139);
}
kernel_panic_regs(exception_names[regs->interrupt], regs);
case EXCEPTION_DOUBLE_FAULT:
kernel_panic_regs("Double Fault", regs);
case EXCEPTION_INVALID_TSS:
case EXCEPTION_SEGMENT_NOT_PRESENT:
case EXCEPTION_STACK_SEGMENT_FAULT:
kernel_panic_regs(exception_names[regs->interrupt], regs);
case EXCEPTION_GENERAL_PROTECTION_FAULT:
if ((regs->cs & 3) == 3) {
serial_printf("[ISR] GPF in userspace at RIP=0x%llx — killing task\n",
regs->rip);
kill_current_task(139);
}
kernel_panic_regs("General Protection Fault (kernel)", regs);
case EXCEPTION_PAGE_FAULT: {
uint64_t cr2val = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2val));
if ((regs->cs & 3) == 3) {
percpu_t *pc = get_percpu();
task_t *me = pc ? (task_t *)pc->current_task : NULL;
if (!me) { uint32_t cpu = lapic_get_id(); me = current_task[cpu]; }
serial_printf("[ISR] Page Fault in userspace: RIP=0x%llx CR2=0x%llx ERR=0x%llx task='%s' pid=%u\n",
regs->rip, cr2val, regs->error,
me ? me->name : "?", me ? me->pid : 0);
dump_rip_bytes_safe(regs->rip);
dump_user_qword_via_task(regs->rsp, "[rsp] ");
dump_user_qword_via_task(regs->rsp + 8, "[rsp+8] ");
dump_user_qword_via_task(regs->rsp + 16, "[rsp+16] ");
dump_user_qword_via_task(regs->rsp + 24, "[rsp+24] ");
kill_current_task(139);
}
kernel_panic_regs("Page Fault (kernel)", regs);
}
case EXCEPTION_MACHINE_CHECK:
kernel_panic_regs("Machine Check Exception", regs);
default: {
char buf[64];
serial_printf("UNKNOWN EXCEPTION %llu\n", regs->interrupt);
const char *pfx = "Unknown Exception #";
int i = 0;
while (pfx[i] && i < 50) { buf[i] = pfx[i]; i++; }
uint64_t v = regs->interrupt;
if (v >= 100) buf[i++] = '0' + (int)(v / 100);
if (v >= 10) buf[i++] = '0' + (int)((v / 10) % 10);
buf[i++] = '0' + (int)(v % 10);
buf[i] = '\0';
kernel_panic_regs(buf, regs);
}
}
}
DEFINE_ISR(0x3, isr_breakpoint) {
(void)frame;
serial_printf("Breakpoint hit\n");
}
void isr_common_handler(struct int_frame_t *regs)
{
uint64_t vec = regs->interrupt;
if (vec >= ISR_EXCEPTION_COUNT) {
serial_printf("Invalid ISR vector %d\n", vec);
while (1) asm volatile("hlt");
}
if (registered_isr_interrupts[vec]) {
registered_isr_interrupts[vec](regs);
return;
}
handle_intercpu_interrupt(regs);
}
void setup_defined_isr_handlers(void)
{
const int_desc_t *desc;
for (desc = __start_isr_handlers; desc < __stop_isr_handlers; desc++) {
if (desc->vector >= ISR_EXCEPTION_COUNT) {
serial_printf("ISR: vector %d out of range\n", desc->vector);
continue;
}
registered_isr_interrupts[desc->vector] = desc->handler;
serial_printf("ISR: Registered vector %d\n", desc->vector);
}
}
+826
View File
@@ -0,0 +1,826 @@
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include "../../include/io/ports.h"
#include "../../include/io/serial.h"
static volatile uint8_t serial_lock = 0;
static uint16_t default_serial_port = 0;
void serial_initialize(uint16_t port, uint32_t baud_rate) {
outb(port + 1, 0x00);
uint16_t divisor = 115200 / baud_rate;
outb(port + 3, 0x80);
outb(port + 0, divisor & 0xFF);
outb(port + 1, (divisor >> 8) & 0xFF);
outb(port + 3, 0x03);
outb(port + 2, 0xC7);
outb(port + 4, 0x0B);
outb(port + 4, 0x1E);
outb(port + 0, 0xAE);
if (inb(port + 0) != 0xAE) {
return;
}
outb(port + 4, 0x0F);
default_serial_port = port;
}
uint16_t serial_get_default_port(void) {
return default_serial_port;
}
void serial_set_default_port(uint16_t port) {
default_serial_port = port;
}
void serial_force_unlock(void) {
__sync_lock_release(&serial_lock);
asm volatile("sti");
}
int serial_received_port(uint16_t port) {
return inb(port + 5) & 1;
}
int serial_received(void) {
if (default_serial_port == 0) return 0;
return serial_received_port(default_serial_port);
}
char serial_read_port(uint16_t port) {
while (serial_received_port(port) == 0);
return inb(port);
}
char serial_read(void) {
if (default_serial_port == 0) return 0;
return serial_read_port(default_serial_port);
}
int serial_is_transmit_empty_port(uint16_t port) {
return inb(port + 5) & 0x20;
}
int serial_is_transmit_empty(void) {
if (default_serial_port == 0) return 1;
return serial_is_transmit_empty_port(default_serial_port);
}
void serial_write_port(uint16_t port, char c) {
while (serial_is_transmit_empty_port(port) == 0);
outb(port, c);
}
void serial_write(char c) {
if (default_serial_port == 0) return;
serial_write_port(default_serial_port, c);
}
void serial_writestring_port(uint16_t port, const char* str) {
while (*str) {
serial_write_port(port, *str++);
}
}
void serial_writestring(const char* str) {
if (default_serial_port == 0) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1))
__asm__ volatile("pause" ::: "memory");
serial_writestring_port(default_serial_port, str);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void serial_writebuf(const char* buf, size_t len) {
if (default_serial_port == 0 || !buf || len == 0) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1))
__asm__ volatile("pause" ::: "memory");
for (size_t i = 0; i < len; i++)
serial_write_port(default_serial_port, buf[i]);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
static void reverse_string(char* str, int length) {
int start = 0;
int end = length - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
}
static void uint_to_str(uint64_t value, char* buffer, int base, bool uppercase) {
char* ptr = buffer;
if (base < 2 || base > 36) {
*ptr = '\0';
return;
}
const char* digits = uppercase ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" :
"0123456789abcdefghijklmnopqrstuvwxyz";
if (value == 0) {
*ptr++ = '0';
*ptr = '\0';
return;
}
while (value > 0) {
int digit = value % base;
*ptr++ = digits[digit];
value /= base;
}
*ptr = '\0';
reverse_string(buffer, ptr - buffer);
}
static void int_to_str(int64_t value, char* buffer, int base, bool uppercase) {
if (value < 0 && (base == 10 || base == 8)) {
buffer[0] = '-';
uint_to_str(-value, buffer + 1, base, uppercase);
} else {
uint_to_str(value, buffer, base, uppercase);
}
}
static uint64_t pow10_u64(int n) {
uint64_t r = 1;
while (n-- > 0) r *= 10;
return r;
}
static int double_to_string(double value, char* buffer, int precision) {
if (isnan(value)) {
strcpy(buffer, "nan");
return 3;
}
if (isinf(value)) {
if (value < 0) {
strcpy(buffer, "-inf");
return 4;
} else {
strcpy(buffer, "inf");
return 3;
}
}
if (precision < 0) precision = 6;
if (precision > 16) precision = 16;
bool negative = (value < 0);
double abs_val = negative ? -value : value;
uint64_t mult = pow10_u64(precision);
double scaled = abs_val * mult + 0.5;
uint64_t int_scaled = (uint64_t)scaled;
uint64_t int_part = int_scaled / mult;
uint64_t frac_part = int_scaled % mult;
char* ptr = buffer;
if (negative) *ptr++ = '-';
char int_buf[32];
char* p = int_buf + sizeof(int_buf) - 1;
*p = '\0';
if (int_part == 0) {
*--p = '0';
} else {
uint64_t n = int_part;
while (n > 0) {
*--p = '0' + (n % 10);
n /= 10;
}
}
size_t int_len = (int_buf + sizeof(int_buf) - 1) - p;
memcpy(ptr, p, int_len);
ptr += int_len;
if (precision > 0) {
*ptr++ = '.';
char frac_buf[32];
char* f = frac_buf + sizeof(frac_buf) - 1;
*f = '\0';
if (frac_part == 0) {
for (int i = 0; i < precision; i++) {
*--f = '0';
}
} else {
uint64_t n = frac_part;
int count = 0;
while (n > 0) {
*--f = '0' + (n % 10);
n /= 10;
count++;
}
while (count < precision) {
*--f = '0';
count++;
}
}
size_t frac_len = (frac_buf + sizeof(frac_buf) - 1) - f;
memcpy(ptr, f, frac_len);
ptr += frac_len;
}
*ptr = '\0';
return ptr - buffer;
}
static int double_to_scientific(double value, char* buffer, int precision, bool uppercase) {
if (isnan(value)) {
strcpy(buffer, "nan");
return 3;
}
if (isinf(value)) {
if (value < 0) {
strcpy(buffer, "-inf");
return 4;
} else {
strcpy(buffer, "inf");
return 3;
}
}
if (value == 0.0) {
if (precision < 0) precision = 6;
buffer[0] = '0';
buffer[1] = '.';
for (int i = 0; i < precision; i++) {
buffer[2 + i] = '0';
}
buffer[2 + precision] = uppercase ? 'E' : 'e';
buffer[3 + precision] = '+';
buffer[4 + precision] = '0';
buffer[5 + precision] = '0';
buffer[6 + precision] = '\0';
return 6 + precision;
}
if (precision < 0) precision = 6;
if (precision > 16) precision = 16;
double abs_val = value < 0 ? -value : value;
int exponent = 0;
if (abs_val >= 10.0) {
while (abs_val >= 10.0) {
abs_val /= 10.0;
exponent++;
}
} else if (abs_val < 1.0 && abs_val > 0.0) {
while (abs_val < 1.0) {
abs_val *= 10.0;
exponent--;
}
}
if (value < 0) abs_val = -abs_val;
int len = double_to_string(abs_val, buffer, precision);
char e_char = uppercase ? 'E' : 'e';
buffer[len++] = e_char;
if (exponent >= 0) {
buffer[len++] = '+';
} else {
buffer[len++] = '-';
exponent = -exponent;
}
if (exponent < 10) {
buffer[len++] = '0';
buffer[len++] = '0' + exponent;
} else {
buffer[len++] = '0' + (exponent / 10);
buffer[len++] = '0' + (exponent % 10);
}
buffer[len] = '\0';
return len;
}
static int double_to_general(double value, char* buffer, int precision, bool uppercase) {
double abs_val = value < 0 ? -value : value;
if (abs_val == 0.0) {
strcpy(buffer, "0");
return 1;
}
if (precision < 0) precision = 6;
if (precision == 0) precision = 1;
bool use_scientific = (abs_val >= 1e6 || (abs_val < 1e-4 && abs_val > 0));
if (use_scientific) {
return double_to_scientific(value, buffer, precision - 1, uppercase);
}
int digits_before = 0;
double temp = abs_val;
while (temp >= 1.0) {
temp /= 10.0;
digits_before++;
}
if (digits_before == 0) digits_before = 1;
int decimal_places = precision - digits_before;
if (decimal_places < 0) decimal_places = 0;
return double_to_string(value, buffer, decimal_places);
}
void serial_printf_port(uint16_t port, const char* format, ...) {
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1)) {
__asm__ volatile("pause" ::: "memory");
}
va_list args;
va_start(args, format);
char buffer[256];
const char* ptr = format;
while (*ptr) {
if (*ptr != '%') {
serial_write_port(port, *ptr);
ptr++;
continue;
}
const char* percent_start = ptr++;
while (*ptr == '0' || *ptr == '-' || *ptr == '+' || *ptr == ' ' || *ptr == '#') {
ptr++;
}
int width = 0;
while (*ptr >= '0' && *ptr <= '9') {
width = width * 10 + (*ptr - '0');
ptr++;
}
int precision = -1;
if (*ptr == '.') {
ptr++;
precision = 0;
while (*ptr >= '0' && *ptr <= '9') {
precision = precision * 10 + (*ptr - '0');
ptr++;
}
}
bool has_ll = false;
bool has_l = false;
bool has_size_t = false;
if (*ptr == 'z') {
ptr++;
has_size_t = true;
} else if (*ptr == 'l') {
ptr++;
if (*ptr == 'l') {
ptr++;
has_ll = true;
} else {
has_l = true;
}
} else if (*ptr == 'h') {
ptr++;
if (*ptr == 'h') ptr++;
} else if (*ptr == 'L' || *ptr == 'j' || *ptr == 't') {
ptr++;
}
switch (*ptr) {
case 'c': {
char c = (char)va_arg(args, int);
serial_write_port(port, c);
break;
}
case 's': {
const char* str = va_arg(args, const char*);
if (!str) {
str = "(null)";
}
serial_writestring_port(port, str);
break;
}
case 'd':
case 'i': {
int64_t num;
if (has_size_t) {
num = (int64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, int64_t);
} else if (has_l) {
num = (int64_t)va_arg(args, long);
} else {
num = (int64_t)va_arg(args, int);
}
int_to_str(num, buffer, 10, false);
serial_writestring_port(port, buffer);
break;
}
case 'u': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 10, false);
serial_writestring_port(port, buffer);
break;
}
case 'x': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, false);
serial_writestring_port(port, buffer);
break;
}
case 'X': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, true);
serial_writestring_port(port, buffer);
break;
}
case 'o': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 8, false);
serial_writestring_port(port, buffer);
break;
}
case 'p': {
void* ptr_val = va_arg(args, void*);
serial_writestring_port(port, "0x");
uint_to_str((uintptr_t)ptr_val, buffer, 16, false);
serial_writestring_port(port, buffer);
break;
}
case 'f':
case 'F': {
double num = va_arg(args, double);
double_to_string(num, buffer, precision);
serial_writestring_port(port, buffer);
break;
}
case 'e':
case 'E': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'E'));
serial_writestring_port(port, buffer);
break;
}
case 'g':
case 'G': {
double num = va_arg(args, double);
double_to_general(num, buffer, precision, (*ptr == 'G'));
serial_writestring_port(port, buffer);
break;
}
case 'a':
case 'A': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'A'));
buffer[0] = '0';
buffer[1] = 'x';
serial_writestring_port(port, buffer);
break;
}
case 'n': {
int* count_ptr = va_arg(args, int*);
*count_ptr = 0;
break;
}
case '%': {
serial_write_port(port, '%');
break;
}
default: {
for (const char* p = percent_start; p <= ptr; p++) {
serial_write_port(port, *p);
}
break;
}
}
if (*ptr != '\0') {
ptr++;
}
}
va_end(args);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void serial_printf(const char* format, ...) {
if (default_serial_port == 0) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
while (__sync_lock_test_and_set(&serial_lock, 1)) {
__asm__ volatile("pause" ::: "memory");
}
va_list args;
va_start(args, format);
char buffer[256];
const char* ptr = format;
while (*ptr) {
if (*ptr != '%') {
serial_write_port(default_serial_port, *ptr);
ptr++;
continue;
}
const char* percent_start = ptr++;
while (*ptr == '0' || *ptr == '-' || *ptr == '+' || *ptr == ' ' || *ptr == '#') {
ptr++;
}
int width = 0;
while (*ptr >= '0' && *ptr <= '9') {
width = width * 10 + (*ptr - '0');
ptr++;
}
int precision = -1;
if (*ptr == '.') {
ptr++;
precision = 0;
while (*ptr >= '0' && *ptr <= '9') {
precision = precision * 10 + (*ptr - '0');
ptr++;
}
}
bool has_ll = false;
bool has_l = false;
bool has_size_t = false;
if (*ptr == 'z') {
ptr++;
has_size_t = true;
} else if (*ptr == 'l') {
ptr++;
if (*ptr == 'l') {
ptr++;
has_ll = true;
} else {
has_l = true;
}
} else if (*ptr == 'h') {
ptr++;
if (*ptr == 'h') ptr++;
} else if (*ptr == 'L' || *ptr == 'j' || *ptr == 't') {
ptr++;
}
switch (*ptr) {
case 'c': {
char c = (char)va_arg(args, int);
serial_write_port(default_serial_port, c);
break;
}
case 's': {
const char* str = va_arg(args, const char*);
if (!str) {
str = "(null)";
}
serial_writestring_port(default_serial_port, str);
break;
}
case 'd':
case 'i': {
int64_t num;
if (has_size_t) {
num = (int64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, int64_t);
} else if (has_l) {
num = (int64_t)va_arg(args, long);
} else {
num = (int64_t)va_arg(args, int);
}
int_to_str(num, buffer, 10, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'u': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 10, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'x': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'X': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 16, true);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'o': {
uint64_t num;
if (has_size_t) {
num = (uint64_t)va_arg(args, size_t);
} else if (has_ll) {
num = va_arg(args, uint64_t);
} else if (has_l) {
num = (uint64_t)va_arg(args, unsigned long);
} else {
num = (uint64_t)va_arg(args, unsigned int);
}
uint_to_str(num, buffer, 8, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'p': {
void* ptr_val = va_arg(args, void*);
serial_writestring_port(default_serial_port, "0x");
uint_to_str((uintptr_t)ptr_val, buffer, 16, false);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'f':
case 'F': {
double num = va_arg(args, double);
double_to_string(num, buffer, precision);
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'e':
case 'E': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'E'));
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'g':
case 'G': {
double num = va_arg(args, double);
double_to_general(num, buffer, precision, (*ptr == 'G'));
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'a':
case 'A': {
double num = va_arg(args, double);
double_to_scientific(num, buffer, precision, (*ptr == 'A'));
buffer[0] = '0';
buffer[1] = 'x';
serial_writestring_port(default_serial_port, buffer);
break;
}
case 'n': {
int* count_ptr = va_arg(args, int*);
*count_ptr = 0;
break;
}
case '%': {
serial_write_port(default_serial_port, '%');
break;
}
default: {
for (const char* p = percent_start; p <= ptr; p++) {
serial_write_port(default_serial_port, *p);
}
break;
}
}
if (*ptr != '\0') {
ptr++;
}
}
va_end(args);
__sync_lock_release(&serial_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
+300
View File
@@ -0,0 +1,300 @@
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <limine.h>
#include "../include/graphics/fb/fb.h"
#include "../include/io/serial.h"
#include "../include/gdt/gdt.h"
#include "../include/interrupts/interrupts.h"
#include "../include/interrupts/idt.h"
#include "../include/sse/fpu.h"
#include "../include/sse/sse.h"
#include "../include/memory/pmm.h"
#include "../include/memory/vmm.h"
#include "../include/memory/paging.h"
#include "../include/acpi/acpi.h"
#include "../include/apic/apic.h"
#include "../include/io/ports.h"
#include "../include/drivers/timer.h"
#include "../include/smp/smp.h"
#include "../include/smp/percpu.h"
#include "../include/sched/sched.h"
#include "../include/elf/elf.h"
#include "../include/syscall/syscall.h"
#include "../include/drivers/ps2.h"
#include "../include/fs/vfs.h"
#include "../include/fs/ramfs.h"
#include "../include/fs/devfs.h"
#include "../include/fs/initramfs.h"
#include "../include/drivers/ata.h"
#include "../include/drivers/blkdev.h"
#include "../include/drivers/disk.h"
#include "../include/drivers/partition.h"
#include "../include/fs/ext2.h"
#include "../include/fs/fat32.h"
__attribute__((used, section(".limine_requests")))
static volatile uint64_t limine_base_revision[] = LIMINE_BASE_REVISION(4);
__attribute__((used, section(".limine_requests")))
static volatile struct limine_framebuffer_request framebuffer_request = {
.id = LIMINE_FRAMEBUFFER_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_memmap_request memmap_request = {
.id = LIMINE_MEMMAP_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_mp_request mp_request = {
.id = LIMINE_MP_REQUEST_ID,
.revision = 0,
.flags = 0
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_hhdm_request hhdm_request = {
.id = LIMINE_HHDM_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests")))
volatile struct limine_rsdp_request rsdp_request = {
.id = LIMINE_RSDP_REQUEST_ID,
.revision = 0,
.response = NULL
};
__attribute__((used, section(".limine_requests")))
static volatile struct limine_module_request module_request = {
.id = LIMINE_MODULE_REQUEST_ID,
.revision = 0
};
__attribute__((used, section(".limine_requests_start")))
static volatile uint64_t limine_requests_start_marker[] = LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end")))
static volatile uint64_t limine_requests_end_marker[] = LIMINE_REQUESTS_END_MARKER;
struct limine_framebuffer *global_framebuffer = NULL;
static void hcf(void) {
for (;;) {
asm ("hlt");
}
}
static void load_elf_module(void) {
if (!module_request.response) {
serial_writestring("[ELF] No module response from Limine\n");
return;
}
if (module_request.response->module_count == 0) {
serial_writestring("[ELF] No modules provided\n");
return;
}
struct limine_file *mod = module_request.response->modules[0];
serial_printf("[ELF] Module: path='%s' size=%llu addr=%p\n", mod->path, mod->size, mod->address);
elf_load_result_t r = elf_load(mod->address, (size_t)mod->size, 0);
if (r.error != ELF_OK) {
serial_printf("[ELF] LOAD FAILED: %s\n", elf_strerror(r.error));
return;
}
serial_printf("[ELF] Load OK! entry=0x%llx stack_top=0x%llx\n", r.entry, r.stack_top);
uint64_t cr3 = (uint64_t)pmm_virt_to_phys(r.pagemap->pml4);
task_t *t = task_create_user("init", r.entry, r.stack_top, cr3, 16, r.pagemap, 0, 0);
if (!t) {
serial_writestring("[ELF] task_create_user failed\n");
elf_unload(&r);
return;
}
t->brk_start = r.load_end;
t->brk_current = r.load_end;
int sr = vfs_init_stdio(t);
if (sr < 0)
serial_printf("[VFS] vfs_init_stdio failed: %d\n", sr);
else
serial_writestring("[VFS] stdio assigned to task\n");
serial_printf("[ELF] Task 'init' created: cr3=0x%llx\n", t->cr3);
}
void ps2_task(void* arg) {
(void)arg;
asm volatile ("sti");
ps2_init();
}
static bool detect_installed_system(void) {
blkdev_t *hda2 = blkdev_get_by_name("hda2");
if (hda2) {
uint16_t magic = 0;
if (blkdev_read(hda2, EXT2_SUPER_OFFSET + 56, &magic, sizeof(magic)) == 0
&& magic == EXT2_SUPER_MAGIC) {
serial_writestring("[boot] ext2 on hda2 -> installed system\n");
return true;
}
}
blkdev_t *hda1 = blkdev_get_by_name("hda1");
if (hda1) {
uint8_t sector[512];
if (hda1->ops->read_sectors(hda1, 0, 1, sector) == 0) {
if (sector[510] == 0x55 && sector[511] == (uint8_t)0xAA
&& memcmp(sector + 82, "FAT32", 5) == 0) {
serial_writestring("[boot] FAT32 on hda1 -> looks like installed system\n");
return true;
}
}
}
blkdev_t *hda = blkdev_get_by_name("hda");
if (hda) {
uint16_t magic = 0;
if (blkdev_read(hda, EXT2_SUPER_OFFSET + 56, &magic, sizeof(magic)) == 0
&& magic == EXT2_SUPER_MAGIC) {
serial_writestring("[boot] legacy ext2-on-whole-disk detected\n");
return true;
}
}
return false;
}
void kernel_main(void) {
serial_initialize(COM1, 115200);
serial_writestring("\n=== SERIAL PORT INITIALIZED ===\n");
if (LIMINE_BASE_REVISION_SUPPORTED(limine_base_revision) == false) {
serial_writestring("ERROR: Unsupported Limine base revision\n");
hcf();
}
gdt_init();
init_interrupt_system();
serial_writestring("GDT&IDT [OK]\n");
fpu_init();
sse_init();
enable_fsgsbase();
serial_writestring("FPU/SSE/FSGSBASE [OK]\n");
if (!framebuffer_request.response ||
framebuffer_request.response->framebuffer_count < 1) {
serial_writestring("ERROR: No framebuffer available\n");
hcf();
}
if (!memmap_request.response) {
serial_writestring("ERROR: No memory map available\n");
hcf();
}
if (!hhdm_request.response) {
serial_writestring("ERROR: No HHDM available\n");
hcf();
}
global_framebuffer = framebuffer_request.response->framebuffers[0];
pmm_init(memmap_request.response, hhdm_request.response);
slab_init();
serial_writestring("PMM [OK]\n");
paging_init();
serial_writestring("Paging [OK]\n");
vmm_init();
serial_writestring("VMM [OK]\n");
vfs_init();
serial_writestring("VFS [OK]\n");
vnode_t *rootfs = ramfs_create_root();
vfs_mount("/", rootfs);
vfs_set_mount_info("/", "ramfs", "ramfs");
vnode_unref(rootfs);
serial_writestring("rootfs [OK]\n");
vfs_mkdir("/dev", 0755);
vfs_mkdir("/bin", 0755);
vfs_mkdir("/etc", 0755);
vfs_mkdir("/tmp", 0755);
vfs_mkdir("/proc", 0755);
vfs_mkdir("/mnt", 0755);
vnode_t *devroot = devfs_create_root();
vfs_mount("/dev", devroot);
vfs_set_mount_info("/dev", "devfs", "devfs");
serial_writestring("devfs [OK]\n");
acpi_init();
acpi_print_tables();
serial_writestring("ACPI [OK]\n");
apic_init();
serial_writestring("APIC [OK]\n");
clear_screen();
smp_init(mp_request.response);
serial_writestring("SMP [OK]\n");
serial_writestring("Waiting until all APs are fully ready...\n");
while (smp_get_online_count() < (smp_get_cpu_count() - 1)) {
timer_sleep_ms(100);
}
serial_writestring("All APs ready.\n");
printf("Kernel initialized successfully!\n\n");
printf("Framebuffer: %dx%d, %d bpp\n", global_framebuffer->width, global_framebuffer->height, global_framebuffer->bpp);
printf("\nMemory Information:\n");
printf("HHDM offset: 0x%llx\n", hhdm_request.response->offset);
printf("Memory map entries: %llu\n", memmap_request.response->entry_count);
pmm_print_stats();
smp_print_info_fb();
printf("\nSystem: %u CPU cores detected\n\n", smp_get_cpu_count());
syscall_init();
disk_init();
serial_writestring("Disk subsystem [OK]\n");
bool skip_initramfs = detect_installed_system();
if (skip_initramfs) {
printf("[boot] installed Cervus detected on disk -- booting from disk\n");
} else {
serial_writestring("[boot] no installed system detected, using initramfs\n");
}
if (!skip_initramfs && module_request.response &&
module_request.response->module_count >= 2) {
struct limine_file *tar = module_request.response->modules[1];
serial_printf("[initramfs] module: '%s' size=%llu\n", tar->path, tar->size);
int r = initramfs_mount(tar->address, (size_t)tar->size);
if (r == 0)
serial_writestring("[initramfs] mounted OK\n");
else
serial_printf("[initramfs] mount FAILED: %d\n", r);
} else if (!skip_initramfs) {
serial_writestring("[initramfs] no TAR module (modules[1] missing)\n");
}
timer_init();
sched_init();
sched_notify_ready();
timer_sleep_ms(10);
clear_screen();
load_elf_module();
task_create("PS/2", ps2_task, NULL, 10);
serial_writestring("Manually triggering first reschedule...\n");
sched_reschedule();
while (1) {
hcf();
}
}
+320
View File
@@ -0,0 +1,320 @@
#include "../../include/memory/paging.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stdio.h>
#define MAX_REGIONS 256
#define MAX_RESERVED 64
#define DEFAULT_ALLOC_BASE 0xFFFFFE0000000000ULL
static paging_region_t regions[MAX_REGIONS];
static size_t region_count = 0;
static struct {
uintptr_t start;
uintptr_t end;
} reserved_ranges[MAX_RESERVED];
static size_t reserved_count = 0;
static uintptr_t next_alloc_virt = DEFAULT_ALLOC_BASE;
static inline uintptr_t align_up(uintptr_t addr, size_t alignment) {
return (addr + alignment - 1) & ~(alignment - 1);
}
static inline bool is_aligned(uintptr_t addr, size_t alignment) {
return (addr & (alignment - 1)) == 0;
}
static inline bool ranges_overlap(uintptr_t s1, uintptr_t e1, uintptr_t s2, uintptr_t e2) {
return s1 < e2 && s2 < e1;
}
void paging_init(void) {
memset(regions, 0, sizeof(regions));
memset(reserved_ranges, 0, sizeof(reserved_ranges));
region_count = 0;
reserved_count = 0;
next_alloc_virt = DEFAULT_ALLOC_BASE;
paging_reserve_range(vmm_get_kernel_pagemap(),
0xFFFFFFFF80000000ULL,
0xFFFFFFFFC0000000ULL);
serial_printf("Paging: subsystem initialized\n");
}
bool paging_reserve_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end) {
(void)pagemap;
if (reserved_count >= MAX_RESERVED) {
serial_printf("PAGING ERROR: too many reserved ranges\n");
return false;
}
if (virt_start >= virt_end) {
serial_printf("PAGING ERROR: invalid reserved range\n");
return false;
}
for (size_t i = 0; i < reserved_count; i++) {
if (ranges_overlap(virt_start, virt_end,
reserved_ranges[i].start,
reserved_ranges[i].end)) {
serial_printf("PAGING ERROR: reserved range overlaps\n");
return false;
}
}
reserved_ranges[reserved_count].start = virt_start;
reserved_ranges[reserved_count].end = virt_end;
reserved_count++;
serial_printf("Paging: reserved range 0x%llx-0x%llx\n", virt_start, virt_end);
return true;
}
static bool is_range_reserved(uintptr_t start, uintptr_t end) {
for (size_t i = 0; i < reserved_count; i++) {
if (ranges_overlap(start, end, reserved_ranges[i].start, reserved_ranges[i].end))
return true;
}
return false;
}
bool paging_is_range_free(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end) {
if (is_range_reserved(virt_start, virt_end))
return false;
for (uintptr_t virt = virt_start; virt < virt_end; virt += PAGE_SIZE) {
uintptr_t phys;
if (vmm_virt_to_phys(pagemap, virt, &phys))
return false;
}
return true;
}
bool paging_map_range(vmm_pagemap_t* pagemap, uintptr_t virt_start,
uintptr_t phys_start, size_t page_count, uint64_t flags) {
if (!pagemap || page_count == 0)
return false;
if (!is_aligned(virt_start, PAGE_SIZE) || !is_aligned(phys_start, PAGE_SIZE)) {
serial_printf("PAGING ERROR: unaligned addresses\n");
return false;
}
uintptr_t virt_end = virt_start + page_count * PAGE_SIZE;
if (is_range_reserved(virt_start, virt_end)) {
serial_printf("PAGING ERROR: range overlaps reserved area\n");
return false;
}
for (size_t i = 0; i < page_count; i++) {
uintptr_t virt = virt_start + i * PAGE_SIZE;
uintptr_t phys = phys_start + i * PAGE_SIZE;
uintptr_t existing_phys;
if (vmm_virt_to_phys(pagemap, virt, &existing_phys)) {
if (existing_phys != phys) {
serial_printf("PAGING ERROR: page at 0x%llx already mapped to 0x%llx\n",
virt, existing_phys);
return false;
}
uint64_t existing_flags;
if (vmm_get_page_flags(pagemap, virt, &existing_flags)) {
if (existing_flags != (flags & 0xFFF)) {
vmm_unmap_page(pagemap, virt);
vmm_map_page(pagemap, virt, phys, flags);
}
}
} else {
if (!vmm_map_page(pagemap, virt, phys, flags)) {
serial_printf("PAGING ERROR: failed to map 0x%llx -> 0x%llx\n", virt, phys);
for (size_t j = 0; j < i; j++)
vmm_unmap_page(pagemap, virt_start + j * PAGE_SIZE);
return false;
}
}
}
return true;
}
bool paging_unmap_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, size_t page_count) {
if (!pagemap || page_count == 0)
return false;
for (size_t i = 0; i < page_count; i++)
vmm_unmap_page(pagemap, virt_start + i * PAGE_SIZE);
return true;
}
bool paging_change_flags(vmm_pagemap_t* pagemap, uintptr_t virt_start,
size_t page_count, uint64_t new_flags) {
if (!pagemap || page_count == 0)
return false;
if (!is_aligned(virt_start, PAGE_SIZE)) {
serial_printf("PAGING ERROR: unaligned address 0x%llx\n", virt_start);
return false;
}
for (size_t i = 0; i < page_count; i++) {
uintptr_t virt = virt_start + i * PAGE_SIZE;
uintptr_t phys;
if (!vmm_virt_to_phys(pagemap, virt, &phys)) {
serial_printf("PAGING ERROR: page at 0x%llx not mapped\n", virt);
return false;
}
vmm_unmap_page(pagemap, virt);
if (!vmm_map_page(pagemap, virt, phys, new_flags)) {
serial_printf("PAGING ERROR: failed to remap 0x%llx\n", virt);
return false;
}
}
return true;
}
paging_region_t* paging_create_region(vmm_pagemap_t* pagemap,
uintptr_t virt_start, size_t size,
uint64_t flags) {
if (region_count >= MAX_REGIONS) {
serial_printf("PAGING ERROR: too many regions\n");
return NULL;
}
size_t page_count = align_up(size, PAGE_SIZE) / PAGE_SIZE;
uintptr_t virt_end = virt_start + page_count * PAGE_SIZE;
if (!paging_is_range_free(pagemap, virt_start, virt_end)) {
serial_printf("PAGING ERROR: region overlaps existing mapping\n");
return NULL;
}
void* phys_mem = pmm_alloc_zero(page_count);
if (!phys_mem) {
serial_printf("PAGING ERROR: out of physical memory\n");
return NULL;
}
uintptr_t phys_start = pmm_virt_to_phys(phys_mem);
if (!paging_map_range(pagemap, virt_start, phys_start, page_count, flags)) {
pmm_free(phys_mem, page_count);
serial_printf("PAGING ERROR: failed to map region\n");
return NULL;
}
paging_region_t* region = &regions[region_count++];
region->virtual_start = virt_start;
region->virtual_end = virt_end;
region->physical_start = phys_start;
region->flags = flags;
region->page_count = page_count;
region->allocated = true;
serial_printf("Paging: created region 0x%llx-0x%llx (%zu pages)\n",
virt_start, virt_end, page_count);
return region;
}
bool paging_destroy_region(vmm_pagemap_t* pagemap, paging_region_t* region) {
if (!pagemap || !region || !region->allocated)
return false;
paging_unmap_range(pagemap, region->virtual_start, region->page_count);
void* phys_virt = pmm_phys_to_virt(region->physical_start);
pmm_free(phys_virt, region->page_count);
memset(region, 0, sizeof(paging_region_t));
return true;
}
void* paging_alloc_pages(vmm_pagemap_t* pagemap, size_t page_count,
uint64_t flags, uintptr_t preferred_virt) {
if (!pagemap || page_count == 0)
return NULL;
uintptr_t virt_start;
if (preferred_virt != 0) {
virt_start = align_up(preferred_virt, PAGE_SIZE);
if (!paging_is_range_free(pagemap, virt_start, virt_start + page_count * PAGE_SIZE))
return NULL;
} else {
virt_start = next_alloc_virt;
while (!paging_is_range_free(pagemap, virt_start, virt_start + page_count * PAGE_SIZE))
virt_start += PAGE_SIZE;
next_alloc_virt = virt_start + page_count * PAGE_SIZE;
}
void* phys_mem = pmm_alloc_zero(page_count);
if (!phys_mem)
return NULL;
uintptr_t phys_start = pmm_virt_to_phys(phys_mem);
if (!paging_map_range(pagemap, virt_start, phys_start, page_count, flags)) {
pmm_free(phys_mem, page_count);
return NULL;
}
serial_printf("Paging: allocated %zu pages at virt 0x%llx\n", page_count, virt_start);
return (void*)virt_start;
}
void paging_free_pages(vmm_pagemap_t* pagemap, void* virt_addr, size_t page_count) {
if (!pagemap || !virt_addr || page_count == 0)
return;
uintptr_t virt_start = (uintptr_t)virt_addr;
paging_unmap_range(pagemap, virt_start, page_count);
serial_printf("Paging: freed %zu pages at virt 0x%llx\n", page_count, virt_start);
}
void paging_print_stats(vmm_pagemap_t* pagemap) {
(void)pagemap;
serial_printf("\n=== Paging Statistics ===\n");
serial_printf("Regions: %zu, Reserved: %zu\n", region_count, reserved_count);
size_t active = 0, total_pages = 0, total_bytes = 0;
for (size_t i = 0; i < region_count; i++) {
if (regions[i].allocated) {
active++;
total_pages += regions[i].page_count;
total_bytes += regions[i].page_count * PAGE_SIZE;
}
}
serial_printf("Active regions: %zu\n", active);
serial_printf("Total pages: %zu (%zu KiB)\n", total_pages, total_bytes / 1024);
serial_printf("Next alloc: 0x%llx\n", next_alloc_virt);
serial_printf("\nReserved ranges:\n");
for (size_t i = 0; i < reserved_count; i++) {
serial_printf(" 0x%llx - 0x%llx\n",
reserved_ranges[i].start, reserved_ranges[i].end);
}
}
void paging_dump_range(vmm_pagemap_t* pagemap, uintptr_t virt_start, uintptr_t virt_end) {
serial_printf("\n=== Page Table Dump (0x%llx - 0x%llx) ===\n", virt_start, virt_end);
size_t mapped = 0, total = 0;
for (uintptr_t virt = virt_start; virt < virt_end; virt += PAGE_SIZE) {
total++;
uintptr_t phys;
uint64_t flags;
if (vmm_virt_to_phys(pagemap, virt, &phys) && vmm_get_page_flags(pagemap, virt, &flags)) {
mapped++;
serial_printf("0x%llx -> 0x%llx [", virt, phys);
if (flags & PAGING_PRESENT) serial_printf("P");
if (flags & PAGING_WRITE) serial_printf("W");
if (flags & PAGING_USER) serial_printf("U");
if (flags & PAGING_NOEXEC) serial_printf("NX");
serial_printf("]\n");
}
}
serial_printf("Mapped: %zu/%zu pages (%zu%%)\n", mapped, total, total ? mapped * 100 / total : 0);
}
+614
View File
@@ -0,0 +1,614 @@
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include "../../include/sched/spinlock.h"
#include <string.h>
#include <stdio.h>
static pmm_buddy_state_t g_buddy;
static spinlock_t g_pmm_lock = SPINLOCK_INIT;
static inline uintptr_t _align_up(uintptr_t v, uintptr_t a) {
return (v + a - 1) & ~(a - 1);
}
static inline int _pages_to_order(size_t pages) {
int order = 0;
size_t cap = 1;
while (cap < pages) { cap <<= 1; order++; }
return (order > PMM_MAX_ORDER) ? -1 : order;
}
static inline uintptr_t _block_phys(pmm_block_t *b) {
return (uintptr_t)b - g_buddy.hhdm_offset;
}
static inline pmm_block_t *_phys_to_block(uintptr_t phys) {
return (pmm_block_t *)(phys + g_buddy.hhdm_offset);
}
static inline void _fl_init(pmm_free_list_t *fl) {
fl->head.next = fl->head.prev = &fl->head;
fl->head.order = -1;
fl->count = 0;
}
static inline void _fl_push(pmm_free_list_t *fl, pmm_block_t *b, int order) {
b->order = order;
b->next = fl->head.next;
b->prev = &fl->head;
fl->head.next->prev = b;
fl->head.next = b;
fl->count++;
}
static inline void _fl_del(pmm_free_list_t *fl, pmm_block_t *b) {
b->prev->next = b->next;
b->next->prev = b->prev;
b->next = b->prev = NULL;
b->order = -1;
fl->count--;
}
static inline pmm_block_t *_fl_first(pmm_free_list_t *fl) {
return (fl->head.next == &fl->head) ? NULL : fl->head.next;
}
static inline int _block_ptr_valid(pmm_free_list_t *fl, pmm_block_t *b) {
uintptr_t p = (uintptr_t)b;
if (p == (uintptr_t)&fl->head) return 1;
if (p < g_buddy.hhdm_offset) return 0;
uintptr_t phys = p - g_buddy.hhdm_offset;
if (phys < g_buddy.mem_start || phys >= g_buddy.mem_end) return 0;
if (phys & 0xFFFULL) return 0;
return 1;
}
static pmm_block_t *_fl_find(pmm_free_list_t *fl, uintptr_t phys) {
size_t limit = g_buddy.total_pages + 16;
pmm_block_t *b = fl->head.next;
while (b != &fl->head) {
if (!_block_ptr_valid(fl, b)) {
serial_printf("[PMM_FREELIST_CORRUPT] bad ptr=%p in freelist walk\n", (void*)b);
return NULL;
}
if (_block_phys(b) == phys) return b;
pmm_block_t *next = b->next;
if (!_block_ptr_valid(fl, next)) {
serial_printf("[PMM_FREELIST_CORRUPT] bad next=%p at block=%p (phys=0x%llx)\n",
(void*)next, (void*)b,
(unsigned long long)_block_phys(b));
return NULL;
}
b = next;
if (limit-- == 0) return NULL;
}
return NULL;
}
static uintptr_t _buddy_alloc_order(int order) {
int found = -1;
for (int o = order; o <= PMM_MAX_ORDER; o++)
if (_fl_first(&g_buddy.orders[o])) { found = o; break; }
if (found < 0) return 0;
pmm_block_t *b = _fl_first(&g_buddy.orders[found]);
_fl_del(&g_buddy.orders[found], b);
uintptr_t phys = _block_phys(b);
while (found > order) {
found--;
uintptr_t buddy_phys = phys + ((uintptr_t)PAGE_SIZE << found);
_fl_push(&g_buddy.orders[found], _phys_to_block(buddy_phys), found);
}
g_buddy.free_pages -= (size_t)1 << order;
return phys;
}
static void _buddy_free_order(uintptr_t phys, int order) {
if (phys < PMM_FREE_MIN_PHYS) return;
while (order < PMM_MAX_ORDER) {
uintptr_t buddy_phys = phys ^ ((uintptr_t)PAGE_SIZE << order);
if (buddy_phys < PMM_FREE_MIN_PHYS) break;
if (buddy_phys < g_buddy.mem_start || buddy_phys >= g_buddy.mem_end)
break;
pmm_block_t *buddy_b = _fl_find(&g_buddy.orders[order], buddy_phys);
if (!buddy_b) break;
if (buddy_b->order != order) break;
_fl_del(&g_buddy.orders[order], buddy_b);
if (buddy_phys < phys) phys = buddy_phys;
order++;
}
_fl_push(&g_buddy.orders[order], _phys_to_block(phys), order);
g_buddy.free_pages += (size_t)1 << order;
}
static void _buddy_free_nocoalesce(uintptr_t phys) {
_fl_push(&g_buddy.orders[0], _phys_to_block(phys), 0);
g_buddy.free_pages += 1;
}
void pmm_init(struct limine_memmap_response *memmap,
struct limine_hhdm_response *hhdm) {
g_buddy.hhdm_offset = hhdm->offset;
g_buddy.free_pages = 0;
uintptr_t max_phys = 0;
size_t usable_pages = 0;
for (uint64_t i = 0; i < memmap->entry_count; i++) {
struct limine_memmap_entry *e = memmap->entries[i];
if (e->type == LIMINE_MEMMAP_USABLE) {
uintptr_t end = e->base + e->length;
if (end > max_phys) max_phys = end;
usable_pages += e->length / PAGE_SIZE;
}
}
max_phys = _align_up(max_phys, PAGE_SIZE);
g_buddy.mem_start = 0;
g_buddy.mem_end = max_phys;
g_buddy.total_pages = max_phys >> PAGE_SHIFT;
g_buddy.usable_pages = usable_pages;
for (int o = 0; o < PMM_MAX_ORDER_NR; o++) _fl_init(&g_buddy.orders[o]);
for (uint64_t i = 0; i < memmap->entry_count; i++) {
struct limine_memmap_entry *e = memmap->entries[i];
if (e->type != LIMINE_MEMMAP_USABLE) continue;
uintptr_t base = _align_up(e->base, PAGE_SIZE);
uintptr_t end = (e->base + e->length) & ~(PAGE_SIZE - 1);
if (base < PMM_FREE_MIN_PHYS) {
base = PMM_FREE_MIN_PHYS;
if (base >= end) continue;
}
while (base < end) {
size_t rem = (end - base) >> PAGE_SHIFT;
int order = PMM_MAX_ORDER;
while (order > 0) {
size_t bpages = (size_t)1 << order;
if ((base & (bpages * PAGE_SIZE - 1)) == 0 && rem >= bpages)
break;
order--;
}
_fl_push(&g_buddy.orders[order], _phys_to_block(base), order);
g_buddy.free_pages += (size_t)1 << order;
base += (uintptr_t)(PAGE_SIZE << order);
}
}
serial_printf("[PMM] buddy init: total=%zu free=%zu\n",
g_buddy.total_pages, g_buddy.free_pages);
}
static volatile uint64_t g_pmm_alloc_count = 0;
static volatile uint64_t g_pmm_free_count = 0;
void *pmm_alloc(size_t pages) {
if (!pages) return NULL;
int order = _pages_to_order(pages);
if (order < 0) return NULL;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
uintptr_t phys = _buddy_alloc_order(order);
size_t free_after = g_buddy.free_pages;
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
if (phys) {
if (phys < PMM_FREE_MIN_PHYS) {
serial_printf("[PMM_BUG] alloc returned low phys=0x%llx — discarding (should not happen after pmm_init fix)\n",
(unsigned long long)phys);
phys = 0;
} else {
__atomic_fetch_add(&g_pmm_alloc_count, (size_t)1 << order, __ATOMIC_RELAXED);
}
}
if (!phys) {
serial_printf("[PMM_OOM] alloc FAILED pages=%zu order=%d free=%zu allocs=%llu frees=%llu\n",
pages, order, free_after,
(unsigned long long)g_pmm_alloc_count,
(unsigned long long)g_pmm_free_count);
}
return phys ? (void *)(phys + g_buddy.hhdm_offset) : NULL;
}
void *pmm_alloc_zero(size_t pages) {
if (!pages) return NULL;
int order = _pages_to_order(pages);
if (order < 0) return NULL;
void *p = pmm_alloc(pages);
if (p) memset(p, 0, pages * PAGE_SIZE);
return p;
}
void *pmm_alloc_aligned(size_t pages, size_t alignment) {
if (!pages) return NULL;
if (alignment < PAGE_SIZE) alignment = PAGE_SIZE;
if (alignment & (alignment - 1)) {
size_t a = 1;
while (a < alignment) a <<= 1;
alignment = a;
}
size_t align_pages = alignment / PAGE_SIZE;
size_t req = pages > align_pages ? pages : align_pages;
int order = _pages_to_order(req);
if (order < 0) return NULL;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
uintptr_t phys = _buddy_alloc_order(order);
if (!phys) { spinlock_release(&g_pmm_lock); asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); return NULL; }
if (phys & (alignment - 1)) {
_buddy_free_order(phys, order);
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return NULL;
}
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return (void *)(phys + g_buddy.hhdm_offset);
}
void pmm_free(void *addr, size_t pages) {
if (!addr || !pages) return;
uintptr_t phys = (uintptr_t)addr - g_buddy.hhdm_offset;
if (phys < g_buddy.mem_start || phys >= g_buddy.mem_end) {
serial_printf("[PMM_FREE_BUG] out-of-range addr=%p phys=0x%llx pages=%zu\n",
addr, (unsigned long long)phys, pages);
return;
}
if (phys < 0x100000ULL) {
serial_printf("[PMM_FREE_BUG] low-phys addr=%p phys=0x%llx pages=%zu\n",
addr, (unsigned long long)phys, pages);
return;
}
int order = _pages_to_order(pages);
if (order < 0) { serial_printf("[PMM_FREE] bad order pages=%zu\n", pages); return; }
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
pmm_block_t *existing = _fl_find(&g_buddy.orders[order], phys);
if (existing) {
serial_printf("[PMM_DOUBLE_FREE] addr=%p phys=0x%llx pages=%zu order=%d ALREADY FREE!\n",
addr, (unsigned long long)phys, pages, order);
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return;
}
size_t free_before = g_buddy.free_pages;
memset(addr, 0, 64);
_buddy_free_order(phys, order);
size_t free_after = g_buddy.free_pages;
__atomic_fetch_add(&g_pmm_free_count, (size_t)1 << order, __ATOMIC_RELAXED);
if (free_after <= free_before) {
serial_printf("[PMM_FREE_BUG] free_pages didn't grow: before=%zu after=%zu phys=0x%llx\n",
free_before, free_after, (unsigned long long)phys);
}
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void pmm_free_single(void *addr) {
if (!addr) return;
uintptr_t phys = (uintptr_t)addr - g_buddy.hhdm_offset;
if (phys < g_buddy.mem_start || phys >= g_buddy.mem_end) return;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_pmm_lock);
_buddy_free_nocoalesce(phys);
spinlock_release(&g_pmm_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void *pmm_phys_to_virt(uintptr_t phys) { return (void *)(phys + g_buddy.hhdm_offset); }
uintptr_t pmm_virt_to_phys(void *vaddr) { return (uintptr_t)vaddr - g_buddy.hhdm_offset; }
uint64_t pmm_get_hhdm_offset(void) { return g_buddy.hhdm_offset; }
size_t pmm_get_total_pages(void) { return g_buddy.total_pages; }
size_t pmm_get_usable_pages(void) { return g_buddy.usable_pages; }
size_t pmm_get_free_pages(void) { return g_buddy.free_pages; }
size_t pmm_get_used_pages(void) { return g_buddy.usable_pages > g_buddy.free_pages
? g_buddy.usable_pages - g_buddy.free_pages : 0; }
static void _print_size(size_t bytes, const char *label) {
uint64_t v = (uint64_t)bytes;
const char *unit;
uint64_t whole, frac;
if (v >= 1024ULL * 1024 * 1024) {
whole = v / (1024ULL * 1024 * 1024);
frac = (v % (1024ULL * 1024 * 1024)) * 100 / (1024ULL * 1024 * 1024);
unit = "GiB";
} else if (v >= 1024ULL * 1024) {
whole = v / (1024ULL * 1024);
frac = (v % (1024ULL * 1024)) * 100 / (1024ULL * 1024);
unit = "MiB";
} else if (v >= 1024ULL) {
whole = v / 1024;
frac = (v % 1024) * 100 / 1024;
unit = "KiB";
} else {
whole = v; frac = 0; unit = "B";
}
printf(" %-16s %llu.%02llu %s\n", label,
(unsigned long long)whole, (unsigned long long)frac, unit);
serial_printf(" %-16s %llu.%02llu %s\n", label,
(unsigned long long)whole, (unsigned long long)frac, unit);
}
void pmm_print_stats(void) {
size_t usable_bytes = g_buddy.usable_pages * PAGE_SIZE;
size_t free_bytes = g_buddy.free_pages * PAGE_SIZE;
size_t total_bytes = g_buddy.total_pages * PAGE_SIZE;
size_t used_bytes = usable_bytes > free_bytes ? usable_bytes - free_bytes : 0;
size_t reserved = total_bytes > usable_bytes ? total_bytes - usable_bytes : 0;
serial_printf("\n=== Physical Memory ===\n");
printf("\n=== Physical Memory ===\n");
_print_size(usable_bytes, "Usable RAM:");
_print_size(free_bytes, "Free RAM:");
_print_size(used_bytes, "Used (kernel):");
_print_size(reserved, "Reserved/MMIO:");
serial_printf(" %-16s %u bytes\n", "Page size:", (unsigned)PAGE_SIZE);
printf(" %-16s %u bytes\n", "Page size:", (unsigned)PAGE_SIZE);
serial_printf("=======================\n");
printf("=======================\n");
serial_printf("[PMM] buddy free-list:\n");
for (int o = 0; o < PMM_MAX_ORDER_NR; o++) {
if (g_buddy.orders[o].count)
serial_printf(" order %2d (%4zu KiB): %zu blocks\n",
o, (PAGE_SIZE << o) / 1024,
g_buddy.orders[o].count);
}
}
#define SLAB_PAGE_ALLOC(n) pmm_alloc_zero(n)
#define SLAB_PAGE_FREE(p, n) pmm_free(p, n)
static const size_t g_size_classes[SLAB_NUM_CACHES] = {
8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096
};
slab_cache_t g_caches[SLAB_NUM_CACHES];
static inline uintptr_t _slab_obj_start(slab_t *s) {
return _align_up((uintptr_t)s + sizeof(slab_t), 8);
}
static inline size_t _slab_pages(size_t obj_size) {
uintptr_t hdr_end = _align_up(sizeof(slab_t), 8);
if (obj_size <= PAGE_SIZE - hdr_end)
return 1;
if (obj_size <= PAGE_SIZE)
return 2;
return 0;
}
static inline uint16_t _slab_capacity(size_t obj_size) {
size_t pages = _slab_pages(obj_size);
if (pages == 0) return 0;
uintptr_t obj_start = _align_up(sizeof(slab_t), 8);
size_t avail = pages * PAGE_SIZE - obj_start;
uint16_t cap = (uint16_t)(avail / obj_size);
return cap > 0 ? cap : 0;
}
static void _slab_list_push(slab_t **head, slab_t *s) {
s->next = *head; s->prev = NULL;
if (*head) (*head)->prev = s;
*head = s;
}
static void _slab_list_remove(slab_t **head, slab_t *s) {
if (s->prev) s->prev->next = s->next;
else *head = s->next;
if (s->next) s->next->prev = s->prev;
s->next = s->prev = NULL;
}
static slab_t *_slab_new(slab_cache_t *cache) {
uint16_t cap = _slab_capacity(cache->obj_size);
if (!cap) return NULL;
size_t pages = _slab_pages(cache->obj_size);
slab_t *s = (slab_t *)SLAB_PAGE_ALLOC(pages);
if (!s) return NULL;
s->obj_size = (uint16_t)cache->obj_size;
s->total = cap;
s->used = 0;
s->next = s->prev = NULL;
uintptr_t start = _slab_obj_start(s);
s->freelist = (void *)start;
for (uint16_t i = 0; i < s->total; i++) {
void **slot = (void **)(start + (uintptr_t)i * cache->obj_size);
*slot = (i + 1 < s->total)
? (void *)(start + (uintptr_t)(i + 1) * cache->obj_size)
: NULL;
}
return s;
}
#define LARGE_ALLOC_MAGIC 0xDEADBEEFCAFEBABEULL
typedef struct {
uint64_t magic;
uint64_t pages;
} large_hdr_t;
static inline bool _is_large_alloc(void *ptr) {
large_hdr_t *hdr = (large_hdr_t *)ptr - 1;
return hdr->magic == LARGE_ALLOC_MAGIC;
}
static slab_cache_t *_cache_for(size_t size) {
for (int i = 0; i < SLAB_NUM_CACHES; i++)
if (g_caches[i].obj_size >= size) return &g_caches[i];
return NULL;
}
void slab_init(void) {
for (int i = 0; i < SLAB_NUM_CACHES; i++) {
g_caches[i].obj_size = g_size_classes[i];
g_caches[i].partial = NULL;
g_caches[i].full = NULL;
g_caches[i].total_allocs = 0;
g_caches[i].total_frees = 0;
}
serial_printf("[PMM] slab init: %d caches, sizes 8..4096 bytes\n",
SLAB_NUM_CACHES);
}
static spinlock_t g_slab_lock = SPINLOCK_INIT;
void *kmalloc(size_t size) {
if (!size) return NULL;
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_slab_lock);
void *result;
if (size > SLAB_MAX_SIZE) {
size_t pages = (size + sizeof(large_hdr_t) + PAGE_SIZE - 1) / PAGE_SIZE;
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
large_hdr_t *hdr = (large_hdr_t *)SLAB_PAGE_ALLOC(pages);
if (!hdr) return NULL;
hdr->magic = LARGE_ALLOC_MAGIC;
hdr->pages = (uint64_t)pages;
return (void *)(hdr + 1);
}
slab_cache_t *cache = _cache_for(size);
if (!cache) { spinlock_release(&g_slab_lock); asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); return NULL; }
if (!cache->partial) {
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
slab_t *s = _slab_new(cache);
if (!s) return NULL;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_slab_lock);
_slab_list_push(&cache->partial, s);
}
slab_t *s = cache->partial;
void *obj = s->freelist;
s->freelist = *(void **)obj;
s->used++;
cache->total_allocs++;
if (s->used == s->total) {
_slab_list_remove(&cache->partial, s);
_slab_list_push(&cache->full, s);
}
result = obj;
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
return result;
}
void *kzalloc(size_t size) {
void *p = kmalloc(size);
if (p) memset(p, 0, size);
return p;
}
void kfree(void *ptr) {
if (!ptr) return;
if (_is_large_alloc(ptr)) {
large_hdr_t *hdr = (large_hdr_t *)ptr - 1;
size_t pages = (size_t)hdr->pages;
hdr->magic = 0;
SLAB_PAGE_FREE(hdr, pages);
return;
}
uint64_t flags;
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
spinlock_acquire(&g_slab_lock);
slab_t *s = (slab_t *)((uintptr_t)ptr & ~((uintptr_t)PAGE_SIZE - 1));
slab_cache_t *cache = NULL;
for (int i = 0; i < SLAB_NUM_CACHES; i++) {
if (g_caches[i].obj_size == s->obj_size) {
cache = &g_caches[i];
break;
}
}
if (!cache) { spinlock_release(&g_slab_lock); asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc"); return; }
bool was_full = (s->used == s->total);
*(void **)ptr = s->freelist;
s->freelist = ptr;
s->used--;
cache->total_frees++;
if (was_full) {
_slab_list_remove(&cache->full, s);
_slab_list_push(&cache->partial, s);
}
if (s->used == 0) {
_slab_list_remove(&cache->partial, s);
size_t pages = _slab_pages(s->obj_size);
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
SLAB_PAGE_FREE(s, pages > 0 ? pages : 1);
return;
}
spinlock_release(&g_slab_lock);
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
}
void *krealloc(void *ptr, size_t new_size) {
if (!ptr) return kmalloc(new_size);
if (!new_size) { kfree(ptr); return NULL; }
size_t old_size;
if (_is_large_alloc(ptr)) {
large_hdr_t *hdr = (large_hdr_t *)ptr - 1;
old_size = (size_t)hdr->pages * PAGE_SIZE - sizeof(large_hdr_t);
} else {
slab_t *s = (slab_t *)((uintptr_t)ptr & ~((uintptr_t)PAGE_SIZE - 1));
old_size = s->obj_size;
}
void *np = kmalloc(new_size);
if (!np) return NULL;
memcpy(np, ptr, old_size < new_size ? old_size : new_size);
kfree(ptr);
return np;
}
void slab_print_stats(void) {
serial_printf("[PMM] slab stats:\n");
for (int i = 0; i < SLAB_NUM_CACHES; i++) {
slab_cache_t *c = &g_caches[i];
size_t np = 0, nf = 0;
for (slab_t *s = c->partial; s && np < 10000; s = s->next) np++;
for (slab_t *s = c->full; s && nf < 10000; s = s->next) nf++;
serial_printf(" [%4zu B] partial=%zu full=%zu allocs=%zu frees=%zu\n",
c->obj_size, np, nf, c->total_allocs, c->total_frees);
}
}
+402
View File
@@ -0,0 +1,402 @@
#include "../../include/memory/vmm.h"
#include "../../include/memory/pmm.h"
#include "../../include/smp/smp.h"
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include <stdio.h>
#include <string.h>
#define KERNEL_TEST_BASE 0xFFFF800000100000ULL
#define PTE_PHYS_MASK 0x000FFFFFFFFFF000ULL
#define MASK 0x1FF
static vmm_pagemap_t kernel_pagemap;
static inline void invlpg(void* addr) {
asm volatile ("invlpg (%0)" :: "r"(addr) : "memory");
}
static vmm_pte_t* alloc_table(void) {
void* page = pmm_alloc_zero(1);
if (!page) {
serial_printf("[VMM] alloc_table: OUT OF MEMORY (pmm_alloc_zero(1) returned NULL)!\n");
serial_printf("[VMM] free_pages=%zu\n", (size_t)pmm_get_free_pages());
for (;;) asm volatile ("hlt");
}
return (vmm_pte_t*)page;
}
static vmm_pte_t* get_table(vmm_pte_t* parent, size_t index, uint64_t flags) {
if (!(parent[index] & VMM_PRESENT)) {
vmm_pte_t* table = alloc_table();
uintptr_t table_phys = pmm_virt_to_phys(table);
parent[index] = table_phys
| VMM_PRESENT
| VMM_WRITE
| (flags & VMM_USER);
} else if (flags & VMM_USER) {
parent[index] |= VMM_USER;
}
return (vmm_pte_t*)pmm_phys_to_virt(parent[index] & PTE_PHYS_MASK);
}
bool vmm_map_page(vmm_pagemap_t* map, uintptr_t virt, uintptr_t phys, uint64_t flags) {
if (!map || !map->pml4) {
serial_printf("[VMM_BUG] vmm_map_page: map=%p pml4=%p virt=0x%llx\n",
(void*)map, map ? (void*)map->pml4 : NULL,
(unsigned long long)virt);
return false;
}
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
vmm_pte_t* pdpt = get_table(map->pml4, pml4_i, flags);
vmm_pte_t* pd = get_table(pdpt, pdpt_i, flags);
vmm_pte_t* pt = get_table(pd, pd_i, flags);
pt[pt_i] = (phys & PTE_PHYS_MASK) | (flags | VMM_PRESENT);
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
invlpg((void*)virt);
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
return true;
}
void vmm_unmap_page_noflush(vmm_pagemap_t* map, uintptr_t virt) {
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
pt[pt_i] = 0;
}
void vmm_unmap_page(vmm_pagemap_t* map, uintptr_t virt) {
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
if (!(pt[pt_i] & VMM_PRESENT)) return;
uintptr_t phys = pt[pt_i] & PTE_PHYS_MASK;
pt[pt_i] = 0;
asm volatile ("lock addl $0, (%%rsp)" ::: "memory", "cc");
invlpg((void*)virt);
if (smp_get_cpu_count() > 1 && (virt >= 0xffff800000000000ULL)) {
ipi_tlb_shootdown_broadcast(&virt, 1);
}
if (phys >= PMM_FREE_MIN_PHYS) {
pmm_free(pmm_phys_to_virt(phys), 1);
}
}
vmm_pagemap_t* vmm_create_pagemap(void) {
vmm_pagemap_t* map = pmm_alloc_zero(1);
if (!map) {
serial_printf("[VMM] vmm_create_pagemap: pmm_alloc_zero for map failed\n");
return NULL;
}
map->pml4 = alloc_table();
if (!map->pml4) {
serial_printf("[VMM] vmm_create_pagemap: alloc_table for pml4 failed\n");
pmm_free(map, 1);
return NULL;
}
for (size_t i = 256; i < 512; i++) {
map->pml4[i] = kernel_pagemap.pml4[i];
}
return map;
}
void vmm_switch_pagemap(vmm_pagemap_t* map) {
uintptr_t phys = pmm_virt_to_phys(map->pml4);
asm volatile ("mov %0, %%cr3" :: "r"(phys) : "memory");
}
bool vmm_virt_to_phys(vmm_pagemap_t* map, uintptr_t virt, uintptr_t* phys_out) {
if (!map || !phys_out) {
serial_printf("VMM_VIRT_TO_PHYS ERROR: null parameters\n");
return false;
}
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return false;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return false;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return false;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
if (!(pt[pt_i] & VMM_PRESENT)) return false;
*phys_out = (pt[pt_i] & PTE_PHYS_MASK) | (virt & 0xFFF);
return true;
}
bool vmm_get_page_flags(vmm_pagemap_t* map, uintptr_t virt, uint64_t* flags_out) {
if (!map || !flags_out) return false;
size_t pml4_i = (virt >> 39) & MASK;
size_t pdpt_i = (virt >> 30) & MASK;
size_t pd_i = (virt >> 21) & MASK;
size_t pt_i = (virt >> 12) & MASK;
if (!(map->pml4[pml4_i] & VMM_PRESENT)) return false;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
if (!(pdpt[pdpt_i] & VMM_PRESENT)) return false;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
if (!(pd[pd_i] & VMM_PRESENT)) return false;
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
if (!(pt[pt_i] & VMM_PRESENT)) return false;
*flags_out = pt[pt_i] & (0xFFF | (1ULL << 63));
return true;
}
vmm_pagemap_t* vmm_clone_pagemap(vmm_pagemap_t* src) {
if (!src) return NULL;
vmm_pagemap_t* dst = pmm_alloc_zero(1);
if (!dst) return NULL;
dst->pml4 = alloc_table();
for (size_t i = 256; i < 512; i++)
dst->pml4[i] = kernel_pagemap.pml4[i];
for (size_t pml4_i = 0; pml4_i < 256; pml4_i++) {
if (!(src->pml4[pml4_i] & VMM_PRESENT)) continue;
vmm_pte_t* src_pdpt = (vmm_pte_t*)pmm_phys_to_virt(src->pml4[pml4_i] & PTE_PHYS_MASK);
vmm_pte_t* dst_pdpt = alloc_table();
dst->pml4[pml4_i] = pmm_virt_to_phys(dst_pdpt) | (src->pml4[pml4_i] & 0xFFF);
for (size_t pdpt_i = 0; pdpt_i < 512; pdpt_i++) {
if (!(src_pdpt[pdpt_i] & VMM_PRESENT)) continue;
vmm_pte_t* src_pd = (vmm_pte_t*)pmm_phys_to_virt(src_pdpt[pdpt_i] & PTE_PHYS_MASK);
vmm_pte_t* dst_pd = alloc_table();
dst_pdpt[pdpt_i] = pmm_virt_to_phys(dst_pd) | (src_pdpt[pdpt_i] & 0xFFF);
for (size_t pd_i = 0; pd_i < 512; pd_i++) {
if (!(src_pd[pd_i] & VMM_PRESENT)) continue;
if (src_pd[pd_i] & VMM_PSE) {
void* new_hp = pmm_alloc(512);
if (!new_hp) continue;
void* old_hp = pmm_phys_to_virt(src_pd[pd_i] & PTE_PHYS_MASK & ~0x1FFFFFULL);
memcpy(new_hp, old_hp, 512 * 0x1000);
uint64_t hp_flags = src_pd[pd_i] & ~(PTE_PHYS_MASK & ~0x1FFFFFULL);
dst_pd[pd_i] = (pmm_virt_to_phys(new_hp) & (PTE_PHYS_MASK & ~0x1FFFFFULL))
| hp_flags;
continue;
}
vmm_pte_t* src_pt = (vmm_pte_t*)pmm_phys_to_virt(src_pd[pd_i] & PTE_PHYS_MASK);
vmm_pte_t* dst_pt = alloc_table();
dst_pd[pd_i] = pmm_virt_to_phys(dst_pt) | (src_pd[pd_i] & 0xFFF);
for (size_t pt_i = 0; pt_i < 512; pt_i++) {
if (!(src_pt[pt_i] & VMM_PRESENT)) continue;
uintptr_t src_phys = src_pt[pt_i] & PTE_PHYS_MASK;
if (src_phys < PMM_FREE_MIN_PHYS) continue;
void* new_page = pmm_alloc_zero(1);
if (!new_page) continue;
void* old_page = pmm_phys_to_virt(src_phys);
memcpy(new_page, old_page, 0x1000);
dst_pt[pt_i] = pmm_virt_to_phys(new_page)
| (src_pt[pt_i] & (0xFFF | (1ULL << 63)));
}
}
}
}
return dst;
}
void vmm_free_pagemap(vmm_pagemap_t* map)
{
if (!map || !map->pml4) return;
for (size_t pml4_i = 0; pml4_i < 256; pml4_i++) {
if (!(map->pml4[pml4_i] & VMM_PRESENT)) continue;
vmm_pte_t* pdpt = (vmm_pte_t*)pmm_phys_to_virt(map->pml4[pml4_i] & PTE_PHYS_MASK);
for (size_t pdpt_i = 0; pdpt_i < 512; pdpt_i++) {
if (!(pdpt[pdpt_i] & VMM_PRESENT)) continue;
vmm_pte_t* pd = (vmm_pte_t*)pmm_phys_to_virt(pdpt[pdpt_i] & PTE_PHYS_MASK);
for (size_t pd_i = 0; pd_i < 512; pd_i++) {
if (!(pd[pd_i] & VMM_PRESENT)) continue;
if (pd[pd_i] & VMM_PSE) {
uintptr_t hp_phys = pd[pd_i] & PTE_PHYS_MASK & ~0x1FFFFFULL;
if (hp_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pmm_phys_to_virt(hp_phys), 512);
continue;
}
vmm_pte_t* pt = (vmm_pte_t*)pmm_phys_to_virt(pd[pd_i] & PTE_PHYS_MASK);
for (size_t pt_i = 0; pt_i < 512; pt_i++) {
if (!(pt[pt_i] & VMM_PRESENT)) continue;
uintptr_t phys = pt[pt_i] & PTE_PHYS_MASK;
if (phys >= PMM_FREE_MIN_PHYS)
pmm_free(pmm_phys_to_virt(phys), 1);
}
uintptr_t pt_phys = pd[pd_i] & PTE_PHYS_MASK;
if (pt_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pt, 1);
}
uintptr_t pd_phys = pdpt[pdpt_i] & PTE_PHYS_MASK;
if (pd_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pd, 1);
}
uintptr_t pdpt_phys = map->pml4[pml4_i] & PTE_PHYS_MASK;
if (pdpt_phys >= PMM_FREE_MIN_PHYS)
pmm_free(pdpt, 1);
}
pmm_free(map->pml4, 1);
pmm_free(map, 1);
}
uintptr_t kernel_pml4_phys;
void vmm_init(void) {
uintptr_t cr3;
asm volatile ("mov %%cr3, %0" : "=r"(cr3));
kernel_pagemap.pml4 = (vmm_pte_t*)pmm_phys_to_virt(cr3);
kernel_pml4_phys = cr3;
serial_printf("VMM: kernel pagemap initialized\n");
serial_printf("VMM: kernel PML4 phys = 0x%llx\n", kernel_pml4_phys);
}
vmm_pagemap_t* vmm_get_kernel_pagemap(void) {
return &kernel_pagemap;
}
void vmm_sync_kernel_mappings(vmm_pagemap_t* map) {
if (!map) return;
for (size_t i = 256; i < 512; i++) {
map->pml4[i] = kernel_pagemap.pml4[i];
}
}
void vmm_test(void) {
serial_printf("\n--- VMM EXTENDED 64-BIT TEST ---\n");
void* phys1 = pmm_alloc_zero(1);
void* phys2 = pmm_alloc_zero(1);
uintptr_t paddr1 = pmm_virt_to_phys(phys1);
uintptr_t paddr2 = pmm_virt_to_phys(phys2);
uintptr_t vaddr1 = KERNEL_TEST_BASE;
uintptr_t vaddr2 = KERNEL_TEST_BASE + 0x1000;
vmm_map_page(&kernel_pagemap, vaddr1, paddr1, VMM_WRITE);
vmm_map_page(&kernel_pagemap, vaddr2, paddr2, VMM_WRITE);
uint64_t* ptr1 = (uint64_t*)vaddr1;
uint64_t* ptr2 = (uint64_t*)vaddr2;
*ptr1 = 0xDEADBEEFCAFEBABE;
*ptr2 = 0xFEEDFACE12345678;
vmm_pagemap_t* new_map = vmm_create_pagemap();
void* phys3 = pmm_alloc_zero(1);
uintptr_t paddr3 = pmm_virt_to_phys(phys3);
uintptr_t vaddr3 = KERNEL_TEST_BASE + 0x2000;
vmm_map_page(new_map, vaddr3, paddr3, VMM_WRITE);
uint64_t* ptr3 = (uint64_t*)vaddr3;
*ptr3 = 0xBAADF00DBAADF00D;
vmm_switch_pagemap(new_map);
serial_printf("Value (new): 0x%llx\n", *ptr3);
vmm_switch_pagemap(&kernel_pagemap);
serial_printf("Value (kernel): 0x%llx\n", *ptr1);
serial_printf("--- VMM TEST DONE ---\n");
serial_printf("\n--- VMM TRANSLATION TEST ---\n");
void* phys_page = pmm_alloc_zero(1);
uintptr_t paddr = pmm_virt_to_phys(phys_page);
uintptr_t vaddr = KERNEL_TEST_BASE + 0x3000;
vmm_map_page(&kernel_pagemap, vaddr, paddr, VMM_WRITE);
uintptr_t translated_phys;
if (vmm_virt_to_phys(&kernel_pagemap, vaddr, &translated_phys)) {
serial_printf("Virt 0x%llx -> Phys 0x%llx\n", vaddr, translated_phys);
serial_printf("Original phys: 0x%llx\n", paddr);
serial_printf("Match: %s\n", translated_phys == paddr ? "YES" : "NO");
} else {
serial_printf("Translation failed!\n");
}
uint64_t flags;
if (vmm_get_page_flags(&kernel_pagemap, vaddr, &flags)) {
serial_printf("Page flags: 0x%llx\n", flags);
serial_printf("Present: %s\n", (flags & VMM_PRESENT) ? "YES" : "NO");
serial_printf("Writable: %s\n", (flags & VMM_WRITE) ? "YES" : "NO");
}
uint64_t hhdm = pmm_get_hhdm_offset();
serial_printf("HHDM offset: 0x%llx\n", hhdm);
serial_printf("\n--- Memory statistics after tests ---\n");
size_t free_before = pmm_get_free_pages();
serial_printf("\n--- Memory free test ---\n");
void* test_alloc = pmm_alloc_aligned(2, 4096);
if (test_alloc) {
size_t free_after_alloc = pmm_get_free_pages();
serial_printf("Allocated 2 pages. Free pages: %zu -> %zu\n",
free_before, free_after_alloc);
pmm_free(test_alloc, 2);
size_t free_after_free = pmm_get_free_pages();
serial_printf("Freed 2 pages. Free pages: %zu -> %zu\n",
free_after_alloc, free_after_free);
if (free_after_free == free_before) {
serial_printf("Memory free test PASSED\n");
} else {
serial_printf("Memory free test FAILED (possible leak)\n");
}
}
serial_printf("--- VMM TRANSLATION TEST DONE ---\n");
}
+211
View File
@@ -0,0 +1,211 @@
#include "../../include/panic/panic.h"
#include "../../include/io/serial.h"
#include "../../include/graphics/fb/fb.h"
#include "../../include/apic/apic.h"
#include "../../include/smp/smp.h"
#include "../../include/smp/percpu.h"
#include "../../include/sched/sched.h"
#include <stddef.h>
#include <stdio.h>
extern struct limine_framebuffer *global_framebuffer;
#define COL_BG 0x000080
#define COL_WHITE 0xFFFFFF
#define COL_YELLOW 0xFFFF00
#define COL_RED 0xFF4444
#define COL_CYAN 0x00FFFF
#define COL_GRAY 0xAAAAAA
static uint32_t fb_x = 0, fb_y = 0;
static struct limine_framebuffer *g_fb = NULL;
static void fb_nl(void) {
fb_x = 0;
fb_y += 18;
}
static void fb_puts_col(const char *s, uint32_t col) {
if (!g_fb) return;
while (*s) {
if (*s == '\n') { fb_nl(); s++; continue; }
fb_draw_char(g_fb, *s, fb_x, fb_y, col);
fb_x += 9;
if (fb_x + 9 > (uint32_t)g_fb->width) fb_nl();
s++;
}
}
static void fb_puts(const char *s) { fb_puts_col(s, COL_WHITE); }
static void fb_puthex(uint64_t v) {
static const char h[] = "0123456789ABCDEF";
char full[19] = "0x";
for (int i = 0; i < 16; i++)
full[2 + i] = h[(v >> (60 - i * 4)) & 0xF];
full[18] = '\0';
fb_puts_col(full, COL_CYAN);
}
static void fb_putdec(uint64_t v) {
char buf[22]; int i = 20; buf[21] = '\0';
if (v == 0) { fb_puts_col("0", COL_CYAN); return; }
while (v && i >= 0) { buf[i--] = '0' + (v % 10); v /= 10; }
fb_puts_col(buf + i + 1, COL_CYAN);
}
volatile int g_panic_owner = 0;
static int panic_try_own(void) {
int expected = 0;
return __atomic_compare_exchange_n(&g_panic_owner, &expected, 1,
0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
}
static void halt_other_cpus(void) {
asm volatile("cli");
lapic_send_nmi_to_all_but_self();
for (volatile int i = 0; i < 200000; i++)
asm volatile("pause");
}
static void draw_panic_screen(const char *msg, struct int_frame_t *regs) {
g_fb = global_framebuffer;
if (!g_fb) return;
uint32_t W = (uint32_t)g_fb->width;
uint32_t H = (uint32_t)g_fb->height;
fb_fill_rect(g_fb, 0, 0, W, H, COL_BG);
fb_fill_rect(g_fb, 0, 0, W, 4, COL_WHITE);
fb_fill_rect(g_fb, 0, H - 4, W, 4, COL_WHITE);
fb_x = 16; fb_y = 16;
fb_puts_col("*** KERNEL PANIC ***\n", COL_YELLOW);
fb_nl();
fb_puts_col("Cervus OS has encountered a fatal error and cannot continue.\n", COL_WHITE);
fb_nl();
fb_puts_col("Reason: ", COL_GRAY);
fb_puts_col(msg, COL_YELLOW);
fb_nl(); fb_nl();
if (regs) {
percpu_t *pc = get_percpu();
task_t *t = pc ? (task_t *)pc->current_task : NULL;
uint32_t cpu = lapic_get_id();
if (!t) t = current_task[cpu];
fb_puts_col("CPU: ", COL_GRAY); fb_putdec(cpu); fb_puts("\n");
if (t) {
fb_puts_col("Task: ", COL_GRAY);
fb_puts_col(t->name, COL_CYAN);
fb_puts(" PID: "); fb_putdec(t->pid);
fb_puts("\n");
}
fb_nl();
fb_puts_col(" Register Dump \n", COL_YELLOW);
fb_puts_col("RIP: ", COL_GRAY); fb_puthex(regs->rip);
fb_puts(" CS: "); fb_puthex(regs->cs); fb_puts("\n");
fb_puts_col("RSP: ", COL_GRAY); fb_puthex(regs->rsp);
fb_puts(" RFL: "); fb_puthex(regs->rflags); fb_puts("\n");
fb_puts_col("RAX: ", COL_GRAY); fb_puthex(regs->rax);
fb_puts(" RBX: "); fb_puthex(regs->rbx); fb_puts("\n");
fb_puts_col("RCX: ", COL_GRAY); fb_puthex(regs->rcx);
fb_puts(" RDX: "); fb_puthex(regs->rdx); fb_puts("\n");
fb_puts_col("RSI: ", COL_GRAY); fb_puthex(regs->rsi);
fb_puts(" RDI: "); fb_puthex(regs->rdi); fb_puts("\n");
fb_puts_col("RBP: ", COL_GRAY); fb_puthex(regs->rbp); fb_puts("\n");
fb_puts_col("R8: ", COL_GRAY); fb_puthex(regs->r8);
fb_puts(" R9: "); fb_puthex(regs->r9); fb_puts("\n");
fb_puts_col("R10: ", COL_GRAY); fb_puthex(regs->r10);
fb_puts(" R11: "); fb_puthex(regs->r11); fb_puts("\n");
fb_puts_col("R12: ", COL_GRAY); fb_puthex(regs->r12);
fb_puts(" R13: "); fb_puthex(regs->r13); fb_puts("\n");
fb_puts_col("R14: ", COL_GRAY); fb_puthex(regs->r14);
fb_puts(" R15: "); fb_puthex(regs->r15); fb_puts("\n");
fb_nl();
uint64_t cr2 = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
fb_puts_col("CR2: ", COL_GRAY); fb_puthex(cr2); fb_puts("\n");
fb_puts_col("ERR: ", COL_GRAY); fb_puthex(regs->error);
fb_puts(" INT: "); fb_putdec(regs->interrupt); fb_puts("\n");
}
fb_nl();
fb_puts_col("System halted. Check serial output for details.\n", COL_GRAY);
}
static void serial_panic_dump(const char *msg, struct int_frame_t *regs) {
serial_printf("\n");
serial_printf(" KERNEL PANIC \n");
serial_printf("Reason: %s\n", msg);
uint32_t cpu = lapic_get_id();
serial_printf("CPU: %u\n", cpu);
percpu_t *pc = get_percpu();
task_t *t = pc ? (task_t *)pc->current_task : NULL;
if (!t) t = current_task[cpu];
if (t) {
serial_printf("Task: %s PID=%u PPID=%u\n",
t->name, t->pid, t->ppid);
}
if (regs) {
uint64_t cr2 = 0;
asm volatile("mov %%cr2, %0" : "=r"(cr2));
serial_printf("\nRegisters:\n");
serial_printf(" RIP=0x%016llx CS=0x%llx\n", regs->rip, regs->cs);
serial_printf(" RSP=0x%016llx SS=0x%llx\n", regs->rsp, regs->ss);
serial_printf(" RFLAGS=0x%016llx\n", regs->rflags);
serial_printf(" RAX=0x%016llx RBX=0x%016llx\n", regs->rax, regs->rbx);
serial_printf(" RCX=0x%016llx RDX=0x%016llx\n", regs->rcx, regs->rdx);
serial_printf(" RSI=0x%016llx RDI=0x%016llx\n", regs->rsi, regs->rdi);
serial_printf(" RBP=0x%016llx\n", regs->rbp);
serial_printf(" R8 =0x%016llx R9 =0x%016llx\n", regs->r8, regs->r9);
serial_printf(" R10=0x%016llx R11=0x%016llx\n", regs->r10, regs->r11);
serial_printf(" R12=0x%016llx R13=0x%016llx\n", regs->r12, regs->r13);
serial_printf(" R14=0x%016llx R15=0x%016llx\n", regs->r14, regs->r15);
serial_printf(" CR2=0x%016llx\n", cr2);
serial_printf(" ERR=0x%016llx INT=%llu\n", regs->error, regs->interrupt);
}
serial_printf("========================================\n");
serial_printf("System halted.\n\n");
}
__attribute__((noreturn))
void kernel_panic(const char *msg) {
kernel_panic_regs(msg, NULL);
}
__attribute__((noreturn))
void kernel_panic_regs(const char *msg, struct int_frame_t *regs) {
asm volatile("cli");
if (!panic_try_own()) {
for (;;) asm volatile("cli; hlt");
}
serial_force_unlock();
halt_other_cpus();
serial_panic_dump(msg, regs);
draw_panic_screen(msg, regs);
for (;;) asm volatile("cli; hlt");
}
+669
View File
@@ -0,0 +1,669 @@
#include "../include/sched/capabilities.h"
#include "../include/sched/sched.h"
#include "../include/sched/spinlock.h"
#include "../include/memory/pmm.h"
#include "../include/memory/vmm.h"
#include "../include/io/serial.h"
#include "../include/smp/smp.h"
#include "../include/smp/percpu.h"
#include "../include/apic/apic.h"
#include "../include/gdt/gdt.h"
#include "../include/fs/vfs.h"
#include "../include/panic/panic.h"
#include <string.h>
#include <stdlib.h>
#define KERNEL_STACK_PAGES (KERNEL_STACK_SIZE / 0x1000)
#define MAX_PIDS 4096
task_t* ready_queues[MAX_PRIORITY + 1] = {0};
task_t* current_task[MAX_CPUS] = {0};
static task_t idle_tasks[MAX_CPUS];
static task_t bootstrap_tasks[MAX_CPUS];
static volatile uint64_t reschedule_calls = 0;
static spinlock_t ready_queue_lock = SPINLOCK_INIT;
static task_t* pid_table[MAX_PIDS] = {0};
static uint32_t next_pid = 1;
static spinlock_t pid_lock = SPINLOCK_INIT;
spinlock_t children_lock = SPINLOCK_INIT;
extern tss_t* tss[MAX_CPUS];
static inline void fix_gs_base(percpu_t* pc) {
uint64_t val = (uint64_t)pc;
asm volatile("wrmsr"
:: "c"(0xC0000101U),
"a"((uint32_t)val),
"d"((uint32_t)(val >> 32)));
}
uint32_t task_alloc_pid(void) {
uint64_t _irqf;
_irqf = spinlock_acquire_irqsave(&pid_lock);
uint32_t found = 0;
for (uint32_t attempt = 0; attempt < MAX_PIDS - 1; attempt++) {
uint32_t i = next_pid;
if (i == 0 || i >= MAX_PIDS) {
next_pid = 1;
i = 1;
}
next_pid = (next_pid + 1 >= MAX_PIDS) ? 1 : next_pid + 1;
if (!pid_table[i]) { found = i; break; }
}
if (!found) {
serial_printf("[PID] FATAL: pid table exhausted (MAX_PIDS=%u)!\n", MAX_PIDS);
}
spinlock_release_irqrestore(&pid_lock, _irqf);
return found;
}
task_t* task_find_by_pid(uint32_t pid) {
if (pid == 0 || pid >= MAX_PIDS) return NULL;
return pid_table[pid];
}
static void pid_register(task_t* t) {
if (t->pid && t->pid < MAX_PIDS) pid_table[t->pid] = t;
}
static void pid_unregister(task_t* t) {
if (t->pid && t->pid < MAX_PIDS) pid_table[t->pid] = NULL;
}
static void idle_loop(void* arg);
#define STACK_CANARY_VALUE 0xDEADC0DEDEADC0DEULL
static uint64_t alloc_and_init_stack(task_t* t) {
uintptr_t stack_virt = (uintptr_t)pmm_alloc_zero(KERNEL_STACK_PAGES);
if (!stack_virt) return 0;
t->stack_base = stack_virt;
uint64_t* canary_page = (uint64_t*)stack_virt;
for (size_t i = 0; i < PAGE_SIZE / sizeof(uint64_t); i++)
canary_page[i] = STACK_CANARY_VALUE;
uintptr_t stack_top = (stack_virt + KERNEL_STACK_SIZE) & ~0xFULL;
uint64_t* sp = (uint64_t*)stack_top;
if (t->flags & TASK_FLAG_FORK) {
extern void task_trampoline_fork(void);
*--sp = (uint64_t)task_trampoline_fork;
} else if (t->is_userspace) {
extern void task_trampoline_user(void);
*--sp = (uint64_t)task_trampoline_user;
} else {
extern void task_trampoline(void);
*--sp = (uint64_t)task_trampoline;
}
*--sp = (uint64_t)t;
*--sp = 0;
*--sp = 0;
*--sp = 0;
*--sp = 0;
*--sp = 0;
return (uint64_t)sp;
}
static void enqueue_global(task_t* t) {
uint64_t f = spinlock_acquire_irqsave(&ready_queue_lock);
t->next = ready_queues[t->priority];
ready_queues[t->priority] = t;
spinlock_release_irqrestore(&ready_queue_lock, f);
}
void __attribute__((used)) ctx_rsp_corruption_detected(task_t* old, uint64_t saved_rsp) {
serial_printf("[CTX-CORRUPT] pid=%u rsp=0x%llx saved but INVALID (stack=0x%llx..0x%llx)!\n",
old ? old->pid : 0,
saved_rsp,
old ? old->stack_base : 0,
old ? (old->stack_base + KERNEL_STACK_SIZE) : 0);
kernel_panic("context_switch: saved invalid RSP into task->rsp");
}
static void process_deferred_free(void) {
percpu_t* pc = get_percpu();
if (!pc) return;
task_t* dead = (task_t*)pc->deferred_free_task;
if (!dead) return;
pc->deferred_free_task = NULL;
task_destroy(dead);
}
void sched_init(void) {
memset(pid_table, 0, sizeof(pid_table));
memset(bootstrap_tasks, 0, sizeof(bootstrap_tasks));
next_pid = 1;
for (uint32_t i = 0; i < smp_get_cpu_count(); i++) {
task_t* idle = &idle_tasks[i];
memset(idle, 0, sizeof(task_t));
idle->priority = 0;
idle->runnable = true;
idle->state = TASK_READY;
idle->cpu_id = i;
idle->last_cpu = i;
idle->time_slice = 1;
idle->time_slice_init = 1;
idle->entry = idle_loop;
idle->arg = NULL;
idle->is_userspace = TASK_TYPE_KERNEL;
idle->pid = 0;
idle->uid = UID_ROOT;
idle->gid = GID_ROOT;
idle->capabilities = CAP_ALL;
atomic_init_bool(&idle->on_cpu, false);
idle->name[0]='i'; idle->name[1]='d';
idle->name[2]='l'; idle->name[3]='e';
idle->rsp = alloc_and_init_stack(idle);
if (!idle->rsp) {
kernel_panic("SCHED: failed to allocate idle stack");
}
current_task[i] = NULL;
}
serial_writestring("Scheduler initialized (PREEMPTIVE SMP MODE)\n");
}
task_t* task_create(const char* name, void (*entry)(void*), void* arg, int priority) {
task_t* t = calloc(1, sizeof(task_t));
if (!t) return NULL;
t->pid = task_alloc_pid();
if (!t->pid) { free(t); return NULL; }
t->ppid = 0;
t->uid = UID_ROOT;
t->gid = GID_ROOT;
t->capabilities = CAP_ALL;
t->entry = entry;
t->arg = arg;
t->priority = priority > MAX_PRIORITY ? MAX_PRIORITY : priority;
t->runnable = true;
t->state = TASK_READY;
t->cpu_id = (uint32_t)-1;
t->time_slice = TASK_DEFAULT_TIMESLICE;
t->time_slice_init = TASK_DEFAULT_TIMESLICE;
t->rip = (uint64_t)entry;
t->is_userspace = TASK_TYPE_KERNEL;
atomic_init_bool(&t->on_cpu, false);
strncpy(t->name, name, sizeof(t->name) - 1);
t->rsp = alloc_and_init_stack(t);
if (!t->rsp) { free(t); return NULL; }
t->fpu_state = (fpu_state_t*)pmm_alloc_zero(1);
pid_register(t);
enqueue_global(t);
serial_printf("[SCHED] task_create: '%s' pid=%u prio=%d\n", t->name, t->pid, t->priority);
return t;
}
task_t* task_create_user(const char* name, uintptr_t entry, uintptr_t user_rsp, uint64_t cr3, int priority, vmm_pagemap_t* pagemap, uint32_t uid, uint32_t gid) {
task_t* t = calloc(1, sizeof(task_t));
if (!t) return NULL;
t->pid = task_alloc_pid();
if (!t->pid) { free(t); return NULL; }
t->ppid = 0;
t->uid = uid;
t->gid = gid;
t->capabilities = cap_initial(uid);
t->entry = (void (*)(void*))entry;
t->arg = NULL;
t->priority = priority > MAX_PRIORITY ? MAX_PRIORITY : priority;
t->runnable = true;
t->state = TASK_READY;
t->cpu_id = (uint32_t)-1;
t->time_slice = TASK_DEFAULT_TIMESLICE;
t->time_slice_init = TASK_DEFAULT_TIMESLICE;
t->rip = entry;
t->is_userspace = TASK_TYPE_USER;
t->user_rsp = user_rsp;
t->cr3 = cr3;
t->pagemap = pagemap;
t->brk_start = 0;
t->brk_current = 0;
t->brk_max = 0x0000700000000000ULL;
atomic_init_bool(&t->on_cpu, false);
strncpy(t->name, name, sizeof(t->name) - 1);
t->rsp = alloc_and_init_stack(t);
if (!t->rsp) { free(t); return NULL; }
t->fpu_state = (fpu_state_t*)pmm_alloc_zero(1);
t->fd_table = fd_table_create();
if (t->fd_table) {
int stdio_ret = vfs_init_stdio(t);
if (stdio_ret < 0)
serial_printf("[SCHED] task_create_user: vfs_init_stdio failed: %d\n",
stdio_ret);
}
t->flags |= TASK_FLAG_OWN_PAGEMAP;
pid_register(t);
enqueue_global(t);
serial_printf("[SCHED] task_create_user: '%s' pid=%u uid=%u entry=0x%llx user_rsp=0x%llx caps=0x%llx\n",
t->name, t->pid, t->uid, entry, user_rsp, t->capabilities);
return t;
}
task_t* task_fork(task_t* parent) {
if (!parent) return NULL;
task_t* child = calloc(1, sizeof(task_t));
if (!child) return NULL;
child->pid = task_alloc_pid();
if (!child->pid) { free(child); return NULL; }
child->ppid = parent->pid;
child->priority = parent->priority;
child->is_userspace = parent->is_userspace;
strncpy(child->name, parent->name, sizeof(child->name)-1);
child->uid = parent->uid;
child->gid = parent->gid;
child->capabilities = parent->capabilities;
child->time_slice = parent->time_slice_init;
child->time_slice_init = parent->time_slice_init;
child->pagemap = vmm_clone_pagemap(parent->pagemap);
if (!child->pagemap) { free(child); return NULL; }
child->cr3 = (uint64_t)pmm_virt_to_phys(child->pagemap->pml4);
child->brk_start = parent->brk_start;
child->brk_current = parent->brk_current;
child->brk_max = parent->brk_max;
child->user_rsp = parent->user_rsp;
serial_printf("[FORK-DBG2] parent pid=%u user_rsp=0x%llx user_saved_rip=0x%llx\n",
parent->pid, parent->user_rsp, parent->user_saved_rip);
child->user_saved_rip = parent->user_saved_rip;
child->user_saved_rbp = parent->user_saved_rbp;
child->user_saved_rbx = parent->user_saved_rbx;
child->user_saved_r12 = parent->user_saved_r12;
child->user_saved_r13 = parent->user_saved_r13;
child->user_saved_r14 = parent->user_saved_r14;
child->user_saved_r15 = parent->user_saved_r15;
child->user_saved_r11 = parent->user_saved_r11 | (1ULL << 9);
child->flags |= TASK_FLAG_FORK;
atomic_init_bool(&child->on_cpu, false);
child->rsp = alloc_and_init_stack(child);
if (!child->rsp) {
vmm_free_pagemap(child->pagemap);
free(child);
return NULL;
}
vmm_sync_kernel_mappings(child->pagemap);
serial_printf("[FORK-DBG] child pid=%u stack_base=0x%llx rsp=0x%llx\n",
child->pid, child->stack_base, child->rsp);
child->fpu_state = (fpu_state_t*)pmm_alloc_zero(1);
if (child->fpu_state && parent->fpu_state) {
memcpy(child->fpu_state, parent->fpu_state, sizeof(fpu_state_t));
child->fpu_used = parent->fpu_used;
}
if (parent->fd_table)
child->fd_table = fd_table_clone(parent->fd_table);
child->state = TASK_READY;
child->runnable = true;
child->parent = parent;
{
uint64_t _cf = spinlock_acquire_irqsave(&children_lock);
child->sibling = parent->children;
parent->children = child;
spinlock_release_irqrestore(&children_lock, _cf);
}
pid_register(child);
serial_printf("[FORK-CHK] child=%p rsp=0x%llx stk=0x%llx rip=0x%llx\n",
(void*)child, child->rsp, child->stack_base, child->user_saved_rip);
serial_printf("[SCHED] fork: parent='%s' pid=%u -> child pid=%u rip=0x%llx\n",
parent->name, parent->pid, child->pid, child->user_saved_rip);
enqueue_global(child);
return child;
}
void task_destroy(task_t* task) {
if (!task) return;
uint32_t old_flags = __atomic_fetch_or(&task->flags, TASK_FLAG_DESTROYED, __ATOMIC_ACQ_REL);
if (old_flags & TASK_FLAG_DESTROYED) return;
serial_printf("[DESTROY] pid=%u flags=0x%x on_cpu=%d\n",
task->pid, task->flags, (int)atomic_load_bool_acq(&task->on_cpu));
pid_unregister(task);
if (task->fpu_state) {
pmm_free(task->fpu_state, 1);
task->fpu_state = NULL;
}
if (task->stack_base) {
pmm_free((void*)task->stack_base, KERNEL_STACK_PAGES);
task->stack_base = 0;
}
if (task->pagemap && (task->flags & (TASK_FLAG_FORK | TASK_FLAG_OWN_PAGEMAP))) {
vmm_free_pagemap(task->pagemap);
task->pagemap = NULL;
}
if (task->fd_table) {
fd_table_destroy(task->fd_table);
task->fd_table = NULL;
}
free(task);
}
void task_reparent(task_t* child, task_t* new_parent) {
if (!child || !new_parent) return;
child->parent = new_parent;
child->ppid = new_parent->pid;
child->sibling = new_parent->children;
new_parent->children = child;
}
void task_wakeup_waiters(uint32_t pid) {
task_t* to_wake[64];
int wake_count = 0;
uint64_t _irqf = spinlock_acquire_irqsave(&pid_lock);
for (uint32_t i = 1; i < MAX_PIDS && wake_count < 64; i++) {
task_t* t = pid_table[i];
if (!t) continue;
if (t->state != TASK_BLOCKED) continue;
if (t->wait_for_pid != pid && t->wait_for_pid != (uint32_t)-1) continue;
serial_printf("[SCHED] wakeup_waiters: waking pid=%u (waited for pid=%u)\n", t->pid, pid);
t->wait_for_pid = 0;
t->runnable = true;
t->state = TASK_READY;
to_wake[wake_count++] = t;
}
spinlock_release_irqrestore(&pid_lock, _irqf);
for (int i = 0; i < wake_count; i++)
enqueue_global(to_wake[i]);
}
__attribute__((noreturn)) void task_exit(void)
{
asm volatile("cli");
uint32_t cpu = lapic_get_id();
percpu_t* pc = get_percpu();
task_t* me = pc ? (task_t*)pc->current_task : current_task[cpu];
if (!me) kernel_panic("task_exit: no current task");
serial_printf("[EXIT] task_exit called cpu=%u me=%p pid=%u\n", cpu, (void*)me, me->pid);
task_t* init = task_find_by_pid(1);
if (init && init != me) {
uint64_t _cf = spinlock_acquire_irqsave(&children_lock);
task_t* child = me->children;
me->children = NULL;
while (child) {
task_t* sib = child->sibling;
task_reparent(child, init);
child = sib;
}
spinlock_release_irqrestore(&children_lock, _cf);
}
vmm_switch_pagemap(vmm_get_kernel_pagemap());
me->runnable = false;
me->state = TASK_ZOMBIE;
me->cr3 = 0;
task_wakeup_waiters(me->pid);
sched_reschedule();
kernel_panic("task_exit: returned from sched_reschedule (should never happen)");
}
void task_kill(task_t* target) {
if (!target) return;
if (target->pid == 0) return;
if (target->is_userspace == TASK_TYPE_KERNEL) return;
if (target->state == TASK_ZOMBIE || target->state == TASK_DEAD) return;
if (target->pending_kill) return;
serial_printf("[KILL] task_kill pid=%u state=%d cpu=%u\n",
target->pid, (int)target->state, lapic_get_id());
target->exit_code = 130;
target->pending_kill = true;
if (target->state == TASK_BLOCKED) {
target->wakeup_time_ns = 0;
task_unblock(target);
}
if (!(target->flags & TASK_FLAG_STARTED)) {
return;
}
for (uint32_t cpu = 0; cpu < smp_get_cpu_count(); cpu++) {
if (current_task[cpu] == target) {
extern uint32_t smp_get_lapic_id_for_cpu(uint32_t);
ipi_reschedule_cpu(smp_get_lapic_id_for_cpu(cpu));
}
}
}
volatile uint32_t g_foreground_pid = 0;
void task_set_foreground(uint32_t pid) {
g_foreground_pid = pid;
}
task_t* task_find_foreground(void) {
uint32_t fpid = g_foreground_pid;
if (fpid == 0) return NULL;
task_t *t = task_find_by_pid(fpid);
if (!t || t->state == TASK_ZOMBIE || t->state == TASK_DEAD) {
g_foreground_pid = 0;
return NULL;
}
return t;
}
static task_t* sched_pick_next(uint32_t cpu) {
uint64_t _irqf;
_irqf = spinlock_acquire_irqsave(&ready_queue_lock);
task_t* found = NULL;
for (int p = MAX_PRIORITY; p >= 0 && !found; p--) {
task_t** head = &ready_queues[p];
task_t* t = *head;
while (t) {
if (t->runnable && t->state != TASK_ZOMBIE && t->state != TASK_DEAD) {
bool expected = false;
if (atomic_cas_bool(&t->on_cpu, &expected, true)) {
*head = t->next;
t->next = NULL;
found = t;
break;
}
} else {
if (t->state == TASK_ZOMBIE || t->state == TASK_DEAD || !t->runnable) {
*head = t->next;
t->next = NULL;
t = *head;
continue;
}
}
head = &t->next;
t = *head;
}
}
spinlock_release_irqrestore(&ready_queue_lock, _irqf);
return found ? found : &idle_tasks[cpu];
}
void sched_reschedule(void) {
asm volatile("cli");
process_deferred_free();
reschedule_calls++;
uint32_t cpu = lapic_get_id();
task_t* old = current_task[cpu];
task_t* next = sched_pick_next(cpu);
if (!next) { asm volatile("sti"); return; }
if (old == next) {
if (old == &idle_tasks[cpu]) {
next = sched_pick_next(cpu);
if (next == &idle_tasks[cpu]) {
asm volatile("sti");
return;
}
} else {
old->time_slice = old->time_slice_init;
asm volatile("sti");
return;
}
}
if (old && old->fpu_state && old->state != TASK_ZOMBIE && old->state != TASK_DEAD) {
fpu_save(old->fpu_state);
old->fpu_used = true;
}
if (old && old != &idle_tasks[cpu]) {
if (old->state == TASK_ZOMBIE) {
percpu_t* pc = get_percpu();
if (pc) pc->deferred_free_task = old;
} else if (old->runnable && old->state != TASK_DEAD) {
old->time_slice = old->time_slice_init;
old->last_cpu = cpu;
old->state = TASK_READY;
enqueue_global(old);
}
}
uint64_t switch_cr3 = 0;
if (next->cr3 && (!old || old->cr3 != next->cr3)) {
if (next->pagemap)
vmm_sync_kernel_mappings(next->pagemap);
asm volatile("lock addl $0, (%%rsp)" ::: "memory", "cc");
switch_cr3 = next->cr3;
} else if (!next->cr3) {
vmm_pagemap_t* kpm = vmm_get_kernel_pagemap();
if (kpm && kpm->pml4) {
uint64_t kphys = (uint64_t)pmm_virt_to_phys(kpm->pml4);
if (!old || old->cr3 != kphys)
switch_cr3 = kphys;
}
}
if (tss[cpu]) {
if (next->is_userspace && next->stack_base == 0) {
kernel_panic("SCHED: userspace task has stack_base=0");
}
tss[cpu]->rsp0 = next->stack_base + KERNEL_STACK_SIZE;
percpu_t* pc = get_percpu();
if (pc) {
pc->syscall_kernel_rsp = tss[cpu]->rsp0;
if (next->is_userspace) {
pc->syscall_user_rsp = next->user_rsp;
}
}
}
next->cpu_id = cpu;
next->state = TASK_RUNNING;
if (next->fpu_state) fpu_restore(next->fpu_state);
if (!(next->flags & TASK_FLAG_STARTED)) {
next->flags |= TASK_FLAG_STARTED;
current_task[cpu] = next;
if (next->cr3) {
serial_printf("[SCHED] CR3 switch cpu=%u old=0x%llx -> new=0x%llx (pid %u->%u)\n",
cpu, old ? old->cr3 : 0ULL, switch_cr3,
old ? old->pid : 0, next->pid);
asm volatile("mov %0, %%cr3" :: "r"(next->cr3) : "memory");
switch_cr3 = 0;
} else {
asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory");
}
if (next->is_userspace) {
serial_printf("[SCHED] CPU %u: first start '%s' pid=%u entry=0x%llx user_rsp=0x%llx\n",
cpu, next->name, next->pid,
(uint64_t)next->entry, next->user_rsp);
}
}
if (old) context_switch(old, next, &current_task[cpu], switch_cr3);
else context_switch(&bootstrap_tasks[cpu], next, &current_task[cpu], switch_cr3);
process_deferred_free();
asm volatile("sti");
}
void task_yield(void) {
sched_reschedule();
}
void sched_print_stats(void) {
uint64_t _irqf;
serial_printf("[SCHED] reschedule_calls=%llu\n", reschedule_calls);
_irqf = spinlock_acquire_irqsave(&ready_queue_lock);
for (int p = MAX_PRIORITY; p >= 0; p--) {
int n = 0;
for (task_t* t = ready_queues[p]; t; t = t->next) n++;
if (n) serial_printf(" prio %d: %d tasks\n", p, n);
}
spinlock_release_irqrestore(&ready_queue_lock, _irqf);
}
void task_unblock(task_t* t) {
if (!t) return;
t->runnable = true;
t->state = TASK_READY;
enqueue_global(t);
}
void sched_wakeup_sleepers(uint64_t now_ns) {
task_t* to_wake[64];
int wake_count = 0;
uint64_t _irqf = spinlock_acquire_irqsave(&pid_lock);
for (uint32_t i = 1; i < MAX_PIDS && wake_count < 64; i++) {
task_t *t = pid_table[i];
if (!t) continue;
if (t->state != TASK_BLOCKED) continue;
if (t->wakeup_time_ns == 0) continue;
if (now_ns >= t->wakeup_time_ns) {
t->wakeup_time_ns = 0;
t->runnable = true;
t->state = TASK_READY;
to_wake[wake_count++] = t;
}
}
spinlock_release_irqrestore(&pid_lock, _irqf);
for (int i = 0; i < wake_count; i++) {
enqueue_global(to_wake[i]);
}
}
static void idle_loop(void* arg) {
(void)arg;
uint32_t cpu = lapic_get_id();
serial_printf("[IDLE] CPU %u entering idle loop\n", cpu);
while (1) {
process_deferred_free();
asm volatile("sti; hlt");
}
}
+157
View File
@@ -0,0 +1,157 @@
section .text
extern task_exit
global context_switch
global first_task_start
global fpu_save
global fpu_restore
global task_trampoline
global task_trampoline_user
global task_trampoline_fork
TASK_ENTRY_OFFSET equ 120
TASK_ARG_OFFSET equ 128
TASK_USER_RSP_OFFSET equ 144
TASK_CR3_OFFSET equ 24
TASK_USER_SAVED_RIP_OFFSET equ 272
TASK_USER_SAVED_RBP_OFFSET equ 280
TASK_USER_SAVED_RBX_OFFSET equ 288
TASK_USER_SAVED_R12_OFFSET equ 296
TASK_USER_SAVED_R13_OFFSET equ 304
TASK_USER_SAVED_R14_OFFSET equ 312
TASK_USER_SAVED_R15_OFFSET equ 320
TASK_USER_SAVED_R11_OFFSET equ 328
PERCPU_CURRENT_TASK equ 24
TASK_ON_CPU_OFFSET equ 0x158
context_switch:
push rbp
push rbx
push r12
push r13
push r14
push r15
mov [rdi], rsp
mov [rdx], rsi
mov [gs:PERCPU_CURRENT_TASK], rsi
mov rsp, [rsi]
test rcx, rcx
jz .skip_cr3
mov cr3, rcx
.skip_cr3:
lock add dword [rsp], 0
mov byte [rdi + TASK_ON_CPU_OFFSET], 0
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
first_task_start:
mov [gs:PERCPU_CURRENT_TASK], rdi
mov rsi, [rdi + TASK_CR3_OFFSET]
mov rsp, [rdi]
test rsi, rsi
jz .skip_cr3_fts
mov cr3, rsi
.skip_cr3_fts:
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
fpu_save:
fxsave [rdi]
ret
fpu_restore:
fxrstor [rdi]
ret
task_trampoline:
mov rdi, [rbp + TASK_ARG_OFFSET]
mov rax, [rbp + TASK_ENTRY_OFFSET]
xor rbp, rbp
sti
call rax
call task_exit
.hang:
cli
hlt
jmp .hang
task_trampoline_user:
mov rax, [rbp + TASK_ENTRY_OFFSET]
mov rcx, [rbp + TASK_USER_RSP_OFFSET]
xor rbp, rbp
push qword 0x1B
push rcx
pushfq
pop rdx
or rdx, (1 << 9)
and rdx, ~(3 << 12)
push rdx
push qword 0x23
push rax
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
xor rdi, rdi
xor rsi, rsi
xor r8, r8
xor r9, r9
xor r10, r10
xor r11, r11
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15
swapgs
iretq
task_trampoline_fork:
cli
mov rax, [rbp + TASK_USER_SAVED_RIP_OFFSET]
mov rcx, [rbp + TASK_USER_RSP_OFFSET]
mov r11, [rbp + TASK_USER_SAVED_R11_OFFSET]
mov rbx, [rbp + TASK_USER_SAVED_RBX_OFFSET]
mov r12, [rbp + TASK_USER_SAVED_R12_OFFSET]
mov r13, [rbp + TASK_USER_SAVED_R13_OFFSET]
mov r14, [rbp + TASK_USER_SAVED_R14_OFFSET]
mov r15, [rbp + TASK_USER_SAVED_R15_OFFSET]
mov rbp, [rbp + TASK_USER_SAVED_RBP_OFFSET]
or r11, (1 << 9)
push qword 0x1B
push rcx
push r11
push qword 0x23
push rax
xor rax, rax
swapgs
iretq
+88
View File
@@ -0,0 +1,88 @@
#include "../../include/smp/percpu.h"
#include "../../include/smp/smp.h"
#include "../../include/memory/pmm.h"
#include "../../include/io/serial.h"
#include <string.h>
#include <stdlib.h>
extern uintptr_t __percpu_start;
extern uintptr_t __percpu_end;
PERCPU_SECTION percpu_t percpu = {0};
PERCPU_SECTION int dummy_percpu = 0xDEADBEEF;
percpu_t* percpu_regions[MAX_CPUS] = {0};
bool g_has_fsgsbase = false;
#define MSR_GS_BASE 0xC0000101
#define MSR_KERNEL_GS_BASE 0xC0000102
static inline uint64_t rdmsr_local(uint32_t msr) {
uint32_t lo, hi;
asm volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((uint64_t)hi << 32) | lo;
}
static inline void wrmsr_local(uint32_t msr, uint64_t val) {
asm volatile("wrmsr" :: "c"(msr), "a"((uint32_t)val), "d"((uint32_t)(val >> 32)));
}
static bool detect_fsgsbase(void) {
uint32_t eax = 7, ebx, ecx = 0, edx;
asm volatile("cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "0"(eax), "2"(ecx));
return (ebx & (1u << 0)) != 0;
}
void init_percpu_regions(void) {
g_has_fsgsbase = detect_fsgsbase();
serial_printf("[PerCPU] FSGSBASE: %s\n", g_has_fsgsbase ? "YES" : "NO (using MSR fallback)");
size_t percpu_size = &__percpu_end - &__percpu_start;
serial_printf("PerCPU size: %zu bytes\n", percpu_size);
smp_info_t* info = smp_get_info();
for (uint32_t i = 0; i < info->cpu_count; i++) {
void* region = malloc(percpu_size);
if (!region) {
serial_printf("PerCPU: Alloc failed for CPU %u\n", i);
continue;
}
memset(region, 0, percpu_size);
memcpy(region, &__percpu_start, percpu_size);
percpu_regions[i] = (percpu_t*)region;
percpu_regions[i]->cpu_id = info->cpus[i].lapic_id;
percpu_regions[i]->syscall_kernel_rsp = 0;
percpu_regions[i]->syscall_user_rsp = 0;
serial_printf("PerCPU region for CPU %u at 0x%llx\n", i, (uint64_t)region);
}
}
percpu_t* get_percpu(void) {
uint64_t gs_base;
if (g_has_fsgsbase) {
asm volatile("rdgsbase %0" : "=r"(gs_base));
} else {
gs_base = rdmsr_local(MSR_GS_BASE);
}
if (gs_base == 0) return NULL;
return (percpu_t*)gs_base;
}
percpu_t* get_percpu_mut(void) {
return get_percpu();
}
void set_percpu_base(percpu_t* base) {
uint64_t val = (uint64_t)base;
if (g_has_fsgsbase) {
asm volatile("wrgsbase %0" :: "r"(val) : "memory");
} else {
wrmsr_local(MSR_GS_BASE, val);
}
wrmsr_local(MSR_KERNEL_GS_BASE, val);
}
+327
View File
@@ -0,0 +1,327 @@
#include "../../include/smp/smp.h"
#include "../../include/smp/percpu.h"
#include "../../include/acpi/acpi.h"
#include "../../include/apic/apic.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/memory/vmm.h"
#include "../../include/gdt/gdt.h"
#include "../../include/interrupts/idt.h"
#include "../../include/sse/fpu.h"
#include "../../include/sse/sse.h"
#include "../../include/sched/sched.h"
#include "../include/syscall/syscall.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
extern tss_t *tss[MAX_CPUS];
extern struct {
gdt_entry_t gdt_entries[5 + (MAX_CPUS * 2)];
} __attribute__((packed)) gdt;
static smp_info_t smp_info = {0};
static volatile uint32_t ap_online_count = 0;
tlb_shootdown_t tlb_shootdown_queue[MAX_CPUS] = {0};
volatile uint32_t sched_ready_flag = 0;
void sched_notify_ready(void) {
__sync_synchronize();
sched_ready_flag = 1;
__sync_synchronize();
serial_writestring("[SCHED] Scheduler ready, notifying all APs\n");
}
__attribute__((used))
void ap_entry_init(struct limine_mp_info* cpu_info) {
(void)cpu_info;
asm volatile ("cli");
lapic_write(0xF0, 0);
gdt_load();
idt_load();
uint32_t lapic_id = lapic_get_id();
smp_info_t* info = smp_get_info();
uint32_t my_index = 0;
for (uint32_t i = 0; i < info->cpu_count; i++) {
if (info->cpus[i].lapic_id == lapic_id && !info->cpus[i].is_bsp) {
my_index = i;
info->cpus[i].state = CPU_ONLINE;
break;
}
}
load_tss(info->cpus[my_index].tss_selector);
serial_printf("TSS Loaded (selector 0x%x)\n", info->cpus[my_index].tss_selector);
fpu_init();
sse_init();
enable_fsgsbase();
lapic_enable();
apic_timer_calibrate();
serial_printf("[SMP] AP %u LAPIC timer started\n", lapic_id);
cpu_info_t* cpu = smp_get_current_cpu();
percpu_t* region = percpu_regions[cpu->cpu_index];
set_percpu_base(region);
serial_printf("PerCPU base set for AP %u: 0x%llx\n",
lapic_id, (uint64_t)region);
syscall_init();
__sync_fetch_and_add(&ap_online_count, 1);
lapic_eoi();
serial_printf("[SMP] AP (LAPIC ID %u) initialized and online!\n", lapic_id);
while (!sched_ready_flag)
asm volatile ("pause");
serial_printf("[SMP] AP %u entering scheduler loop\n", lapic_id);
asm volatile ("sti");
sched_reschedule();
while (1)
asm volatile ("hlt");
}
void ap_entry_point(struct limine_mp_info* cpu_info) {
uint64_t stack_top;
asm volatile (
"mov 24(%%rdi), %0"
: "=r"(stack_top)
: "D"(cpu_info)
);
asm volatile (
"mov %0, %%rsp\n"
"cli\n"
"jmp ap_entry_init\n"
:
: "r"(stack_top), "D"(cpu_info)
: "memory"
);
}
static uint64_t smp_allocate_stack(uint32_t cpu_index, size_t stack_size) {
size_t pages = (stack_size + PAGE_SIZE - 1) / PAGE_SIZE;
void* stack_pages = pmm_alloc(pages);
if (!stack_pages) {
serial_printf("[SMP WARNING] Failed to allocate stack for CPU %u\n", cpu_index);
return 0;
}
uint64_t stack_virt = (uint64_t)stack_pages + stack_size;
serial_printf("[SMP] Allocated stack for CPU %u at 0x%llx (size %zu)\n",
cpu_index, stack_virt, stack_size);
return stack_virt;
}
void smp_boot_aps(struct limine_mp_response* mp_response) {
if (!mp_response) {
serial_writestring("[SMP ERROR] MP response is NULL\n");
return;
}
serial_writestring("\n[SMP] Booting Application Processors \n");
smp_info_t* info = smp_get_info();
uint32_t bsp_lapic_id = info->bsp_lapic_id;
uint32_t ap_count = 0;
for (uint64_t i = 0; i < mp_response->cpu_count; i++) {
struct limine_mp_info* cpu = mp_response->cpus[i];
if (cpu->lapic_id == bsp_lapic_id) continue;
uint64_t stack_top = smp_allocate_stack(i, AP_STACK_SIZE);
if (stack_top == 0) {
serial_printf("[SMP] Skipping CPU %u (stack alloc failed)\n", i);
info->cpus[i].state = CPU_FAULTED;
continue;
}
info->cpus[i].stack_top = stack_top;
info->cpus[i].state = CPU_BOOTED;
cpu->extra_argument = stack_top;
cpu->goto_address = (void*)ap_entry_point;
serial_printf("[SMP] Configured AP %lu (LAPIC ID %u) to boot at 0x%llx\n",
i, cpu->lapic_id, (uint64_t)ap_entry_point);
ap_count++;
}
__sync_synchronize();
if (ap_count > 0) {
serial_printf("[SMP] Waiting for %u AP(s) to initialize...\n", ap_count);
uint64_t timeout = 10000000;
while (ap_online_count < ap_count && timeout--)
asm volatile ("pause");
uint32_t online = ap_online_count;
if (online == ap_count)
serial_printf("[SMP SUCCESS] All %u AP(s) online!\n", ap_count);
else
serial_printf("[SMP WARNING] Only %u/%u AP(s) online (timeout)\n",
online, ap_count);
info->online_count = 1 + online;
} else {
serial_writestring("[SMP] No APs to boot\n");
}
serial_writestring("[SMP] AP Boot Sequence Complete \n\n");
}
static void smp_init_limine(struct limine_mp_response* response) {
if (!response) { serial_writestring("[SMP] Limine MP response is NULL\n"); return; }
serial_printf("[SMP] Initializing via Limine MP (CPU count: %u)\n",
response->cpu_count);
smp_info.cpu_count = response->cpu_count;
smp_info.online_count = 1;
acpi_madt_t* madt = (acpi_madt_t*)acpi_find_table("APIC", 0);
if (madt) {
smp_info.lapic_base = madt->local_apic_address;
serial_printf("[SMP] LAPIC base from ACPI: 0x%x\n", smp_info.lapic_base);
} else {
smp_info.lapic_base = 0xFEE00000;
}
for (uint64_t i = 0; i < response->cpu_count; i++) {
struct limine_mp_info* cpu = response->cpus[i];
smp_info.cpus[i].lapic_id = cpu->lapic_id;
smp_info.cpus[i].processor_id = cpu->lapic_id;
smp_info.cpus[i].acpi_id = 0;
smp_info.cpus[i].state = CPU_UNINITIALIZED;
smp_info.cpus[i].is_bsp = (cpu->lapic_id == response->bsp_lapic_id);
smp_info.cpus[i].cpu_index = i;
if (smp_info.cpus[i].is_bsp) {
smp_info.bsp_lapic_id = cpu->lapic_id;
smp_info.cpus[i].state = CPU_ONLINE;
serial_printf("[SMP] BSP detected - APIC ID: %u\n", cpu->lapic_id);
}
serial_printf("[SMP] CPU[%lu] - APIC ID: %u, Processor ID: %u, BSP: %s\n",
i, cpu->lapic_id, cpu->lapic_id,
smp_info.cpus[i].is_bsp ? "YES" : "NO");
}
}
void smp_init(struct limine_mp_response* mp_response) {
serial_writestring("\n[SMP] Initialization\n");
smp_init_limine(mp_response);
uint32_t bsp_index = 0;
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
if (smp_info.cpus[i].is_bsp) { bsp_index = i; break; }
for (uint32_t i = 0; i < smp_info.cpu_count; i++) {
tss[i] = (tss_t *)calloc(1, sizeof(tss_t));
if (!tss[i]) { serial_printf("[SMP ERROR] FAILED to allocate TSS for CPU %u\n", i); continue; }
tss[i]->rsp0 = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[0] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[1] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[2] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->ist[3] = smp_allocate_stack(i, KERNEL_STACK_SIZE);
tss[i]->iobase = sizeof(tss_t);
serial_printf("TSS[%u] base: 0x%llx\n", i, (uint64_t)tss[i]);
tss_entry_t *entry = (tss_entry_t *)&gdt.gdt_entries[5 + (i * 2)];
entry->limit_low = sizeof(tss_t) - 1;
uint64_t addr = (uint64_t)tss[i];
entry->base_low = addr & 0xffff;
entry->base_middle = (addr >> 16) & 0xff;
entry->access = 0x89;
entry->limit_high_and_flags = 0;
entry->base_high = (addr >> 24) & 0xff;
entry->base_higher = addr >> 32;
entry->zero = 0;
smp_info.cpus[i].tss_selector = TSS_SELECTOR_BASE + (i * 0x10);
}
gdtr.size = (5 + (smp_info.cpu_count * 2)) * sizeof(gdt_entry_t) - 1;
serial_printf("Reloading extended GDT on BSP...\n");
gdt_load();
load_tss(smp_info.cpus[bsp_index].tss_selector);
serial_printf("BSP TSS loaded (selector 0x%x)\n",
smp_info.cpus[bsp_index].tss_selector);
uint32_t current_lapic_id = lapic_get_id();
serial_printf("[SMP] Current LAPIC ID: %u\n", current_lapic_id);
for (uint32_t i = 0; i < smp_info.cpu_count; i++) {
if (smp_info.cpus[i].lapic_id == current_lapic_id) {
smp_info.cpus[i].is_bsp = true;
smp_info.cpus[i].state = CPU_ONLINE;
smp_info.bsp_lapic_id = current_lapic_id;
break;
}
}
smp_print_info();
init_percpu_regions();
smp_boot_aps(mp_response);
set_percpu_base(percpu_regions[bsp_index]);
serial_printf("PerCPU base set for BSP %u: 0x%llx\n",
smp_info.bsp_lapic_id, (uint64_t)percpu_regions[bsp_index]);
serial_writestring("[SMP] Initialization Complete \n\n");
}
smp_info_t* smp_get_info(void) { return &smp_info; }
uint32_t smp_get_cpu_count(void) { return smp_info.cpu_count; }
uint32_t smp_get_online_count(void){ return smp_info.online_count; }
bool smp_is_bsp(void) { return lapic_get_id() == smp_info.bsp_lapic_id; }
cpu_info_t* smp_get_current_cpu(void) {
uint32_t id = lapic_get_id();
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
if (smp_info.cpus[i].lapic_id == id) return &smp_info.cpus[i];
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
if (smp_info.cpus[i].is_bsp) return &smp_info.cpus[i];
return NULL;
}
void smp_wait_for_ready(void) {
serial_writestring("Waiting until all APs are fully ready...\n");
while (smp_get_online_count() < smp_get_cpu_count())
asm volatile ("pause");
serial_writestring("All APs ready.\n");
}
uint32_t smp_get_lapic_id_for_cpu(uint32_t cpu_index) {
smp_info_t* info = smp_get_info();
if (cpu_index >= info->cpu_count) return 0xFFFFFFFF;
return info->cpus[cpu_index].lapic_id;
}
void smp_print_info(void) {
serial_printf("\n[SMP] CPU Information \n");
serial_printf("Total CPUs: %u\n", smp_info.cpu_count);
serial_printf("Online CPUs: %u\n", smp_info.online_count);
serial_printf("BSP APIC ID: %u\n", smp_info.bsp_lapic_id);
serial_printf("LAPIC Base: 0x%llx\n",smp_info.lapic_base);
const char* states[] = {"UNINITIALIZED","BOOTED","ONLINE","OFFLINE","FAULTED"};
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
serial_printf("CPU[%u]: APIC ID: %u, Processor ID: %u, ACPI ID: %u, State: %s, BSP: %s\n",
i, smp_info.cpus[i].lapic_id, smp_info.cpus[i].processor_id,
smp_info.cpus[i].acpi_id, states[smp_info.cpus[i].state],
smp_info.cpus[i].is_bsp ? "YES" : "NO");
serial_printf("[SMP] End CPU Information \n");
}
void smp_print_info_fb(void) {
printf("[SMP] CPU Information \n");
printf("Total CPUs: %u\n", smp_info.cpu_count);
printf("Online CPUs: %u\n", smp_info.online_count);
printf("BSP APIC ID: %u\n", smp_info.bsp_lapic_id);
const char* states[] = {"UNINITIALIZED","BOOTED","ONLINE","OFFLINE","FAULTED"};
for (uint32_t i = 0; i < smp_info.cpu_count; i++)
printf("CPU[%u]: APIC ID: %u, State: %s, BSP: %s\n",
i, smp_info.cpus[i].lapic_id, states[smp_info.cpus[i].state],
smp_info.cpus[i].is_bsp ? "YES" : "NO");
}
+96
View File
@@ -0,0 +1,96 @@
#include "../../include/sse/fpu.h"
#include "../../include/io/serial.h"
#define COM1 0x3F8
void fpu_init(void) {
serial_writestring("[FPU] Initializing x87 FPU...\n");
if (!fpu_detect()) {
serial_writestring("[FPU] No FPU detected!\n");
return;
}
uint64_t cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 &= ~(1 << 2);
cr0 |= (1 << 1);
cr0 &= ~(1 << 3);
cr0 &= ~(1 << 5);
asm volatile("mov %0, %%cr0" : : "r"(cr0));
asm volatile("finit");
fpu_set_control_word(0x037F);
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 9);
cr4 |= (1ULL << 10);
asm volatile("mov %0, %%cr4" : : "r"(cr4));
uint32_t mxcsr = 0x1F80;
asm volatile("ldmxcsr %0" : : "m"(mxcsr));
serial_writestring("[FPU] SSE/SSE2 enabled (CR4.OSFXSR|OSXMMEXCPT)\n");
serial_writestring("[FPU] FPU initialized successfully\n");
}
bool fpu_detect(void) {
uint32_t edx;
asm volatile(
"mov $1, %%eax\n"
"cpuid\n"
"mov %%edx, %0\n"
: "=r"(edx)
:
: "eax", "ebx", "ecx", "edx"
);
return (edx & (1 << 0)) != 0;
}
void fpu_reset(void) {
asm volatile("finit");
}
void fpu_set_control_word(uint16_t cw) {
asm volatile("fldcw %0" : : "m"(cw));
}
uint16_t fpu_get_control_word(void) {
uint16_t cw;
asm volatile("fnstcw %0" : "=m"(cw));
return cw;
}
void fpu_set_status_word(uint16_t sw) {
(void)sw;
}
uint16_t fpu_get_status_word(void) {
uint16_t sw;
asm volatile("fnstsw %0" : "=m"(sw));
return sw;
}
void fpu_set_tag_word(uint16_t tw) {
(void)tw;
}
uint16_t fpu_get_tag_word(void) {
struct {
uint16_t control_word;
uint16_t status_word;
uint16_t tag_word;
uint16_t fpu_ip;
uint16_t fpu_cs;
uint16_t fpu_opcode;
uint16_t fpu_dp;
uint16_t fpu_ds;
} __attribute__((packed)) fpu_env;
asm volatile("fstenv %0" : "=m"(fpu_env));
return fpu_env.tag_word;
}
+238
View File
@@ -0,0 +1,238 @@
#include "../../include/sse/sse.h"
#include "../../include/io/serial.h"
#include "../../include/apic/apic.h"
#include <string.h>
#include <stdio.h>
static uint32_t cpuid_edx = 0;
static uint32_t cpuid_ecx = 0;
static uint32_t cpuid_ebx_ext = 0;
static bool features_cached = false;
static void cpuid_cache_features(void) {
if (features_cached) return;
uint32_t eax, ebx, ecx, edx;
asm volatile(
"mov $1, %%eax\n"
"cpuid\n"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
:
: "memory"
);
cpuid_edx = edx;
cpuid_ecx = ecx;
uint32_t max_leaf;
asm volatile("cpuid" : "=a"(max_leaf) : "0"(0) : "ebx", "ecx", "edx");
if (max_leaf >= 7) {
asm volatile(
"mov $7, %%eax\n"
"xor %%ecx, %%ecx\n"
"cpuid\n"
"mov %%ebx, %0\n"
: "=r"(cpuid_ebx_ext)
:
: "eax", "ecx", "edx"
);
}
features_cached = true;
}
bool sse_supported(void) { cpuid_cache_features(); return (cpuid_edx & (1 << 25)) != 0; }
bool sse2_supported(void) { cpuid_cache_features(); return (cpuid_edx & (1 << 26)) != 0; }
bool sse3_supported(void) { cpuid_cache_features(); return (cpuid_ecx & (1 << 0)) != 0; }
bool ssse3_supported(void) { cpuid_cache_features(); return (cpuid_ecx & (1 << 9)) != 0; }
bool sse4_1_supported(void){ cpuid_cache_features(); return (cpuid_ecx & (1 << 19)) != 0; }
bool sse4_2_supported(void){ cpuid_cache_features(); return (cpuid_ecx & (1 << 20)) != 0; }
bool avx_supported(void) { cpuid_cache_features(); return (cpuid_ecx & (1 << 28)) != 0; }
bool avx2_supported(void) { cpuid_cache_features(); return (cpuid_ebx_ext & (1 << 5)) != 0; }
bool mmx_supported(void) { cpuid_cache_features(); return (cpuid_edx & (1 << 23)) != 0; }
void sse_init(void) {
serial_writestring("[SSE] Initializing SSE/AVX...\n");
cpuid_cache_features();
if (!sse_supported()) {
serial_writestring("[SSE] SSE not supported — skipping SSE/AVX init\n");
return;
}
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1 << 9);
cr4 |= (1 << 10);
if (avx_supported()) {
uint32_t ecx;
asm volatile(
"mov $1, %%eax\n"
"cpuid\n"
"mov %%ecx, %0\n"
: "=r"(ecx)
:
: "eax", "ebx", "edx"
);
if (ecx & (1 << 27)) {
serial_writestring("[SSE] AVX supported, enabling...\n");
cr4 |= (1 << 18);
uint64_t xcr0;
asm volatile("xgetbv" : "=a"(xcr0) : "c"(0) : "edx");
xcr0 |= (1 << 1) | (1 << 2);
asm volatile("xsetbv" : : "c"(0), "a"(xcr0), "d"(xcr0 >> 32));
}
}
asm volatile("mov %0, %%cr4" : : "r"(cr4));
sse_set_mxcsr(MXCSR_DEFAULT);
serial_writestring("[SSE] SSE initialized successfully\n");
if (sse_supported()) serial_writestring("[SSE] SSE: YES\n");
if (sse2_supported()) serial_writestring("[SSE] SSE2: YES\n");
if (sse3_supported()) serial_writestring("[SSE] SSE3: YES\n");
if (sse4_1_supported())serial_writestring("[SSE] SSE4.1: YES\n");
if (sse4_2_supported())serial_writestring("[SSE] SSE4.2: YES\n");
if (avx_supported()) serial_writestring("[SSE] AVX: YES\n");
if (mmx_supported()) serial_writestring("[SSE] MMX: YES\n");
}
void sse_set_mxcsr(uint32_t mxcsr) {
if (!sse_supported()) return;
asm volatile("ldmxcsr %0" : : "m"(mxcsr));
}
uint32_t sse_get_mxcsr(void) {
if (!sse_supported()) return 0;
uint32_t mxcsr;
asm volatile("stmxcsr %0" : "=m"(mxcsr));
return mxcsr;
}
void mmx_enter(void) {
if (!mmx_supported()) return;
}
void mmx_exit(void) {
if (!mmx_supported()) return;
asm volatile("emms");
}
void sse_memcpy_fast(void *dest, const void *src, size_t n) {
if (!sse_supported() || n < 64) {
memcpy(dest, src, n);
return;
}
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
size_t align_offset = (16 - ((uintptr_t)d & 0xF)) & 0xF;
if (align_offset > n) align_offset = n;
for (size_t i = 0; i < align_offset; i++) d[i] = s[i];
d += align_offset; s += align_offset; n -= align_offset;
size_t i;
for (i = 0; i + 64 <= n; i += 64) {
asm volatile(
"movdqu (%0), %%xmm0\n"
"movdqu 16(%0), %%xmm1\n"
"movdqu 32(%0), %%xmm2\n"
"movdqu 48(%0), %%xmm3\n"
"movdqu %%xmm0, (%1)\n"
"movdqu %%xmm1, 16(%1)\n"
"movdqu %%xmm2, 32(%1)\n"
"movdqu %%xmm3, 48(%1)\n"
:
: "r"(s + i), "r"(d + i)
: "xmm0", "xmm1", "xmm2", "xmm3", "memory"
);
}
for (; i < n; i++) d[i] = s[i];
}
void sse_memset_fast(void *dest, int value, size_t n) {
if (!sse_supported() || n < 64) {
memset(dest, value, n);
return;
}
uint8_t *d = (uint8_t *)dest;
uint8_t v = (uint8_t)value;
uint64_t pattern64 =
(uint64_t)v << 56 | (uint64_t)v << 48 | (uint64_t)v << 40 | (uint64_t)v << 32 |
(uint64_t)v << 24 | (uint64_t)v << 16 | (uint64_t)v << 8 | (uint64_t)v;
size_t align_offset = (16 - ((uintptr_t)d & 0xF)) & 0xF;
if (align_offset > n) align_offset = n;
for (size_t i = 0; i < align_offset; i++) d[i] = v;
d += align_offset; n -= align_offset;
asm volatile(
"movq %0, %%xmm0\n"
"punpcklqdq %%xmm0, %%xmm0\n"
: : "r"(pattern64) : "xmm0"
);
size_t i;
for (i = 0; i + 64 <= n; i += 64) {
asm volatile(
"movdqu %%xmm0, (%0)\n"
"movdqu %%xmm0, 16(%0)\n"
"movdqu %%xmm0, 32(%0)\n"
"movdqu %%xmm0, 48(%0)\n"
:
: "r"(d + i)
: "memory"
);
}
for (; i < n; i++) d[i] = v;
}
void print_simd_cpuid(void) {
printf("\n=== CPUID/SIMD INFORMATION ===\n");
uint32_t eax, ebx, ecx, edx;
char vendor[13] = {0};
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(0));
*(uint32_t *)vendor = ebx;
*(uint32_t *)(vendor + 4) = edx;
*(uint32_t *)(vendor + 8) = ecx;
printf("CPU Vendor: %s\n", vendor);
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
printf("Processor Signature: 0x%08x\n", eax);
printf("Stepping: %d Model: %d Family: %d\n",
eax & 0xF, (eax >> 4) & 0xF, (eax >> 8) & 0xF);
printf("\nSIMD Extensions:\n");
printf("MMX: %s\n", (edx & (1 << 23)) ? "YES" : "NO");
printf("SSE: %s\n", (edx & (1 << 25)) ? "YES" : "NO");
printf("SSE2: %s\n", (edx & (1 << 26)) ? "YES" : "NO");
printf("SSE3: %s\n", (ecx & (1 << 0)) ? "YES" : "NO");
printf("SSSE3: %s\n", (ecx & (1 << 9)) ? "YES" : "NO");
printf("SSE4.1: %s\n", (ecx & (1 << 19)) ? "YES" : "NO");
printf("SSE4.2: %s\n", (ecx & (1 << 20)) ? "YES" : "NO");
printf("AVX: %s\n", (ecx & (1 << 28)) ? "YES" : "NO");
printf("=== END OF CPUID/SIMD INFO ===\n\n");
}
void enable_fsgsbase(void) {
uint32_t eax = 7, ebx, ecx = 0, edx;
asm volatile("cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "0"(eax), "2"(ecx));
if (ebx & (1 << 0)) {
uint64_t cr4;
asm volatile("mov %%cr4, %0" : "=r"(cr4));
cr4 |= (1ULL << 16);
asm volatile("mov %0, %%cr4" :: "r"(cr4));
serial_printf("[FSGSBASE] Enabled on CPU %u\n", lapic_get_id());
} else {
serial_writestring("[FSGSBASE] Not supported — using MSR fallback\n");
}
}
+431
View File
@@ -0,0 +1,431 @@
#include "../../include/syscall/syscall.h"
#include "../../include/syscall/syscall_nums.h"
#include "../../include/syscall/errno.h"
#include "../../include/drivers/disk.h"
#include "../../include/drivers/blkdev.h"
#include "../../include/drivers/ata.h"
#include "../../include/drivers/partition.h"
#include "../../include/sched/sched.h"
#include "../../include/sched/capabilities.h"
#include "../../include/smp/percpu.h"
#include "../../include/io/serial.h"
#include "../../include/memory/pmm.h"
#include "../../include/fs/vfs.h"
#include "../../include/fs/ext2.h"
#include "../../include/fs/fat32.h"
#include <string.h>
#include <stdlib.h>
static inline task_t* disk_cur_task(void) {
percpu_t* pc = get_percpu();
return pc ? (task_t*)pc->current_task : NULL;
}
static int disk_strncpy_from_user(char *dst, const char *src, size_t max) {
uintptr_t addr = (uintptr_t)src;
if (addr < 0x1000ULL || addr >= 0x0000800000000000ULL) return -EFAULT;
for (size_t i = 0; i < max - 1; i++) {
dst[i] = src[i];
if (!dst[i]) return (int)i;
}
dst[max-1] = '\0';
return (int)(max-1);
}
static int user_ptr_ok(uint64_t p) {
return p >= 0x1000ULL && p < 0x0000800000000000ULL;
}
int64_t sys_disk_mount(uint64_t devname_ptr, uint64_t path_ptr, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64], path[256];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
serial_printf("[SYSCALL] disk_mount('%s', '%s') by pid=%u\n", devname, path, t->pid);
return disk_mount(devname, path);
}
int64_t sys_disk_umount(uint64_t path_ptr, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a2; (void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char path[256];
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
return disk_umount(path);
}
int64_t sys_disk_format(uint64_t devname_ptr, uint64_t label_ptr,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64], label[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (label_ptr) {
if (disk_strncpy_from_user(label, (const char *)label_ptr, sizeof(label)) < 0) return -EFAULT;
} else {
strncpy(label, devname, sizeof(label) - 1);
}
return disk_format(devname, label);
}
int64_t sys_disk_info(uint64_t index, uint64_t buf_ptr, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
if (!buf_ptr) return -EINVAL;
blkdev_t *dev = blkdev_get((int)index);
if (!dev || !dev->present) return -ENODEV;
struct {
char name[32];
uint64_t sectors;
uint64_t size_bytes;
char model[41];
uint8_t present;
uint8_t _pad[6];
} info;
memset(&info, 0, sizeof(info));
strncpy(info.name, dev->name, 31);
info.sectors = dev->sector_count;
info.size_bytes = dev->size_bytes;
info.present = 1;
ata_drive_t *ata = (ata_drive_t *)dev->priv;
if (ata) strncpy(info.model, ata->model, 40);
if (!user_ptr_ok(buf_ptr)) return -EFAULT;
memcpy((void *)buf_ptr, &info, sizeof(info));
return 0;
}
int64_t sys_disk_read_raw(uint64_t devname_ptr, uint64_t lba, uint64_t count,
uint64_t buf_ptr, uint64_t a5, uint64_t a6)
{
(void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (!user_ptr_ok(buf_ptr)) return -EFAULT;
if (count == 0 || count > 256) return -EINVAL;
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
if (lba + count > dev->sector_count) return -EINVAL;
int r = dev->ops->read_sectors(dev, lba, (uint32_t)count, (void *)buf_ptr);
if (r < 0) return r;
return (int64_t)(count * dev->sector_size);
}
int64_t sys_disk_write_raw(uint64_t devname_ptr, uint64_t lba, uint64_t count,
uint64_t buf_ptr, uint64_t a5, uint64_t a6)
{
(void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (!user_ptr_ok(buf_ptr)) return -EFAULT;
if (count == 0 || count > 256) return -EINVAL;
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
if (lba + count > dev->sector_count) return -EINVAL;
int r = dev->ops->write_sectors(dev, lba, (uint32_t)count, (const void *)buf_ptr);
if (r < 0) return r;
if (dev->ops->flush) dev->ops->flush(dev);
return (int64_t)(count * dev->sector_size);
}
int64_t sys_disk_partition(uint64_t devname_ptr, uint64_t specs_ptr, uint64_t nparts,
uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (!user_ptr_ok(specs_ptr)) return -EFAULT;
if (nparts == 0 || nparts > 4) return -EINVAL;
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
cervus_mbr_part_t specs[4];
memset(specs, 0, sizeof(specs));
memcpy(specs, (const void *)specs_ptr, sizeof(cervus_mbr_part_t) * nparts);
mbr_partition_t parts[4];
memset(parts, 0, sizeof(parts));
for (uint64_t i = 0; i < nparts; i++) {
parts[i].boot_flag = specs[i].boot_flag ? 0x80 : 0x00;
parts[i].type = specs[i].type;
parts[i].lba_start = specs[i].lba_start;
parts[i].sector_count = specs[i].sector_count;
parts[i].chs_start[0] = 0xFE;
parts[i].chs_start[1] = 0xFF;
parts[i].chs_start[2] = 0xFF;
parts[i].chs_end[0] = 0xFE;
parts[i].chs_end[1] = 0xFF;
parts[i].chs_end[2] = 0xFF;
}
uint32_t sig = 0xCE705CE7;
int r = partition_write_mbr(dev, parts, sig);
if (r < 0) return r;
if (dev->ops->flush) dev->ops->flush(dev);
partition_scan(dev);
return 0;
}
int64_t sys_disk_mkfs_fat32(uint64_t devname_ptr, uint64_t label_ptr,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a3; (void)a4; (void)a5; (void)a6;
task_t *t = disk_cur_task();
if (!t) return -ESRCH;
if (t->uid != 0 && !(t->capabilities & (1ULL << 1))) return -EPERM;
char devname[64], label[16];
if (disk_strncpy_from_user(devname, (const char *)devname_ptr, sizeof(devname)) < 0) return -EFAULT;
if (label_ptr) {
if (disk_strncpy_from_user(label, (const char *)label_ptr, sizeof(label)) < 0) return -EFAULT;
} else {
strncpy(label, "CERVUS", sizeof(label) - 1);
label[sizeof(label) - 1] = '\0';
}
const char *name = devname;
if (strncmp(name, "/dev/", 5) == 0) name += 5;
blkdev_t *dev = blkdev_get_by_name(name);
if (!dev) return -ENODEV;
return fat32_format(dev, label);
}
int64_t sys_disk_bios_install(uint64_t a1, uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a4; (void)a5; (void)a6;
const char *disk_name = (const char *)a1;
const void *sys_data = (const void *)a2;
uint32_t sys_size = (uint32_t)a3;
if (!disk_name || !sys_data || sys_size < 512) return -EINVAL;
blkdev_t *dev = blkdev_get_by_name(disk_name);
if (!dev) return -ENOENT;
if (dev->sector_size != 512) return -EINVAL;
uint8_t sector0[512];
int r = dev->ops->read_sectors(dev, 0, 1, sector0);
if (r < 0) return r;
uint8_t saved_timestamp[6];
uint8_t saved_parttable[70];
memcpy(saved_timestamp, sector0 + 218, 6);
memcpy(saved_parttable, sector0 + 440, 70);
const uint8_t *src = (const uint8_t *)sys_data;
memcpy(sector0, src, 512);
memcpy(sector0 + 218, saved_timestamp, 6);
memcpy(sector0 + 440, saved_parttable, 70);
uint64_t stage2_loc = 512;
memcpy(sector0 + 0x1A4, &stage2_loc, 8);
uint32_t stage2_bytes = sys_size - 512;
uint32_t stage2_sectors = (stage2_bytes + 511) / 512;
if (1 + stage2_sectors >= 2048) return -ENOSPC;
uint8_t sector_buf[512];
for (uint32_t i = 0; i < stage2_sectors; i++) {
uint32_t off = i * 512;
uint32_t take = (stage2_bytes - off >= 512) ? 512 : (stage2_bytes - off);
memset(sector_buf, 0, 512);
memcpy(sector_buf, src + 512 + off, take);
r = dev->ops->write_sectors(dev, 1 + i, 1, sector_buf);
if (r < 0) return r;
}
r = dev->ops->write_sectors(dev, 0, 1, sector0);
if (r < 0) return r;
if (dev->ops->flush) dev->ops->flush(dev);
serial_printf("[bios-install] deployed: stage1=512B at LBA 0, stage2=%uB at LBA 1..%u\n",
stage2_bytes, stage2_sectors);
return 0;
}
int64_t sys_disk_list_parts(uint64_t out_ptr, uint64_t max,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
{
(void)a3; (void)a4; (void)a5; (void)a6;
if (!user_ptr_ok(out_ptr)) return -EFAULT;
if (max == 0) return -EINVAL;
cervus_part_info_t *out = (cervus_part_info_t *)out_ptr;
int total = blkdev_count();
uint64_t written = 0;
for (int i = 0; i < total && written < max; i++) {
blkdev_t *d = blkdev_get(i);
if (!d || !d->present) continue;
size_t nlen = strlen(d->name);
bool is_part = false;
if (nlen >= 2) {
for (size_t k = 0; k < nlen; k++) {
if (d->name[k] >= '0' && d->name[k] <= '9') { is_part = true; break; }
}
}
cervus_part_info_t info;
memset(&info, 0, sizeof(info));
strncpy(info.part_name, d->name, sizeof(info.part_name) - 1);
if (is_part) {
char base[32]; int bi = 0;
for (size_t k = 0; k < nlen && bi < 31; k++) {
if (d->name[k] >= '0' && d->name[k] <= '9') break;
base[bi++] = d->name[k];
}
base[bi] = '\0';
strncpy(info.disk_name, base, sizeof(info.disk_name) - 1);
info.part_num = (uint32_t)atoi(d->name + bi);
} else {
strncpy(info.disk_name, d->name, sizeof(info.disk_name) - 1);
info.part_num = 0;
}
info.size_bytes = d->size_bytes;
info.sector_count = d->sector_count;
info.lba_start = 0;
info.type = 0;
info.bootable = 0;
memcpy(&out[written], &info, sizeof(info));
written++;
}
return (int64_t)written;
}
int64_t sys_unlink(uint64_t path_ptr, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a2;(void)a3;(void)a4;(void)a5;(void)a6;
char path[256];
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
char dirpath[256]; strncpy(dirpath, path, 255);
char *slash = NULL;
for (int i = (int)strlen(dirpath)-1; i >= 0; i--) { if (dirpath[i]=='/') { slash=&dirpath[i]; break; } }
if (!slash) return -EINVAL;
char name[256]; strncpy(name, slash+1, 255);
if (slash==dirpath) dirpath[1]='\0'; else *slash='\0';
vnode_t *dir = NULL;
int r = vfs_lookup(dirpath, &dir);
if (r<0) return r;
if (!dir->ops || !dir->ops->unlink) { vnode_unref(dir); return -ENOSYS; }
r = dir->ops->unlink(dir, name);
vnode_unref(dir);
if (r == 0) vfs_sync_all();
return r;
}
int64_t sys_rmdir(uint64_t p, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
return sys_unlink(p,a2,a3,a4,a5,a6);
}
int64_t sys_mkdir(uint64_t path_ptr, uint64_t mode, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3;(void)a4;(void)a5;(void)a6;
char path[256];
if (disk_strncpy_from_user(path, (const char *)path_ptr, sizeof(path)) < 0) return -EFAULT;
int r = vfs_mkdir(path, (uint32_t)mode);
if (r == 0) vfs_sync_all();
return r;
}
int64_t sys_rename(uint64_t old_ptr, uint64_t new_ptr, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3;(void)a4;(void)a5;(void)a6;
char oldp[256], newp[256];
if (disk_strncpy_from_user(oldp, (const char *)old_ptr, 256) < 0) return -EFAULT;
if (disk_strncpy_from_user(newp, (const char *)new_ptr, 256) < 0) return -EFAULT;
vnode_t *src_node = NULL;
int r = vfs_lookup(oldp, &src_node);
if (r < 0) return r;
vnode_t *dst_node = NULL;
if (vfs_lookup(newp, &dst_node) == 0) {
if (dst_node->type == VFS_NODE_DIR) {
const char *base = oldp;
for (const char *p = oldp; *p; p++) if (*p == '/') base = p + 1;
size_t dlen = strlen(newp);
if (dlen + 1 + strlen(base) < 255) {
if (newp[dlen-1] != '/') { newp[dlen] = '/'; newp[dlen+1] = '\0'; dlen++; }
strncat(newp, base, 254 - dlen);
}
}
vnode_unref(dst_node);
}
vfs_file_t *src_f = NULL, *dst_f = NULL;
r = vfs_open(oldp, O_RDONLY, 0, &src_f);
if (r < 0) { vnode_unref(src_node); return r; }
r = vfs_open(newp, O_WRONLY | O_CREAT | O_TRUNC, src_node->mode, &dst_f);
if (r < 0) { vfs_close(src_f); vnode_unref(src_node); return r; }
char buf[512]; int64_t n;
while ((n = vfs_read(src_f, buf, sizeof(buf))) > 0) vfs_write(dst_f, buf, (size_t)n);
vfs_close(src_f);
vfs_close(dst_f);
vnode_unref(src_node);
char dirp[256]; strncpy(dirp, oldp, 255); dirp[255] = '\0';
char *sl = NULL;
for (int i = (int)strlen(dirp)-1; i >= 0; i--) if (dirp[i] == '/') { sl = &dirp[i]; break; }
if (sl) {
char nm[256]; strncpy(nm, sl+1, 255);
if (sl == dirp) dirp[1] = '\0'; else *sl = '\0';
vnode_t *dir = NULL;
if (vfs_lookup(dirp, &dir) == 0) {
if (dir->ops && dir->ops->unlink) dir->ops->unlink(dir, nm);
vnode_unref(dir);
}
}
vfs_sync_all();
return 0;
}
int64_t sys_list_mounts(uint64_t a1, uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
vfs_mount_info_t *out = (vfs_mount_info_t *)a1;
int max = (int)a2;
if (!out || max <= 0) return -EINVAL;
return vfs_list_mounts(out, max);
}
int64_t sys_statvfs(uint64_t a1, uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5, uint64_t a6) {
(void)a3; (void)a4; (void)a5; (void)a6;
const char *path = (const char *)a1;
vfs_statvfs_t *out = (vfs_statvfs_t *)a2;
if (!path || !out) return -EINVAL;
return vfs_statvfs(path, out);
}
File diff suppressed because it is too large Load Diff
+145
View File
@@ -0,0 +1,145 @@
section .text
global syscall_entry
extern syscall_handler_c
extern sched_reschedule
PERCPU_KERNEL_RSP equ 0
PERCPU_USER_RSP equ 8
PERCPU_SAVED_RBP equ 48
PERCPU_SAVED_RBX equ 56
PERCPU_SAVED_R12 equ 64
PERCPU_SAVED_R13 equ 72
PERCPU_SAVED_R14 equ 80
PERCPU_SAVED_R15 equ 88
PERCPU_SAVED_R11 equ 96
PERCPU_SAVED_RIP equ 104
PERCPU_NEED_RESCHED equ 40
PERCPU_CURRENT_TASK equ 24
TASK_USER_RSP equ 144
TASK_USER_SAVED_RIP equ 272
TASK_USER_SAVED_RBP equ 280
TASK_USER_SAVED_RBX equ 288
TASK_USER_SAVED_R12 equ 296
TASK_USER_SAVED_R13 equ 304
TASK_USER_SAVED_R14 equ 312
TASK_USER_SAVED_R15 equ 320
TASK_USER_SAVED_R11 equ 328
syscall_entry:
swapgs
mov [gs:PERCPU_USER_RSP], rsp
mov [gs:PERCPU_SAVED_RBP], rbp
mov [gs:PERCPU_SAVED_RBX], rbx
mov [gs:PERCPU_SAVED_R12], r12
mov [gs:PERCPU_SAVED_R13], r13
mov [gs:PERCPU_SAVED_R14], r14
mov [gs:PERCPU_SAVED_R15], r15
mov [gs:PERCPU_SAVED_R11], r11
mov [gs:PERCPU_SAVED_RIP], rcx
mov rsp, [gs:PERCPU_KERNEL_RSP]
push r11
push rcx
push rbx
push r12
push r13
push r14
push r15
push rbp
push rcx
mov rcx, rdx
mov r9, r8
mov r8, r10
mov rdx, rsi
mov rsi, rdi
mov rdi, rax
call syscall_handler_c
add rsp, 8
pop rbp
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rcx
pop r11
.check_resched:
cmp byte [gs:PERCPU_NEED_RESCHED], 0
je .no_resched
mov byte [gs:PERCPU_NEED_RESCHED], 0
push rax
call sched_reschedule
pop rax
jmp .check_resched
.no_resched:
cli
mov r10, rax
mov rax, [gs:PERCPU_CURRENT_TASK]
mov rcx, [rax + TASK_USER_SAVED_RIP]
test rcx, rcx
jnz .rip_ok
push rax
push rcx
extern serial_printf
mov rdi, rax
mov rsi, [rax + 168]
mov esi, esi
mov rdx, [rax + 272]
mov rcx, [rax + 144]
lea rdi, [rel .fmt_zero_rip]
call serial_printf
lea rdi, [rel .msg_zero_rip]
extern kernel_panic
call kernel_panic
.fmt_zero_rip: db "[NO_RESCHED-BUG] task=0x%llx pid=%u user_saved_rip=0x%llx user_rsp=0x%llx", 10, 0
.msg_zero_rip: db "sysret: user_saved_rip=0 — would fault at NULL", 0
.rip_ok:
mov rsp, [rax + TASK_USER_RSP]
mov rcx, [rax + TASK_USER_SAVED_RIP]
mov r11, [rax + TASK_USER_SAVED_R11]
mov rbp, [rax + TASK_USER_SAVED_RBP]
mov rbx, [rax + TASK_USER_SAVED_RBX]
mov r12, [rax + TASK_USER_SAVED_R12]
mov r13, [rax + TASK_USER_SAVED_R13]
mov r14, [rax + TASK_USER_SAVED_R14]
mov r15, [rax + TASK_USER_SAVED_R15]
mov rax, r10
and r11, 0x00000000003C0FFF
or r11, 0x0000000000000202
mov r9, rcx
shr r9, 47
jnz .sysret_bad_rip
mov r9, rsp
shr r9, 47
jnz .sysret_bad_rsp
swapgs
o64 sysret
.sysret_bad_rip:
extern sysret_bad_rip_panic
sti
mov rdi, rcx
mov rsi, rax
call sysret_bad_rip_panic
cli
hlt
.sysret_bad_rsp:
extern sysret_bad_rsp_panic
sti
mov rdi, rsp
mov rsi, rcx
call sysret_bad_rsp_panic
cli
hlt
+19
View File
@@ -0,0 +1,19 @@
#ifndef _CTYPE_H
#define _CTYPE_H
int isalnum(int c);
int isalpha(int c);
int isblank(int c);
int iscntrl(int c);
int isdigit(int c);
int isgraph(int c);
int islower(int c);
int isupper(int c);
int isprint(int c);
int ispunct(int c);
int isspace(int c);
int isxdigit(int c);
int tolower(int c);
int toupper(int c);
#endif
+62
View File
@@ -0,0 +1,62 @@
#ifndef _MATH_H
#define _MATH_H
#include <stdint.h>
int abs(int x);
double fabs(double x);
double pow(double base, double exp);
double pow10(int n);
int isinf(double x);
int isnan(double x);
static inline double floor(double x) {
int64_t i = (int64_t)x;
return (double)(i - (x < (double)i));
}
static inline double ceil(double x) {
int64_t i = (int64_t)x;
return (double)(i + (x > (double)i));
}
static inline double round(double x) {
return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
static inline double sqrt(double x) {
double result;
asm volatile ("sqrtsd %1, %0" : "=x"(result) : "x"(x));
return result;
}
static inline float sqrtf(float x) {
float result;
asm volatile ("sqrtss %1, %0" : "=x"(result) : "x"(x));
return result;
}
static inline double log2(double x) {
double result;
asm volatile (
"fld1\n\t"
"fld %1\n\t"
"fyl2x\n\t"
"fstp %0\n\t"
: "=m"(result) : "m"(x)
);
return result;
}
#define MIN(a, b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a < _b ? _a : _b; })
#define MAX(a, b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a > _b ? _a : _b; })
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
#define IS_POWER_OF_TWO(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
#define INFINITY (1.0/0.0)
#define NAN (0.0/0.0)
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef _STDIO_H
#define _STDIO_H
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define EOF (-1)
#define MAX_SCROLL_LINES 1000
int putchar(int c);
int puts(const char *str);
int printf(const char *format, ...);
int sprintf (char* restrict buf, const char* restrict fmt, ...);
int snprintf(char* restrict buf, size_t size, const char* restrict fmt, ...);
int vsprintf (char* restrict buf, const char* restrict fmt, va_list ap);
int vsnprintf(char* restrict buf, size_t size, const char* restrict fmt, va_list ap);
int vprintf(const char* restrict fmt, va_list ap);
int getchar(void);
int scanf (const char * restrict fmt, ...);
int vscanf(const char * restrict fmt, va_list ap);
int sscanf (const char * restrict str, const char * restrict fmt, ...);
int vsscanf(const char *str, const char *fmt, va_list ap);
extern uint32_t cursor_x;
extern uint32_t cursor_y;
extern uint32_t text_color;
extern uint32_t bg_color;
void scroll_screen(int lines);
uint32_t get_screen_width(void);
uint32_t get_screen_height(void);
void set_cursor_position(uint32_t x, uint32_t y);
void get_cursor_position(uint32_t *x, uint32_t *y);
void set_text_color(uint32_t color);
void clear_screen(void);
void scroll_up(int lines);
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef _STDLIB_H
#define _STDLIB_H
#include <stddef.h>
#include <math.h>
char* itoa(int val, char* restrict str, int base);
long strtol (const char * restrict s, char ** restrict end, int base);
unsigned long strtoul (const char * restrict s, char ** restrict end, int base);
long long strtoll (const char * restrict s, char ** restrict end, int base);
unsigned long long strtoull(const char * restrict s, char ** restrict end, int base);
static inline int atoi(const char* s) { return (int)strtol (s, (char**)0, 10); }
static inline long atol(const char* s) { return strtol (s, (char**)0, 10); }
void* malloc (size_t size);
void* calloc (size_t nmemb, size_t size);
void* realloc(void* ptr, size_t size);
void free (void* ptr);
void* aligned_alloc(size_t alignment, size_t size);
void aligned_free (void* ptr);
static inline void abort(void) {
__asm__ volatile ("cli; hlt");
__builtin_unreachable();
}
#endif
+45
View File
@@ -0,0 +1,45 @@
#ifndef _STRING_H
#define _STRING_H
#include <stddef.h>
#include <stdint.h>
void* memcpy (void* restrict dst, const void* restrict src, size_t n);
void* memmove(void* dst, const void* src, size_t n);
void* memset (void* dst, int c, size_t n);
int memcmp (const void* a, const void* b, size_t n);
void *memchr(void *p, int val, size_t n);
void *rawmemchr(void *p, int c);
void* memset_explicit(void* dst, int c, size_t n);
size_t strlen (const char* s);
size_t strnlen(const char* s, size_t maxlen);
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
char *strcat(char *dest, const char *src);
char* strncat(char* restrict dst, const char* restrict src, size_t n);
int strcmp (const char* a, const char* b);
int strncmp(const char* a, const char* b, size_t n);
char* strchr (const char* s, int c);
char* strrchr(const char* s, int c);
char* strstr (const char* haystack, const char* needle);
char* strpbrk(const char* s, const char* accept);
size_t strspn (const char* s, const char* accept);
size_t strcspn(const char* s, const char* reject);
char *strtok(char *str, const char *delim);
long strtol (const char* restrict s, char** restrict end, int base);
unsigned long strtoul(const char* restrict s, char** restrict end, int base);
char* strdup(const char* s);
static inline void bzero(void* s, size_t n) {
memset(s, 0, n);
}
#endif
+7
View File
@@ -0,0 +1,7 @@
#include <ctype.h>
int isalnum(int c) {
return (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z');
}
+6
View File
@@ -0,0 +1,6 @@
#include <ctype.h>
int isalpha(int c) {
return (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z');
}
+5
View File
@@ -0,0 +1,5 @@
#include <ctype.h>
int isblank(int c) {
return c == ' ' || c == '\t';
}
+8
View File
@@ -0,0 +1,8 @@
#include <ctype.h>
int iscntrl(int c) {
return c == '\n' || c == '\t' ||
c == '\r' || c == '\b' ||
c == '\f' || c == '\a' ||
c == '\0';
}
+5
View File
@@ -0,0 +1,5 @@
#include <ctype.h>
int isdigit(int c) {
return c >= '0' && c <= '9';
}
+5
View File
@@ -0,0 +1,5 @@
#include <ctype.h>
int isgraph(int c) {
return c >= 33 && c <= 126;
}
+5
View File
@@ -0,0 +1,5 @@
#include <ctype.h>
int islower(int c) {
return c >= 'a' && c <= 'z';
}
+5
View File
@@ -0,0 +1,5 @@
#include <ctype.h>
int isprint(int c) {
return c >= 32 && c <= 126;
}
+8
View File
@@ -0,0 +1,8 @@
#include <ctype.h>
int ispunct(int c) {
return (c >= 33 && c <= 47) ||
(c >= 58 && c <= 64) ||
(c >= 91 && c <= 96) ||
(c >= 123 && c <= 126);
}
+6
View File
@@ -0,0 +1,6 @@
#include <ctype.h>
int isspace(int c) {
return c == ' ' || c == '\t' || c == '\n' ||
c == '\v' || c == '\f' || c == '\r';
}

Some files were not shown because too many files have changed in this diff Show More