├── .gitignore ├── .gitmodules ├── LICENSE ├── LINKS.md ├── README.md ├── apps ├── cat.nf ├── echo.nf └── ls.nf ├── config.py ├── data ├── bg-blue.pix ├── bg-green.pix ├── bofh │ ├── ex1 │ ├── ex2 │ ├── ex3 │ └── ex4 ├── fortune │ ├── fort1 │ └── fort2 ├── hello │ └── world ├── init.nf └── vga_8x16.fnt ├── gui ├── bar.c ├── fbcon.c ├── font.c ├── gui.c └── win.c ├── include ├── gui │ └── gui.h ├── kernel │ └── kernel.h └── libc │ ├── array.h │ ├── stdio.h │ ├── string.h │ └── types.h ├── kernel ├── cmos.c ├── cpu.s ├── crtc.c ├── devfs.c ├── file.c ├── intr.c ├── kbd.c ├── kheap.c ├── kmain.c ├── mboot.c ├── pit.c ├── pmem.c ├── printk.c ├── ptt.c ├── romfs.c ├── task.c ├── uart.c ├── vbe.c ├── vfs.c └── vt.c ├── libc ├── snprintf.c └── string.c ├── loader └── loader.s ├── make.py └── misc ├── app.ld ├── bochscmd ├── bochsrc ├── grub.cfg ├── kernel.ld ├── shot-qemu.png └── shot-vbox.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /build/* 3 | /tmp/* 4 | *.swp 5 | __pycache__ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nf"] 2 | path = nf 3 | url = https://github.com/luke8086/nf.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /LINKS.md: -------------------------------------------------------------------------------- 1 | ###External documentation### 2 | 3 | **Coding style** 4 | 5 | - ISO/IEC 9899:201x (C11) - http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 6 | - NetBSD C style guide - http://cvsweb.netbsd.org/bsdweb.cgi/src/share/misc/style?rev=HEAD&content-type=text/x-cvsweb-markup 7 | 8 | **CPU** 9 | 10 | - AMD64 APM Vol 2: System Programming - http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/24593_APM_v21.pdf 11 | - Intel 80386 Reference Programmer's Manual - http://css.csail.mit.edu/6.858/2014/readings/i386/toc.htm 12 | 13 | **Boot sequence** 14 | 15 | - Multiboot Specification - http://www.gnu.org/software/grub/manual/multiboot/multiboot.html 16 | - AMD64 APM V2 - CH14. (Processor Initialization and Long Mode Activation) 17 | - GRUB 2 source code 18 | 19 | **Calling conventions** 20 | 21 | - System V Application Binary Interface AMD64 Architecture Processor Supplement - http://www.x86-64.org/documentation/abi.pdf 22 | 23 | **PIC** 24 | 25 | - 8259A PIC datasheet - http://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf 26 | 27 | **PIT** 28 | 29 | - 8254 PIT datasheet - http://www.scs.stanford.edu/10wi-cs140/pintos/specs/8254.pdf 30 | 31 | **VGA / CRTC** 32 | 33 | - Motorola MC6845 controller datasheet - http://www.reenigne.org/crtc/mc6845.pdf 34 | - SVGALib source code 35 | 36 | **CMOS** 37 | 38 | - https://web.archive.org/web/20111209041013/http://www-ivs.cs.uni-magdeburg.de/~zbrog/asm/cmos.html 39 | 40 | **UART** 41 | 42 | - 8250 UART datasheet - http://pdf.datasheetcatalog.com/datasheets/700/255656_DS.pdf 43 | 44 | **ELF** 45 | 46 | - ELF-64 Object File Format - http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf 47 | 48 | **VESA** 49 | 50 | - VBE core functions standard - http://www.petesqbsite.com/sections/tutorials/tuts/vbe3.pdf 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### os/64 ### 2 | 3 | A minimal 64-bit operating system for the x86-64 architecture, currently at an early stage of development. 4 | 5 | ### Build requirements ### 6 | 7 | ``` 8 | python-3 9 | binutils 10 | clang 11 | nasm 12 | genromfs 13 | grub-2 14 | util-linux 2.21+ 15 | ``` 16 | 17 | ### Testing ### 18 | 19 | ``` 20 | vim config.py # edit configuration 21 | ./make.py # build 22 | ./make.py qemu # launch in QEMU 23 | ./make.py bochs # launch in Bochs 24 | ``` 25 | 26 | ### Screenshots ### 27 | 28 | ![](misc/shot-qemu.png) 29 | 30 | ![](misc/shot-vbox.png) 31 | -------------------------------------------------------------------------------- /apps/cat.nf: -------------------------------------------------------------------------------- 1 | \ ------------------------------------- 2 | \ cat.nf - display file contents (os64) 3 | \ ------------------------------------- 4 | 5 | \ global variables 6 | 7 | 32 "size" var 8 | 0 "buf" var 9 | -1 "fd_in" var 10 | -1 "fd_out" var 11 | 0 "count" var 12 | "/dev/vt" "path_out" var 13 | 14 | \ cleanup and exit 15 | 16 | : 17 | buf 0 != if buf sys-free then 18 | fd_in 0 >= if fd_in sys-close drop then 19 | fd_out 0 >= if fd_out sys-close drop then 20 | sys-exit 21 | ; "cat-exit" def 22 | 23 | \ check command line arguments 24 | 25 | argc 2 < if 26 | "usage: cat.nf \n" printf drop 27 | -1 cat-exit 28 | then 29 | 30 | \ allocate buffer 31 | 32 | size sys-malloc "buf" := 33 | buf 0 == if -1 cat-exit then 34 | 35 | \ open input file 36 | 37 | 1 argv sys-open "fd_in" := 38 | fd_in 0 < if -1 cat-exit then 39 | 40 | \ open virtual terminal device 41 | 42 | path_out sys-open "fd_out" := 43 | fd_out 0 < if -1 cat-exit then 44 | 45 | \ copy from input file to terminal until end of file 46 | 47 | do 48 | size buf fd_in sys-read "count" := 49 | count 0 < if -1 cat-exit then 50 | count 0 == if 0 cat-exit then 51 | count buf fd_out sys-write drop 52 | 0 until 53 | -------------------------------------------------------------------------------- /apps/echo.nf: -------------------------------------------------------------------------------- 1 | \ ------------------------------------------------- 2 | \ echo.nf - print the command line arguments (os64) 3 | \ ------------------------------------------------- 4 | 5 | 1 do 6 | dup argc < 7 | while 8 | dup 1 != if 9 | " " printf drop 10 | then 11 | 12 | dup argv "%s" printf drop 13 | 14 | 1 + 15 | repeat 16 | 17 | "\n" printf 18 | -------------------------------------------------------------------------------- /apps/ls.nf: -------------------------------------------------------------------------------- 1 | \ ------------------------------- 2 | \ ls.nf - list directory contents 3 | \ ------------------------------- 4 | 5 | \ global variables 6 | 7 | 48 "file_info_size" var 8 | 12 "file_info_name_ofs" var 9 | -1 "fd" var 10 | 0 "buf" var 11 | 0 "count" var 12 | 13 | \ cleanup and exit 14 | 15 | : 16 | buf 0 != if buf sys-free then 17 | fd -1 > if fd sys-close then 18 | sys-exit 19 | ; "ls-exit" def 20 | 21 | \ check command line arguments 22 | 23 | argc 2 < if 24 | "usage: ls.nf \n" printf drop 25 | -1 ls-exit 26 | then 27 | 28 | \ allocate buffer 29 | 30 | file_info_size sys-malloc "buf" := 31 | buf 0 == if -1 ls-exit then 32 | 33 | \ open directory 34 | 35 | 1 argv sys-open "fd" := 36 | fd 0 < if -1 ls-exit then 37 | 38 | \ FIXME: check if the file is a directory 39 | 40 | \ iterate over directory entries 41 | 42 | do 43 | file_info_size buf fd sys-read "count" := 44 | count 0 == if 0 ls-exit then 45 | count file_info_size != if -1 ls-exit then 46 | buf file_info_name_ofs + "- %s\n" printf drop 47 | 0 until 48 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | config = {} 5 | 6 | config['CC'] = 'clang' 7 | config['LD'] = 'ld' 8 | config['NASM'] = 'nasm' 9 | config['QEMU'] = 'qemu-system-x86_64' 10 | 11 | config['BASE_DIR'] = os.path.dirname(sys.argv[0]) 12 | config['BUILD_DIR'] = os.path.join(config['BASE_DIR'], 'build') 13 | config['MOUNT_DIR'] = os.path.join(config['BASE_DIR'], 'tmp', 'mnt') 14 | 15 | config['DISK_IMAGE'] = os.path.join(config['BUILD_DIR'], 'disk.img') 16 | config['DISK_FS_OFFSET'] = 1048576 17 | config['LOOP_DEVICE'] = '/dev/loop20' 18 | config['SERIAL_OUT'] = os.path.join(config['BASE_DIR'], 'tmp', 'serial.out') 19 | 20 | config['CFLAGS'] = ( 21 | "-std=c11 -march=x86-64 -mcmodel=large -mno-red-zone -mno-mmx -mno-sse " \ 22 | "-mno-sse2 -ffreestanding -Wall -Wextra -pedantic -Wno-unused-parameter " \ 23 | "-I{BASE_DIR}/include -I{BASE_DIR}/nf/include -O3" 24 | ).format(BASE_DIR = config['BASE_DIR']) 25 | 26 | config['LDFLAGS'] = ( 27 | "-m elf_x86_64 -nostdlib -nodefaultlibs " \ 28 | "-T{BASE_DIR}/misc/kernel.ld" 29 | ).format(BASE_DIR = config['BASE_DIR']) 30 | -------------------------------------------------------------------------------- /data/bg-blue.pix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke8086/os64/35ffe752f1e09dcd968403df999dae4f97daacca/data/bg-blue.pix -------------------------------------------------------------------------------- /data/bg-green.pix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke8086/os64/35ffe752f1e09dcd968403df999dae4f97daacca/data/bg-green.pix -------------------------------------------------------------------------------- /data/bofh/ex1: -------------------------------------------------------------------------------- 1 | BOFH excuse #133: It's not plugged in. 2 | -------------------------------------------------------------------------------- /data/bofh/ex2: -------------------------------------------------------------------------------- 1 | BOFH excuse #195: We only support a 28000 bps connection. 2 | 3 | -------------------------------------------------------------------------------- /data/bofh/ex3: -------------------------------------------------------------------------------- 1 | BOFH excuse #28: CPU radiator broken 2 | -------------------------------------------------------------------------------- /data/bofh/ex4: -------------------------------------------------------------------------------- 1 | BOFH excuse #169: broadcast packets on wrong frequency 2 | -------------------------------------------------------------------------------- /data/fortune/fort1: -------------------------------------------------------------------------------- 1 | "Reality is that which, when you stop believing in it, doesn't go away". 2 | -- Philip K. Dick 3 | -------------------------------------------------------------------------------- /data/fortune/fort2: -------------------------------------------------------------------------------- 1 | "Reality is that which, when you stop believing in it, doesn't go away". 2 | -- Philip K. Dick 3 | -------------------------------------------------------------------------------- /data/hello/world: -------------------------------------------------------------------------------- 1 | hello world! 2 | -------------------------------------------------------------------------------- /data/init.nf: -------------------------------------------------------------------------------- 1 | : "nf" sys-spawn sys-wait ; "launch" def 2 | : "/apps/cat.nf" 2 "nf" sys-spawn sys-wait ; "cat" def 3 | : "/apps/ls.nf" 2 "nf" sys-spawn sys-wait ; "ls" def 4 | -------------------------------------------------------------------------------- /data/vga_8x16.fnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke8086/os64/35ffe752f1e09dcd968403df999dae4f97daacca/data/vga_8x16.fnt -------------------------------------------------------------------------------- /gui/bar.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/bar.c - status bar task 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | /* dimensions and padding */ 14 | enum { 15 | BAR_H_PAD = 10, 16 | BAR_V_PAD = 5, 17 | BAR_WIDTH = GUI_WIDTH, 18 | BAR_HEIGHT = FONT_HEIGHT + (2 * BAR_V_PAD), 19 | }; 20 | 21 | /* foreground and background colors */ 22 | #define BAR_FG 0xffffffff 23 | #define BAR_BG 0xc0000000 24 | 25 | /* private functions */ 26 | static void bar_draw_bg(uint32_t *buf); 27 | static void bar_draw_name(void); 28 | static void bar_draw_time(void); 29 | static void bar_draw_stat(void); 30 | 31 | /* window buffers */ 32 | static uint32_t b1_buf[BAR_WIDTH * BAR_HEIGHT]; 33 | static uint32_t b2_buf[BAR_WIDTH * BAR_HEIGHT]; 34 | 35 | /* window descriptors */ 36 | static int b1_wd; 37 | static int b2_wd; 38 | 39 | /* fill the buffer with the background color */ 40 | static void 41 | bar_draw_bg(uint32_t *buf) 42 | { 43 | uint64_t *bufp; 44 | uint64_t bg; 45 | size_t i; 46 | 47 | bufp = (uint64_t *)buf; 48 | bg = ((uint64_t)BAR_BG << 32) | BAR_BG; 49 | 50 | for (i = 0; i < BAR_WIDTH * BAR_HEIGHT / 2; ++i) { 51 | *(bufp++) = bg; 52 | } 53 | } 54 | 55 | /* render system name and version */ 56 | static void 57 | bar_draw_name(void) 58 | { 59 | char name_buf[32]; 60 | int name_pos; 61 | 62 | snprintf(name_buf, sizeof(name_buf), "%s %s", SYSTEM_NAME, SYSTEM_VERSION); 63 | name_pos = BAR_WIDTH * BAR_V_PAD + BAR_H_PAD; 64 | font_render_str(b1_buf + name_pos, BAR_WIDTH, name_buf, BAR_FG, BAR_BG); 65 | } 66 | 67 | /* render current date and time */ 68 | static void 69 | bar_draw_time(void) 70 | { 71 | char time_buf[20]; 72 | struct time tm; 73 | int time_pos; 74 | int time_width; 75 | 76 | cmos_get_time(&tm); 77 | 78 | snprintf(time_buf, sizeof(time_buf), "%04d-%02d-%02d %02d:%02d:%02d", 79 | tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second); 80 | 81 | time_width = (sizeof(time_buf) - 1) * FONT_WIDTH; 82 | time_pos = BAR_WIDTH * BAR_V_PAD + BAR_WIDTH - BAR_H_PAD - time_width; 83 | 84 | font_render_str(b1_buf + time_pos, BAR_WIDTH, time_buf, BAR_FG, BAR_BG); 85 | } 86 | 87 | /* render memory and task info */ 88 | static void 89 | bar_draw_stat(void) 90 | { 91 | char stat_buf[40]; 92 | int width, pos; 93 | size_t tasks, kheap; 94 | 95 | tasks = task_count(); 96 | kheap = kheap_used() >> 10; 97 | 98 | snprintf(stat_buf, sizeof(stat_buf), "heap: %uK, tasks: %d", kheap, tasks); 99 | 100 | width = strlen(stat_buf) * FONT_WIDTH; 101 | pos = BAR_WIDTH * BAR_V_PAD + BAR_WIDTH - BAR_H_PAD - width; 102 | 103 | bar_draw_bg(b2_buf); 104 | font_render_str(b2_buf + pos, BAR_WIDTH, stat_buf, BAR_FG, BAR_BG); 105 | } 106 | 107 | /* main entry point of the task */ 108 | static void 109 | bar_main(int argc, char **argv) 110 | { 111 | b1_wd = win_create(0, 0, BAR_WIDTH, BAR_HEIGHT, b1_buf); 112 | kassert(b1_wd >= 0, "cannot create top bar window"); 113 | 114 | b2_wd = win_create(0, GUI_HEIGHT - BAR_HEIGHT, 115 | BAR_WIDTH, BAR_HEIGHT, b2_buf); 116 | kassert(b2_wd >= 0, "cannot create bottom bar window"); 117 | 118 | bar_draw_bg(b1_buf); 119 | bar_draw_bg(b2_buf); 120 | 121 | while (1) { 122 | bar_draw_name(); 123 | bar_draw_time(); 124 | bar_draw_stat(); 125 | gui_redraw(); 126 | task_sleep(500); 127 | } 128 | 129 | // NOTREACHED 130 | } 131 | 132 | /* spawn the background task */ 133 | void 134 | bar_init(void) 135 | { 136 | task_spawn((uintptr_t)bar_main, 0, 0); 137 | } 138 | -------------------------------------------------------------------------------- /gui/fbcon.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/fbcon.c - frame buffer console 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | /* window dimensions */ 14 | enum { 15 | FBCON_WIDTH = VT_COLS * FONT_WIDTH, 16 | FBCON_HEIGHT = VT_ROWS * FONT_HEIGHT, 17 | }; 18 | 19 | /* color palette */ 20 | #define FBCON_RGB(r, g, b) ((r << 16) | (g << 8) | (b << 0)) 21 | 22 | static uint32_t fbcon_palette[] = { 23 | FBCON_RGB(0x00, 0x00, 0x00), // black 24 | FBCON_RGB(0x00, 0x00, 0xC0), // blue 25 | FBCON_RGB(0x00, 0xC0, 0x00), // green 26 | FBCON_RGB(0x00, 0xC0, 0xC0), // cyan 27 | FBCON_RGB(0xC0, 0x00, 0x00), // red 28 | FBCON_RGB(0xC0, 0x00, 0xC0), // magenta 29 | FBCON_RGB(0xC0, 0x80, 0x00), // brown 30 | FBCON_RGB(0xC0, 0xC0, 0xC0), // light gray 31 | FBCON_RGB(0x80, 0x80, 0x80), // dark gray 32 | FBCON_RGB(0x00, 0x00, 0xFF), // light blue 33 | FBCON_RGB(0x00, 0xFF, 0x00), // light green 34 | FBCON_RGB(0x00, 0xFF, 0xFF), // light cyan 35 | FBCON_RGB(0xFF, 0x00, 0x00), // light red 36 | FBCON_RGB(0xFF, 0x00, 0xFF), // light magenta 37 | FBCON_RGB(0xFF, 0xFF, 0x00), // yellow 38 | FBCON_RGB(0xFF, 0xFF, 0xFF), // white 39 | }; 40 | 41 | /* window buffer and descriptor */ 42 | static uint32_t fbcon_buf[FBCON_WIDTH * FBCON_HEIGHT]; 43 | static int fbcon_wd; 44 | 45 | /* redraw the terminal buffer */ 46 | void 47 | fbcon_flush(uint16_t *tbuf, int cols, int rows) 48 | { 49 | int i, j, linew; 50 | uint8_t ch; 51 | uint32_t fg, bg; 52 | uint32_t *gbufp; 53 | uint16_t *tbufp; 54 | int fg_idx, bg_idx; 55 | 56 | memset(fbcon_buf, 0, sizeof(fbcon_buf)); 57 | 58 | linew = FONT_WIDTH * VT_COLS; 59 | tbufp = tbuf; 60 | gbufp = fbcon_buf; 61 | 62 | for (j = 0; j < rows; ++j) { 63 | for (i = 0; i < cols; ++i) { 64 | ch = *tbufp & 0xFF; 65 | fg_idx = (*tbufp >> 8) & 0x0F; 66 | bg_idx = (*tbufp >> 8) & 0xF0; 67 | 68 | fg = fbcon_palette[fg_idx] | 0xFF000000; 69 | bg = fbcon_palette[bg_idx] | 0xC0000000; 70 | 71 | font_render_char(gbufp, linew, ch, fg, bg); 72 | 73 | tbufp++; 74 | gbufp += FONT_WIDTH; 75 | } 76 | gbufp += linew * (FONT_HEIGHT - 1); 77 | } 78 | 79 | gui_redraw(); 80 | } 81 | 82 | /* create window and connect to the vt driver */ 83 | void 84 | fbcon_init(void) 85 | { 86 | int x, y; 87 | 88 | x = (GUI_WIDTH - FBCON_WIDTH) >> 1; 89 | y = (GUI_HEIGHT - FBCON_HEIGHT) >> 1; 90 | 91 | fbcon_wd = win_create(x, y, FBCON_WIDTH, FBCON_HEIGHT, fbcon_buf); 92 | kassert(fbcon_wd >= 0, "cannot create fbcon window"); 93 | 94 | vt_set_flush_cb(fbcon_flush); 95 | } 96 | -------------------------------------------------------------------------------- /gui/font.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/font.c - character rendering routines 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #define FONT_PATH "/data/vga_8x16.fnt" 14 | 15 | /* private functions */ 16 | static inline void font_render_space(uint32_t *buf, int linew, uint32_t bg); 17 | static void font_load(void); 18 | 19 | /* private data */ 20 | static uint8_t font_buffer[FONT_HEIGHT * FONT_LENGTH]; 21 | 22 | /* load font from file */ 23 | static void 24 | font_load(void) 25 | { 26 | ssize_t size; 27 | int fd; 28 | 29 | fd = vfs_open(FONT_PATH); 30 | kassert(fd >= 0, "cannot open font file"); 31 | 32 | size = file_read(fd, font_buffer, sizeof(font_buffer)); 33 | kassert(size == sizeof(font_buffer), "error reading font file"); 34 | 35 | (void)file_close(fd); 36 | } 37 | 38 | /* render a single blank character to the specified buffer */ 39 | static inline void 40 | font_render_space(uint32_t *buf, int linew, uint32_t bg) 41 | { 42 | uint64_t *charp; 43 | uint64_t bg64; 44 | int i, j; 45 | 46 | bg64 = bg | ((uint64_t)bg << 32); 47 | charp = (uint64_t *)buf; 48 | 49 | for (j = 0; j < FONT_HEIGHT; ++j) { 50 | for (i = 0; i < FONT_WIDTH >> 1; ++i) { 51 | *charp++ = bg64; 52 | } 53 | charp += (linew - FONT_WIDTH) >> 1; 54 | } 55 | } 56 | 57 | /* render a single character to the specified buffer */ 58 | void 59 | font_render_char(uint32_t *buf, int linew, uint8_t ch, uint32_t fg, uint32_t bg) 60 | { 61 | uint8_t *glyph; 62 | uint8_t active; 63 | int i, j; 64 | 65 | if (!ch || ch == ' ') { 66 | font_render_space(buf, linew, bg); 67 | return; 68 | } 69 | 70 | glyph = font_buffer + (ch * FONT_HEIGHT); 71 | 72 | for (j = 0; j < FONT_HEIGHT; ++j) { 73 | for (i = 0; i < FONT_WIDTH; ++i) { 74 | active = (glyph[j] & (1 << (7 - i))); 75 | *buf++ = fg * !!active + bg * !active; 76 | } 77 | buf += linew - FONT_WIDTH; 78 | } 79 | } 80 | 81 | /* render a string to the specified buffer */ 82 | void 83 | font_render_str(uint32_t *buf, int linew, char *s, uint32_t fg, uint32_t bg) 84 | { 85 | while (*s) { 86 | font_render_char(buf, linew, *s, fg, bg); 87 | s++; 88 | buf += FONT_WIDTH; 89 | } 90 | } 91 | 92 | /* load font data from file */ 93 | void 94 | font_init(void) 95 | { 96 | font_load(); 97 | } 98 | -------------------------------------------------------------------------------- /gui/gui.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * gui/gui.c - basic graphical user interface routines 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | /* filesystem paths */ 14 | #define GUI_BG_FILE "/data/bg-green.pix" 15 | 16 | /* private functions */ 17 | static void gui_draw_buf(void); 18 | 19 | /* private data */ 20 | static uint32_t gui_buffer[GUI_WIDTH * GUI_HEIGHT]; 21 | static uint32_t gui_bg_buffer[GUI_WIDTH * GUI_HEIGHT]; 22 | 23 | /* set the background image */ 24 | void 25 | gui_set_bg(const char *path) 26 | { 27 | int fd; 28 | uint16_t w, h; 29 | 30 | fd = vfs_open(path); 31 | if (fd < 0) 32 | return; 33 | 34 | w = h = 0; 35 | (void)file_read(fd, &w, sizeof(w)); 36 | (void)file_read(fd, &h, sizeof(h)); 37 | if (w != GUI_WIDTH || h != GUI_HEIGHT) 38 | return; 39 | 40 | (void)file_read(fd, gui_bg_buffer, sizeof(gui_bg_buffer)); 41 | (void)file_close(fd); 42 | 43 | gui_redraw(); 44 | } 45 | 46 | /* draw the gui buffer to the video memory */ 47 | static void 48 | gui_draw_buf(void) 49 | { 50 | uint32_t *fb = (uint32_t*)vbe_gfx_addr(); 51 | uint32_t *gb = gui_buffer; 52 | 53 | if (vbe_bpp() == 4) { 54 | memcpy(fb, gui_buffer, sizeof(gui_buffer)); 55 | return; 56 | } 57 | 58 | if (vbe_bpp() != 3) { 59 | return; 60 | } 61 | 62 | for (int y = 0; y < GUI_HEIGHT; ++y) { 63 | for (int x = 0; x < GUI_WIDTH; ++x) { 64 | *fb = *gb | (*fb & 0xFF000000); 65 | fb = (uint32_t*)((uintptr_t)fb + 3); 66 | gb++; 67 | } 68 | } 69 | } 70 | 71 | /* redraw background and all active windows to the screen */ 72 | void 73 | gui_redraw(void) 74 | { 75 | memcpy(gui_buffer, gui_bg_buffer, sizeof(gui_buffer)); 76 | win_draw_all(gui_buffer); 77 | gui_draw_buf(); 78 | } 79 | 80 | /* load the background */ 81 | void 82 | gui_init(void) 83 | { 84 | gui_set_bg(GUI_BG_FILE); 85 | } 86 | -------------------------------------------------------------------------------- /gui/win.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/gui.c - graphical user interface structures and routines 8 | */ 9 | 10 | #include 11 | 12 | /* window object */ 13 | struct win { 14 | uint8_t active; 15 | uint16_t x, y, w, h; 16 | uint32_t *buf; 17 | }; 18 | 19 | /* private functions */ 20 | static inline uint64_t win_blend_pixels(uint64_t b, uint64_t w); 21 | static void win_draw(uint32_t *buf, struct win *win); 22 | 23 | /* array of windows */ 24 | static struct win windows[8]; 25 | 26 | /* alpha-blend two pairs of pixels */ 27 | static inline uint64_t 28 | win_blend_pixels(uint64_t b, uint64_t w) 29 | { 30 | uint16_t a1, a2; 31 | 32 | a1 = (w & 0xFF00000000000000) >> 56; 33 | a2 = (w & 0x00000000FF000000) >> 24; 34 | 35 | b += ( 36 | ((((w & 0x00FF000000000000) - (b & 0x00FF000000000000)) * a1) & 0xFF00000000000000) + 37 | ((((w & 0x0000FF0000000000) - (b & 0x0000FF0000000000)) * a1) & 0x00FF000000000000) + 38 | ((((w & 0x000000FF00000000) - (b & 0x000000FF00000000)) * a1) & 0x0000FF0000000000) + 39 | ((((w & 0x0000000000FF0000) - (b & 0x0000000000FF0000)) * a2) & 0x00000000FF000000) + 40 | ((((w & 0x000000000000FF00) - (b & 0x000000000000FF00)) * a2) & 0x0000000000FF0000) + 41 | ((((w & 0x00000000000000FF) - (b & 0x00000000000000FF)) * a2) & 0x000000000000FF00) 42 | ) >> 8; 43 | 44 | return b; 45 | } 46 | /* draw a window to the given buffer */ 47 | static void 48 | win_draw(uint32_t *buf, struct win *win) 49 | { 50 | uint16_t y, x; 51 | uint64_t *winp64, *bufp64; 52 | 53 | winp64 = (uint64_t *)win->buf; 54 | for (y = 0; y < win->h; ++y) { 55 | 56 | bufp64 = (uint64_t *)(buf + (win->y + y) * GUI_WIDTH + win->x); 57 | for (x = 0; x < (win->w >> 1); ++x, ++bufp64, ++winp64) { 58 | *bufp64 = win_blend_pixels(*bufp64, *winp64); 59 | } 60 | } 61 | } 62 | 63 | /* draw all active winows to the given buffer */ 64 | void 65 | win_draw_all(uint32_t *buf) 66 | { 67 | ARRAY_FOREACH(windows, i) { 68 | if (!windows[i].active) 69 | continue; 70 | win_draw(buf, &windows[i]); 71 | } 72 | } 73 | 74 | /* create a new window */ 75 | int 76 | win_create(int x, int y, int w, int h, uint32_t *buf) 77 | { 78 | struct win *win; 79 | 80 | win = ARRAY_TAKE(windows); 81 | if (!win) { 82 | return -1; 83 | } 84 | 85 | win->x = x; 86 | win->y = y; 87 | win->w = w; 88 | win->h = h; 89 | win->buf = buf; 90 | 91 | return (win - windows); 92 | } 93 | 94 | /* release a window */ 95 | int 96 | win_close(int wd) 97 | { 98 | struct win *win; 99 | 100 | win = &windows[wd]; 101 | ARRAY_RELEASE(win); 102 | 103 | return 0; 104 | } 105 | 106 | /* initialize the array of windows */ 107 | void 108 | win_init(void) 109 | { 110 | ARRAY_INIT(windows); 111 | } 112 | -------------------------------------------------------------------------------- /include/gui/gui.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | #ifndef _GUI_GUI_H_ 7 | #define _GUI_GUI_H_ 8 | 9 | #include 10 | 11 | /* font dimensions */ 12 | enum { 13 | FONT_WIDTH = 8, 14 | FONT_HEIGHT = 16, 15 | FONT_LENGTH = 256, 16 | }; 17 | 18 | /* gui/bar.c */ 19 | void bar_init(void); 20 | 21 | /* gui/fbcon.c */ 22 | void fbcon_flush(uint16_t *buf, int cols, int rows); 23 | void fbcon_init(void); 24 | 25 | /* gui/font.c */ 26 | void font_render_char(uint32_t *buf, int linew, uint8_t ch, uint32_t fg, uint32_t bg); 27 | void font_render_str(uint32_t *buf, int linew, char *s, uint32_t fg, uint32_t bg); 28 | void font_init(void); 29 | 30 | /* gui/gui.c */ 31 | void gui_set_bg(const char *path); 32 | void gui_redraw(void); 33 | void gui_init(void); 34 | 35 | /* gui/win.c */ 36 | void win_draw_all(uint32_t *buf); 37 | int win_create(int x, int y, int w, int h, uint32_t *buf); 38 | int win_close(int wd); 39 | void win_init(void); 40 | 41 | #endif // _GUI_GUI_H_ 42 | -------------------------------------------------------------------------------- /include/kernel/kernel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | #ifndef _KERNEL_KERNEL_H_ 7 | #define _KERNEL_KERNEL_H_ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | * shared constants 16 | */ 17 | 18 | /* system version */ 19 | #define SYSTEM_NAME "os/64" 20 | #define SYSTEM_VERSION "0.0.1" 21 | 22 | /* supported page size and memory amount */ 23 | enum { 24 | MEM_PAGE_SIZE = 0x200000, 25 | MEM_FRAME_COUNT = 0x200, 26 | }; 27 | 28 | /* virtual base addresses */ 29 | enum { 30 | KHEAP_BASE_ADDR = 0x4000000, // 64MB 31 | VID_PAGE_ADDR = 0x3e800000, // 1000MB 32 | }; 33 | 34 | /* virtual terminal parameters */ 35 | enum { 36 | VT_COLS = 80, 37 | VT_ROWS = 25, 38 | }; 39 | 40 | /* misc gui parameters */ 41 | enum { 42 | GUI_WIDTH = 800, 43 | GUI_HEIGHT = 600, 44 | }; 45 | 46 | /* max lengths of vfs path and path component */ 47 | enum { 48 | PATH_MAX = 128, 49 | NAME_MAX = 32, 50 | }; 51 | 52 | /* printk levels */ 53 | enum { 54 | KERN_DEBUG = 1, 55 | KERN_INFO = 2, 56 | KERN_WARN = 3, 57 | KERN_ERR = 4, 58 | }; 59 | 60 | /* supported interrupt count */ 61 | enum { 62 | INTR_COUNT = 0x40, 63 | }; 64 | 65 | /* file types */ 66 | enum { 67 | FT_NONE = 0, 68 | FT_REG = 1, 69 | FT_DIR = 2, 70 | FT_CHR = 3, 71 | FT_BLK = 4, 72 | FT_UNK = 5 73 | }; 74 | 75 | /* 76 | * misc helper macros 77 | */ 78 | 79 | /* trigger breakpoint in bochs */ 80 | #define BOCHS_MAGIC_BREAK \ 81 | __asm__("xchgw %bx, %bx") 82 | 83 | /* display an error and stop the kernel */ 84 | #define kpanic(msg) \ 85 | { \ 86 | cpu_cli(); \ 87 | printk(KERN_ERR, msg); \ 88 | while(1); \ 89 | } \ 90 | 91 | /* panic unless given condition is true */ 92 | #define kassert(cond, msg) \ 93 | if (!(cond)) { \ 94 | kpanic("assertion failed: " msg); \ 95 | } \ 96 | 97 | /* 98 | * shared data types 99 | */ 100 | 101 | /* storage for cpu registers */ 102 | struct regs { 103 | uint64_t rax, rbx, rcx, rdx; 104 | uint64_t rdi, rsi, rbp; 105 | uint64_t r8, r9, r10, r11, r12, r13, r14, r15; 106 | }; 107 | 108 | /* layout of the stack after interrupt */ 109 | struct intr_stack { 110 | uint64_t rip; 111 | uint64_t res; 112 | uint64_t rflags; 113 | uint64_t rsp; 114 | }; 115 | 116 | /* vfs operations */ 117 | struct vfs_ops { 118 | int (*open_fn)(uintptr_t sbh, uintptr_t inh); 119 | }; 120 | 121 | /* file operations */ 122 | struct file; 123 | struct file_ops { 124 | int (*close_fn)(struct file *file); 125 | ssize_t (*read_fn)(struct file *file, void *buf, size_t nbyte); 126 | ssize_t (*write_fn)(struct file *file, const void *buf, size_t nbyte); 127 | }; 128 | 129 | /* file object */ 130 | struct file { 131 | uint8_t active; 132 | uintptr_t sbh; 133 | uintptr_t inh; 134 | struct file_ops *ops; 135 | size_t pos; 136 | }; 137 | 138 | /* file info object */ 139 | struct file_info { 140 | uintptr_t inh; 141 | int type; 142 | char name[NAME_MAX]; 143 | }; 144 | 145 | /* time object */ 146 | struct time { 147 | uint8_t second; 148 | uint8_t minute; 149 | uint8_t hour; 150 | uint8_t day; 151 | uint8_t month; 152 | uint16_t year; 153 | }; 154 | 155 | /* 156 | * shared functions 157 | */ 158 | 159 | 160 | /* kernel/cmos.c */ 161 | void cmos_get_time(struct time *t); 162 | 163 | /* kernel/cpu.c */ 164 | uint8_t cpu_inb(uint16_t port); 165 | void cpu_outb(uint16_t port, uint8_t val); 166 | void cpu_invlpg(uint64_t vaddr); 167 | void cpu_cli(void); 168 | void cpu_sti(void); 169 | uint64_t cpu_task_switch(void); 170 | uint64_t cpu_get_flags(void); 171 | void cpu_set_flags(uint64_t flags); 172 | 173 | /* kernel/crtc.c */ 174 | void crtc_cursor_set(uint16_t pos); 175 | uint16_t crtc_cursor_get(void); 176 | 177 | /* kernel/devfs.c */ 178 | int devfs_mount(const char *path); 179 | 180 | /* kernel/file.c */ 181 | int file_new(uintptr_t sbh, uintptr_t inh, struct file_ops *ops); 182 | void file_release(struct file *file); 183 | int file_close(int fd); 184 | ssize_t file_read(int fd, void *buf, size_t nbyte); 185 | ssize_t file_write(int fd, void *buf, size_t nbyte); 186 | void files_init(void); 187 | 188 | /* kernel/intr.c */ 189 | typedef void (*intr_handler_fn)(uint8_t intno, struct intr_stack *intr_stack, 190 | struct regs *regs); 191 | void intr_init(void); 192 | void intr_set_handler(uint8_t intno, intr_handler_fn intr_handler); 193 | void intr_handle(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs); 194 | 195 | /* kernel/kbd.c */ 196 | void kbd_init(void); 197 | int kbd_read(uint16_t *key); 198 | 199 | /* kernel/kheap.c */ 200 | size_t kheap_used(void); 201 | void *kheap_alloc(size_t size); 202 | void kheap_free(void *ptr); 203 | void kheap_init(void); 204 | 205 | /* kernel/mboot.c */ 206 | void mboot_init(uintptr_t paddr); 207 | void mboot_dump(void); 208 | uintptr_t mboot_mod(uint8_t n); 209 | uintptr_t mboot_mods_end(void); 210 | uintptr_t mboot_vbe_mode_info_ptr(void); 211 | size_t mboot_mmap_entry_count(void); 212 | void mboot_mmap_entry_read(size_t n, uintptr_t *addr, size_t *len, int *avail); 213 | 214 | /* kernel/pit.c */ 215 | void pit_init(void); 216 | uint64_t pit_get_msecs(void); 217 | 218 | /* kernel/pmem.c */ 219 | void pmem_init(void); 220 | uintptr_t pmem_alloc(void); 221 | void pmem_free(uintptr_t paddr); 222 | void pmem_dump_avail(void); 223 | void pmem_dump_kern(void); 224 | size_t pmem_total(void); 225 | 226 | /* kernel/printk.c */ 227 | int printk(int level, const char *fmt, ...); 228 | int vprintk(int level, const char *fmt, va_list args); 229 | 230 | /* kernel/ptt.c */ 231 | void ptt_init(void); 232 | void ptt_map(uintptr_t vaddr, uintptr_t paddr, uint8_t user, uint8_t present); 233 | void ptt_unmap(uintptr_t vaddr); 234 | 235 | /* kernel/romfs.c */ 236 | int romfs_mount(uintptr_t addr, const char *path); 237 | 238 | /* kernel/task.c */ 239 | void tasks_init(void); 240 | task_pid_t task_spawn(uintptr_t entry, int argc, char **argv); 241 | task_pid_t task_spawn_name(char *name, int argc, char **argv); 242 | int task_waitpid(task_pid_t pid); 243 | int task_switch(void); 244 | void task_sleep(uint64_t msecs); 245 | void task_exit(uint8_t code); 246 | int task_count(void); 247 | 248 | /* kernel/uart.c */ 249 | void uart_init(void); 250 | void uart_write(const char *msg, size_t nbytes); 251 | 252 | /* kernel/vbe.c */ 253 | int vbe_gfx_mode(void); 254 | uint8_t *vbe_gfx_addr(void); 255 | uint8_t vbe_bpp(void); 256 | void vbe_init(void); 257 | 258 | /* kernel/vfs.c */ 259 | void vfs_init(void); 260 | int vfs_mount(const char *path, struct vfs_ops *ops, uintptr_t sbh); 261 | int vfs_open(const char *path); 262 | 263 | /* kernel/vt.c */ 264 | typedef void (*vt_flush_cb)(uint16_t *buf, int cols, int rows); 265 | size_t vt_write(const char *buf, size_t n); 266 | void vt_set_flush_cb(vt_flush_cb cb); 267 | void vt_init(void); 268 | 269 | #endif // _KERNEL_KERNEL_H_ 270 | -------------------------------------------------------------------------------- /include/libc/array.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | #ifndef _LIBC_ARRAY_H_ 7 | #define _LIBC_ARRAY_H_ 8 | 9 | #include 10 | 11 | #define ARRAY_FOREACH(a, i) \ 12 | for (size_t i = 0, __len = ARRAY_LENGTH(a); i < __len; ++i) 13 | 14 | #define ARRAY_INDEX_NONE ((array_index_t)-1) 15 | 16 | #define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a[0])) 17 | 18 | #define ARRAY_CHECK_INDEX(a, i) \ 19 | (i >= 0 && (size_t)i < ARRAY_LENGTH(a) && a[i].active) 20 | 21 | #define ARRAY_INIT(a) \ 22 | ARRAY_FOREACH(a, i) { a[i].active = 0; } \ 23 | 24 | #define ARRAY_FIND_INACTIVE(a) \ 25 | (__extension__({ \ 26 | array_index_t __ret = ARRAY_INDEX_NONE; \ 27 | size_t __len = ARRAY_LENGTH(a); \ 28 | for (size_t __i = 0; __i < __len; ++__i) { \ 29 | if (!a[__i].active) { \ 30 | __ret = __i; \ 31 | break; \ 32 | } \ 33 | } \ 34 | __ret; \ 35 | })) 36 | 37 | #define ARRAY_TAKE(a) \ 38 | (__extension__({ \ 39 | array_index_t i = ARRAY_FIND_INACTIVE(a); \ 40 | if (i != ARRAY_INDEX_NONE) { \ 41 | a[i].active = 1; \ 42 | } \ 43 | i == ARRAY_INDEX_NONE ? NULL : &a[i]; \ 44 | })) \ 45 | 46 | #define ARRAY_RELEASE(x) \ 47 | (__extension__({ \ 48 | x->active = 0; \ 49 | })) \ 50 | 51 | #define ARRAY_FIND(a, i) \ 52 | (__extension__({ \ 53 | i == ARRAY_INDEX_NONE ? NULL : &a[i]; \ 54 | })) \ 55 | 56 | #define ARRAY_FIND_BY(a, f, v, i) \ 57 | (__extension__({ \ 58 | *i = ARRAY_INDEX_NONE; \ 59 | __typeof__(a[0]) *__ret = NULL; \ 60 | ARRAY_FOREACH(a, __i) { \ 61 | if (a[__i].active && !strcmp(a[__i].f, v)) { \ 62 | __ret = &a[__i]; \ 63 | *i = __i; \ 64 | break; \ 65 | } \ 66 | }; \ 67 | __ret; \ 68 | })) 69 | 70 | #endif // _LIBC_ARRAY_H_ 71 | -------------------------------------------------------------------------------- /include/libc/stdio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | #ifndef _LIBC_STDIO_H_ 7 | #define _LIBC_STDIO_H_ 8 | 9 | #include 10 | 11 | int pf_vsnprintf(void *buf, size_t nbyte, const char *fmt, va_list args); 12 | int pf_snprintf(char *buf, size_t nbyte, const char *fmt, ...); 13 | 14 | #define vsnprintf pf_vsnprintf 15 | #define snprintf pf_snprintf 16 | 17 | #endif // _LIBC_STDIO_H_ 18 | -------------------------------------------------------------------------------- /include/libc/string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | #ifndef _LIBC_STRING_H_ 7 | #define _LIBC_STRING_H_ 8 | 9 | #include 10 | 11 | void *memcpy(void *dest, const void *src, size_t n); 12 | void *memset(void *dest, uint8_t c, size_t n); 13 | int16_t strcmp(const char *s1, const char *s2); 14 | size_t strlen(const char *s); 15 | char *strncpy(char *dest, const char *src, size_t n); 16 | char *strchr(const char *str, int c); 17 | 18 | #endif // _LIBC_STRING_H_ 19 | -------------------------------------------------------------------------------- /include/libc/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | #ifndef _LIBC_TYPES_H_ 7 | #define _LIBC_TYPES_H_ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | typedef intptr_t ssize_t; 14 | typedef int64_t task_pid_t; 15 | typedef int64_t array_index_t; 16 | 17 | #endif // _LIBC_TYPES_H_ 18 | -------------------------------------------------------------------------------- /kernel/cmos.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/cmos.c - basic CMOS driver 8 | */ 9 | 10 | #include 11 | 12 | /* i/o port addresses */ 13 | enum { 14 | CMOS_PORT_ADDR = 0x70, 15 | CMOS_PORT_DATA = 0x71, 16 | }; 17 | 18 | /* private functions */ 19 | static uint16_t cmos_load_bcd(uint16_t bcd); 20 | static uint8_t cmos_get_reg(uint8_t reg); 21 | static uint8_t cmos_check_uip(void); 22 | static void cmos_read_time(struct time *t); 23 | static int cmos_compare_time(struct time *t1, struct time *t2); 24 | static void cmos_sanitize_time(struct time *t); 25 | 26 | /* load a binary coded decimal */ 27 | static uint16_t 28 | cmos_load_bcd(uint16_t bcd) 29 | { 30 | uint16_t ret = 0; 31 | 32 | ret += ((bcd >> 8) & 0x0F) * 100; 33 | ret += ((bcd >> 4) & 0x0F) * 10; 34 | ret += ((bcd >> 0) & 0x0F); 35 | 36 | return ret; 37 | } 38 | 39 | /* read a CMOS register */ 40 | static uint8_t 41 | cmos_get_reg(uint8_t reg) 42 | { 43 | cpu_outb(CMOS_PORT_ADDR, reg); 44 | return cpu_inb(CMOS_PORT_DATA); 45 | } 46 | 47 | /* check the update-in-progress flag */ 48 | static uint8_t 49 | cmos_check_uip(void) 50 | { 51 | return cmos_get_reg(0x0a) & (0x80); 52 | } 53 | 54 | /* load raw values from CMOS time registers to a time struct */ 55 | static void 56 | cmos_read_time(struct time *t) 57 | { 58 | while (cmos_check_uip()) { }; 59 | 60 | t->second = cmos_get_reg(0x00); 61 | t->minute = cmos_get_reg(0x02); 62 | t->hour = cmos_get_reg(0x04); 63 | t->day = cmos_get_reg(0x07); 64 | t->month = cmos_get_reg(0x08); 65 | t->year = cmos_get_reg(0x09); 66 | } 67 | 68 | /* sanitize raw values in a time struct */ 69 | static void 70 | cmos_sanitize_time(struct time *t) 71 | { 72 | uint8_t reg_b; 73 | int is_bcd, is_12h, is_pm; 74 | 75 | reg_b = cmos_get_reg(0x0b); 76 | is_bcd = !(reg_b & 0x04); 77 | is_12h = !(reg_b & 0x02); 78 | is_pm = !!(t->hour & 0x80); 79 | 80 | // clear the PM bit 81 | t->hour = t->hour & 0x7F; 82 | 83 | if (is_bcd) { 84 | t->second = cmos_load_bcd(t->second); 85 | t->minute = cmos_load_bcd(t->minute); 86 | t->hour = cmos_load_bcd(t->hour); 87 | t->day = cmos_load_bcd(t->day); 88 | t->month = cmos_load_bcd(t->month); 89 | t->year = cmos_load_bcd(t->year); 90 | } 91 | 92 | if (is_12h && is_pm) { 93 | t->hour = (t->hour + 12) % 24; 94 | } 95 | 96 | // calculate full year 97 | t->year += (t->year < 70) ? 2000 : 1900; 98 | } 99 | 100 | /* compare two time structs */ 101 | static int 102 | cmos_compare_time(struct time *t1, struct time *t2) 103 | { 104 | uint64_t *i1, *i2; 105 | 106 | i1 = (uint64_t *)t1; 107 | i2 = (uint64_t *)t2; 108 | 109 | return *i2 - *i1; 110 | } 111 | 112 | /* load verified and sanitized CMOS time to a time struct */ 113 | void 114 | cmos_get_time(struct time *t) 115 | { 116 | struct time t1, t2; 117 | 118 | do { 119 | cmos_read_time(&t1); 120 | cmos_read_time(&t2); 121 | } while (cmos_compare_time(&t1, &t2)); 122 | 123 | cmos_sanitize_time(&t2); 124 | 125 | memcpy(t, &t2, sizeof(t2)); 126 | } 127 | -------------------------------------------------------------------------------- /kernel/cpu.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2014-2015 Łukasz S. 3 | ; Distributed under the terms of GPL-2 License. 4 | ; 5 | ; kernel/cpu.s - low-level cpu instructions 6 | ; 7 | 8 | [section .text] 9 | 10 | [global cpu_inb] 11 | [global cpu_outb] 12 | [global cpu_invlpg] 13 | [global cpu_cli] 14 | [global cpu_sti] 15 | [global cpu_task_switch] 16 | [global cpu_get_flags] 17 | [global cpu_set_flags] 18 | 19 | ; input a byte from a port 20 | cpu_inb: 21 | mov dx, di 22 | in al, dx 23 | movzx eax, al 24 | ret 25 | 26 | ; output a byte to a port 27 | cpu_outb: 28 | mov al, sil 29 | mov dx, di 30 | out dx, al 31 | ret 32 | 33 | ; invalidate a TLB entry 34 | cpu_invlpg: 35 | invlpg [rdi] 36 | ret 37 | 38 | # get flags 39 | cpu_get_flags: 40 | pushf 41 | pop rax 42 | ret 43 | 44 | # set flags 45 | cpu_set_flags: 46 | push rdi 47 | popf 48 | ret 49 | 50 | ; clear the interrupts flag 51 | cpu_cli: 52 | cli 53 | ret 54 | 55 | ; set the interrupts flag 56 | cpu_sti: 57 | sti 58 | ret 59 | 60 | ; trigger task-switching interrupt 61 | cpu_task_switch: 62 | int 0x31 63 | ret 64 | -------------------------------------------------------------------------------- /kernel/crtc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/crtc.c - basic 6845 CRTC driver 8 | */ 9 | 10 | #include 11 | 12 | /* i/o port addresses */ 13 | enum { 14 | CRTC_PORT_INDEX = 0x3D4, 15 | CRTC_PORT_DATA = 0x3D5, 16 | }; 17 | 18 | /* crtc register indexes */ 19 | enum { 20 | CRTC_REG_CURSOR_H = 0x0E, 21 | CRTC_REG_CURSOR_L = 0x0F, 22 | }; 23 | 24 | /* set cursor position */ 25 | void 26 | crtc_cursor_set(uint16_t pos) 27 | { 28 | uint8_t pos_l = pos & 0xff; 29 | uint8_t pos_h = (pos >> 8) & 0xff; 30 | 31 | cpu_outb(CRTC_PORT_INDEX, CRTC_REG_CURSOR_L); 32 | cpu_outb(CRTC_PORT_DATA, pos_l); 33 | 34 | cpu_outb(CRTC_PORT_INDEX, CRTC_REG_CURSOR_H); 35 | cpu_outb(CRTC_PORT_DATA, pos_h); 36 | } 37 | 38 | /* get cursor position */ 39 | uint16_t 40 | crtc_cursor_get(void) 41 | { 42 | uint8_t pos_l = 0; 43 | uint8_t pos_h = 0; 44 | uint16_t pos = 0; 45 | 46 | cpu_outb(CRTC_PORT_INDEX, CRTC_REG_CURSOR_H); 47 | pos_h = cpu_inb(CRTC_PORT_DATA); 48 | 49 | cpu_outb(CRTC_PORT_INDEX, CRTC_REG_CURSOR_L); 50 | pos_l = cpu_inb(CRTC_PORT_DATA); 51 | 52 | pos = (pos_h << 8) | pos_l; 53 | 54 | return pos; 55 | } 56 | -------------------------------------------------------------------------------- /kernel/devfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/devfs.c - basic device filesystem 8 | */ 9 | 10 | #include 11 | 12 | /* devfs node handles */ 13 | enum { 14 | DEVFS_NODE_ROOT = 0, 15 | DEVFS_NODE_VT = 1, 16 | DEVFS_NODE_KBD = 2, 17 | DEVFS_NODE_TIME = 3, 18 | DEVFS_NODE_COUNT = 4, 19 | }; 20 | 21 | /* private functions */ 22 | static int devfs_open(uintptr_t sbh, uintptr_t inh); 23 | static int devfs_close(struct file *file); 24 | static ssize_t devfs_read_kbd(struct file *file, void *buf, size_t nbyte); 25 | static ssize_t devfs_read_time(struct file *file, void *buf, size_t nbyte); 26 | static ssize_t devfs_read_dir(struct file *file, void *buf, size_t nbyte); 27 | static ssize_t devfs_read(struct file *file, void *buf, size_t nbyte); 28 | static ssize_t devfs_write(struct file *file, const void *buf, size_t nbyte); 29 | 30 | /* file operations */ 31 | static struct file_ops devfs_file_ops = { 32 | .close_fn = &devfs_close, 33 | .read_fn = &devfs_read, 34 | .write_fn = &devfs_write, 35 | }; 36 | 37 | /* vfs operations */ 38 | static struct vfs_ops devfs_ops = { 39 | .open_fn = &devfs_open, 40 | }; 41 | 42 | /* initialize a file object for specified superblock and inode */ 43 | static int 44 | devfs_open(uintptr_t sbh, uintptr_t inh) 45 | { 46 | return file_new(sbh, inh, &devfs_file_ops); 47 | } 48 | 49 | /* close a file */ 50 | static int 51 | devfs_close(struct file *file) 52 | { 53 | file_release(file); 54 | return 0; 55 | } 56 | 57 | /* read from a kbd device */ 58 | static ssize_t 59 | devfs_read_kbd(struct file *file, void *buf, size_t nbyte) 60 | { 61 | uint16_t key; 62 | 63 | if (nbyte < sizeof(key)) { 64 | return 0; 65 | } 66 | 67 | if (kbd_read(&key)) { 68 | return 0; 69 | } 70 | 71 | memcpy(buf, &key, sizeof(key)); 72 | 73 | return sizeof(key); 74 | } 75 | 76 | /* read from a time device */ 77 | static ssize_t 78 | devfs_read_time(struct file *file, void *buf, size_t nbyte) 79 | { 80 | struct time tm; 81 | size_t size = 20; 82 | char tmpbuf[size + 1]; 83 | 84 | if (nbyte < size || file->pos > 0) { 85 | return 0; 86 | } 87 | 88 | cmos_get_time(&tm); 89 | 90 | (void)snprintf(tmpbuf, sizeof(tmpbuf), "%04d-%02d-%02d %02d:%02d:%02d\n", 91 | tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second); 92 | 93 | memcpy(buf, tmpbuf, size); 94 | 95 | file->pos += size; 96 | 97 | return size; 98 | } 99 | 100 | /* read directory entry */ 101 | static ssize_t 102 | devfs_read_dir(struct file *file, void *buf, size_t nbyte) 103 | { 104 | if (nbyte < sizeof(struct file_info)) { 105 | return 0; 106 | } 107 | 108 | if (file->inh) { 109 | return 0; 110 | } 111 | 112 | file->pos++; 113 | 114 | if (file->pos >= DEVFS_NODE_COUNT) { 115 | return 0; 116 | } 117 | 118 | struct file_info *info = (struct file_info *)buf; 119 | 120 | info->inh = file->pos; 121 | info->type = FT_REG; 122 | 123 | switch(file->pos) { 124 | case DEVFS_NODE_VT: memcpy(info->name, "vt", 3); break; 125 | case DEVFS_NODE_KBD: memcpy(info->name, "kbd", 4); break; 126 | case DEVFS_NODE_TIME: memcpy(info->name, "time", 5); break; 127 | default: break; 128 | } 129 | 130 | return sizeof(struct file_info); 131 | } 132 | 133 | /* read data to a memory buffer */ 134 | static ssize_t 135 | devfs_read(struct file *file, void *buf, size_t nbyte) 136 | { 137 | switch (file->inh) { 138 | case DEVFS_NODE_ROOT: return devfs_read_dir(file, buf, nbyte); 139 | case DEVFS_NODE_KBD: return devfs_read_kbd(file, buf, nbyte); 140 | case DEVFS_NODE_TIME: return devfs_read_time(file, buf, nbyte); 141 | default: return 0; 142 | } 143 | } 144 | 145 | /* write to a device */ 146 | static ssize_t 147 | devfs_write(struct file *file, const void *buf, size_t nbyte) 148 | { 149 | switch (file->inh) { 150 | case DEVFS_NODE_VT: return vt_write((char *)buf, nbyte); 151 | default: return -1; 152 | } 153 | } 154 | 155 | /* mount a dev filesystem */ 156 | int 157 | devfs_mount(const char *volume) 158 | { 159 | return vfs_mount(volume, &devfs_ops, 0); 160 | } 161 | -------------------------------------------------------------------------------- /kernel/file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/file.c - file routines 8 | */ 9 | 10 | #include 11 | 12 | /* fixed array of file objects */ 13 | static struct file files[32]; 14 | 15 | /* 16 | * initialize a new file object with given superblock handle, 17 | * inode handle and file ops. return file descriptor. 18 | */ 19 | int 20 | file_new(uintptr_t sbh, uintptr_t inh, struct file_ops *ops) 21 | { 22 | struct file *file = ARRAY_TAKE(files); 23 | 24 | if (!file) { 25 | printk(KERN_WARN, "too many files open\n"); 26 | return -1; 27 | } 28 | 29 | file->inh = inh; 30 | file->ops = ops; 31 | file->sbh = sbh; 32 | file->pos = 0; 33 | 34 | return (file - files); 35 | } 36 | 37 | /* release a file object */ 38 | void 39 | file_release(struct file *file) 40 | { 41 | ARRAY_RELEASE(file); 42 | } 43 | 44 | /* close a file */ 45 | int 46 | file_close(int fd) 47 | { 48 | struct file *file; 49 | int ret; 50 | 51 | file = &files[fd]; 52 | ret = file->ops->close_fn(file); 53 | 54 | return ret; 55 | } 56 | 57 | /* read data to a memory buffer */ 58 | ssize_t 59 | file_read(int fd, void *buf, size_t nbyte) 60 | { 61 | struct file *file; 62 | ssize_t ret; 63 | 64 | file = &files[fd]; 65 | ret = file->ops->read_fn(file, buf, nbyte); 66 | 67 | return ret; 68 | } 69 | 70 | /* write data from a memory buffer */ 71 | ssize_t 72 | file_write(int fd, void *buf, size_t nbyte) 73 | { 74 | struct file *file; 75 | ssize_t ret; 76 | 77 | file = &files[fd]; 78 | ret = file->ops->write_fn(file, buf, nbyte); 79 | 80 | return ret; 81 | } 82 | 83 | /* initialize the array of files */ 84 | void 85 | files_init(void) 86 | { 87 | ARRAY_INIT(files); 88 | } 89 | -------------------------------------------------------------------------------- /kernel/intr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/intr.c - interrupt handling 8 | */ 9 | 10 | #include 11 | 12 | /* array of interrupt handling routines */ 13 | static intr_handler_fn intr_handlers[INTR_COUNT]; 14 | 15 | /* assign an interrupt handling routine */ 16 | void 17 | intr_set_handler(uint8_t intno, intr_handler_fn intr_handler) 18 | { 19 | intr_handlers[intno] = intr_handler; 20 | } 21 | 22 | /* handle specified interrupt */ 23 | void 24 | intr_handle(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs) 25 | { 26 | intr_handler_fn fn = intr_handlers[intno]; 27 | 28 | if (!fn) { 29 | return; 30 | } 31 | 32 | fn(intno, intr_stack, regs); 33 | } 34 | 35 | /* initialize array of interrupt handling routines and enable interrupts */ 36 | void 37 | intr_init(void) 38 | { 39 | memset(intr_handlers, 0, sizeof(intr_handlers)); 40 | cpu_sti(); 41 | } 42 | -------------------------------------------------------------------------------- /kernel/kbd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/kbd.c - PS/2 (8042) keyboard driver 8 | */ 9 | 10 | #include 11 | 12 | /* character map for the XT scancode set */ 13 | static unsigned char kbd_map_default[] = { 14 | 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', 15 | '\t', 'q', 'w', 'e', 'r', 't','y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 16 | 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`', 0, '\\', 'z', 17 | 'x', 'c', 'v', 'b', 'n','m', ',', '.', '/', 0,'*', 0,' ', 0, 0, 0, 0, 0, 0, 18 | 0, 0, 0, 0, 0, 0, 0, 0, 254, 0, '-', 252, 0, 253, '+', 0, 255, 0, 0, 0, 0, 0, 0, 0, 19 | 0, 0, 20 | }; 21 | 22 | /* character map for the XT scancode set with shift */ 23 | static unsigned char kbd_map_shift[] = { 24 | 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', 25 | '\t', 'Q', 'W', 'E', 'R', 'T','Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0, 26 | 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':','"', '~', 0, '|', 'Z', 27 | 'X', 'C', 'V', 'B', 'N','M', '<', '>', '?', 0,'*', 0,' ', 0, 0, 0, 0, 0, 0, 28 | 0, 0, 0, 0, 0, 0, 0, 0, 254, 0, '-', 252, 0, 253, '+', 0, 255, 0, 0, 0, 0, 0, 0, 0, 29 | 0, 0, 30 | }; 31 | 32 | /* i/o port address */ 33 | enum { 34 | KBD_DATA_PORT = 0x60, 35 | }; 36 | 37 | /* keyboard buffer */ 38 | static struct kbd_buf { 39 | uint16_t buf[16]; 40 | uint8_t write_ptr; 41 | uint8_t read_ptr; 42 | uint8_t count; 43 | uint8_t size; 44 | } kbd_buf; 45 | 46 | /* private functions */ 47 | static void kbd_handler(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs); 48 | static int kbd_buf_append(uint16_t key); 49 | static int kbd_buf_pop(uint16_t *key); 50 | 51 | /* handle keyboard interrupt */ 52 | static void 53 | kbd_handler(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs) 54 | { 55 | static uint8_t shift = 0; 56 | unsigned char *map; 57 | uint16_t key; 58 | uint8_t code; 59 | 60 | code = cpu_inb(KBD_DATA_PORT); 61 | 62 | if (code == 0xb6 || code == 0xaa) { 63 | shift = 0; 64 | return; 65 | } 66 | 67 | if (code & 0x80) { 68 | return; 69 | } 70 | 71 | if (code == 0x36 || code == 0x2a) { 72 | shift = 1; 73 | return; 74 | } 75 | 76 | map = shift ? kbd_map_shift : kbd_map_default; 77 | 78 | key = ((uint16_t)code << 8) | map[code]; 79 | 80 | (void)kbd_buf_append(key); 81 | } 82 | 83 | /* 84 | * append a single key to the buffer 85 | * returns 0 on success and 1 on full buffer 86 | */ 87 | static int 88 | kbd_buf_append(uint16_t key) 89 | { 90 | if (kbd_buf.count >= kbd_buf.size) { 91 | return 1; 92 | } 93 | 94 | kbd_buf.buf[kbd_buf.write_ptr] = key; 95 | kbd_buf.count += 1; 96 | kbd_buf.write_ptr = (kbd_buf.write_ptr + 1) % kbd_buf.size; 97 | 98 | return 0; 99 | } 100 | 101 | /* 102 | * take single key from the buffer, not thread-safe. 103 | * returns 0 on success and 1 on empty buffer. 104 | */ 105 | static int 106 | kbd_buf_pop(uint16_t *key) 107 | { 108 | if (kbd_buf.count == 0) { 109 | return 1; 110 | } 111 | 112 | *key = kbd_buf.buf[kbd_buf.read_ptr]; 113 | kbd_buf.count -= 1; 114 | kbd_buf.read_ptr = (kbd_buf.read_ptr + 1) % kbd_buf.size; 115 | 116 | return 0; 117 | } 118 | 119 | /* 120 | * public, thread-safe interface to read a single key from the buffer 121 | * returns 0 on success and 1 on empty buffer. 122 | */ 123 | int 124 | kbd_read(uint16_t *key) 125 | { 126 | int ret; 127 | uint64_t flags; 128 | 129 | flags = cpu_get_flags(); 130 | cpu_cli(); 131 | ret = kbd_buf_pop(key); 132 | cpu_set_flags(flags); 133 | 134 | return ret; 135 | } 136 | 137 | /* initialize the keyboard buffer and set the interrupt handler */ 138 | void 139 | kbd_init(void) 140 | { 141 | kbd_buf.write_ptr = 0; 142 | kbd_buf.read_ptr = 0; 143 | kbd_buf.count = 0; 144 | kbd_buf.size = sizeof(kbd_buf.buf) / sizeof(kbd_buf.buf[0]); 145 | 146 | intr_set_handler(0x21, kbd_handler); 147 | } 148 | -------------------------------------------------------------------------------- /kernel/kheap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/kheap.c - heap allocator 8 | */ 9 | 10 | #include 11 | 12 | /* pointer to the end of the heap */ 13 | static uintptr_t kheap_ptr; 14 | 15 | /* amount of allocated frames */ 16 | static size_t kheap_frames; 17 | 18 | /* private methods */ 19 | static inline size_t kheap_avail(void); 20 | static int kheap_expand(void); 21 | 22 | /* return amount of allocated memory in bytes */ 23 | size_t 24 | kheap_used(void) 25 | { 26 | return kheap_ptr - KHEAP_BASE_ADDR; 27 | } 28 | 29 | /* return amount of memory available without allocating new frames */ 30 | static inline size_t 31 | kheap_avail(void) 32 | { 33 | return kheap_frames * MEM_PAGE_SIZE - kheap_used(); 34 | } 35 | 36 | /* expand heap by one frame */ 37 | static int 38 | kheap_expand(void) 39 | { 40 | uintptr_t paddr, vaddr; 41 | 42 | // allocate physical frame 43 | paddr = pmem_alloc(); 44 | if (!paddr) 45 | return -1; 46 | 47 | // map physical frame to the next virtual address 48 | vaddr = KHEAP_BASE_ADDR + kheap_frames * MEM_PAGE_SIZE; 49 | ptt_map(vaddr, paddr, 1, 1); 50 | ++kheap_frames; 51 | 52 | return 0; 53 | } 54 | 55 | /* 56 | * allocate a memory chunk with the given size 57 | * return pointer on success or 0 on failure 58 | */ 59 | void * 60 | kheap_alloc(size_t size) 61 | { 62 | void *ret; 63 | 64 | // try to expand heap to the required size 65 | while (kheap_avail() < size) { 66 | if (kheap_expand()) 67 | return 0; 68 | } 69 | 70 | ret = (void*)kheap_ptr; 71 | kheap_ptr += size; 72 | 73 | return ret; 74 | } 75 | 76 | /* free the memory chunk pointed by ptr (currently does nothing) */ 77 | void 78 | kheap_free(void *ptr) 79 | { 80 | } 81 | 82 | /* initialize the kernel heap */ 83 | void 84 | kheap_init(void) 85 | { 86 | kheap_ptr = KHEAP_BASE_ADDR; 87 | kheap_frames = 0; 88 | } 89 | -------------------------------------------------------------------------------- /kernel/kmain.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/kmain.c - kernel c entry points 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | /* entry point for the low-level interrupt service routines */ 14 | void 15 | kmain_intr(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs) 16 | { 17 | intr_handle(intno, intr_stack, regs); 18 | } 19 | 20 | /* entry point for the low-level kernel loader */ 21 | void 22 | kmain(uintptr_t mboot_paddr) 23 | { 24 | // initialize video terminal and uart early for debugging 25 | vt_init(); 26 | uart_init(); 27 | 28 | printk(KERN_INFO, "booting %s...\n", SYSTEM_NAME); 29 | 30 | // initialize and dump multiboot structures 31 | mboot_init(mboot_paddr); 32 | mboot_dump(); 33 | 34 | // initialize physical memory manager and dump memory map 35 | pmem_init(); 36 | pmem_dump_kern(); 37 | pmem_dump_avail(); 38 | 39 | // initialize page allocator (initial page tables are already configured by the loader) 40 | ptt_init(); 41 | 42 | // initialize kernel heap 43 | kheap_init(); 44 | 45 | // initialize video mode 46 | vbe_init(); 47 | 48 | // initialize interrupt handlers 49 | intr_init(); 50 | 51 | // initialize timer, multi-tasking and system calls 52 | pit_init(); 53 | tasks_init(); 54 | 55 | // initialize keyboard driver 56 | kbd_init(); 57 | 58 | // initialize virtual filesystem switch and mount basic filesystems 59 | files_init(); 60 | vfs_init(); 61 | (void)devfs_mount("/dev"); 62 | (void)romfs_mount((uintptr_t)mboot_mod(0), "/data"); 63 | (void)romfs_mount((uintptr_t)mboot_mod(1), "/apps"); 64 | 65 | // initialize GUI features 66 | if (vbe_gfx_mode()) { 67 | win_init(); 68 | font_init(); 69 | gui_init(); 70 | fbcon_init(); 71 | bar_init(); 72 | } 73 | 74 | // execute nf interpreter 75 | task_spawn_name("nf", 0, 0); 76 | 77 | // terminate curren task 78 | task_exit(0); 79 | } 80 | -------------------------------------------------------------------------------- /kernel/mboot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/mboot.c - multiboot structures and routines 8 | */ 9 | 10 | #include 11 | 12 | /* multiboot flags */ 13 | enum { 14 | MBOOT_INFO_CMDLINE = 0x00000004, // command line present 15 | MBOOT_INFO_MODS = 0x00000008, // modules present 16 | MBOOT_INFO_ELF_SHDR = 0X00000020, // elf section hdr table present 17 | MBOOT_INFO_MMAP = 0x00000040, // memory map present 18 | MBOOT_INFO_LOADER_NAME = 0x00000200, // boot loader name present 19 | MBOOT_INFO_VIDEO = 0x00000800, // video info present 20 | MBOOT_MEM_AVAIL = 0x00000001, // memory available 21 | }; 22 | 23 | /* memory map entry */ 24 | struct mboot_mmap_entry { 25 | uint32_t size; 26 | uint64_t addr; 27 | uint64_t len; 28 | uint32_t type; 29 | } __attribute__((packed)); 30 | 31 | /* module entry */ 32 | struct mboot_mod { 33 | uint32_t start; 34 | uint32_t end; 35 | uint32_t cmdline; 36 | uint32_t reserved; 37 | } __attribute__ ((packed)); 38 | 39 | /* multiboot info structure */ 40 | struct mboot_info { 41 | uint32_t flags; 42 | uint32_t mem_lower; 43 | uint32_t mem_upper; 44 | uint32_t boot_device; 45 | uint32_t cmdline; 46 | uint32_t mods_count; 47 | uint32_t mods_addr; 48 | 49 | uint32_t elf_num; 50 | uint32_t elf_size; 51 | uint32_t elf_addr; 52 | uint32_t elf_shndx; 53 | 54 | uint32_t mmap_length; 55 | uint32_t mmap_addr; 56 | 57 | uint32_t drives_length; 58 | uint32_t drives_addr; 59 | 60 | uint32_t config_table; 61 | uint32_t boot_loader_name; 62 | uint32_t apm_table; 63 | 64 | uint32_t vbe_control_info; 65 | uint32_t vbe_mode_info; 66 | uint16_t vbe_mode; 67 | uint16_t vbe_interface_seg; 68 | uint16_t vbe_interface_off; 69 | uint16_t vbe_interface_len; 70 | } __attribute__ ((packed)); 71 | 72 | /* pointer to the mboot_info struct of the bootloader */ 73 | static struct mboot_info *mboot_info; 74 | 75 | /* private functions */ 76 | static void mboot_dump_mods(uint32_t count, uint32_t paddr) __attribute__((unused)); 77 | static void mboot_dump_mmap(uint64_t len, uint64_t paddr) __attribute__((unused)); 78 | 79 | /* dump module entries */ 80 | static void 81 | mboot_dump_mods(uint32_t count, uint32_t paddr) 82 | { 83 | struct mboot_mod *start, *m; 84 | size_t i; 85 | 86 | start = (struct mboot_mod*)(uint64_t)paddr; 87 | 88 | printk(KERN_INFO, "modules:\n"); 89 | 90 | for (i = 0; i < count; ++i) { 91 | m = start + i; 92 | printk(KERN_INFO, " %08x - %08x %s\n", m->start, m->end, 93 | (char*)(uint64_t)m->cmdline); 94 | } 95 | } 96 | 97 | /* dump memory map entries */ 98 | static void 99 | mboot_dump_mmap(uint64_t len, uint64_t paddr) 100 | { 101 | struct mboot_mmap_entry *mmap_start, *mmap_end, *e; 102 | const char *type; 103 | 104 | mmap_start = (struct mboot_mmap_entry*)paddr; 105 | mmap_end = (struct mboot_mmap_entry*)(paddr + len); 106 | 107 | printk(KERN_INFO, "bios memory map:\n"); 108 | 109 | for (e = mmap_start; e < mmap_end; ++e) { 110 | type = e->type == MBOOT_MEM_AVAIL ? "AVL" : "RES"; 111 | printk(KERN_INFO, " %s %016x - %016x\n", 112 | type, e->addr, e->addr + e->len); 113 | } 114 | } 115 | 116 | /* return amount of the memory map entries */ 117 | size_t 118 | mboot_mmap_entry_count(void) 119 | { 120 | return mboot_info->mmap_length / sizeof(struct mboot_mmap_entry); 121 | } 122 | 123 | /* fetch n-th memory map entry */ 124 | void 125 | mboot_mmap_entry_read(size_t n, uintptr_t *addr, size_t *len, int *avail) 126 | { 127 | struct mboot_mmap_entry *entry; 128 | 129 | entry = (struct mboot_mmap_entry*)(uint64_t)(mboot_info->mmap_addr) + n; 130 | *addr = entry->addr; 131 | *len = entry->len; 132 | *avail = (entry->type == MBOOT_MEM_AVAIL); 133 | } 134 | 135 | /* dump information provided by multiboot bootloader */ 136 | void 137 | mboot_dump(void) 138 | { 139 | struct mboot_info *m = mboot_info; 140 | 141 | if (m->flags & MBOOT_INFO_LOADER_NAME) { 142 | printk(KERN_INFO, "bootloader: %s\n", (char*)(uint64_t)m->boot_loader_name); 143 | } 144 | 145 | if (m->flags & MBOOT_INFO_CMDLINE) { 146 | //printk(KERN_INFO, "cmdline: %s\n", (char*)(uint64_t)m->cmdline); 147 | } 148 | 149 | if (m->flags & MBOOT_INFO_MODS) { 150 | //mboot_dump_mods(m->mods_count, m->mods_addr); 151 | } 152 | 153 | if (m->flags & MBOOT_INFO_MMAP) { 154 | mboot_dump_mmap(m->mmap_length, m->mmap_addr); 155 | } 156 | } 157 | 158 | /* get memory address of the nth module */ 159 | uintptr_t 160 | mboot_mod(uint8_t n) 161 | { 162 | struct mboot_mod *mod = (struct mboot_mod*)(uintptr_t)mboot_info->mods_addr; 163 | return mod[n].start; 164 | } 165 | 166 | /* get final memory address of multiboot modules */ 167 | uintptr_t 168 | mboot_mods_end(void) 169 | { 170 | uint32_t count = mboot_info->mods_count; 171 | 172 | if (count == 0) { 173 | return 0; 174 | } 175 | 176 | struct mboot_mod *mods = (struct mboot_mod*)(uint64_t)mboot_info->mods_addr; 177 | struct mboot_mod *last = mods + count - 1; 178 | 179 | return (uintptr_t)(last->start + last->end); 180 | } 181 | 182 | /* get physical memory address of the vbe mode info */ 183 | uintptr_t 184 | mboot_vbe_mode_info_ptr(void) 185 | { 186 | int has_info = mboot_info->flags & MBOOT_INFO_VIDEO; 187 | uintptr_t ptr = has_info ? mboot_info->vbe_mode_info : 0; 188 | return ptr; 189 | } 190 | 191 | /* initialize multiboot info structure */ 192 | void 193 | mboot_init(uintptr_t paddr) 194 | { 195 | mboot_info = (struct mboot_info*)paddr; 196 | } 197 | -------------------------------------------------------------------------------- /kernel/pit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/pit.c - basic 8254 PIT driver 8 | */ 9 | 10 | #include 11 | 12 | /* i/o port addresses */ 13 | enum { 14 | PIT_CR0 = 0x40, // counter 0 register 15 | PIT_CWR = 0x43, // control word register 16 | }; 17 | 18 | /* tick counter */ 19 | volatile static uint64_t pit_nticks; 20 | 21 | /* read current value of the millisecond timer */ 22 | uint64_t 23 | pit_get_msecs(void) 24 | { 25 | uint64_t ret; 26 | uint64_t flags; 27 | 28 | flags = cpu_get_flags(); 29 | cpu_cli(); 30 | ret = pit_nticks * 50; 31 | cpu_set_flags(flags); 32 | 33 | return ret; 34 | } 35 | 36 | /* handle timer interrupt */ 37 | static void 38 | pit_intr_handle(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs) 39 | { 40 | ++pit_nticks; 41 | } 42 | 43 | /* 44 | * initialize counter 0 45 | */ 46 | void 47 | pit_init(void) 48 | { 49 | uint8_t hz = 20; // 20 hz divisor 50 | uint32_t div = 1193180 / hz; 51 | uint8_t div_l = (uint8_t)((div >> 0) & 0xFF); 52 | uint8_t div_h = (uint8_t)((div >> 8) & 0xFF); 53 | 54 | // counter 0, write both lsb and msb, use mode 3, binary counter 55 | cpu_outb(PIT_CWR, 0x36); 56 | 57 | // write lsb and msb for counter 0 58 | cpu_outb(PIT_CR0, div_l); 59 | cpu_outb(PIT_CR0, div_h); 60 | 61 | pit_nticks = 0; 62 | 63 | intr_set_handler(0x20, pit_intr_handle); 64 | } 65 | -------------------------------------------------------------------------------- /kernel/pmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/pmem.c - physical memory manager 8 | */ 9 | 10 | #include 11 | 12 | /* frame status */ 13 | enum { 14 | PMEM_FRAME_RESV = 0, 15 | PMEM_FRAME_AVAIL = 1, 16 | }; 17 | 18 | /* left / right alignment macros */ 19 | #define ALIGN_L(x, s) ((x % s == 0) ? x : (x) - (x % s)) 20 | #define ALIGN_R(x, s) ((x % s == 0) ? x : (x + s) - (x % s)) 21 | 22 | /* memory map */ 23 | static uint8_t pmem_frames[MEM_FRAME_COUNT]; 24 | 25 | /* saved total amount of available memory */ 26 | static size_t pmem_total_p; 27 | 28 | /* private methods */ 29 | static void pmem_set_avail(uintptr_t start, uintptr_t end); 30 | static void pmem_set_resv(uintptr_t start, uintptr_t end); 31 | static void pmem_init_kern(void); 32 | static void pmem_init_bios(void); 33 | 34 | /* mark frames contained in a specified memory region as available */ 35 | static void 36 | pmem_set_avail(uintptr_t start, uintptr_t end) 37 | { 38 | start = ALIGN_R(start, MEM_PAGE_SIZE); 39 | end = ALIGN_L(end, MEM_PAGE_SIZE); 40 | 41 | while (start < end) { 42 | pmem_frames[start / MEM_PAGE_SIZE] = PMEM_FRAME_AVAIL; 43 | start += MEM_PAGE_SIZE; 44 | } 45 | } 46 | 47 | /* mark frames containing a specified memory region as reserved */ 48 | static void 49 | pmem_set_resv(uintptr_t start, uintptr_t end) 50 | { 51 | start = ALIGN_L(start, MEM_PAGE_SIZE); 52 | end = ALIGN_R(end, MEM_PAGE_SIZE); 53 | 54 | while (start < end) { 55 | pmem_frames[start / MEM_PAGE_SIZE] = PMEM_FRAME_RESV; 56 | start += MEM_PAGE_SIZE; 57 | } 58 | } 59 | 60 | /* dump available memory regions */ 61 | void 62 | pmem_dump_avail(void) 63 | { 64 | intptr_t start = -1; 65 | intptr_t end = -1; 66 | 67 | printk(KERN_INFO, "usable memory map:\n"); 68 | 69 | for (ssize_t i = 0; i < MEM_FRAME_COUNT; ++i) { 70 | if (pmem_frames[i] == PMEM_FRAME_AVAIL) { 71 | if (start < 0) { 72 | start = i; 73 | } 74 | end = i; 75 | } else { 76 | if (start >= 0) { 77 | printk(KERN_INFO, " AVL %016x - %016x\n", 78 | start * MEM_PAGE_SIZE, end * MEM_PAGE_SIZE); 79 | } 80 | start = -1; 81 | end = -1; 82 | } 83 | } 84 | 85 | if (start >= 0) { 86 | printk(KERN_INFO, " AVL %016x - %016x\n", 87 | start * MEM_PAGE_SIZE, end * MEM_PAGE_SIZE); 88 | } 89 | } 90 | 91 | /* dump kernel memory */ 92 | void 93 | pmem_dump_kern(void) 94 | { 95 | // extern void *kernel_start; 96 | // extern void *kernel_end; 97 | extern void *kernel_text_start; 98 | extern void *kernel_text_end; 99 | extern void *kernel_data_start; 100 | extern void *kernel_data_end; 101 | extern void *kernel_bss_start; 102 | extern void *kernel_bss_end; 103 | 104 | // printk(KERN_INFO, "kernel: %016x - %016x\n", 105 | // &kernel_start, &kernel_end); 106 | printk(KERN_INFO, "kernel memory map:\n"); 107 | printk(KERN_INFO, " TEXT %016x - %016x\n", 108 | &kernel_text_start, &kernel_text_end); 109 | printk(KERN_INFO, " DATA %016x - %016x\n", 110 | &kernel_data_start, &kernel_data_end); 111 | printk(KERN_INFO, " BSS %016x - %016x\n", 112 | &kernel_bss_start, &kernel_bss_end); 113 | } 114 | 115 | /* alloc a single frame. return physical address or 0 on error */ 116 | uintptr_t 117 | pmem_alloc(void) 118 | { 119 | for (size_t i = 0; i < MEM_FRAME_COUNT; ++i) { 120 | if (pmem_frames[i] == PMEM_FRAME_AVAIL) { 121 | pmem_frames[i] = PMEM_FRAME_RESV; 122 | return i * MEM_PAGE_SIZE; 123 | } 124 | } 125 | 126 | return 0; 127 | } 128 | 129 | /* release a single frame */ 130 | void 131 | pmem_free(uintptr_t paddr) 132 | { 133 | kassert((paddr % MEM_PAGE_SIZE == 0), "paddr must be page-aligned"); 134 | pmem_frames[paddr / MEM_PAGE_SIZE] = PMEM_FRAME_AVAIL; 135 | } 136 | 137 | /* setup available memory regions basing on bios memory map */ 138 | static void 139 | pmem_init_bios(void) 140 | { 141 | size_t mmap_entry_count = mboot_mmap_entry_count(); 142 | uintptr_t addr; 143 | size_t len; 144 | int avail; 145 | 146 | pmem_total_p = 0; 147 | for (size_t i = 0; i < mmap_entry_count; ++i) { 148 | mboot_mmap_entry_read(i, &addr, &len, &avail); 149 | if (avail) { 150 | pmem_set_avail(addr, addr + len - 1); 151 | pmem_total_p += len; 152 | } 153 | } 154 | } 155 | 156 | /* mark kernel memory as reserved */ 157 | static void 158 | pmem_init_kern(void) 159 | { 160 | // FIXME: figure out a better way to check where the mboot data ends 161 | extern void *kernel_end; 162 | uintptr_t mods_end; 163 | uintptr_t end; 164 | 165 | mods_end = mboot_mods_end(); 166 | end = (uintptr_t)&kernel_end; 167 | end = (mods_end > end) ? mods_end : end; 168 | 169 | pmem_set_resv(0, end); 170 | } 171 | 172 | /* return total amount of memory */ 173 | size_t 174 | pmem_total(void) 175 | { 176 | return pmem_total_p; 177 | } 178 | 179 | /* initialize physical memory map */ 180 | void 181 | pmem_init(void) 182 | { 183 | memset(pmem_frames, PMEM_FRAME_RESV, sizeof(pmem_frames)); 184 | pmem_init_bios(); 185 | pmem_init_kern(); 186 | } 187 | -------------------------------------------------------------------------------- /kernel/printk.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/printk.c - formatted print for kernel messages 8 | */ 9 | 10 | #include 11 | 12 | /* print a formatted string to uart and vt */ 13 | int 14 | vprintk(int level, const char *fmt, va_list args) 15 | { 16 | int count; 17 | static char buf[4096]; 18 | 19 | switch (level) { 20 | case KERN_DEBUG: printk(0, "debug: "); break; 21 | case KERN_WARN: printk(0, "warn: "); break; 22 | case KERN_ERR: printk(0, "err: "); break; 23 | case KERN_INFO: break; 24 | default: break; 25 | } 26 | 27 | count = vsnprintf(buf, sizeof(buf), fmt, args); 28 | 29 | if (count < 0) { 30 | return count; 31 | } 32 | 33 | vt_write(buf, count); 34 | uart_write(buf, count); 35 | 36 | return count; 37 | } 38 | 39 | /* print a formatted string to uart and vt */ 40 | int 41 | printk(int level, const char *fmt, ...) 42 | { 43 | int ret; 44 | va_list args; 45 | 46 | va_start(args, fmt); 47 | ret = vprintk(level, fmt, args); 48 | va_end(args); 49 | 50 | return ret; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /kernel/ptt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/ptt.c - page table manager 8 | */ 9 | 10 | #include 11 | 12 | /* amount of entries in a single page table */ 13 | enum { 14 | PTT_ENTRY_COUNT = 0x200, 15 | }; 16 | 17 | /* page table entry */ 18 | union ptt_entry { 19 | struct { 20 | uint8_t present : 1; // present 21 | uint8_t rw : 1; // read/write 22 | uint8_t user : 1; // user/supervisor 23 | uint8_t pwt : 1; // page-level writethrough 24 | uint8_t pcd : 1; // page-level cache disable 25 | uint8_t accessed : 1; // accessed 26 | uint8_t dirty : 1; // dirty 27 | uint8_t page_size : 1; // page size 28 | uint8_t global : 1; // global page 29 | uint32_t _avl0 : 3; // available to software 30 | uint64_t addr : 40; // pdpt / pd / page base address 31 | uint64_t _avl1 : 11; // available to software 32 | uint8_t nx : 1; // no-execute 33 | } __attribute__((packed)); 34 | 35 | uint64_t raw; 36 | }; 37 | 38 | /* top-level page table (page map level 4) */ 39 | static union ptt_entry *ptt_pml4; 40 | 41 | /* private methods */ 42 | static void ptt_dump(union ptt_entry entry) __attribute__((unused)); 43 | 44 | /* dump a specified page table entry */ 45 | static void 46 | ptt_dump(union ptt_entry entry) 47 | { 48 | uint64_t flags = entry.raw & 0xFFF; 49 | uint64_t addr = entry.raw & ~0xFFF; 50 | printk(KERN_INFO, "ptt: addr:%016x addr:%016x flags:%016x\n", addr, entry.addr << 12, flags); 51 | } 52 | 53 | /* map a virtual address to a physical address with given flags */ 54 | void 55 | ptt_map(uint64_t vaddr, uint64_t paddr, uint8_t user, uint8_t present) 56 | { 57 | kassert(paddr % MEM_PAGE_SIZE == 0, "paddr must be page-aligned"); 58 | 59 | uint16_t pml4_offs = (vaddr & 0xFF8000000000) >> 39; 60 | uint16_t pdpt_offs = (vaddr & 0x007FC0000000) >> 30; 61 | uint16_t pd_offs = (vaddr & 0x00003FE00000) >> 21; 62 | uint16_t page_offs = (vaddr & 0x0000001FFFFF) >> 00; 63 | 64 | kassert(!pml4_offs, "vaddr must be below 1GB"); 65 | kassert(!pdpt_offs, "vaddr must be below 1GB"); 66 | kassert(!page_offs, "vaddr must be page-aligned"); 67 | 68 | union ptt_entry *ptt_pdpt = (union ptt_entry*)(ptt_pml4[pml4_offs].addr << 12); 69 | union ptt_entry *ptt_pd = (union ptt_entry*)(ptt_pdpt[pdpt_offs].addr << 12); 70 | union ptt_entry *ptt_pde = (union ptt_entry*)(&ptt_pd[pd_offs]); 71 | 72 | ptt_pde->present = present; 73 | ptt_pde->rw = 1; 74 | ptt_pde->user = user; 75 | ptt_pde->page_size = 1; 76 | ptt_pde->addr = (paddr >> 12); 77 | ptt_pde->nx = 0; 78 | 79 | cpu_invlpg(vaddr); 80 | } 81 | 82 | /* unmap a virtual address */ 83 | void 84 | ptt_unmap(uint64_t vaddr) 85 | { 86 | ptt_map(vaddr, 0, 0, 0); 87 | } 88 | 89 | /* initialize the page table manager */ 90 | void 91 | ptt_init(void) 92 | { 93 | extern union ptt_entry loader_pml4[PTT_ENTRY_COUNT]; 94 | ptt_pml4 = loader_pml4; 95 | } 96 | -------------------------------------------------------------------------------- /kernel/romfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/romfs.c - basic romfs driver 8 | */ 9 | 10 | #include 11 | 12 | /* inode types */ 13 | enum { 14 | IN_DIR = 0x01, 15 | IN_REG = 0x02, 16 | }; 17 | 18 | /* superblock structure */ 19 | struct romfs_sb { 20 | uintptr_t addr; 21 | uint32_t size; 22 | uint32_t cksum; 23 | char *volume; 24 | }; 25 | 26 | /* inode structure */ 27 | struct romfs_inode { 28 | uintptr_t addr; 29 | uint8_t flags; 30 | uint32_t next; 31 | uint32_t info; 32 | uint32_t size; 33 | uint32_t cksum; 34 | char *name; 35 | uintptr_t data; 36 | }; 37 | 38 | /* private functions */ 39 | static inline uintptr_t romfs_align_ptr(uintptr_t ptr); 40 | static inline uint32_t romfs_load_be32(uint32_t x); 41 | static void romfs_load_sb(struct romfs_sb *sb, uintptr_t addr); 42 | static void romfs_load_inode(struct romfs_inode *i, uintptr_t inh); 43 | static uintptr_t romfs_first_inode(uintptr_t sbh); 44 | static uintptr_t romfs_first_child_inode(uintptr_t sbh, uintptr_t inh); 45 | static uintptr_t romfs_next_inode(uintptr_t sbh, uintptr_t inh); 46 | static void romfs_load_file_info(struct file_info *info, uintptr_t inh); 47 | static int romfs_open(uintptr_t sbh, uintptr_t inh); 48 | static int romfs_close(struct file *file); 49 | static ssize_t romfs_read_reg(struct file *file, void *buf, size_t nbyte); 50 | static ssize_t romfs_read_dir(struct file *file, void *buf, size_t nbyte); 51 | static ssize_t romfs_read(struct file *file, void *buf, size_t nbyte); 52 | static ssize_t romfs_write(struct file *file, const void *buf, size_t nbyte); 53 | 54 | /* file operations */ 55 | static struct file_ops romfs_file_ops = { 56 | .close_fn = &romfs_close, 57 | .read_fn = &romfs_read, 58 | .write_fn = &romfs_write, 59 | }; 60 | 61 | /* vfs operations */ 62 | static struct vfs_ops romfs_ops = { 63 | .open_fn = &romfs_open, 64 | }; 65 | 66 | /* align pointer to 16-bit boundary */ 67 | static inline uintptr_t 68 | romfs_align_ptr(uintptr_t ptr) 69 | { 70 | return (ptr & 0xF) ? ((ptr + 0x10) & ~0xF) : ptr; 71 | } 72 | 73 | /* load a big-endian 32-bit value */ 74 | static inline uint32_t 75 | romfs_load_be32(uint32_t x) 76 | { 77 | return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | 78 | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)); 79 | } 80 | 81 | /* load a superblock from a specified address */ 82 | static void 83 | romfs_load_sb(struct romfs_sb *sb, uintptr_t addr) 84 | { 85 | uint32_t *size, *cksum; 86 | 87 | sb->addr = addr; 88 | 89 | size = (uint32_t *) (sb->addr + 8); 90 | cksum = (uint32_t *) (sb->addr + 12); 91 | 92 | sb->size = romfs_load_be32(*size); 93 | sb->cksum = romfs_load_be32(*cksum); 94 | 95 | sb->volume = (char *) (sb->addr + 16); 96 | } 97 | 98 | /* load an inode structure for a specified inode handle */ 99 | static void 100 | romfs_load_inode(struct romfs_inode *i, uintptr_t inh) 101 | { 102 | uint32_t *next, *spec, *size, *cksum; 103 | 104 | next = (uint32_t*)(inh); 105 | spec = (uint32_t*)(inh + 4); 106 | size = (uint32_t*)(inh + 8); 107 | cksum = (uint32_t*)(inh + 12); 108 | 109 | i->addr = inh; 110 | i->next = romfs_load_be32(*next) & (~0xF); 111 | i->flags = romfs_load_be32(*next) & 0xF; 112 | i->info = romfs_load_be32(*spec); 113 | i->size = romfs_load_be32(*size); 114 | i->cksum = romfs_load_be32(*cksum); 115 | i->name = (char*)inh + 16; 116 | i->data = inh + 16 + strlen(i->name) + 1; 117 | i->data = romfs_align_ptr(i->data); 118 | } 119 | 120 | /* get the first inode handle in a specified superblock */ 121 | static uintptr_t 122 | romfs_first_inode(uintptr_t sbh) 123 | { 124 | struct romfs_sb sb; 125 | romfs_load_sb(&sb, sbh); 126 | 127 | uintptr_t inh; 128 | inh = (uintptr_t)sb.volume + strlen(sb.volume) + 1; 129 | inh = romfs_align_ptr(inh); 130 | 131 | return inh; 132 | } 133 | 134 | /* get the first child inode handle in a specified superblock */ 135 | static uintptr_t 136 | romfs_first_child_inode(uintptr_t sbh, uintptr_t inh) 137 | { 138 | struct romfs_inode inode; 139 | 140 | romfs_load_inode(&inode, inh); 141 | 142 | return sbh + inode.info; 143 | } 144 | 145 | /* get the next inode handle in a specified superblock */ 146 | static uintptr_t 147 | romfs_next_inode(uintptr_t sbh, uintptr_t inh) 148 | { 149 | struct romfs_inode inode; 150 | 151 | romfs_load_inode(&inode, inh); 152 | 153 | if (!inode.next) { 154 | return 0; 155 | } 156 | 157 | return sbh + inode.next; 158 | } 159 | 160 | /* load a file info structure for a specified inode */ 161 | static void 162 | romfs_load_file_info(struct file_info *info, uintptr_t inh) 163 | { 164 | struct romfs_inode inode; 165 | romfs_load_inode(&inode, inh); 166 | 167 | info->inh = inh; 168 | 169 | strncpy(info->name, inode.name, NAME_MAX); 170 | info->name[NAME_MAX - 1] = '\x00'; 171 | 172 | if (inode.flags & IN_DIR) { 173 | info->type = FT_DIR; 174 | } else if (inode.flags & IN_REG) { 175 | info->type = FT_REG; 176 | } else { 177 | info->type = FT_UNK; 178 | } 179 | } 180 | 181 | /* initialize a file object for a specified superblock and inode */ 182 | static int 183 | romfs_open(uintptr_t sbh, uintptr_t inh) 184 | { 185 | struct romfs_sb sb; 186 | 187 | romfs_load_sb(&sb, sbh); 188 | 189 | if (!inh) { 190 | inh = romfs_first_inode(sbh); 191 | } 192 | 193 | return file_new(sbh, inh, &romfs_file_ops); 194 | } 195 | 196 | /* close a file */ 197 | static int 198 | romfs_close(struct file *file) 199 | { 200 | file_release(file); 201 | return 0; 202 | } 203 | 204 | /* read from a regular file to a buffer */ 205 | static ssize_t 206 | romfs_read_reg(struct file *file, void *buf, size_t nbyte) 207 | { 208 | struct romfs_inode inode; 209 | 210 | romfs_load_inode(&inode, file->inh); 211 | 212 | if (file->pos + nbyte > inode.size) { 213 | nbyte = inode.size - file->pos; 214 | } 215 | 216 | memcpy(buf, (void*)(inode.data + file->pos), nbyte); 217 | 218 | file->pos += nbyte; 219 | 220 | return nbyte; 221 | } 222 | 223 | /* read a directory entry to a buffer */ 224 | static ssize_t 225 | romfs_read_dir(struct file *file, void *buf, size_t nbyte) 226 | { 227 | uintptr_t inh; 228 | struct file_info info; 229 | 230 | if (nbyte < sizeof(struct file_info)) { 231 | return 0; 232 | } 233 | 234 | if (file->pos) { 235 | inh = file->pos; 236 | } else if (file->inh) { 237 | inh = romfs_first_child_inode(file->sbh, file->inh); 238 | } else { 239 | inh = romfs_first_inode(file->sbh); 240 | } 241 | 242 | inh = romfs_next_inode(file->sbh, inh); 243 | 244 | if (!inh) { 245 | return 0; 246 | } 247 | 248 | romfs_load_file_info(&info, inh); 249 | memcpy(buf, &info, sizeof(info)); 250 | 251 | file->pos = inh; 252 | 253 | // skip .. entry 254 | if (!strcmp(info.name, "..")) { 255 | return romfs_read_dir(file, buf, nbyte); 256 | } 257 | 258 | return sizeof(struct file_info); 259 | } 260 | 261 | /* read data from a file to a buffer */ 262 | static ssize_t 263 | romfs_read(struct file *file, void *buf, size_t nbyte) 264 | { 265 | struct file_info info; 266 | romfs_load_file_info(&info, file->inh); 267 | 268 | switch (info.type) { 269 | case FT_REG: return romfs_read_reg(file, buf, nbyte); 270 | case FT_DIR: return romfs_read_dir(file, buf, nbyte); 271 | default: return 0; 272 | } 273 | } 274 | 275 | /* write to a file (not supported, returns error) */ 276 | static ssize_t 277 | romfs_write(struct file *file, const void *buf, size_t nbyte) 278 | { 279 | return -1; 280 | } 281 | 282 | /* mount a rom filesystem */ 283 | int 284 | romfs_mount(uintptr_t addr, const char *volume) 285 | { 286 | return vfs_mount(volume, &romfs_ops, addr); 287 | } 288 | -------------------------------------------------------------------------------- /kernel/task.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* kernel/task.c - multi-tasking routines */ 7 | 8 | #include 9 | 10 | enum { 11 | TASK_COUNT = 8, // max number of tasks 12 | TASK_STACK_SIZE = 32768, // size of task stack 13 | TASK_RFLAGS = (0x01 << 9), // initial RFLAGS (interrupts enabled) 14 | }; 15 | 16 | /* structure representing the entire state of a task */ 17 | struct task { 18 | uint8_t active; 19 | 20 | task_pid_t pid; 21 | task_pid_t waits_for; 22 | 23 | uint8_t exit_req; 24 | uint8_t exit_code; 25 | uint64_t sleep_until; 26 | 27 | uint64_t rip; 28 | uint64_t rflags; 29 | uint64_t rsp; 30 | struct regs regs; 31 | }; 32 | 33 | /* private methods */ 34 | static void task_save(struct task *task, struct intr_stack *intr_stack, struct regs *regs); 35 | static void task_restore(struct task *task, struct intr_stack *intr_stack, struct regs *regs); 36 | static void task_do_exit(struct task *task); 37 | static struct task *task_next(struct task *task); 38 | static void task_intr_handle(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs); 39 | 40 | /* static data */ 41 | static uint64_t task_next_pid = 0; 42 | static struct task *task_current; 43 | static struct task tasks[TASK_COUNT]; 44 | typedef uint8_t stack_t[TASK_STACK_SIZE]; 45 | static stack_t stacks[TASK_COUNT]; 46 | 47 | /* save task state from the interrupt stack */ 48 | static void 49 | task_save(struct task *task, struct intr_stack *intr_stack, struct regs *regs) 50 | { 51 | memcpy(&task->regs, regs, sizeof(struct regs)); 52 | 53 | task->rip = intr_stack->rip; 54 | task->rflags = intr_stack->rflags; 55 | task->rsp = intr_stack->rsp; 56 | } 57 | 58 | /* restore task state to the interrupt stack */ 59 | static void 60 | task_restore(struct task *task, struct intr_stack *intr_stack, struct regs *regs) 61 | { 62 | memcpy(regs, &task->regs, sizeof(struct regs)); 63 | 64 | intr_stack->rip = task->rip; 65 | intr_stack->rflags = task->rflags; 66 | intr_stack->rsp = task->rsp; 67 | } 68 | 69 | /* terminate task and unlock tasks waiting for it */ 70 | static void 71 | task_do_exit(struct task *task) 72 | { 73 | ARRAY_FOREACH(tasks, i) { 74 | if (tasks[i].waits_for == task->pid) { 75 | tasks[i].waits_for = -1; 76 | tasks[i].regs.rax = task->exit_code; 77 | } 78 | } 79 | ARRAY_RELEASE(task); 80 | } 81 | 82 | /* return task which should be activated after the given one */ 83 | static struct task * 84 | task_next(struct task *task) 85 | { 86 | uint64_t now; 87 | int idx; 88 | 89 | now = pit_get_msecs(); 90 | idx = task - tasks; 91 | 92 | while (1) { 93 | idx = (idx + 1) % ARRAY_LENGTH(tasks); 94 | task = &tasks[idx]; 95 | 96 | if (!task->active) 97 | continue; 98 | 99 | if (task->waits_for != -1) 100 | continue; 101 | 102 | if (task->sleep_until > now) 103 | continue; 104 | 105 | return task; 106 | } 107 | 108 | // NOTREACHED 109 | return 0; 110 | } 111 | 112 | /* handle task switch interrupt */ 113 | static void 114 | task_intr_handle(uint8_t intno, struct intr_stack *intr_stack, struct regs *regs) 115 | { 116 | if (task_current->exit_req) { 117 | task_do_exit(task_current); 118 | } else { 119 | task_save(task_current, intr_stack, regs); 120 | } 121 | 122 | task_current = task_next(task_current); 123 | task_restore(task_current, intr_stack, regs); 124 | } 125 | 126 | /* spawn a new task with given entry point and args. return its pid or -1 */ 127 | task_pid_t 128 | task_spawn(uintptr_t entry, int argc, char **argv) 129 | { 130 | struct task *task; 131 | int idx; 132 | 133 | task = ARRAY_TAKE(tasks); 134 | if (!task) { 135 | return -1; 136 | } 137 | 138 | // fail on pid overflow 139 | if (task_next_pid <= 0) { 140 | ARRAY_RELEASE(task); 141 | return -1; 142 | } 143 | 144 | idx = task - tasks; 145 | 146 | task->pid = task_next_pid++; 147 | task->exit_req = 0; 148 | task->waits_for = -1; 149 | task->sleep_until = 0; 150 | task->rflags = TASK_RFLAGS; 151 | task->rip = (uint64_t)entry; 152 | task->rsp = (uint64_t)&stacks[idx+1]; 153 | task->regs.rdi = (uint64_t)argc; 154 | task->regs.rsi = (uint64_t)argv; 155 | 156 | return task->pid; 157 | } 158 | 159 | /* spawn a new task with given name and args. return its pid or -1 */ 160 | task_pid_t 161 | task_spawn_name(char *name, int argc, char **argv) 162 | { 163 | extern void nf_main(int argc, char **argv); 164 | 165 | uintptr_t entry; 166 | 167 | if (!strcmp(name, "nf")) 168 | entry = (uintptr_t)nf_main; 169 | else 170 | return -1; 171 | 172 | return task_spawn(entry, argc, argv); 173 | } 174 | 175 | /* switch to the next task */ 176 | int 177 | task_switch(void) 178 | { 179 | return cpu_task_switch(); 180 | } 181 | 182 | /* delay current task until specified task terminates and return its exit code */ 183 | int 184 | task_waitpid(task_pid_t pid) 185 | { 186 | int code; 187 | 188 | if (task_current->pid == pid) { 189 | return -1; 190 | } 191 | 192 | ARRAY_FOREACH(tasks, i) { 193 | if (tasks[i].active && tasks[i].pid == pid) { 194 | task_current->waits_for = pid; 195 | break; 196 | } 197 | } 198 | 199 | code = task_switch(); 200 | 201 | return code; 202 | } 203 | 204 | /* delay current task for a given amount of milliseconds */ 205 | void 206 | task_sleep(uint64_t msecs) 207 | { 208 | uint64_t time; 209 | 210 | time = pit_get_msecs() + msecs; 211 | task_current->sleep_until = time; 212 | 213 | (void)task_switch(); 214 | } 215 | 216 | /* terminate current task with the given status code */ 217 | void 218 | task_exit(uint8_t code) 219 | { 220 | task_current->exit_req = 1; 221 | task_current->exit_code = code; 222 | 223 | (void)task_switch(); 224 | } 225 | 226 | /* return amount of currently running tasks */ 227 | int 228 | task_count(void) 229 | { 230 | int count = 0; 231 | 232 | ARRAY_FOREACH(tasks, i) { 233 | count += !!tasks[i].active; 234 | } 235 | 236 | return count; 237 | } 238 | 239 | /* initialize task structures and interrupt handler */ 240 | void 241 | tasks_init(void) 242 | { 243 | ARRAY_INIT(tasks); 244 | 245 | // first task is the kernel itself 246 | tasks[0].active = 1; 247 | tasks[0].exit_req = 0; 248 | tasks[0].waits_for = -1; 249 | tasks[0].sleep_until = 0; 250 | tasks[0].pid = task_next_pid++; 251 | tasks[0].rflags = TASK_RFLAGS; 252 | task_current = &tasks[0]; 253 | 254 | // enable interrupt handler for switching tasks 255 | intr_set_handler(0x31, task_intr_handle); 256 | } 257 | -------------------------------------------------------------------------------- /kernel/uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/uart.c - UART 8250 driver 8 | */ 9 | 10 | #include 11 | 12 | /* i/o port address and register indexes */ 13 | enum { 14 | UART_COM1 = 0x3F8, // COM1 base address 15 | UART_THR = 0, // transmitter holding register 16 | UART_DLL = 0, // divisor latch low byte register 17 | UART_IER = 1, // interrupt enable register 18 | UART_DLH = 1, // divisor latch high byte register 19 | UART_FCR = 2, // fifo control register 20 | UART_LCR = 3, // line control register 21 | UART_MCR = 4, // modem control register 22 | UART_LSR = 5, // line status register 23 | }; 24 | 25 | /* write to the specified register of the specified port */ 26 | static inline void 27 | uart_reg_write(uint16_t port, uint8_t reg, uint8_t val) 28 | { 29 | cpu_outb(port + reg, val); 30 | } 31 | 32 | /* read from the specified register of the specified port */ 33 | static inline uint8_t 34 | uart_reg_read(uint16_t port, uint8_t reg) 35 | { 36 | return cpu_inb(port + reg); 37 | } 38 | 39 | /* check transmitter holding register empty bit */ 40 | static uint8_t 41 | uart_thr_empty(uint16_t port) 42 | { 43 | uint8_t status = uart_reg_read(port, UART_LSR); 44 | return status & 0x20; 45 | } 46 | 47 | /* write a character to the specified port */ 48 | static void 49 | uart_thr_write(uint16_t port, char c) 50 | { 51 | while (!uart_thr_empty(port)) {}; 52 | uart_reg_write(port, UART_THR, c); 53 | } 54 | 55 | /* initialize the specified port */ 56 | static void 57 | uart_port_init(uint16_t port) 58 | { 59 | // disable interrupts 60 | cpu_outb(port + UART_IER, 0x00); 61 | 62 | // enable DLAB (divisor latch access bit) 63 | cpu_outb(port + UART_LCR, 0x80); 64 | 65 | // set divisor to 0x0003 66 | // 115200 BPS / 0x0003 = 38400 BPS 67 | cpu_outb(port + UART_DLL, 0x03); 68 | cpu_outb(port + UART_DLH, 0x00); 69 | 70 | // disable DLAB, disable parity bit, set one stop bit, set 8-bit word 71 | cpu_outb(port + UART_LCR, 0x03); 72 | 73 | // clear and enable FIFO, set 14-byte interrupt trigger level 74 | cpu_outb(port + UART_FCR, 0xC7); 75 | 76 | // set RTS and DTR 77 | cpu_outb(port + UART_MCR, 0x03); 78 | } 79 | 80 | /* public interface to write string to the default serial port (COM1) */ 81 | void 82 | uart_write(const char *msg, size_t nbytes) 83 | { 84 | while (nbytes--) { 85 | char c = *msg++; 86 | if (c == '\n') { 87 | uart_thr_write(UART_COM1, '\r'); 88 | } 89 | uart_thr_write(UART_COM1, c); 90 | } 91 | } 92 | 93 | /* initialize uart driver */ 94 | void 95 | uart_init(void) 96 | { 97 | uart_port_init(UART_COM1); 98 | } 99 | -------------------------------------------------------------------------------- /kernel/vbe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/vbe.c - vesa bios extensions driver 8 | */ 9 | 10 | #include 11 | 12 | /* vbe 2.0 mode info block */ 13 | struct vbe_mode_info { 14 | uint16_t mode_attributes; 15 | uint8_t win_a_attributes; 16 | uint8_t win_b_attributes; 17 | uint16_t win_granularity; 18 | uint16_t win_size; 19 | uint16_t win_a_segment; 20 | uint16_t win_b_segment; 21 | uint32_t win_func_ptr; 22 | uint16_t bytes_per_scan_line; 23 | 24 | uint16_t x_res; 25 | uint16_t y_res; 26 | uint8_t x_char_size; 27 | uint8_t y_char_size; 28 | uint8_t number_of_planes; 29 | uint8_t bits_per_pixel; 30 | uint8_t number_of_banks; 31 | uint8_t memory_model; 32 | uint8_t bank_size; 33 | uint8_t number_of_image_pages; 34 | uint8_t reserved_0; 35 | 36 | uint8_t red_mask_size; 37 | uint8_t red_field_position; 38 | uint8_t green_mask_size; 39 | uint8_t green_field_position; 40 | uint8_t blue_mask_size; 41 | uint8_t blue_field_position; 42 | uint8_t rsvd_mask_size; 43 | uint8_t rsvd_field_position; 44 | uint8_t direct_color_mode_info; 45 | 46 | uint32_t phys_base_ptr; 47 | uint32_t reserved_1; 48 | uint16_t reserved_2; 49 | } __attribute__ ((packed)); 50 | 51 | /* linear frame buffer address */ 52 | static uint8_t *vbe_gfx_addr_p = 0; 53 | 54 | /* bytes per pixel */ 55 | static uint8_t vbe_bpp_p = 0; 56 | 57 | /* return 0 for text mode, 1 for graphics mode */ 58 | int 59 | vbe_gfx_mode(void) 60 | { 61 | return !!vbe_gfx_addr_p; 62 | } 63 | 64 | /* return the linear frame buffer address */ 65 | uint8_t * 66 | vbe_gfx_addr(void) 67 | { 68 | return vbe_gfx_addr_p; 69 | } 70 | 71 | /* return bytes per pixel */ 72 | uint8_t 73 | vbe_bpp(void) 74 | { 75 | return vbe_bpp_p; 76 | } 77 | 78 | /* initialize video mode */ 79 | void 80 | vbe_init(void) 81 | { 82 | // get vbe_mode_info structure from multiboot 83 | uintptr_t info_ptr = mboot_vbe_mode_info_ptr(); 84 | struct vbe_mode_info *info = (struct vbe_mode_info *)info_ptr; 85 | kassert(info, "vbe mode info not set"); 86 | 87 | // return if not in gfx mode 88 | if (!info->phys_base_ptr) 89 | return; 90 | 91 | printk(KERN_INFO, "video mode:\n"); 92 | printk(KERN_INFO, " resolution: %dx%dx%d\n", 93 | info->x_res, info->y_res, info->bits_per_pixel); 94 | printk(KERN_INFO, " pitch: %d, memory_model: %d\n", 95 | info->bytes_per_scan_line, info->memory_model); 96 | 97 | // make sure current video mode is the supported one 98 | kassert(info->x_res == GUI_WIDTH, "unsupported video mode"); 99 | kassert(info->y_res == GUI_HEIGHT, "unsupported video mode"); 100 | 101 | kassert((info->bytes_per_scan_line == GUI_WIDTH * 4) || 102 | (info->bytes_per_scan_line == GUI_WIDTH * 3), 103 | "unsupported video mode (pitch)"); 104 | kassert(info->bits_per_pixel == 24 || info->bits_per_pixel == 32, 105 | "unsupported video mode (bpp)"); 106 | kassert(info->memory_model == 0x06, 107 | "unsupported video mode (memory model)"); 108 | 109 | // page map framebuffer address, calculate virtual address 110 | uintptr_t base_ptr = info->phys_base_ptr; 111 | uintptr_t base_ofs = base_ptr % MEM_PAGE_SIZE; 112 | uintptr_t base_page = base_ptr - base_ofs; 113 | ptt_map(VID_PAGE_ADDR, base_page, 1, 1); 114 | 115 | // store private data 116 | vbe_gfx_addr_p = (uint8_t*)(VID_PAGE_ADDR + base_ofs); 117 | vbe_bpp_p = info->bits_per_pixel / 8; 118 | } 119 | -------------------------------------------------------------------------------- /kernel/vfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/vfs.c - virtual filesystem switch 8 | */ 9 | 10 | #include 11 | 12 | /* mount point info */ 13 | struct vfs_mountpoint { 14 | uint8_t active; 15 | char volume[PATH_MAX]; 16 | uintptr_t sbh; 17 | struct vfs_ops *ops; 18 | }; 19 | 20 | /* private functions */ 21 | static ssize_t vfs_path_len(const char *path); 22 | static const char *vfs_path_next(const char *path); 23 | static int vfs_path_first(char buf[NAME_MAX], const char *path); 24 | static int vfs_find_name(struct vfs_mountpoint *mp, struct file_info *parent, 25 | const char *name, struct file_info *dest); 26 | static int vfs_find_path(struct vfs_mountpoint *mp, const char *path, 27 | struct file_info *dest); 28 | static struct vfs_mountpoint *vfs_find_mountpoint(const char *path); 29 | 30 | /* array of the mountpoints */ 31 | static struct vfs_mountpoint vfs_mountpoints[8]; 32 | 33 | /* get the length of the first component of a path. return -1 on invalid path */ 34 | static ssize_t 35 | vfs_path_len(const char *path) 36 | { 37 | if (path[0] != '/') { 38 | return -1; 39 | } 40 | 41 | ++path; 42 | 43 | for (ssize_t i = 0; i < NAME_MAX; ++i) { 44 | if (!path[i] || path[i] == '/') { 45 | return i; 46 | } 47 | } 48 | 49 | return -1; 50 | } 51 | 52 | /* get address of the next component of a path. return NULL on invalid path */ 53 | static const char * 54 | vfs_path_next(const char *path) 55 | { 56 | ssize_t len = vfs_path_len(path); 57 | 58 | if (len < 0) { 59 | return NULL; 60 | } 61 | 62 | return path + 1 + len; 63 | } 64 | 65 | /* get the first component of a path. return 0 on success */ 66 | static int 67 | vfs_path_first(char buf[NAME_MAX], const char *path) 68 | { 69 | ssize_t len = vfs_path_len(path); 70 | 71 | if (len < 0) { 72 | return -1; 73 | } 74 | 75 | strncpy(buf, path + 1, len); 76 | buf[len] = 0; 77 | 78 | return 0; 79 | } 80 | 81 | /* mount a filesystem on a given volume */ 82 | int 83 | vfs_mount(const char *volume, struct vfs_ops *ops, uintptr_t sbh) 84 | { 85 | struct vfs_mountpoint *mp = ARRAY_TAKE(vfs_mountpoints); 86 | 87 | if (!mp) { 88 | printk(KERN_WARN, "cannot mount %s, too many filesystems\n", volume); 89 | return -1; 90 | } 91 | 92 | vfs_path_first(mp->volume, volume); 93 | mp->ops = ops; 94 | mp->sbh = sbh; 95 | 96 | return 0; 97 | } 98 | 99 | /* find a mounpoint for a given path */ 100 | static struct vfs_mountpoint * 101 | vfs_find_mountpoint(const char *path) 102 | { 103 | char buf[NAME_MAX]; 104 | array_index_t i; 105 | 106 | if (vfs_path_first(buf, path) < 0) { 107 | return NULL; 108 | } 109 | 110 | return ARRAY_FIND_BY(vfs_mountpoints, volume, buf, &i); 111 | } 112 | 113 | /* find a file with a given name in a given parent directory */ 114 | static int 115 | vfs_find_name(struct vfs_mountpoint *mp, struct file_info *parent, 116 | const char *name, struct file_info *dest) 117 | { 118 | int found = 0; 119 | struct file_info info; 120 | 121 | int fd = mp->ops->open_fn(mp->sbh, parent->inh); 122 | 123 | if (fd < 0) { 124 | return -1; 125 | } 126 | 127 | while(file_read(fd, &info, sizeof(info))) { 128 | if (!strcmp(info.name, name)) { 129 | found = 1; 130 | break; 131 | } 132 | } 133 | 134 | file_close(fd); 135 | 136 | if (!found) { 137 | return -1; 138 | } 139 | 140 | memcpy(dest, &info, sizeof(info)); 141 | 142 | return 0; 143 | } 144 | 145 | /* find a file with a given path on a given mountpoint */ 146 | static int 147 | vfs_find_path(struct vfs_mountpoint *mp, const char *path, struct file_info *dest) 148 | { 149 | char buf[NAME_MAX]; 150 | struct file_info info; 151 | struct file_info parent; 152 | 153 | // set parent to the root directory 154 | parent.type = FT_DIR; 155 | parent.inh = 0; 156 | 157 | // for empty path return a root directory 158 | if (!strlen(path) || !strcmp(path, "/")) { 159 | dest->type = parent.type; 160 | dest->inh = parent.inh; 161 | return 0; 162 | } 163 | 164 | while (1) { 165 | // load the first path component 166 | if (vfs_path_first(buf, path) < 0) { 167 | return -1; 168 | } 169 | 170 | // find file in the parent directory 171 | if (vfs_find_name(mp, &parent, buf, &info)) { 172 | return -1; 173 | } 174 | 175 | // skip to the next component 176 | path = vfs_path_next(path); 177 | 178 | // end of path 179 | if (!path || !path[0]) { 180 | break; 181 | } 182 | 183 | // return error if the current entry is not a directory 184 | if (info.type != FT_DIR) { 185 | return -1; 186 | } 187 | 188 | // ignore trailing slash for directories 189 | if (!strcmp(path, "/")) { 190 | break; 191 | } 192 | 193 | // set current inode as the parent 194 | memcpy(&parent, &info, sizeof(parent)); 195 | } 196 | 197 | memcpy(dest, &info, sizeof(info)); 198 | 199 | return 0; 200 | } 201 | 202 | /* open a file with a given path */ 203 | int 204 | vfs_open(const char *path) 205 | { 206 | struct file_info info; 207 | struct vfs_mountpoint *mp; 208 | 209 | if (!(mp = vfs_find_mountpoint(path))) { 210 | return -1; 211 | } 212 | 213 | path = vfs_path_next(path); 214 | 215 | if (vfs_find_path(mp, path, &info)) { 216 | return -1; 217 | } 218 | 219 | return mp->ops->open_fn(mp->sbh, info.inh); 220 | } 221 | 222 | /* initialize mountpoints */ 223 | void 224 | vfs_init(void) 225 | { 226 | ARRAY_INIT(vfs_mountpoints); 227 | } 228 | -------------------------------------------------------------------------------- /kernel/vt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * kernel/vt.c - minimal video terminal 8 | */ 9 | 10 | #include 11 | 12 | /* video memory address */ 13 | enum { 14 | VT_VIDMEM = 0xB8000, 15 | }; 16 | 17 | /* current input state */ 18 | enum { 19 | VT_ST_CHAR = 0, // regular character 20 | VT_ST_CMD = 1, // command number 21 | VT_ST_PARAM = 2, // command parameter 22 | }; 23 | 24 | /* control commands */ 25 | enum { 26 | VT_CMD_CLR = 1, 27 | VT_CMD_GOTOX = 2, 28 | VT_CMD_GOTOY = 3, 29 | VT_CMD_CHATTR = 4, 30 | }; 31 | 32 | /* convenience macros */ 33 | #define VT_BUF_SIZE (VT_COLS * VT_ROWS) 34 | #define VT_CR_POS (cr_y * VT_COLS + cr_x) 35 | 36 | /* private data */ 37 | static vt_flush_cb flush_cb = 0; 38 | static uint16_t buffer[VT_BUF_SIZE]; 39 | static uint16_t *vidmem = (uint16_t*)VT_VIDMEM; 40 | static uint8_t cr_x = 0; 41 | static uint8_t cr_y = 0; 42 | static uint8_t cr_attr = 0x0F; 43 | 44 | /* private functions */ 45 | static void vt_goto(uint8_t x, uint8_t y); 46 | static void vt_clr(void); 47 | static void vt_dispatch(uint8_t cmd, uint8_t param); 48 | static void vt_advance(void); 49 | static void vt_scroll(void); 50 | static void vt_putc(unsigned char chr); 51 | static void vt_flush(void); 52 | 53 | /* move cursor to the specified position */ 54 | static void 55 | vt_goto(uint8_t x, uint8_t y) 56 | { 57 | cr_x = x; 58 | cr_y = y; 59 | crtc_cursor_set(VT_CR_POS); 60 | } 61 | 62 | /* clear the internal buffer */ 63 | static void 64 | vt_clr(void) 65 | { 66 | uint16_t word = (cr_attr << 8) | ' '; 67 | for (size_t i = 0; i < VT_BUF_SIZE; ++i) { 68 | buffer[i] = word; 69 | } 70 | vt_goto(0, 0); 71 | } 72 | 73 | /* dispatch command */ 74 | static void 75 | vt_dispatch(uint8_t cmd, uint8_t param) 76 | { 77 | switch(cmd) { 78 | 79 | case VT_CMD_CLR: 80 | vt_clr(); 81 | break; 82 | 83 | case VT_CMD_GOTOX: 84 | vt_goto(param, cr_y); 85 | break; 86 | 87 | case VT_CMD_GOTOY: 88 | vt_goto(cr_x, param); 89 | break; 90 | 91 | case VT_CMD_CHATTR: 92 | cr_attr = param; 93 | break; 94 | } 95 | } 96 | 97 | /* advance cursor by one character */ 98 | static void 99 | vt_advance(void) 100 | { 101 | cr_x += 1; 102 | 103 | if (cr_x >= VT_COLS) { 104 | cr_y += cr_x / VT_COLS; 105 | cr_x = cr_x % VT_COLS; 106 | } 107 | 108 | crtc_cursor_set(VT_CR_POS); 109 | } 110 | 111 | /* scroll buffer down to the cursor position */ 112 | static void 113 | vt_scroll(void) 114 | { 115 | while (cr_y >= VT_ROWS) { 116 | 117 | for (uint8_t row = 0; row < VT_ROWS - 1; ++row) { 118 | void *dst = buffer + row * VT_COLS; 119 | void *src = buffer + (row + 1) * VT_COLS; 120 | size_t count = sizeof(buffer[0]) * VT_COLS; 121 | memcpy(dst, src, count); 122 | } 123 | 124 | for (int16_t i = 0; i < VT_COLS; ++i) { 125 | buffer[VT_COLS * VT_ROWS - 1 - i] = cr_attr << 8 | ' '; 126 | } 127 | 128 | cr_y--; 129 | } 130 | } 131 | 132 | /* handle one character of input */ 133 | static void 134 | vt_putc(unsigned char chr) 135 | { 136 | static uint8_t state = VT_ST_CHAR; 137 | static uint8_t command = 0; 138 | static uint8_t param = 0; 139 | 140 | switch (state) { 141 | 142 | case VT_ST_CHAR: 143 | if (chr == '\n') { 144 | vt_goto(0, cr_y + 1); 145 | vt_scroll(); 146 | } else if (chr == '\t') { 147 | cr_x += 8; 148 | cr_x &= ~7; 149 | cr_x -= 1; 150 | vt_advance(); 151 | } else if (chr == '\b') { 152 | if (cr_x > 0) { 153 | --cr_x; 154 | buffer[VT_CR_POS] = (cr_attr << 8) | ' '; 155 | crtc_cursor_set(VT_CR_POS); 156 | } 157 | } else if (chr == '\033') { 158 | state = VT_ST_CMD; 159 | } else if (chr >= 32) { 160 | vt_scroll(); 161 | buffer[VT_CR_POS] = (cr_attr << 8) | chr; 162 | vt_advance(); 163 | } 164 | break; 165 | 166 | case VT_ST_CMD: 167 | command = chr; 168 | state = VT_ST_PARAM; 169 | break; 170 | 171 | case VT_ST_PARAM: 172 | param = chr; 173 | vt_dispatch(command, param); 174 | state = VT_ST_CHAR; 175 | break; 176 | } 177 | } 178 | 179 | /* flush internal buffer to the video memory */ 180 | static void 181 | vt_flush(void) 182 | { 183 | memcpy(vidmem, buffer, VT_COLS * VT_ROWS * 2); 184 | if (flush_cb) { 185 | flush_cb(buffer, VT_COLS, VT_ROWS); 186 | } 187 | } 188 | 189 | /* handle n characters from the given buffer */ 190 | size_t 191 | vt_write(const char *buf, size_t n) 192 | { 193 | for (size_t i = 0; i < n; ++i) { 194 | vt_putc((unsigned char)buf[i]); 195 | } 196 | vt_flush(); 197 | 198 | return n; 199 | } 200 | 201 | /* set flush callback */ 202 | void 203 | vt_set_flush_cb(vt_flush_cb cb) 204 | { 205 | flush_cb = cb; 206 | } 207 | 208 | /* initialize terminal */ 209 | void 210 | vt_init(void) 211 | { 212 | // read cursor position 213 | uint16_t pos = crtc_cursor_get(); 214 | cr_x = pos % VT_COLS; 215 | cr_y = pos / VT_COLS; 216 | 217 | // copy existing video memory to our buffer 218 | for (int i = 0; i < (VT_COLS * VT_ROWS); ++i) { 219 | buffer[i] = vidmem[i] != 0xFFFF ? vidmem[i] : 0; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /libc/snprintf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * minimal standalone implementation of {v,a,}snprintf 8 | */ 9 | 10 | #include 11 | 12 | #ifdef __SIZE_TYPE__ 13 | typedef __SIZE_TYPE__ size_t; 14 | #else 15 | typedef unsigned long long size_t; 16 | #endif 17 | 18 | #ifdef NF_PRINTF 19 | #define PF_ASNPRINTF nf_asnprintf 20 | #define PF_VSNPRINTF nf_vsnprintf 21 | #define PF_SNPRINTF nf_snprintf 22 | extern size_t nf_strlen(const char *s); 23 | #define strlen nf_strlen 24 | #else 25 | #define PF_ASNPRINTF pf_asnprintf 26 | #define PF_VSNPRINTF pf_vsnprintf 27 | #define PF_SNPRINTF pf_snprintf 28 | extern size_t strlen(const char *s); 29 | #endif 30 | 31 | /* consecutive input states */ 32 | enum { 33 | PF_DEFAULT, 34 | PF_FLAGS, 35 | PF_WIDTH, 36 | PF_LENGTH, 37 | PF_CONV, 38 | }; 39 | 40 | /* length specifiers */ 41 | enum { 42 | PF_NONE, 43 | PF_h, 44 | PF_l, 45 | PF_ll, 46 | }; 47 | 48 | /* conversion specifiers */ 49 | enum { 50 | PF_c, 51 | PF_s, 52 | PF_d, 53 | PF_u, 54 | PF_x, 55 | PF_X, 56 | }; 57 | 58 | /* char-emitter and arg-provider function typedefs */ 59 | typedef int (pf_emit_fn)(void *payload, char c); 60 | typedef unsigned long long (pf_arg_fn)(void *payload); 61 | 62 | /* configuration and state of a single printf command */ 63 | struct pf_config { 64 | pf_emit_fn *emit_fn; 65 | void *emit_payload; 66 | 67 | pf_arg_fn *arg_fn; 68 | void *arg_payload; 69 | va_list *arg_list; 70 | 71 | int emitted; 72 | int error; 73 | int state; 74 | 75 | int zpad; 76 | int rpad; 77 | int width; 78 | int length; 79 | int conv; 80 | }; 81 | 82 | /* vasnprintf payload struct */ 83 | struct pf_vasnprintf_payload { 84 | char *buf; 85 | size_t i; 86 | size_t nbyte; 87 | }; 88 | 89 | /* local functions */ 90 | static unsigned long long pf_get_arg_va(va_list *va, int len, int conv); 91 | static unsigned long long pf_get_arg(struct pf_config *c); 92 | 93 | static void pf_emit_char(struct pf_config *c, char ch); 94 | static void pf_emit_str(struct pf_config *c, char *s); 95 | static void pf_emit_uint(struct pf_config *c, unsigned long long n, int neg); 96 | static void pf_emit_int(struct pf_config *c, long long n); 97 | 98 | static void pf_cprintf(const char *fmt, struct pf_config *c); 99 | static int pf_vasnprintf_emit(void *payload, char ch); 100 | static int pf_vasnprintf(char *buf, size_t nbyte, const char *fmt, 101 | va_list *arg_list, pf_arg_fn *arg_fn, void *arg_payload); 102 | 103 | /* get an argument with given len and conv from a va_list */ 104 | static unsigned long long 105 | pf_get_arg_va(va_list *va, int len, int conv) 106 | { 107 | if (conv == PF_x || conv == PF_X) 108 | conv = PF_u; 109 | 110 | if (conv == PF_c && len == PF_NONE) { 111 | return va_arg(*va, int); 112 | } 113 | 114 | if (conv == PF_s && len == PF_NONE) 115 | return (unsigned long long)va_arg(*va, char *); 116 | 117 | if (conv == PF_d && len == PF_h) 118 | return va_arg(*va, int); 119 | 120 | if (conv == PF_d && len == PF_NONE) 121 | return va_arg(*va, int); 122 | 123 | if (conv == PF_d && len == PF_l) 124 | return va_arg(*va, long); 125 | 126 | if (conv == PF_d && len == PF_ll) 127 | return va_arg(*va, long long); 128 | 129 | if (conv == PF_u && len == PF_h) 130 | return va_arg(*va, unsigned); 131 | 132 | if (conv == PF_u && len == PF_NONE) 133 | return va_arg(*va, unsigned); 134 | 135 | if (conv == PF_u && len == PF_l) 136 | return va_arg(*va, unsigned long); 137 | 138 | if (conv == PF_u && len == PF_ll) 139 | return va_arg(*va, unsigned long long); 140 | 141 | return 0; 142 | } 143 | 144 | /* get a single argument */ 145 | static unsigned long long 146 | pf_get_arg(struct pf_config *c) 147 | { 148 | if (c->arg_list) 149 | return pf_get_arg_va(c->arg_list, c->length, c->conv); 150 | 151 | if (c->arg_fn) 152 | return c->arg_fn(c->arg_payload); 153 | 154 | return 0; 155 | } 156 | 157 | /* emit a single unformatted character */ 158 | static void 159 | pf_emit(struct pf_config *c, char ch) 160 | { 161 | if (c->error) 162 | return; 163 | 164 | if (c->emit_fn(c->emit_payload, ch)) { 165 | c->error = 1; 166 | } else { 167 | c->emitted++; 168 | } 169 | } 170 | 171 | /* emit a single formatted character */ 172 | static void 173 | pf_emit_char(struct pf_config *c, char ch) 174 | { 175 | int pad; 176 | 177 | // emit left padding 178 | for (pad = c->width - 1; !c->rpad && pad > 0; --pad) { 179 | pf_emit(c, ' '); 180 | } 181 | 182 | // emit character 183 | pf_emit(c, ch); 184 | 185 | 186 | // emit right padding 187 | for (pad = c->width - 1; c->rpad && pad > 0; --pad) { 188 | pf_emit(c, ' '); 189 | } 190 | } 191 | 192 | /* emit a string */ 193 | static void 194 | pf_emit_str(struct pf_config *c, char *s) 195 | { 196 | int pad, len; 197 | 198 | len = strlen(s); 199 | 200 | // emit left padding 201 | for (pad = c->width - len; !c->rpad && pad > 0; --pad) { 202 | pf_emit(c, ' '); 203 | } 204 | 205 | // emit string 206 | while (*s) { 207 | pf_emit(c, *s++); 208 | } 209 | 210 | // emit right padding 211 | for (pad = c->width - len; c->rpad && pad > 0; --pad) { 212 | pf_emit(c, ' '); 213 | } 214 | 215 | } 216 | 217 | /* emit an unsigned integer */ 218 | static void 219 | pf_emit_uint(struct pf_config *c, unsigned long long n, int neg) 220 | { 221 | static const char *l_hex_digits = "0123456789abcdef"; 222 | static const char *u_hex_digits = "0123456789ABCDEF"; 223 | 224 | long long sn = (long long)sn; 225 | char buf[32]; 226 | const char *digits; 227 | int i, d, base; 228 | int pad; 229 | int num_width, sign_width; 230 | 231 | sign_width = !!neg; 232 | 233 | // "crop" the value to the requested size 234 | switch (c->length) { 235 | case PF_h: n = (unsigned short)n; break; 236 | case PF_NONE: n = (unsigned)n; break; 237 | case PF_l: n = (unsigned long)n; break; 238 | case PF_ll: n = (unsigned long long)n; break; 239 | }; 240 | 241 | // select uppercase or lowercase character set 242 | switch (c->conv) { 243 | case PF_d: base = 10; digits = l_hex_digits; break; 244 | case PF_u: base = 10; digits = l_hex_digits; break; 245 | case PF_x: base = 16; digits = l_hex_digits; break; 246 | case PF_X: base = 16; digits = u_hex_digits; break; 247 | default: c->error = 1; return; 248 | } 249 | 250 | // save digits to the temporary buffer in a reverse order 251 | i = 0; 252 | do { 253 | d = n % base; 254 | n = n / base; 255 | buf[i++] = digits[d]; 256 | } while (n != 0 && (size_t)i < sizeof(buf)); 257 | 258 | // save the amount of digits 259 | num_width = i; 260 | 261 | // in case of zero padding of a negative number, emit '-' before padding 262 | if (neg && c->zpad) { 263 | pf_emit(c, '-'); 264 | } 265 | 266 | // emit left padding (spaces or zeros) 267 | for (pad = c->width - num_width - sign_width; !c->rpad && pad > 0; --pad) { 268 | pf_emit(c, c->zpad ? '0' : ' '); 269 | } 270 | 271 | // in case of space padding of a negative number, emit '-' after padding 272 | if (neg && !c->zpad) { 273 | pf_emit(c, '-'); 274 | } 275 | 276 | // emit digits in the correct order 277 | for (i = num_width - 1; i >= 0; --i) { 278 | pf_emit(c, buf[i]); 279 | } 280 | 281 | // emit right padding (only spaces) 282 | for (pad = c->width - num_width - sign_width; c->rpad && pad > 0; --pad) { 283 | pf_emit(c, ' '); 284 | } 285 | } 286 | 287 | /* emit a signed integer */ 288 | static void 289 | pf_emit_int(struct pf_config *c, long long n) 290 | { 291 | int neg = 0; 292 | 293 | if (n < 0) { 294 | neg = 1; 295 | n *= -1; 296 | } 297 | 298 | pf_emit_uint(c, (unsigned long long)n, neg); 299 | } 300 | 301 | /* internal printf routine, operating on a pf_config struct */ 302 | static void 303 | pf_cprintf(const char *fmt, struct pf_config *c) 304 | { 305 | unsigned long long arg; 306 | char ch; 307 | 308 | c->state = PF_DEFAULT; 309 | c->error = 0; 310 | c->emitted = 0; 311 | 312 | while ((ch = *fmt++)) { 313 | 314 | // 315 | // PF_DEFAULT 316 | // 317 | 318 | // regular character 319 | if (c->state == PF_DEFAULT && ch != '%') { 320 | pf_emit(c, ch); 321 | continue; 322 | } 323 | 324 | // beginning of format specifier 325 | if (c->state == PF_DEFAULT && ch == '%') { 326 | c->state = PF_FLAGS; 327 | c->length = PF_NONE; 328 | c->conv = PF_NONE; 329 | c->width = 0; 330 | c->zpad = 0; 331 | c->rpad = 0; 332 | continue; 333 | } 334 | 335 | // 336 | // PF_FLAGS 337 | // 338 | 339 | // handle flag '0' 340 | if (c->state == PF_FLAGS && ch == '0') { 341 | c->zpad = 1; 342 | continue; 343 | } 344 | 345 | // handle flag '-' 346 | if (c->state == PF_FLAGS && ch == '-') { 347 | c->rpad = 1; 348 | continue; 349 | } 350 | 351 | // handle character other than a flag 352 | if (c->state == PF_FLAGS) { 353 | c->state = PF_WIDTH; 354 | // FALLTHROUGH 355 | } 356 | 357 | // 358 | // PF_WIDTH 359 | // 360 | 361 | // handle a single digit of the width 362 | if (c->state == PF_WIDTH && ch >= '0' && ch <= '9') { 363 | c->width = c->width * 10 + (ch - '0'); 364 | continue; 365 | } 366 | 367 | // handle character other than a digit 368 | if (c->state == PF_WIDTH) { 369 | c->state = PF_LENGTH; 370 | // FALLTHROUGH 371 | } 372 | 373 | // 374 | // PF_LENGTH 375 | // 376 | 377 | // handle %h 378 | if (c->state == PF_LENGTH && c->length == PF_NONE && ch == 'h') { 379 | c->length = PF_h; 380 | continue; 381 | } 382 | 383 | // handle %l 384 | if (c->state == PF_LENGTH && c->length == PF_NONE && ch == 'l') { 385 | c->length = PF_l; 386 | continue; 387 | } 388 | 389 | // handle %ll 390 | if (c->state == PF_LENGTH && c->length == PF_l && ch == 'l') { 391 | c->length = PF_ll; 392 | continue; 393 | } 394 | 395 | // handle character other than a length specifier 396 | if (c->state == PF_LENGTH) { 397 | c->state = PF_CONV; 398 | // FALLTHROUGH 399 | } 400 | 401 | // 402 | // PF_CONV 403 | // 404 | 405 | // handle %d 406 | if (c->state == PF_CONV && ch == 'd') { 407 | c->conv = PF_d; 408 | arg = pf_get_arg(c); 409 | pf_emit_int(c, (long long)arg); 410 | c->state = PF_DEFAULT; 411 | continue; 412 | } 413 | 414 | // handle %u 415 | if (c->state == PF_CONV && ch == 'u') { 416 | c->conv = PF_u; 417 | arg = pf_get_arg(c); 418 | pf_emit_uint(c, (unsigned long long)arg, 0); 419 | c->state = PF_DEFAULT; 420 | continue; 421 | } 422 | 423 | // handle %x 424 | if (c->state == PF_CONV && ch == 'x') { 425 | c->conv = PF_x; 426 | arg = pf_get_arg(c); 427 | pf_emit_uint(c, (unsigned long long)arg, 0); 428 | c->state = PF_DEFAULT; 429 | continue; 430 | } 431 | 432 | // handle %X 433 | if (c->state == PF_CONV && ch == 'X') { 434 | c->conv = PF_X; 435 | arg = pf_get_arg(c); 436 | pf_emit_uint(c, (unsigned long long)arg, 0); 437 | c->state = PF_DEFAULT; 438 | continue; 439 | } 440 | 441 | // handle %c 442 | if (c->state == PF_CONV && c->length == PF_NONE && ch == 'c') { 443 | c->conv = PF_c; 444 | arg = pf_get_arg(c); 445 | pf_emit_char(c, (char)arg); 446 | c->state = PF_DEFAULT; 447 | continue; 448 | } 449 | 450 | // handle %s 451 | if (c->state == PF_CONV && c->length == PF_NONE && ch == 's') { 452 | c->conv = PF_s; 453 | arg = pf_get_arg(c); 454 | pf_emit_str(c, (char *)arg); 455 | c->state = PF_DEFAULT; 456 | continue; 457 | } 458 | 459 | // invalid format 460 | c->error = 1; 461 | } 462 | 463 | pf_emit(c, 0); 464 | } 465 | 466 | /* vasnprintf's emit function: write to the character buffer */ 467 | static int 468 | pf_vasnprintf_emit(void *payload, char ch) 469 | { 470 | struct pf_vasnprintf_payload *p = (struct pf_vasnprintf_payload *)payload; 471 | 472 | if (p->i < p->nbyte) 473 | p->buf[p->i] = ch; 474 | 475 | p->i++; 476 | 477 | return 0; 478 | } 479 | 480 | /* snprintf interface supporting both va_list and arg_fn/arg_payload */ 481 | static int 482 | pf_vasnprintf(char *buf, size_t nbyte, const char *fmt, 483 | va_list *arg_list, pf_arg_fn *arg_fn, void *arg_payload) 484 | { 485 | struct pf_vasnprintf_payload payload, *p = &payload; 486 | struct pf_config config, *c = &config; 487 | 488 | // setup payload 489 | p->buf = buf; 490 | p->nbyte = nbyte; 491 | p->i = 0; 492 | 493 | // setup pf_config 494 | c->emit_fn = pf_vasnprintf_emit; 495 | c->emit_payload = p; 496 | c->arg_list = arg_list; 497 | c->arg_fn = arg_fn; 498 | c->arg_payload = arg_payload; 499 | 500 | // process 501 | pf_cprintf(fmt, c); 502 | 503 | // in case of overflow, ensure that buffer is null-terminated 504 | if (p->i > p->nbyte) { 505 | p->buf[p->nbyte - 1] = 0; 506 | } 507 | 508 | // the return value is the amount of characters which would 509 | // be emitted, given enough space, or -1 on error 510 | return c->error ? -1 : (p->i - 1); 511 | } 512 | 513 | /* snprintf interface accepting arg_fn/arg_payload arguments */ 514 | int 515 | PF_ASNPRINTF(char *buf, size_t nbyte, const char *fmt, 516 | pf_arg_fn *arg_fn, void *arg_payload) 517 | { 518 | return pf_vasnprintf(buf, nbyte, fmt, 0, arg_fn, arg_payload); 519 | } 520 | 521 | /* snprintf interface accepting a va_list argument */ 522 | int 523 | PF_VSNPRINTF(char *buf, size_t nbyte, const char *fmt, va_list va) 524 | { 525 | va_list va_copy; 526 | int ret; 527 | 528 | va_copy(va_copy, va); 529 | ret = pf_vasnprintf(buf, nbyte, fmt, &va_copy, 0, 0); 530 | va_end(va_copy); 531 | 532 | return ret; 533 | } 534 | 535 | /* snprintf interface accepting variable amount of arguments */ 536 | int 537 | PF_SNPRINTF(char *buf, size_t nbyte, const char *fmt, ...) 538 | { 539 | va_list va; 540 | int ret; 541 | 542 | va_start(va, fmt); 543 | ret = pf_vasnprintf(buf, nbyte, fmt, &va, 0, 0); 544 | va_end(va); 545 | 546 | return ret; 547 | } 548 | -------------------------------------------------------------------------------- /libc/string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Łukasz S. 3 | * Distributed under the terms of GPL-2 License. 4 | */ 5 | 6 | /* 7 | * libc/string.c - string handling routines 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | /* 14 | * copy memory area 15 | */ 16 | void * 17 | memcpy(void *dest, const void *src, size_t n) 18 | { 19 | register uint64_t *srcq; 20 | register uint64_t *destq; 21 | uint8_t *srcb; 22 | uint8_t *destb; 23 | 24 | srcq = (uint64_t *)src; 25 | destq = (uint64_t *)dest; 26 | for (; n > sizeof(*srcq); n -= sizeof(*srcq)) { 27 | *(destq++) = *(srcq++); 28 | } 29 | 30 | srcb = (uint8_t *)srcq; 31 | destb = (uint8_t *)destq; 32 | while (n--) { 33 | *(destb++) = *(srcb++); 34 | } 35 | 36 | return dest; 37 | } 38 | 39 | /* 40 | * fill memory with a constant byte 41 | */ 42 | void * 43 | memset(void *dest, uint8_t c, size_t n) 44 | { 45 | uint8_t *my_dest = (uint8_t *)dest; 46 | 47 | while (n--) { 48 | *(my_dest++) = c; 49 | } 50 | 51 | return dest; 52 | } 53 | 54 | /* 55 | * compare two strings 56 | */ 57 | int16_t 58 | strcmp(const char *s1, const char *s2) 59 | { 60 | while (*s1 && (*s1 == *s2)) { 61 | ++s1; 62 | ++s2; 63 | } 64 | return (*s1 - *s2); 65 | } 66 | 67 | /* 68 | * calculate the length of a string 69 | */ 70 | size_t 71 | strlen(const char *s1) 72 | { 73 | size_t ret = 0; 74 | 75 | while (*s1++) { 76 | ++ret; 77 | } 78 | 79 | return ret; 80 | } 81 | 82 | /* 83 | * copy a string 84 | */ 85 | char * 86 | strncpy(char *dest, const char *src, size_t n) 87 | { 88 | size_t i; 89 | 90 | for (i = 0; i < n && src[i] != '\0'; ++i) { 91 | dest[i] = src[i]; 92 | } 93 | 94 | while (i < n) { 95 | dest[i++] = '\0'; 96 | } 97 | 98 | return dest; 99 | } 100 | 101 | /* 102 | * locate a character in a string 103 | */ 104 | char * 105 | strchr(const char *str, int c) 106 | { 107 | char *s = (char *)str; 108 | 109 | while (*s) { 110 | if (*s == c) { 111 | return s; 112 | } 113 | ++s; 114 | } 115 | 116 | if (!c) { 117 | return s; 118 | } 119 | 120 | return NULL; 121 | } 122 | -------------------------------------------------------------------------------- /loader/loader.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2014-2015 Łukasz S. 3 | ; Distributed under the terms of GPL-2 License. 4 | ; 5 | ; loader/loader.s - multiboot compatible 64-bit kernel loader 6 | ; 7 | 8 | global entry 9 | global loader_pml4 10 | 11 | extern kmain 12 | extern kmain_intr 13 | 14 | section .text 15 | 16 | ; multiboot header items 17 | 18 | MBOOT_PAGE_ALIGN equ 1<<0 19 | MBOOT_MEM_INFO equ 1<<1 20 | MBOOT_VIDEO_MODE equ 1<<2 21 | MBOOT_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO | MBOOT_VIDEO_MODE 22 | MBOOT_MAGIC equ 0x1BADB002 23 | MBOOT_CKSUM equ -(MBOOT_MAGIC + MBOOT_FLAGS) 24 | 25 | MBOOT_VID_TYPE equ 0 26 | MBOOT_VID_WIDTH equ 800 27 | MBOOT_VID_HEIGHT equ 600 28 | MBOOT_VID_DEPTH equ 24 29 | 30 | ; control registers 31 | 32 | CR0_PG_BIT equ 0x1F ; paging 33 | CR0_CD_BIT equ 0x1E ; cache-disable 34 | 35 | CR4_PAE_BIT equ 0x05 ; physical-address extension 36 | CR4_PGE_BIT equ 0x07 ; page-global-enable 37 | 38 | MSR_EFER_ADDR equ 0xC0000080 ; extended feature enable register 39 | MSR_EFER_LME_BIT equ 0x08 ; long mode enable 40 | 41 | ; page translation tables 42 | 43 | PAGE_SIZE equ 0x200000 ; 2MB pages 44 | MEM_START equ 0x0 ; map memory from the beginning 45 | 46 | PML4E_SIZE equ 0x08 ; 64-bit entries 47 | PML4E_COUNT equ 0x200 ; 512 entries 48 | PML4E_LOWER equ 0x00 ; 0GB 49 | PML4E_FLAGS equ 0x03 ; present, read-write 50 | 51 | PDPE_COUNT equ 0x200 ; 512 entries 52 | PDPE_SIZE equ 0x08 ; 64-bit entries 53 | PDPE_LOWER equ 0x00 ; 0GB 54 | PDPE_FLAGS equ 0x03 ; present, read-write 55 | 56 | PDE_INITIAL_COUNT equ 0x10 ; 16 * 2MB = 32MB 57 | PDE_COUNT equ 0x200 ; 512 entries 58 | PDE_SIZE equ 0x08 ; 64-bit entries 59 | PDE_FLAGS equ 0x83 ; present, read-write, page-size 60 | 61 | ; stack 62 | 63 | STACK_SIZE equ 0x10000 ; 64KB stack 64 | 65 | ; segment selectors 66 | 67 | SS_SI_OFFSET equ 0x03 ; index offset in the selector 68 | SS_NULL equ 0x00 << SS_SI_OFFSET ; null segment selector 69 | SS_CODE32 equ 0x01 << SS_SI_OFFSET ; 32-bit code segment selector 70 | SS_CODE64 equ 0x02 << SS_SI_OFFSET ; 64-bit code segment selector 71 | SS_DATA equ 0x03 << SS_SI_OFFSET ; data segment selector 72 | 73 | ; segment descriptors 74 | 75 | SD_SIZE equ 0x08 ; 64-bit entries 76 | SD_COUNT equ 0x04 ; null, code32, code64, data 77 | SD_ALIGNMENT equ 0x08 ; align gdt to 8 bytes 78 | 79 | ; gate descriptors 80 | 81 | GATE_SIZE equ 0x10 ; 128-bit entries 82 | GATE_EXC_COUNT equ 0x20 ; 32 exceptions 83 | GATE_INT_COUNT equ 0x10 ; 16 hardware interrupts 84 | GATE_SYS_COUNT equ 0x10 ; 16 system interrupts 85 | GATE_COUNT equ GATE_EXC_COUNT + GATE_INT_COUNT + GATE_SYS_COUNT 86 | GATE_ALIGNMENT equ 0x10 ; align idt to 16 bytes 87 | 88 | ; 8259 pic 89 | 90 | PIC1_CMD equ 0x20 ; io address for master pic command 91 | PIC1_DATA equ 0x21 ; io address for master pic data 92 | PIC2_CMD equ 0xA0 ; io address for master pic command 93 | PIC2_DATA equ 0xA1 ; io address for master pic data 94 | 95 | PIC_EOI equ 0x20 ; end of interrupt 96 | 97 | PIC1_OFFSET equ 0x20 ; vector offset for master pic 98 | PIC2_OFFSET equ 0x28 ; vector offset for slave pic 99 | 100 | PIC_ICW1 equ 00010001b ; init & require ICW4 101 | PIC1_ICW2 equ PIC1_OFFSET 102 | PIC2_ICW2 equ PIC2_OFFSET 103 | PIC1_ICW3 equ 00000100b ; master pic has slave on irq 2 104 | PIC2_ICW3 equ 00000010b ; slave pic id is 2 105 | PIC_ICW4 equ 00000001b ; 8086 mode 106 | 107 | PIC1_DEF_MASK equ 00000000b ; enable all interrupts, except rtc 108 | PIC2_DEF_MASK equ 00000000b ; enable all interrupts 109 | 110 | ; multiboot header 111 | 112 | mboot_header: 113 | 114 | ; basic 115 | 116 | dd MBOOT_MAGIC 117 | dd MBOOT_FLAGS 118 | dd MBOOT_CKSUM 119 | 120 | ; aout kludge (unused) 121 | 122 | dd 0 123 | dd 0 124 | dd 0 125 | dd 0 126 | dd 0 127 | 128 | ; video mode 129 | 130 | dd MBOOT_VID_TYPE 131 | dd MBOOT_VID_WIDTH 132 | dd MBOOT_VID_HEIGHT 133 | dd MBOOT_VID_DEPTH 134 | 135 | ; loader code 136 | 137 | [bits 32] 138 | entry: 139 | 140 | ; disable interrupts 141 | 142 | cli 143 | 144 | ; setup stack 145 | 146 | mov esp, stack + STACK_SIZE 147 | mov ebp, esp 148 | 149 | ; save multiboot structure pointer 150 | 151 | mov [mboot_ptr], ebx 152 | 153 | ; load gdt 154 | 155 | lgdt [gdt_pointer_32] 156 | jmp SS_CODE32:.flush_gdt 157 | .flush_gdt: 158 | 159 | ; setup lower PDEs 160 | 161 | mov eax, MEM_START 162 | or eax, PDE_FLAGS 163 | mov ecx, PDE_INITIAL_COUNT 164 | mov edi, pd_lower 165 | .loop_pde_lower: 166 | mov [edi], eax 167 | add edi, PDE_SIZE 168 | add eax, PAGE_SIZE 169 | loop .loop_pde_lower 170 | 171 | ; setup the lower PDPE 172 | 173 | mov eax, pd_lower 174 | or eax, PDPE_FLAGS 175 | mov dword [pdpt + (PDPE_LOWER * PDPE_SIZE)], eax 176 | 177 | ; setup a single PML4E in PML4 178 | 179 | mov eax, pdpt 180 | or eax, PML4E_FLAGS 181 | mov dword [loader_pml4 + (PML4E_LOWER * PML4E_SIZE)], eax 182 | 183 | ; enable 64-bit page table entries (physical-address extension) 184 | 185 | mov eax, cr4 186 | bts eax, CR4_PAE_BIT 187 | mov cr4, eax 188 | 189 | ; initialize page-table base address with the PML4 table 190 | 191 | mov eax, loader_pml4 192 | mov cr3, eax 193 | 194 | ; set long-mode-enable bit of the extended-feature-enable-register 195 | 196 | mov ecx, MSR_EFER_ADDR 197 | rdmsr 198 | bts eax, MSR_EFER_LME_BIT 199 | wrmsr 200 | 201 | ; enable paging and disable cache 202 | 203 | mov eax, cr0 204 | bts eax, CR0_PG_BIT 205 | bts eax, CR0_CD_BIT 206 | mov cr0, eax 207 | 208 | ; jump to 64-bit code selector 209 | 210 | jmp SS_CODE64:.code64 211 | [bits 64] 212 | .code64: 213 | 214 | ; reload gdt using 64-bit pointer 215 | 216 | lgdt [gdt_pointer_64] 217 | mov r9, .flush_gdt_64 218 | jmp r9 219 | .flush_gdt_64: 220 | 221 | ; clear data segment registers 222 | 223 | xor eax, eax 224 | mov fs, eax 225 | mov gs, eax 226 | 227 | ; initialize idt gates 228 | 229 | mov rcx, 0 230 | mov rdi, idt 231 | .generate_idt_gates_loop: 232 | mov rax, [isr_stubs + rcx * 8] 233 | stosw 234 | mov rax, 0x10 235 | stosw 236 | mov ax, 0x8e00 237 | stosw 238 | mov rax, [isr_stubs + rcx * 8] 239 | shr rax, 16 240 | stosw 241 | shr rax, 16 242 | stosd 243 | xor rax, rax 244 | stosd 245 | inc rcx 246 | cmp rcx, GATE_COUNT 247 | jnz .generate_idt_gates_loop 248 | 249 | ; initialize 8259 PIC 250 | 251 | mov al, PIC_ICW1 252 | out PIC1_CMD, al 253 | mov al, PIC_ICW1 254 | out PIC2_CMD, al 255 | mov al, PIC1_ICW2 256 | out PIC1_DATA, al 257 | mov al, PIC2_ICW2 258 | out PIC2_DATA, al 259 | mov al, PIC1_ICW3 260 | out PIC1_DATA, al 261 | mov al, PIC2_ICW3 262 | out PIC2_DATA, al 263 | mov al, PIC_ICW4 264 | out PIC1_DATA, al 265 | mov al, PIC_ICW4 266 | out PIC2_DATA, al 267 | mov al, PIC1_DEF_MASK 268 | out PIC1_DATA, al 269 | mov al, PIC2_DEF_MASK 270 | out PIC2_DATA, al 271 | 272 | ; load idtr and enable interrupts 273 | 274 | lidt [idt_pointer] 275 | 276 | ; jump to the main c function 277 | 278 | mov rdi, [mboot_ptr] 279 | mov r9, kmain 280 | jmp r9 281 | 282 | ; isr stubs 283 | 284 | %macro build_isr_stub 1 285 | isr_stub_%1: 286 | cli 287 | 288 | push rax 289 | mov rax, %1 290 | mov [current_interrupt], rax 291 | pop rax 292 | 293 | jmp isr_common 294 | %endmacro 295 | 296 | %assign i 0 297 | %rep GATE_COUNT 298 | build_isr_stub i 299 | %assign i i+1 300 | %endrep 301 | 302 | ; common interrupt handling code 303 | 304 | isr_common: 305 | 306 | ; save state 307 | 308 | sub rsp, 0x100 309 | 310 | mov [rsp+0x00], rax 311 | mov [rsp+0x08], rbx 312 | mov [rsp+0x10], rcx 313 | mov [rsp+0x18], rdx 314 | mov [rsp+0x20], rdi 315 | mov [rsp+0x28], rsi 316 | mov [rsp+0x30], rbp 317 | mov [rsp+0x38], r8 318 | mov [rsp+0x40], r9 319 | mov [rsp+0x48], r10 320 | mov [rsp+0x50], r11 321 | mov [rsp+0x58], r12 322 | mov [rsp+0x60], r13 323 | mov [rsp+0x68], r14 324 | mov [rsp+0x70], r15 325 | 326 | ; call interrupt handler 327 | 328 | mov rdi, [current_interrupt] 329 | mov rsi, rsp 330 | add rsi, 0x100 331 | mov rdx, rsp 332 | mov r9, kmain_intr 333 | call r9 334 | 335 | ; handle end-of-interrupt command 336 | 337 | mov rax, [current_interrupt] 338 | 339 | cmp rax, GATE_EXC_COUNT 340 | jb .skip_eoi 341 | cmp rax, GATE_EXC_COUNT + GATE_INT_COUNT 342 | ja .skip_eoi 343 | 344 | cmp rax, PIC2_OFFSET 345 | jb .skip_eoi_pic2 346 | 347 | mov al, PIC_EOI 348 | out PIC2_CMD, al 349 | 350 | .skip_eoi_pic2: 351 | mov al, PIC_EOI 352 | out PIC1_CMD, al 353 | 354 | .skip_eoi: 355 | 356 | ; restore state 357 | 358 | mov r15, [rsp+0x70] 359 | mov r14, [rsp+0x68] 360 | mov r13, [rsp+0x60] 361 | mov r12, [rsp+0x58] 362 | mov r11, [rsp+0x50] 363 | mov r10, [rsp+0x48] 364 | mov r9, [rsp+0x40] 365 | mov r8, [rsp+0x38] 366 | mov rbp, [rsp+0x30] 367 | mov rsi, [rsp+0x28] 368 | mov rdi, [rsp+0x20] 369 | mov rdx, [rsp+0x18] 370 | mov rcx, [rsp+0x10] 371 | mov rbx, [rsp+0x08] 372 | mov rax, [rsp+0x00] 373 | 374 | add rsp, 0x100 375 | 376 | iretq 377 | 378 | [section .bss] 379 | 380 | ; page translation tables 381 | 382 | loader_pml4: 383 | resb (PML4E_COUNT * PML4E_SIZE) 384 | 385 | pdpt: 386 | resb (PDPE_COUNT * PDPE_SIZE) 387 | 388 | pd_lower: 389 | resb (PDE_COUNT * PDE_SIZE) 390 | 391 | ; stack 392 | 393 | stack: 394 | resb STACK_SIZE 395 | 396 | ; temporary storage 397 | 398 | mboot_ptr: 399 | resq 1 400 | 401 | current_interrupt: 402 | resq 1 403 | 404 | [section .rodata] 405 | 406 | ; global descriptor table with universal 32 & 64 bit segment descriptors 407 | 408 | align SD_ALIGNMENT 409 | 410 | gdt: 411 | 412 | ; null segment 413 | 414 | dw 0x00 ; segment limit[15:0] 415 | dw 0x00 ; base addr[15:0] 416 | db 0x00 ; base addr[23:16] 417 | db 00000000b ; P, DPL, S, type 418 | db 00000000b ; G, DB, _, AVL, segment limit[19:16] 419 | db 0x00 ; base addr[31:24] 420 | 421 | ; 32-bit code segment 422 | 423 | dw 0xFFFF ; segment limit[15:0] 424 | dw 0x0000 ; base addr[15:0] 425 | db 0x00 ; base addr[23:16] 426 | db 10011010b ; P, DPL, 1, 1, C, R, A 427 | db 11001111b ; G, D, L, AVL, segment limit[19:16] 428 | db 0x00 ; base addr[31:24] 429 | 430 | ; 64-bit code segment 431 | 432 | dw 0x00 ; segment limit[15:0] 433 | dw 0x00 ; base addr[15:0] 434 | db 0x00 ; base addr[23:16] 435 | db 10011000b ; P, DPL, 1, 1, C, R, A 436 | db 00100000b ; G, D, L, AVL, segment limit[19:16] 437 | db 0x00 ; base addr[31:24] 438 | 439 | ; data segment 440 | 441 | dw 0xFFFF ; segment limit[15:0] 442 | dw 0x00 ; base addr[15:0] 443 | db 0x00 ; base addr[23:16] 444 | db 10010010b ; P, DPL, 1, 0, E, W, A 445 | db 00001111b ; G, DB, _, AVL, segment limit[19:16] 446 | db 0x00 ; base addr[31:24] 447 | 448 | gdt_pointer_32: 449 | 450 | dw (SD_SIZE * SD_COUNT - 1) ; limit 451 | dd gdt ; base 452 | 453 | gdt_pointer_64: 454 | 455 | dw (SD_SIZE * SD_COUNT - 1) ; limit 456 | dq gdt ; base 457 | 458 | ; interrupt descriptor table with 64 bit gate descriptors 459 | 460 | align GATE_ALIGNMENT 461 | 462 | idt: 463 | 464 | times (GATE_COUNT * GATE_SIZE) db 0 465 | 466 | idt_pointer: 467 | 468 | dw (GATE_COUNT * GATE_SIZE) - 1 ; limit 469 | dq idt ; base 470 | 471 | ; convenience array of pointers to isrs 472 | 473 | isr_stubs: 474 | 475 | %assign i 0 476 | %rep GATE_COUNT 477 | dq isr_stub_%[i] 478 | %assign i i+1 479 | %endrep 480 | -------------------------------------------------------------------------------- /make.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright (c) 2015 Łukasz S. 4 | # Distributed under the terms of GPL-2 License. 5 | # 6 | 7 | """ 8 | os64 task automation system 9 | """ 10 | 11 | import argparse 12 | import copy 13 | import glob 14 | import inspect 15 | import os 16 | import sys 17 | import time 18 | 19 | from config import config 20 | 21 | # helpers 22 | 23 | def expand(s, **kw): 24 | """Expand string with values from config and kwargs""" 25 | 26 | args = copy.copy(config) 27 | args.update(kw) 28 | return s.format(**args) 29 | 30 | def exists(path): 31 | """Checks if the given path exists""" 32 | 33 | path = expand(path) 34 | return os.path.exists(path) 35 | 36 | def makedir(path): 37 | """Make sure the given directory exists""" 38 | 39 | path = expand(path) 40 | if not exists(path): 41 | run('mkdir -p "%s"' % path) 42 | 43 | def mtime(path): 44 | """Return modification time of given file""" 45 | 46 | path = expand(path) 47 | return os.stat(path).st_mtime 48 | 49 | def files(pattern): 50 | """List all the files matching the given pattern""" 51 | 52 | pattern = expand(pattern) 53 | return glob.glob(pattern) 54 | 55 | def newer(obj, srcs): 56 | """Check if the object file exists and is newer than all the source files""" 57 | 58 | # return if obj doesn't exist 59 | obj = expand(obj) 60 | if not exists(obj): 61 | return False 62 | 63 | # compare mtimes of obj and srcs 64 | src_mtime = max(mtime(expand(x)) for x in srcs) 65 | obj_mtime = mtime(obj) 66 | return obj_mtime > src_mtime 67 | 68 | def run(cmd, ignore_error = False): 69 | """Print and execute command. Fail on error, unless ignore_error is set""" 70 | 71 | # print command 72 | cmd = expand(cmd) 73 | print(cmd) 74 | 75 | # execute and break on error 76 | ret = os.system(cmd) 77 | if ret != 0 and not ignore_error: 78 | sys.exit('command failed') 79 | 80 | # task handlers 81 | 82 | def task_clean(): 83 | """Clean build directory""" 84 | 85 | for subdir in ('kernel', 'gui', 'libc', 'loader', 'nf', '*.img', '*.elf'): 86 | run('rm -rf "{BUILD_DIR}"/%s' % subdir) 87 | 88 | def task_kernel_objs(): 89 | """Build kernel objects""" 90 | 91 | # find relevant inclue files 92 | incl = files("{BASE_DIR}/include/*/*.h") + files("{BASE_DIR}/nf/include/*.h") 93 | 94 | for subdir in ('kernel', 'gui', 'libc', 'loader'): 95 | makedir("{BUILD_DIR}/%s" % subdir) 96 | 97 | # compile c sources 98 | for src in files("{BASE_DIR}/%s/*.c" % subdir): 99 | obj = src.replace(config['BASE_DIR'], config['BUILD_DIR'], 1) + '.o' 100 | 101 | # skip if the object file is newer than sources 102 | if newer(obj, incl + [src]): 103 | continue 104 | 105 | # build object file 106 | run('{CC} {CFLAGS} -c "%s" -o "%s"' % (src, obj)) 107 | 108 | # compile assembly sources 109 | for src in files("{BASE_DIR}/%s/*.s" % subdir): 110 | obj = src.replace(config['BASE_DIR'], config['BUILD_DIR'], 1) + '.o' 111 | 112 | # skip if the object file is newer than sources 113 | if newer(obj, incl + [src]): 114 | continue 115 | 116 | # build object file 117 | run('{NASM} -f elf64 "%s" -o "%s"' % (src, obj)) 118 | 119 | def task_nf_objs(): 120 | """Build nf objects""" 121 | 122 | makedir("{BUILD_DIR}/nf") 123 | 124 | # find all relevant files 125 | incl = files("{BASE_DIR}/include/*/*.h") + files("{BASE_DIR}/nf/src/*.h") 126 | 127 | for src in files("{BASE_DIR}/nf/src/*.c") + files("{BASE_DIR}/nf/src/os64/*.c"): 128 | 129 | base = os.path.basename(src) 130 | obj = expand("{BUILD_DIR}/nf/%s.o" % base) 131 | 132 | # skip if the object file is newer than sources 133 | if newer(obj, incl + [src]): 134 | continue 135 | 136 | # build object file 137 | run('{CC} {CFLAGS} -DNF_PLAT_OS64 -DNF_PRINTF -c "%s" -o "%s"' % (src, obj)) 138 | 139 | def task_kernel(): 140 | """Build kernel image""" 141 | 142 | # dependencies 143 | task_kernel_objs() 144 | task_nf_objs() 145 | 146 | # find all relevant object files 147 | objs = ' '.join( 148 | files("{BUILD_DIR}/loader/*.o") 149 | + files("{BUILD_DIR}/gui/*.o") 150 | + files("{BUILD_DIR}/libc/*.o") 151 | + files("{BUILD_DIR}/kernel/*.o") 152 | + files("{BUILD_DIR}/nf/*.o") 153 | ) 154 | 155 | # link the kernel image 156 | run("{LD} {LDFLAGS} %s -o {BUILD_DIR}/kernel.elf" % objs) 157 | 158 | def task_fs_images(): 159 | """Build apps/data filesystem images""" 160 | 161 | makedir("{BUILD_DIR}") 162 | run("genromfs -d {BASE_DIR}/apps -f {BUILD_DIR}/apps.img") 163 | run("genromfs -d {BASE_DIR}/data -f {BUILD_DIR}/data.img") 164 | 165 | def task_disk_image(): 166 | """Build disk image""" 167 | 168 | if exists("{DISK_IMAGE}"): 169 | return 170 | 171 | # cleanup 172 | makedir("{MOUNT_DIR}") 173 | run('sudo umount "{MOUNT_DIR}" &>/dev/null', ignore_error=True) 174 | run('sudo losetup -d "{LOOP_DEVICE}" &>/dev/null', ignore_error=True) 175 | 176 | # create blank disk image and partition 177 | makedir("{BUILD_DIR}") 178 | run('dd if=/dev/zero of="{DISK_IMAGE}" bs=1M count=32') 179 | run('chmod 666 {DISK_IMAGE}') 180 | run('fdisk "{DISK_IMAGE}" << EOF\no\nn\np\n1\n2048\n\na\nw\nEOF\n') 181 | run('sudo losetup "{LOOP_DEVICE}" -P "{DISK_IMAGE}"') 182 | 183 | # create and mount ext2 filesystem 184 | run('sudo mkfs.ext2 "{LOOP_DEVICE}p1"') 185 | run('sudo fsck -fy "{LOOP_DEVICE}p1"') 186 | run('sudo mount -o sync "{LOOP_DEVICE}p1" "{MOUNT_DIR}"') 187 | time.sleep(0.5) 188 | 189 | # instal grub 190 | run('sudo grub-install --no-floppy --modules="part_msdos multiboot ext2" '\ 191 | '--target=i386-pc --boot-directory="{MOUNT_DIR}/boot/" "{LOOP_DEVICE}"') 192 | 193 | # unmount and cleaunp 194 | run('sudo umount {MOUNT_DIR}') 195 | run('sudo losetup -d {LOOP_DEVICE}') 196 | 197 | 198 | def task_install(): 199 | """Install kernel, apps/data filesystems and GRUB config on the disk image""" 200 | 201 | # dependencies 202 | task_kernel() 203 | task_fs_images() 204 | task_disk_image() 205 | 206 | # mount filesystem on the disk image 207 | makedir("{MOUNT_DIR}") 208 | run('sudo umount "{MOUNT_DIR}" &>/dev/null', ignore_error=True) 209 | run('sudo mount -o loop,offset="{DISK_FS_OFFSET}" "{DISK_IMAGE}" "{MOUNT_DIR}"') 210 | time.sleep(0.5) 211 | 212 | # copy grub config, kernel and romfs images 213 | makedir("{MOUNT_DIR}/boot/grub") 214 | run('sudo cp "{BASE_DIR}/misc/grub.cfg" "{MOUNT_DIR}/boot/grub"') 215 | run('sudo cp "{BUILD_DIR}/kernel.elf" "{MOUNT_DIR}"') 216 | run('sudo cp "{BUILD_DIR}/apps.img" "{MOUNT_DIR}"') 217 | run('sudo cp "{BUILD_DIR}/data.img" "{MOUNT_DIR}"') 218 | 219 | # unmount filesystem 220 | run('sudo umount "{MOUNT_DIR}"') 221 | 222 | def task_qemu(): 223 | """Launch in QEMU""" 224 | 225 | # dependencies 226 | task_install() 227 | 228 | # launch qemu 229 | run("{QEMU} -hda {DISK_IMAGE} -m 64") 230 | 231 | def task_bochs(): 232 | """Launch in Bochs""" 233 | 234 | # dependencies 235 | task_install() 236 | 237 | # update paths in bochsrc 238 | bochsrc = open(expand("{BASE_DIR}/misc/bochsrc")).read() 239 | bochsrc = bochsrc.replace("DISK_IMAGE", config['DISK_IMAGE']) 240 | bochsrc = bochsrc.replace("SERIAL_OUT", config['SERIAL_OUT']) 241 | open(expand("{BUILD_DIR}/bochsrc"), 'w').write(bochsrc) 242 | 243 | # launch bochs 244 | run("bochs -f {BUILD_DIR}/bochsrc -rc {BASE_DIR}/misc/bochscmd") 245 | 246 | # entry point 247 | 248 | def main(): 249 | """Execute application""" 250 | 251 | desc = "os64 task automation system" 252 | epilog = "available tasks:\n" 253 | 254 | # find task handlers 255 | handlers = {} 256 | for name in sorted(globals()): 257 | if not name.startswith('task_'): 258 | continue 259 | fn = globals()[name] 260 | name = name.replace('task_', '').replace('_', '-') 261 | handlers[name] = fn 262 | epilog += " %-12s - %s\n" % (name, inspect.getdoc(fn)) 263 | 264 | # parse command line arguments 265 | parser = argparse.ArgumentParser(description=desc, epilog=epilog, 266 | formatter_class=argparse.RawDescriptionHelpFormatter) 267 | parser.add_argument('tasks', nargs=argparse.REMAINDER, help='tasks to execute') 268 | args = parser.parse_args() 269 | tasks = args.tasks if args.tasks else ['install'] 270 | 271 | # execute tasks 272 | for task in tasks: 273 | if not task in handlers: 274 | sys.exit('Unknown task: %s' % task) 275 | handlers[task]() 276 | 277 | if __name__ == '__main__': 278 | main() 279 | -------------------------------------------------------------------------------- /misc/app.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-x86-64) 2 | ENTRY(entry) 3 | 4 | APP_START = 0x10000000; 5 | 6 | SECTIONS { 7 | 8 | . = APP_START; 9 | 10 | .text : { 11 | *(.text) 12 | } 13 | 14 | .data : { 15 | *(.data) 16 | *(.rodata) 17 | *(.eh_frame) 18 | } 19 | 20 | .bss : ALIGN(0x1000) { 21 | *(.bss) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /misc/bochscmd: -------------------------------------------------------------------------------- 1 | continue 2 | -------------------------------------------------------------------------------- /misc/bochsrc: -------------------------------------------------------------------------------- 1 | display_library: sdl 2 | megs: 64 3 | ata0: enabled=1 4 | ata0-master: type=disk, path="DISK_IMAGE", mode=flat, cylinders=16, heads=16, spt=63 5 | boot: disk 6 | com1: enabled=1, mode=file, dev="SERIAL_OUT" 7 | magic_break: enabled=1 8 | -------------------------------------------------------------------------------- /misc/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=1 2 | set default=0 3 | serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1 4 | terminal_output --append serial 5 | #set debug=all 6 | 7 | menuentry "os64 (graphic mode)" { 8 | multiboot /kernel.elf hello world 9 | module /data.img data.img 10 | module /apps.img apps.img 11 | } 12 | 13 | menuentry "os64 (text mode)" { 14 | multiboot /kernel.elf hello world 15 | module /data.img data.img 16 | module /apps.img apps.img 17 | set gfxpayload=text 18 | } 19 | -------------------------------------------------------------------------------- /misc/kernel.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(elf64-x86-64) 2 | ENTRY(entry) 3 | 4 | KERNEL_START = 0x00100000; 5 | 6 | SECTIONS { 7 | 8 | . = KERNEL_START; 9 | 10 | kernel_start = .; 11 | 12 | .text : { 13 | kernel_text_start = .; 14 | *(.text) 15 | } 16 | kernel_text_end = .; 17 | 18 | .data : { 19 | kernel_data_start = .; 20 | *(.data) 21 | *(.rodata) 22 | *(.eh_frame) 23 | } 24 | kernel_data_end = .; 25 | 26 | .bss : ALIGN(0x1000) { 27 | kernel_bss_start = .; 28 | *(.bss) 29 | } 30 | kernel_bss_end = .; 31 | 32 | kernel_end = .; 33 | 34 | /DISCARD/ : { 35 | *(.comment) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /misc/shot-qemu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke8086/os64/35ffe752f1e09dcd968403df999dae4f97daacca/misc/shot-qemu.png -------------------------------------------------------------------------------- /misc/shot-vbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luke8086/os64/35ffe752f1e09dcd968403df999dae4f97daacca/misc/shot-vbox.png --------------------------------------------------------------------------------