├── .gitignore ├── GNUmakefile ├── LICENSE ├── README.md ├── asm └── kmain.S ├── build-mlibc ├── c └── keep ├── get-deps ├── linker-aarch64.ld ├── linker-loongarch64.ld ├── linker-riscv64.ld ├── linker-x86_64.ld ├── main.v ├── modules ├── flanterm │ └── flanterm.v ├── framebuffer │ └── framebuffer.v ├── katomic │ └── katomic.v ├── klock │ └── klock.v ├── lib │ ├── bit.v │ └── math.v ├── limine │ └── limine.v ├── pmm │ └── pmm.v └── term │ └── term.v └── v.mod /.gitignore: -------------------------------------------------------------------------------- 1 | /freestanding-headers 2 | /mlibc 3 | /c/cc-runtime.c 4 | /c/flanterm 5 | /bin-* 6 | /obj-* 7 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # Nuke built-in rules and variables. 2 | MAKEFLAGS += -rR 3 | .SUFFIXES: 4 | 5 | # This is the name that our final executable will have. 6 | # Change as needed. 7 | override OUTPUT := kernel 8 | 9 | # Convenience macro to reliably declare user overridable variables. 10 | override USER_VARIABLE = $(if $(filter $(origin $(1)),default undefined),$(eval override $(1) := $(2))) 11 | 12 | # Target architecture to build for. Default to x86_64. 13 | $(call USER_VARIABLE,KARCH,x86_64) 14 | 15 | # Destination directory on install (should always be empty by default). 16 | $(call USER_VARIABLE,DESTDIR,) 17 | 18 | # Install prefix; /usr/local is a good, standard default pick. 19 | $(call USER_VARIABLE,PREFIX,/usr/local) 20 | 21 | # Check if the architecture is supported. 22 | ifeq ($(filter $(KARCH),aarch64 loongarch64 riscv64 x86_64),) 23 | $(error Architecture $(KARCH) not supported) 24 | endif 25 | 26 | # User controllable C compiler command. 27 | $(call USER_VARIABLE,KCC,clang) 28 | 29 | # User controllable C++ compiler command. 30 | $(call USER_VARIABLE,KCXX,clang++) 31 | 32 | # User controllable linker command. 33 | $(call USER_VARIABLE,KLD,ld.lld) 34 | 35 | # User controllable V command. 36 | $(call USER_VARIABLE,KV,v) 37 | 38 | # User controllable C flags. 39 | $(call USER_VARIABLE,KCFLAGS,-g -O2 -pipe) 40 | 41 | # User controllable C++ flags. We default to same as C flags. 42 | $(call USER_VARIABLE,KCXXFLAGS,$(KCFLAGS)) 43 | 44 | # User controllable C/C++ preprocessor flags. We set none by default. 45 | $(call USER_VARIABLE,KCPPFLAGS,) 46 | 47 | # User controllable V flags. We set none by default. 48 | $(call USER_VARIABLE,KVFLAGS,) 49 | 50 | # User controllable linker flags. We set none by default. 51 | $(call USER_VARIABLE,KLDFLAGS,) 52 | 53 | # Internal flags shared by both C and C++ compilers. 54 | override SHARED_FLAGS := \ 55 | -Wall \ 56 | -Wextra \ 57 | -nostdinc \ 58 | -ffreestanding \ 59 | -fno-stack-protector \ 60 | -fno-stack-check \ 61 | -fno-lto \ 62 | -fno-PIC \ 63 | -ffunction-sections \ 64 | -fdata-sections 65 | 66 | # Internal C/C++ preprocessor flags that should not be changed by the user. 67 | override KCPPFLAGS := \ 68 | -I c \ 69 | $(KCPPFLAGS) \ 70 | -isystem mlibc/build-$(KARCH)/prefix/include \ 71 | -isystem freestanding-headers \ 72 | -MMD \ 73 | -MP 74 | 75 | # Architecture specific internal flags. 76 | ifeq ($(KARCH),x86_64) 77 | ifeq ($(KCC),clang) 78 | override KCC += \ 79 | -target x86_64-unknown-none 80 | endif 81 | ifeq ($(KCXX),clang++) 82 | override KCXX += \ 83 | -target x86_64-unknown-none 84 | endif 85 | override SHARED_FLAGS += \ 86 | -m64 \ 87 | -march=x86-64 \ 88 | -mno-red-zone \ 89 | -mcmodel=kernel 90 | override KLDFLAGS += \ 91 | -m elf_x86_64 92 | override KNASMFLAGS += \ 93 | -f elf64 94 | endif 95 | ifeq ($(KARCH),aarch64) 96 | ifeq ($(KCC),clang) 97 | override KCC += \ 98 | -target aarch64-unknown-none 99 | endif 100 | ifeq ($(KCXX),clang++) 101 | override KCXX += \ 102 | -target aarch64-unknown-none 103 | endif 104 | override KLDFLAGS += \ 105 | -m aarch64elf 106 | endif 107 | ifeq ($(KARCH),riscv64) 108 | ifeq ($(KCC),clang) 109 | override KCC += \ 110 | -target riscv64-unknown-none 111 | endif 112 | ifeq ($(shell $(KCC) --version | grep -i 'clang'),) 113 | override KCFLAGS += \ 114 | -march=rv64imac_zicsr_zifencei 115 | else 116 | override KCFLAGS += \ 117 | -march=rv64imac 118 | endif 119 | ifeq ($(KCXX),clang++) 120 | override KCXX += \ 121 | -target riscv64-unknown-none 122 | endif 123 | ifeq ($(shell $(KCXX) --version | grep -i 'clang'),) 124 | override KCXXFLAGS += \ 125 | -march=rv64imac_zicsr_zifencei 126 | else 127 | override KCXXFLAGS += \ 128 | -march=rv64imac 129 | endif 130 | override SHARED_FLAGS += \ 131 | -mabi=lp64 \ 132 | -mno-relax 133 | override KLDFLAGS += \ 134 | -m elf64lriscv \ 135 | --no-relax 136 | endif 137 | ifeq ($(KARCH),loongarch64) 138 | ifeq ($(KCC),clang) 139 | override KCC += \ 140 | -target loongarch64-unknown-none 141 | endif 142 | ifeq ($(KCXX),clang++) 143 | override KCXX += \ 144 | -target loongarch64-unknown-none 145 | endif 146 | override SHARED_FLAGS += \ 147 | -march=loongarch64 \ 148 | -mabi=lp64s 149 | override KLDFLAGS += \ 150 | -m elf64loongarch \ 151 | --no-relax 152 | endif 153 | 154 | override MLIBC_SHARED_FLAGS := \ 155 | -D__thread='' \ 156 | -D_Thread_local='' \ 157 | -D_GNU_SOURCE 158 | 159 | override MLIBC_CFLAGS := \ 160 | $(KCFLAGS) \ 161 | $(SHARED_FLAGS) \ 162 | $(MLIBC_SHARED_FLAGS) 163 | 164 | override MLIBC_CXXFLAGS := \ 165 | $(KCXXFLAGS) \ 166 | $(SHARED_FLAGS) \ 167 | -fno-rtti \ 168 | -fno-exceptions \ 169 | $(MLIBC_SHARED_FLAGS) 170 | 171 | # Internal C flags that should not be changed by the user. 172 | override KCFLAGS += \ 173 | -std=gnu99 \ 174 | $(SHARED_FLAGS) 175 | 176 | obj-$(KARCH)/flanterm/backends/fb.c.o: override KCPPFLAGS += \ 177 | -DFLANTERM_FB_DISABLE_BUMP_ALLOC 178 | 179 | # Internal linker flags that should not be changed by the user. 180 | override KLDFLAGS += \ 181 | -nostdlib \ 182 | -static \ 183 | -z max-page-size=0x1000 \ 184 | -gc-sections \ 185 | -T linker-$(KARCH).ld 186 | 187 | override KVFLAGS += \ 188 | -os vinix \ 189 | -enable-globals \ 190 | -nofloat \ 191 | -manualfree \ 192 | -experimental \ 193 | -message-limit 10000 \ 194 | -gc none \ 195 | -d no_backtrace 196 | 197 | # Use "find" to glob all *.v, *.c, and *.S files in the tree and obtain the 198 | # object and header dependency file names. 199 | override VFILES := $(shell find -L * -type f -name '*.v' | LC_ALL=C sort) 200 | override CFILES := $(shell cd c && find -L * -type f -name '*.c' | LC_ALL=C sort) 201 | override ASFILES := $(shell cd asm && find -L * -type f -name '*.S' | LC_ALL=C sort) 202 | override OBJ := $(addprefix obj-$(KARCH)/,$(CFILES:.c=.c.o) $(ASFILES:.S=.S.o)) 203 | override HEADER_DEPS := $(addprefix obj-$(KARCH)/,$(CFILES:.c=.c.d) $(ASFILES:.S=.S.d)) 204 | 205 | # Ensure the dependencies have been obtained. 206 | override MISSING_DEPS := $(shell if ! test -d freestanding-headers || ! test -f c/cc-runtime.c || ! test -d c/flanterm || ! test -d mlibc; then echo 1; fi) 207 | ifeq ($(MISSING_DEPS),1) 208 | $(error Please run the ./get-deps script first) 209 | endif 210 | 211 | # Default target. 212 | .PHONY: all 213 | all: bin-$(KARCH)/$(OUTPUT) 214 | 215 | # Link rules for the final executable. 216 | bin-$(KARCH)/$(OUTPUT): GNUmakefile linker-$(KARCH).ld obj-$(KARCH)/blob.c.o mlibc/build-$(KARCH)/libc.a $(OBJ) 217 | mkdir -p "$$(dirname $@)" 218 | $(KLD) obj-$(KARCH)/blob.c.o mlibc/build-$(KARCH)/libc.a $(OBJ) $(KLDFLAGS) -o $@ 219 | 220 | obj-$(KARCH)/blob.c.o: mlibc/build-$(KARCH)/libc.a $(VFILES) 221 | mkdir -p "$$(dirname $@)" 222 | $(KV) $(KVFLAGS) -o obj-$(KARCH)/blob.c . 223 | sed 's/call 0(/call *(/g' < obj-$(KARCH)/blob.c > obj-$(KARCH)/blob.c.tmp 224 | mv obj-$(KARCH)/blob.c.tmp obj-$(KARCH)/blob.c 225 | $(KCC) $(KCFLAGS) $(KCPPFLAGS) -w -c obj-$(KARCH)/blob.c -o $@ 226 | 227 | # Include header dependencies. 228 | -include $(HEADER_DEPS) 229 | 230 | # Compilation rules for *.c files. 231 | obj-$(KARCH)/%.c.o: c/%.c mlibc/build-$(KARCH)/libc.a GNUmakefile 232 | mkdir -p "$$(dirname $@)" 233 | $(KCC) $(KCFLAGS) $(KCPPFLAGS) -c $< -o $@ 234 | 235 | # Compilation rules for *.S files. 236 | obj-$(KARCH)/%.S.o: asm/%.S mlibc/build-$(KARCH)/libc.a GNUmakefile 237 | mkdir -p "$$(dirname $@)" 238 | $(KCC) $(KCFLAGS) $(KCPPFLAGS) -c $< -o $@ 239 | 240 | .PHONY: mlibc 241 | mlibc: 242 | rm -f mlibc/build-$(KARCH)/libc.a 243 | $(MAKE) mlibc/build-$(KARCH)/libc.a 244 | 245 | mlibc/build-$(KARCH)/libc.a: 246 | ARCH="$(KARCH)" \ 247 | CFLAGS="$(MLIBC_CFLAGS)" \ 248 | CXXFLAGS="$(MLIBC_CXXFLAGS)" \ 249 | CC="$(KCC)" \ 250 | CXX="$(KCXX)" \ 251 | ./build-mlibc 252 | 253 | # Remove object files and the final executable. 254 | .PHONY: clean 255 | clean: 256 | rm -rf bin-$(KARCH) obj-$(KARCH) 257 | 258 | # Remove everything built and generated including downloaded dependencies. 259 | .PHONY: distclean 260 | distclean: 261 | rm -rf bin-* obj-* freestanding-headers c/cc-runtime.c c/flanterm mlibc 262 | 263 | # Install the final built executable to its final on-root location. 264 | .PHONY: install 265 | install: all 266 | install -d "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)" 267 | install -m 644 bin-$(KARCH)/$(OUTPUT) "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)/$(OUTPUT)-$(KARCH)" 268 | 269 | # Try to undo whatever the "install" target did. 270 | .PHONY: uninstall 271 | uninstall: 272 | rm -f "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)/$(OUTPUT)-$(KARCH)" 273 | -rmdir "$(DESTDIR)$(PREFIX)/share/$(OUTPUT)" 274 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # V-Unikernel 2 | 3 | V-Unikernel is an effort to write a modern, fast, and useful unikernel in [the V programming language](https://vlang.io). 4 | 5 | Join the [Discord chat](https://discord.gg/S5Nm6ZDU38). 6 | 7 | ## What is V-Unikernel all about? 8 | 9 | - Keeping the code as simple and easy to understand as possible, while not sacrificing performance and prioritising code correctness. 10 | - Making a *usable* unikernel which can *run on real hardware*, not just on emulators or virtual machines. 11 | - Targeting modern 64-bit architectures, CPU features, and multi-core computing. 12 | - Maintaining good source-level compatibility with Linux to allow to easily port programs over. 13 | - Exploring V capabilities in unikernel development. 14 | - Having fun. 15 | 16 | **Note: V-Unikernel is still pre-alpha software not meant for daily or production usage!** 17 | 18 | ## What is a unikernel? 19 | 20 | - A unikernel is a computer program statically linked with the operating system code on which it depends. -------------------------------------------------------------------------------- /asm/kmain.S: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .globl kmain 4 | kmain: 5 | // Enable SSE and SSE2 since we compile with support for that. 6 | mov %cr0, %rax 7 | btrq $2, %rax 8 | btsq $1, %rax 9 | mov %rax, %cr0 10 | mov %cr4, %rax 11 | btsq $9, %rax 12 | btsq $10, %rax 13 | mov %rax, %cr4 14 | 15 | // Jump to V code. 16 | call main__kmain 17 | -------------------------------------------------------------------------------- /build-mlibc: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -ex 4 | 5 | cd mlibc 6 | 7 | rm -rf build-$ARCH 8 | mkdir build-$ARCH && cd build-$ARCH 9 | 10 | cat <cc 11 | #! /bin/sh 12 | $CC $CFLAGS "\$@" 13 | EOF 14 | chmod +x cc 15 | unset CC 16 | unset CFLAGS 17 | 18 | cat <c++ 19 | #! /bin/sh 20 | $CXX $CXXFLAGS "\$@" 21 | EOF 22 | chmod +x c++ 23 | unset CXX 24 | unset CXXFLAGS 25 | 26 | export PATH="$(pwd -P):$PATH" 27 | 28 | cat <cross_file.txt 29 | [binaries] 30 | c = 'cc' 31 | cpp = 'c++' 32 | 33 | [host_machine] 34 | system = 'v-unikernel' 35 | cpu_family = '$ARCH' 36 | cpu = '$ARCH' 37 | endian = 'little' 38 | EOF 39 | unset ARCH 40 | 41 | meson setup .. \ 42 | --cross-file cross_file.txt \ 43 | --buildtype=debugoptimized \ 44 | --prefix="$(pwd -P)"/prefix \ 45 | -Ddefault_library=static \ 46 | -Ddisable_libgcc_dependency=true \ 47 | -Duse_freestnd_hdrs=enabled 48 | 49 | ninja -v 50 | ninja install 51 | -------------------------------------------------------------------------------- /c/keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlang/unikernel/523bd23065c906cb9d10b8e2edb7e0331e4f662b/c/keep -------------------------------------------------------------------------------- /get-deps: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | set -ex 4 | 5 | srcdir="$(dirname "$0")" 6 | test -z "$srcdir" && srcdir=. 7 | 8 | cd "$srcdir" 9 | 10 | clone_repo_commit() { 11 | if test -d $2/.git; then 12 | git -C $2 reset --hard 13 | git -C $2 clean -fd 14 | if ! git -C $2 checkout $3; then 15 | rm -rf $2 16 | fi 17 | else 18 | if test -d $2; then 19 | echo "error: '$2' is not a Git repository" 20 | exit 1 21 | fi 22 | fi 23 | if ! test -d $2; then 24 | git clone $1 $2 25 | git -C $2 checkout $3 26 | fi 27 | } 28 | 29 | download_by_hash() { 30 | DOWNLOAD_COMMAND="curl -Lo" 31 | if ! command -v $DOWNLOAD_COMMAND >/dev/null 2>&1; then 32 | DOWNLOAD_COMMAND="wget -O" 33 | if ! command -v $DOWNLOAD_COMMAND >/dev/null 2>&1; then 34 | echo "error: Neither curl nor wget found" 35 | exit 1 36 | fi 37 | fi 38 | SHA256_COMMAND="sha256sum" 39 | if ! command -v $SHA256_COMMAND >/dev/null 2>&1; then 40 | SHA256_COMMAND="sha256" 41 | if ! command -v $SHA256_COMMAND >/dev/null 2>&1; then 42 | echo "error: Cannot find sha256(sum) command" 43 | exit 1 44 | fi 45 | fi 46 | if ! test -f $2 || ! $SHA256_COMMAND $2 | grep $3 >/dev/null 2>&1; then 47 | rm -f $2 48 | mkdir -p $2 && rm -rf $2 49 | $DOWNLOAD_COMMAND $2 $1 50 | if ! $SHA256_COMMAND $2 | grep $3 >/dev/null 2>&1; then 51 | echo "error: Cannot download file '$2' by hash" 52 | exit 1 53 | fi 54 | fi 55 | } 56 | 57 | if ! test -f version; then 58 | clone_repo_commit \ 59 | https://github.com/osdev0/freestanding-headers.git \ 60 | freestanding-headers \ 61 | f890bb927859719061fe020119f3d5439d90aecf 62 | 63 | download_by_hash \ 64 | https://github.com/osdev0/cc-runtime/raw/dcdf5d82973e77edee597a047a3ef66300903de9/cc-runtime.c \ 65 | c/cc-runtime.c \ 66 | 199907f5303ab15a963377fabcc1f2ee736e4ed18d54c59aab08345aa5485e8a 67 | 68 | clone_repo_commit \ 69 | https://github.com/mintsuki/flanterm.git \ 70 | c/flanterm \ 71 | ef07a10cc38b34aa003d17be97a9f3542e275069 72 | 73 | clone_repo_commit \ 74 | https://github.com/v-unikernel/mlibc.git \ 75 | mlibc \ 76 | a20b6dbb0af6c19ba7accc70f912f817a158b051 77 | fi 78 | -------------------------------------------------------------------------------- /linker-aarch64.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want an aarch64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-littleaarch64) 3 | 4 | /* We want the symbol kmain to be our entry point */ 5 | ENTRY(kmain) 6 | 7 | /* Define the program headers we want so the bootloader gives us the right */ 8 | /* MMU permissions; this also allows us to exert more control over the linking */ 9 | /* process. */ 10 | PHDRS 11 | { 12 | text PT_LOAD; 13 | rodata PT_LOAD; 14 | data PT_LOAD; 15 | } 16 | 17 | SECTIONS 18 | { 19 | /* We want to be placed in the topmost 2GiB of the address space, for optimisations */ 20 | /* and because that is what the Limine spec mandates. */ 21 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80000000; 24 | 25 | .text : { 26 | *(.text .text.*) 27 | } :text 28 | 29 | /* Move to the next memory page for .rodata */ 30 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 31 | 32 | .rodata : { 33 | *(.rodata .rodata.*) 34 | } :rodata 35 | 36 | /* C++ is a language that allows for global constructors. In order to obtain the */ 37 | /* address of the ".init_array" section we need to define a symbol for it. */ 38 | .init_array : { 39 | __init_array = .; 40 | *(.init_array .init_array.*) 41 | __init_array_end = .; 42 | } :rodata 43 | 44 | /* Move to the next memory page for .data */ 45 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 46 | 47 | .data : { 48 | *(.data .data.*) 49 | 50 | /* Place the sections that contain the Limine requests as part of the .data */ 51 | /* output section. */ 52 | KEEP(*(.requests_start_marker)) 53 | KEEP(*(.requests)) 54 | KEEP(*(.requests_end_marker)) 55 | } :data 56 | 57 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 58 | /* unnecessary zeros will be written to the binary. */ 59 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 60 | /* above this. */ 61 | .bss : { 62 | *(.bss .bss.*) 63 | *(COMMON) 64 | } :data 65 | 66 | /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */ 67 | /DISCARD/ : { 68 | *(.eh_frame*) 69 | *(.note .note.*) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /linker-loongarch64.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want a loongarch64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-loongarch) 3 | 4 | /* We want the symbol kmain to be our entry point */ 5 | ENTRY(kmain) 6 | 7 | /* Define the program headers we want so the bootloader gives us the right */ 8 | /* MMU permissions; this also allows us to exert more control over the linking */ 9 | /* process. */ 10 | PHDRS 11 | { 12 | text PT_LOAD; 13 | rodata PT_LOAD; 14 | data PT_LOAD; 15 | } 16 | 17 | SECTIONS 18 | { 19 | /* We want to be placed in the topmost 2GiB of the address space, for optimisations */ 20 | /* and because that is what the Limine spec mandates. */ 21 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80000000; 24 | 25 | .text : { 26 | *(.text .text.*) 27 | } :text 28 | 29 | /* Move to the next memory page for .rodata */ 30 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 31 | 32 | .rodata : { 33 | *(.rodata .rodata.*) 34 | } :rodata 35 | 36 | /* C++ is a language that allows for global constructors. In order to obtain the */ 37 | /* address of the ".init_array" section we need to define a symbol for it. */ 38 | .init_array : { 39 | __init_array = .; 40 | *(.init_array .init_array.*) 41 | __init_array_end = .; 42 | } :rodata 43 | 44 | /* Move to the next memory page for .data */ 45 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 46 | 47 | .data : { 48 | *(.data .data.*) 49 | 50 | /* Place the sections that contain the Limine requests as part of the .data */ 51 | /* output section. */ 52 | KEEP(*(.requests_start_marker)) 53 | KEEP(*(.requests)) 54 | KEEP(*(.requests_end_marker)) 55 | } :data 56 | 57 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 58 | /* unnecessary zeros will be written to the binary. */ 59 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 60 | /* above this. */ 61 | .bss : { 62 | *(.bss .bss.*) 63 | *(COMMON) 64 | } :data 65 | 66 | /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */ 67 | /DISCARD/ : { 68 | *(.eh_frame*) 69 | *(.note .note.*) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /linker-riscv64.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want a riscv64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-littleriscv) 3 | 4 | /* We want the symbol kmain to be our entry point */ 5 | ENTRY(kmain) 6 | 7 | /* Define the program headers we want so the bootloader gives us the right */ 8 | /* MMU permissions; this also allows us to exert more control over the linking */ 9 | /* process. */ 10 | PHDRS 11 | { 12 | text PT_LOAD; 13 | rodata PT_LOAD; 14 | data PT_LOAD; 15 | } 16 | 17 | SECTIONS 18 | { 19 | /* We want to be placed in the topmost 2GiB of the address space, for optimisations */ 20 | /* and because that is what the Limine spec mandates. */ 21 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80000000; 24 | 25 | .text : { 26 | *(.text .text.*) 27 | } :text 28 | 29 | /* Move to the next memory page for .rodata */ 30 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 31 | 32 | .rodata : { 33 | *(.rodata .rodata.*) 34 | } :rodata 35 | 36 | /* C++ is a language that allows for global constructors. In order to obtain the */ 37 | /* address of the ".init_array" section we need to define a symbol for it. */ 38 | .init_array : { 39 | __init_array = .; 40 | *(.init_array .init_array.*) 41 | __init_array_end = .; 42 | } :rodata 43 | 44 | /* Move to the next memory page for .data */ 45 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 46 | 47 | .data : { 48 | *(.data .data.*) 49 | 50 | /* Place the sections that contain the Limine requests as part of the .data */ 51 | /* output section. */ 52 | KEEP(*(.requests_start_marker)) 53 | KEEP(*(.requests)) 54 | KEEP(*(.requests_end_marker)) 55 | 56 | *(.sdata .sdata.*) 57 | } :data 58 | 59 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 60 | /* unnecessary zeros will be written to the binary. */ 61 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 62 | /* above this. */ 63 | .bss : { 64 | *(.sbss .sbss.*) 65 | *(.bss .bss.*) 66 | *(COMMON) 67 | } :data 68 | 69 | /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */ 70 | /DISCARD/ : { 71 | *(.eh_frame*) 72 | *(.note .note.*) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /linker-x86_64.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want an x86_64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-x86-64) 3 | 4 | /* We want the symbol kmain to be our entry point */ 5 | ENTRY(kmain) 6 | 7 | /* Define the program headers we want so the bootloader gives us the right */ 8 | /* MMU permissions; this also allows us to exert more control over the linking */ 9 | /* process. */ 10 | PHDRS 11 | { 12 | text PT_LOAD; 13 | rodata PT_LOAD; 14 | data PT_LOAD; 15 | } 16 | 17 | SECTIONS 18 | { 19 | /* We want to be placed in the topmost 2GiB of the address space, for optimisations */ 20 | /* and because that is what the Limine spec mandates. */ 21 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80000000; 24 | 25 | .text : { 26 | *(.text .text.*) 27 | } :text 28 | 29 | /* Move to the next memory page for .rodata */ 30 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 31 | 32 | .rodata : { 33 | *(.rodata .rodata.*) 34 | } :rodata 35 | 36 | /* C++ is a language that allows for global constructors. In order to obtain the */ 37 | /* address of the ".init_array" section we need to define a symbol for it. */ 38 | .init_array : { 39 | __init_array = .; 40 | *(.init_array .init_array.*) 41 | __init_array_end = .; 42 | } :rodata 43 | 44 | /* Move to the next memory page for .data */ 45 | . = ALIGN(CONSTANT(MAXPAGESIZE)); 46 | 47 | .data : { 48 | *(.data .data.*) 49 | 50 | /* Place the sections that contain the Limine requests as part of the .data */ 51 | /* output section. */ 52 | KEEP(*(.requests_start_marker)) 53 | KEEP(*(.requests)) 54 | KEEP(*(.requests_end_marker)) 55 | } :data 56 | 57 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 58 | /* unnecessary zeros will be written to the binary. */ 59 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 60 | /* above this. */ 61 | .bss : { 62 | *(.bss .bss.*) 63 | *(COMMON) 64 | } :data 65 | 66 | /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */ 67 | /DISCARD/ : { 68 | *(.eh_frame*) 69 | *(.note .note.*) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /main.v: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | import limine 4 | import pmm 5 | 6 | // Only support Limine base revision 2 and up. 7 | @[_linker_section: '.requests'] 8 | @[cinit] 9 | __global ( 10 | volatile limine_base_revision = limine.LimineBaseRevision{ 11 | revision: 2 12 | } 13 | ) 14 | 15 | // Stub V main() (we need to call the pmm_init() before initialising the 16 | // runtime, which wouldn't be possible if using normal main()). 17 | pub fn main() { 18 | kmain() 19 | } 20 | 21 | fn C._vinit(argc int, argv voidptr) 22 | 23 | pub fn kmain() { 24 | // Ensure the Limine base revision is supported. 25 | if limine_base_revision.revision != 0 { 26 | for {} 27 | } 28 | 29 | for {} 30 | 31 | // Initialize the memory allocator. 32 | pmm.initialise() 33 | 34 | // Call Vinit to initialise the V runtime. 35 | C._vinit(0, 0) 36 | 37 | print('hello world') 38 | 39 | for {} 40 | } 41 | -------------------------------------------------------------------------------- /modules/flanterm/flanterm.v: -------------------------------------------------------------------------------- 1 | module flanterm 2 | 3 | #include 4 | #include 5 | 6 | fn C.flanterm_fb_init(_malloc voidptr, _free voidptr, 7 | framebuffer &u32, width u64, height u64, pitch u64, 8 | red_mask_size u8, red_mask_shift u8, 9 | green_mask_size u8, green_mask_shift u8, 10 | blue_mask_size u8, blue_mask_shift u8, 11 | canvas &u32, 12 | ansi_colours &u32, ansi_bright_colours &u32, 13 | default_bg &u32, default_fg &u32, 14 | default_bg_bright &u32, default_fg_bright &u32, 15 | font voidptr, font_width u64, font_height u64, font_spacing u64, 16 | font_scale_x u64, font_scale_y u64, 17 | margin u64) voidptr 18 | 19 | fn C.flanterm_write(voidptr Context, buf charptr, count u64) 20 | -------------------------------------------------------------------------------- /modules/framebuffer/framebuffer.v: -------------------------------------------------------------------------------- 1 | module framebuffer 2 | 3 | import limine 4 | 5 | @[_linker_section: '.requests'] 6 | @[cinit] 7 | __global ( 8 | volatile framebuffer_req = limine.LimineFramebufferRequest{ 9 | response: unsafe { nil } 10 | } 11 | ) 12 | 13 | __global ( 14 | framebuffer_tag = &limine.LimineFramebuffer(unsafe { nil }) 15 | ) 16 | 17 | pub fn initialise() { 18 | if fb_req.response == unsafe { nil } { 19 | panic('Framebuffer bootloader response missing') 20 | } 21 | framebuffer_tag = unsafe { fb_req.response.framebuffers[0] } 22 | } 23 | -------------------------------------------------------------------------------- /modules/katomic/katomic.v: -------------------------------------------------------------------------------- 1 | module katomic 2 | 3 | pub fn bts[T](mut var T, bit u8) bool { 4 | mut ret := false 5 | unsafe { 6 | asm volatile amd64 { 7 | lock bts var, bit 8 | ; +m (*var) as var 9 | =@ccc (ret) 10 | ; r (u16(bit)) as bit 11 | ; memory 12 | } 13 | } 14 | return ret 15 | } 16 | 17 | pub fn btr[T](mut var T, bit u8) bool { 18 | mut ret := false 19 | unsafe { 20 | asm volatile amd64 { 21 | lock btr var, bit 22 | ; +m (*var) as var 23 | =@ccc (ret) 24 | ; r (u16(bit)) as bit 25 | ; memory 26 | } 27 | } 28 | return ret 29 | } 30 | 31 | pub fn cas[T](mut here T, _ifthis T, writethis T) bool { 32 | mut ret := false 33 | mut ifthis := _ifthis 34 | unsafe { 35 | asm volatile amd64 { 36 | lock cmpxchg here, writethis 37 | ; +a (ifthis) 38 | +m (*here) as here 39 | =@ccz (ret) 40 | ; r (writethis) 41 | ; memory 42 | } 43 | } 44 | return ret 45 | } 46 | 47 | pub fn inc[T](mut var T) T { 48 | mut diff := unsafe { T(1) } 49 | unsafe { 50 | asm volatile amd64 { 51 | lock xadd var, diff 52 | ; +m (*var) as var 53 | +r (diff) 54 | ; ; memory 55 | } 56 | } 57 | return diff 58 | } 59 | 60 | pub fn dec[T](mut var T) bool { 61 | mut ret := false 62 | unsafe { 63 | mut diff := T(-1) 64 | asm volatile amd64 { 65 | lock xadd var, diff 66 | ; +m (*var) as var 67 | +r (diff) 68 | =@ccnz (ret) 69 | ; ; memory 70 | } 71 | } 72 | return ret 73 | } 74 | 75 | pub fn store[T](mut var T, value T) { 76 | unsafe { 77 | asm volatile amd64 { 78 | lock xchg var, value 79 | ; +m (*var) as var 80 | +r (value) 81 | ; ; memory 82 | } 83 | } 84 | } 85 | 86 | pub fn load[T](var &T) T { 87 | mut ret := unsafe { T(0) } 88 | unsafe { 89 | asm volatile amd64 { 90 | lock xadd var, ret 91 | ; +m (*var) as var 92 | +r (ret) 93 | ; ; memory 94 | } 95 | } 96 | return ret 97 | } 98 | -------------------------------------------------------------------------------- /modules/klock/klock.v: -------------------------------------------------------------------------------- 1 | module klock 2 | 3 | import katomic 4 | 5 | pub struct Lock { 6 | pub mut: 7 | l bool 8 | caller u64 9 | } 10 | 11 | fn C.__builtin_return_address(int) voidptr 12 | 13 | pub fn (mut l Lock) acquire() { 14 | caller := u64(C.__builtin_return_address(0)) 15 | 16 | for { 17 | if l.test_and_acquire() == true { 18 | l.caller = caller 19 | return 20 | } 21 | asm volatile amd64 { 22 | pause 23 | ; ; ; memory 24 | } 25 | } 26 | } 27 | 28 | pub fn (mut l Lock) release() { 29 | katomic.store(mut &l.l, false) 30 | } 31 | 32 | pub fn (mut l Lock) test_and_acquire() bool { 33 | caller := u64(C.__builtin_return_address(0)) 34 | 35 | ret := katomic.cas(mut &l.l, false, true) 36 | if ret == true { 37 | l.caller = caller 38 | } 39 | 40 | return ret 41 | } 42 | -------------------------------------------------------------------------------- /modules/lib/bit.v: -------------------------------------------------------------------------------- 1 | module lib 2 | 3 | pub fn bittest(bitmap voidptr, index u64) bool { 4 | unsafe { 5 | mut fbitmap := &u64(bitmap) 6 | bits_type := sizeof(u64) * 8 7 | test_index := index % bits_type 8 | test_sample := fbitmap[index / bits_type] 9 | return ((test_sample >> test_index) & u64(1)) != 0 10 | } 11 | } 12 | 13 | pub fn bitset(bitmap voidptr, index u64) { 14 | unsafe { 15 | mut fbitmap := &u64(bitmap) 16 | bits_type := sizeof(u64) * 8 17 | test_index := index % bits_type 18 | fbitmap[index / bits_type] |= u64(1) << test_index 19 | } 20 | } 21 | 22 | pub fn bitreset(bitmap voidptr, index u64) { 23 | unsafe { 24 | mut fbitmap := &u64(bitmap) 25 | bits_type := sizeof(u64) * 8 26 | test_index := index % bits_type 27 | fbitmap[index / bits_type] &= ~(u64(1) << test_index) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /modules/lib/math.v: -------------------------------------------------------------------------------- 1 | module lib 2 | 3 | pub fn div_roundup[T](a T, b T) T { 4 | return (a + (b - 1)) / b 5 | } 6 | 7 | pub fn align_up(value u64, alignment u64) u64 { 8 | return div_roundup(value, alignment) * alignment 9 | } 10 | 11 | pub fn align_down(value u64, alignment u64) u64 { 12 | return (value / alignment) * alignment 13 | } 14 | -------------------------------------------------------------------------------- /modules/limine/limine.v: -------------------------------------------------------------------------------- 1 | module limine 2 | 3 | @[_linker_section: '.requests_start_marker'] 4 | @[cinit] 5 | __global ( 6 | volatile limine_requests_start_marker = [ 7 | u64(0xf6b8f4b39de7d1ae), 8 | 0xfab91a6940fcb9cf, 9 | 0x785c6ed015d3e316, 10 | 0x181e920a7852b9d9, 11 | ]! 12 | ) 13 | 14 | @[_linker_section: '.requests_end_marker'] 15 | @[cinit] 16 | __global ( 17 | volatile limine_requests_end_marker = [ 18 | u64(0xadc0e0531bb10d03), 19 | 0x9572709f31764c62, 20 | ]! 21 | ) 22 | 23 | pub struct LimineBaseRevision { 24 | pub mut: 25 | id [2]u64 = [u64(0xf9562b2d5c95a6c8), 0x6a7b384944536bdc]! 26 | revision u64 27 | } 28 | 29 | pub struct LimineUUID { 30 | pub mut: 31 | a u32 32 | b u16 33 | c u16 34 | d [8]u8 35 | } 36 | 37 | pub struct LimineFile { 38 | pub mut: 39 | revision u64 40 | address voidptr 41 | size u64 42 | path charptr 43 | cmdline charptr 44 | media_type u32 45 | unused u32 46 | tftp_ip u32 47 | tftp_port u32 48 | partition_index u32 49 | mbr_disk_id u32 50 | gpt_disk_uuid LimineUUID 51 | gpt_part_uuid LimineUUID 52 | part_uuid LimineUUID 53 | } 54 | 55 | // Boot info 56 | 57 | pub struct LimineBootloaderInfoResponse { 58 | pub mut: 59 | revision u64 60 | name charptr 61 | version charptr 62 | } 63 | 64 | pub struct LimineBootloaderInfoRequest { 65 | pub mut: 66 | id [4]u64 = [ 67 | u64(0xc7b1dd30df4c8b88), 68 | 0x0a82e883a194f07b, 69 | 0xf55038d8e2a1202f, 70 | 0x279426fcf5f59740, 71 | ]! 72 | revision u64 73 | response &LimineBootloaderInfoResponse 74 | } 75 | 76 | // Stack size 77 | 78 | pub struct LimineStackSizeResponse { 79 | pub mut: 80 | revision u64 81 | } 82 | 83 | pub struct LimineStackSizeRequest { 84 | pub mut: 85 | id [4]u64 = [ 86 | u64(0xc7b1dd30df4c8b88), 87 | 0x0a82e883a194f07b, 88 | 0x224ef0460a8e8926, 89 | 0xe1cb0fc25f46ea3d, 90 | ]! 91 | revision u64 92 | response &LimineStackSizeResponse 93 | stack_size u64 94 | } 95 | 96 | // HHDM 97 | 98 | pub struct LimineHHDMResponse { 99 | pub mut: 100 | revision u64 101 | offset u64 102 | } 103 | 104 | pub struct LimineHHDMRequest { 105 | pub mut: 106 | id [4]u64 = [ 107 | u64(0xc7b1dd30df4c8b88), 108 | 0x0a82e883a194f07b, 109 | 0x48dcf1cb8ad2b852, 110 | 0x63984e959a98244b, 111 | ]! 112 | revision u64 113 | response &LimineHHDMResponse 114 | } 115 | 116 | // Framebuffer 117 | 118 | pub struct LimineFramebuffer { 119 | pub mut: 120 | address voidptr 121 | width u64 122 | height u64 123 | pitch u64 124 | bpp u16 125 | memory_model u8 126 | red_mask_size u8 127 | red_mask_shift u8 128 | green_mask_size u8 129 | green_mask_shift u8 130 | blue_mask_size u8 131 | blue_mask_shift u8 132 | unused u8 133 | edid_size u64 134 | edid voidptr 135 | } 136 | 137 | pub struct LimineFramebufferResponse { 138 | pub mut: 139 | revision u64 140 | framebuffer_count u64 141 | framebuffers &&LimineFramebuffer 142 | } 143 | 144 | pub struct LimineFramebufferRequest { 145 | pub mut: 146 | id [4]u64 = [ 147 | u64(0xc7b1dd30df4c8b88), 148 | 0x0a82e883a194f07b, 149 | 0x9d5827dcd881dd75, 150 | 0xa3148604f6fab11b, 151 | ]! 152 | revision u64 153 | response &LimineFramebufferResponse 154 | } 155 | 156 | // Paging mode 157 | 158 | pub const limine_paging_mode_x86_64_4lvl = 0 159 | pub const limine_paging_mode_x86_64_5lvl = 1 160 | 161 | pub struct LiminePagingModeResponse { 162 | pub mut: 163 | revision u64 164 | mode u64 165 | } 166 | 167 | pub struct LiminePagingModeRequest { 168 | pub mut: 169 | id [4]u64 = [ 170 | u64(0xc7b1dd30df4c8b88), 171 | 0x0a82e883a194f07b, 172 | 0x95c1a0edab0944cb, 173 | 0xa4e5cb3842f7488a, 174 | ]! 175 | revision u64 176 | response &LiminePagingModeResponse 177 | mode u64 178 | max_mode u64 179 | min_mode u64 180 | } 181 | 182 | // SMP 183 | 184 | pub struct LimineSMPInfo { 185 | pub mut: 186 | processor_id u32 187 | lapic_id u32 188 | reserved u64 189 | goto_address fn (&LimineSMPInfo) = unsafe { nil } 190 | extra_argument u64 191 | } 192 | 193 | pub struct LimineSMPResponse { 194 | pub mut: 195 | revision u64 196 | flags u32 197 | bsp_lapic_id u32 198 | cpu_count u64 199 | cpus &&LimineSMPInfo 200 | } 201 | 202 | pub struct LimineSMPRequest { 203 | pub mut: 204 | id [4]u64 = [ 205 | u64(0xc7b1dd30df4c8b88), 206 | 0x0a82e883a194f07b, 207 | 0x95a67b819a1b857e, 208 | 0xa0b61b723b6a73e0, 209 | ]! 210 | revision u64 211 | response &LimineSMPResponse 212 | flags u64 213 | } 214 | 215 | // Memory map 216 | 217 | pub const limine_memmap_usable = 0 218 | pub const limine_memmap_bootloader_reclaimable = 5 219 | pub const limine_memmap_kernel_and_modules = 6 220 | 221 | pub struct LimineMemmapEntry { 222 | pub mut: 223 | base u64 224 | length u64 225 | @type u64 226 | } 227 | 228 | pub struct LimineMemmapResponse { 229 | pub mut: 230 | revision u64 231 | entry_count u64 232 | entries &&LimineMemmapEntry 233 | } 234 | 235 | pub struct LimineMemmapRequest { 236 | pub mut: 237 | id [4]u64 = [ 238 | u64(0xc7b1dd30df4c8b88), 239 | 0x0a82e883a194f07b, 240 | 0x67cf3d9d378a806f, 241 | 0xe304acdfc50c3c62, 242 | ]! 243 | revision u64 244 | response &LimineMemmapResponse 245 | } 246 | 247 | // Entry point 248 | 249 | pub struct LimineEntryPointResponse { 250 | pub mut: 251 | revision u64 252 | } 253 | 254 | pub struct LimineEntryPointRequest { 255 | pub mut: 256 | id [4]u64 = [ 257 | u64(0xc7b1dd30df4c8b88), 258 | 0x0a82e883a194f07b, 259 | 0x13d86c035a1cd3e1, 260 | 0x2b0caa89d8f3026a, 261 | ]! 262 | revision u64 263 | response &LimineEntryPointResponse 264 | entry fn () = unsafe { nil } 265 | } 266 | 267 | // Kernel file 268 | 269 | pub struct LimineKernelFileResponse { 270 | pub mut: 271 | revision u64 272 | kernel_file &LimineFile 273 | } 274 | 275 | pub struct LimineKernelFileRequest { 276 | pub mut: 277 | id [4]u64 = [ 278 | u64(0xc7b1dd30df4c8b88), 279 | 0x0a82e883a194f07b, 280 | 0xad97e90e83f1ed67, 281 | 0x31eb5d1c5ff23b69, 282 | ]! 283 | revision u64 284 | response &LimineKernelFileResponse 285 | } 286 | 287 | // Module 288 | 289 | pub struct LimineModuleResponse { 290 | pub mut: 291 | revision u64 292 | module_count u64 293 | modules &&LimineFile 294 | } 295 | 296 | pub struct LimineModuleRequest { 297 | pub mut: 298 | id [4]u64 = [ 299 | u64(0xc7b1dd30df4c8b88), 300 | 0x0a82e883a194f07b, 301 | 0x3e7e279702be32af, 302 | 0xca1c4f3bd1280cee, 303 | ]! 304 | revision u64 305 | response &LimineModuleResponse 306 | } 307 | 308 | // RSDP 309 | 310 | pub struct LimineRSDPResponse { 311 | pub mut: 312 | revision u64 313 | address voidptr 314 | } 315 | 316 | pub struct LimineRSDPRequest { 317 | pub mut: 318 | id [4]u64 = [ 319 | u64(0xc7b1dd30df4c8b88), 320 | 0x0a82e883a194f07b, 321 | 0xc5e77b6b397e7b43, 322 | 0x27637845accdcf3c, 323 | ]! 324 | revision u64 325 | response &LimineRSDPResponse 326 | } 327 | 328 | // SMBIOS 329 | 330 | pub struct LimineSMBIOSResponse { 331 | pub mut: 332 | revision u64 333 | entry_32 voidptr 334 | entry_64 voidptr 335 | } 336 | 337 | pub struct LimineSMBIOSRequest { 338 | pub mut: 339 | id [4]u64 = [ 340 | u64(0xc7b1dd30df4c8b88), 341 | 0x0a82e883a194f07b, 342 | 0x9e9046f11e095391, 343 | 0xaa4a520fefbde5ee, 344 | ]! 345 | revision u64 346 | response &LimineSMBIOSResponse 347 | } 348 | 349 | // EFI system table 350 | 351 | pub struct LimineEFISystemTableResponse { 352 | pub mut: 353 | revision u64 354 | address voidptr 355 | } 356 | 357 | pub struct LimineEFISystemTableRequest { 358 | pub mut: 359 | id [4]u64 = [ 360 | u64(0xc7b1dd30df4c8b88), 361 | 0x0a82e883a194f07b, 362 | 0x5ceba5163eaaf6d6, 363 | 0x0a6981610cf65fcc, 364 | ]! 365 | revision u64 366 | response &LimineEFISystemTableResponse 367 | } 368 | 369 | // Boot time 370 | 371 | pub struct LimineBootTimeResponse { 372 | pub mut: 373 | revision u64 374 | boot_time i64 375 | } 376 | 377 | pub struct LimineBootTimeRequest { 378 | pub mut: 379 | id [4]u64 = [ 380 | u64(0xc7b1dd30df4c8b88), 381 | 0x0a82e883a194f07b, 382 | 0x502746e184c088aa, 383 | 0xfbc5ec83e6327893, 384 | ]! 385 | revision u64 386 | response &LimineBootTimeResponse 387 | } 388 | 389 | // Kernel address 390 | 391 | pub struct LimineKernelAddressResponse { 392 | pub mut: 393 | revision u64 394 | physical_base u64 395 | virtual_base u64 396 | } 397 | 398 | pub struct LimineKernelAddressRequest { 399 | pub mut: 400 | id [4]u64 = [ 401 | u64(0xc7b1dd30df4c8b88), 402 | 0x0a82e883a194f07b, 403 | 0x71ba76863cc55f63, 404 | 0xb2644a48c516a487, 405 | ]! 406 | revision u64 407 | response &LimineKernelAddressResponse 408 | } 409 | -------------------------------------------------------------------------------- /modules/pmm/pmm.v: -------------------------------------------------------------------------------- 1 | module pmm 2 | 3 | import lib 4 | import klock 5 | import limine 6 | 7 | __global ( 8 | pmm_lock klock.Lock 9 | pmm_bitmap = unsafe { nil } 10 | pmm_avl_page_count = u64(0) 11 | pmm_last_used_index = u64(0) 12 | page_size = u64(0x1000) 13 | free_pages = u64(0) 14 | higher_half = u64(0) 15 | ) 16 | 17 | @[_linker_section: '.requests'] 18 | @[cinit] 19 | __global ( 20 | volatile hhdm_req = limine.LimineHHDMRequest{ 21 | response: unsafe { nil } 22 | } 23 | volatile memmap_req = limine.LimineMemmapRequest{ 24 | response: unsafe { nil } 25 | } 26 | ) 27 | 28 | pub fn print_free() { 29 | pmm_lock.acquire() 30 | defer { 31 | pmm_lock.release() 32 | } 33 | C.printf(c'pmm: Free pages: %llu\n', free_pages) 34 | } 35 | 36 | pub fn initialise() { 37 | if hhdm_req.response == unsafe { nil } { 38 | panic('HHDM bootloader response missing') 39 | } 40 | higher_half = hhdm_req.response.offset 41 | 42 | C.printf(c'pmm: Higher half direct map at: %p\n', higher_half) 43 | 44 | if memmap_req.response == unsafe { nil } { 45 | panic('Memory map bootloader response missing') 46 | } 47 | memmap := memmap_req.response 48 | 49 | unsafe { 50 | mut highest_address := u64(0) 51 | mut entries := memmap.entries 52 | 53 | // Calculate how big the memory map needs to be. 54 | for i := 0; i < memmap.entry_count; i++ { 55 | C.printf(c'pmm: Memory map entry %d: 0x%llx->0x%llx 0x%llx\n', i, entries[i].base, 56 | entries[i].length, entries[i].@type) 57 | 58 | if entries[i].@type != u32(limine.limine_memmap_usable) 59 | && entries[i].@type != u32(limine.limine_memmap_bootloader_reclaimable) 60 | && entries[i].@type != u32(limine.limine_memmap_kernel_and_modules) { 61 | continue 62 | } 63 | top := entries[i].base + entries[i].length 64 | if top > highest_address { 65 | highest_address = top 66 | } 67 | } 68 | 69 | // Calculate the needed size for the bitmap in bytes and align it to page size. 70 | pmm_avl_page_count = lib.div_roundup(highest_address, page_size) 71 | bitmap_size := lib.align_up(pmm_avl_page_count / 8, page_size) 72 | 73 | C.printf(c'pmm: Bitmap size: %llu\n', bitmap_size) 74 | 75 | // Find a hole for the bitmap in the memory map. 76 | for i := 0; i < memmap.entry_count; i++ { 77 | if entries[i].@type != u32(limine.limine_memmap_usable) { 78 | continue 79 | } 80 | if entries[i].length >= bitmap_size { 81 | pmm_bitmap = voidptr(entries[i].base + higher_half) 82 | 83 | // Initialise entire bitmap to 1 (non-free) 84 | C.memset(pmm_bitmap, 0xff, bitmap_size) 85 | 86 | entries[i].length -= bitmap_size 87 | entries[i].base += bitmap_size 88 | 89 | break 90 | } 91 | } 92 | 93 | // Populate free bitmap entries according to the memory map. 94 | for i := 0; i < memmap.entry_count; i++ { 95 | if entries[i].@type != u32(limine.limine_memmap_usable) { 96 | continue 97 | } 98 | 99 | for j := u64(0); j < entries[i].length; j += page_size { 100 | free_pages++ 101 | lib.bitreset(pmm_bitmap, (entries[i].base + j) / page_size) 102 | } 103 | } 104 | } 105 | print_free() 106 | 107 | // Initialise slabs 108 | slabs[0].init(8) 109 | slabs[1].init(16) 110 | slabs[2].init(32) 111 | slabs[3].init(64) 112 | slabs[4].init(128) 113 | slabs[5].init(256) 114 | slabs[6].init(512) 115 | slabs[7].init(1024) 116 | slabs[8].init(2048) 117 | } 118 | 119 | fn inner_alloc(count u64, limit u64) voidptr { 120 | mut p := 0 121 | 122 | for pmm_last_used_index < limit { 123 | if !lib.bittest(pmm_bitmap, pmm_last_used_index) { 124 | pmm_last_used_index++ 125 | p++ 126 | if u64(p) == count { 127 | page := pmm_last_used_index - count 128 | for i := page; i < pmm_last_used_index; i++ { 129 | lib.bitset(pmm_bitmap, i) 130 | } 131 | return voidptr(page * page_size) 132 | } 133 | } else { 134 | pmm_last_used_index++ 135 | p = 0 136 | } 137 | } 138 | return 0 139 | } 140 | 141 | pub fn pmm_alloc_nozero(count u64) voidptr { 142 | pmm_lock.acquire() 143 | defer { 144 | pmm_lock.release() 145 | } 146 | 147 | last := pmm_last_used_index 148 | mut ret := inner_alloc(count, pmm_avl_page_count) 149 | 150 | if ret == 0 { 151 | pmm_last_used_index = 0 152 | 153 | ret = inner_alloc(count, last) 154 | if ret == 0 { 155 | panic('Out of memory') 156 | } 157 | } 158 | 159 | free_pages -= count 160 | 161 | return ret 162 | } 163 | 164 | pub fn pmm_alloc(count u64) voidptr { 165 | ret := pmm_alloc_nozero(count) 166 | 167 | // We always zero out memory for security reasons 168 | unsafe { 169 | mut ptr := &u64(u64(ret) + higher_half) 170 | for i := u64(0); i < (count * page_size) / 8; i++ { 171 | ptr[i] = 0 172 | } 173 | } 174 | return ret 175 | } 176 | 177 | pub fn pmm_free(ptr voidptr, count u64) { 178 | pmm_lock.acquire() 179 | defer { 180 | pmm_lock.release() 181 | } 182 | unsafe { 183 | mut p := &u64(u64(ptr) + higher_half) 184 | for i := u64(0); i < (count * page_size) / 8; i++ { 185 | p[i] = 0xaaaaaaaaaaaaaaaa 186 | } 187 | } 188 | page := u64(ptr) / page_size 189 | for i := page; i < page + count; i++ { 190 | lib.bitreset(pmm_bitmap, i) 191 | } 192 | free_pages += count 193 | } 194 | 195 | pub struct Slab { 196 | mut: 197 | @lock klock.Lock 198 | first_free u64 199 | ent_size u64 200 | } 201 | 202 | struct SlabHeader { 203 | mut: 204 | slab &Slab 205 | } 206 | 207 | pub fn (mut this Slab) init(ent_size u64) { 208 | this.ent_size = ent_size 209 | this.first_free = u64(pmm_alloc_nozero(1)) 210 | this.first_free += higher_half 211 | 212 | avl_size := page_size - lib.align_up(sizeof(SlabHeader), ent_size) 213 | mut slabptr := &SlabHeader(this.first_free) 214 | unsafe { 215 | (*slabptr).slab = this 216 | } 217 | this.first_free += lib.align_up(sizeof(SlabHeader), ent_size) 218 | 219 | mut arr := &u64(this.first_free) 220 | max := avl_size / ent_size - 1 221 | fact := ent_size / 8 222 | for i := u64(0); i < max; i++ { 223 | unsafe { 224 | arr[i * fact] = u64(&arr[(i + 1) * fact]) 225 | } 226 | } 227 | 228 | unsafe { 229 | arr[max * fact] = u64(0) 230 | } 231 | } 232 | 233 | pub fn (mut this Slab) alloc() voidptr { 234 | this.@lock.acquire() 235 | defer { 236 | this.@lock.release() 237 | } 238 | 239 | if this.first_free == 0 { 240 | this.init(this.ent_size) 241 | } 242 | 243 | mut old_free := &u64(this.first_free) 244 | this.first_free = *old_free 245 | 246 | unsafe { C.memset(voidptr(old_free), 0, this.ent_size) } 247 | 248 | return voidptr(old_free) 249 | } 250 | 251 | pub fn (mut this Slab) sfree(ptr voidptr) { 252 | this.@lock.acquire() 253 | defer { 254 | this.@lock.release() 255 | } 256 | 257 | if ptr == unsafe { nil } { 258 | return 259 | } 260 | 261 | unsafe { C.memset(ptr, 0xaa, this.ent_size) } 262 | 263 | mut new_head := &u64(ptr) 264 | unsafe { 265 | *new_head = this.first_free 266 | } 267 | this.first_free = u64(new_head) 268 | } 269 | 270 | __global ( 271 | slabs [9]Slab 272 | ) 273 | 274 | struct MallocMetadata { 275 | mut: 276 | pages u64 277 | size u64 278 | } 279 | 280 | pub fn free(ptr voidptr) { 281 | if ptr == unsafe { nil } { 282 | return 283 | } 284 | 285 | if u64(ptr) & u64(0xfff) == 0 { 286 | big_free(ptr) 287 | return 288 | } 289 | 290 | mut slab_hdr := &SlabHeader(u64(ptr) & ~u64(0xfff)) 291 | 292 | slab_hdr.slab.sfree(ptr) 293 | } 294 | 295 | fn big_free(ptr voidptr) { 296 | metadata := &MallocMetadata(u64(ptr) - page_size) 297 | 298 | pmm_free(voidptr(u64(metadata) - higher_half), metadata.pages + 1) 299 | } 300 | 301 | fn slab_for(size u64) ?&Slab { 302 | for mut s in slabs { 303 | if s.ent_size >= size { 304 | return unsafe { s } 305 | } 306 | } 307 | 308 | return none 309 | } 310 | 311 | pub fn malloc(size u64) voidptr { 312 | mut slab := slab_for(size) or { return big_alloc(size) } 313 | 314 | return slab.alloc() 315 | } 316 | 317 | fn big_alloc(size u64) voidptr { 318 | page_count := lib.div_roundup(size, page_size) 319 | 320 | ptr := pmm_alloc(page_count + 1) 321 | 322 | if ptr == 0 { 323 | return 0 324 | } 325 | 326 | mut metadata := &MallocMetadata(u64(ptr) + higher_half) 327 | 328 | metadata.pages = page_count 329 | metadata.size = size 330 | 331 | return voidptr(u64(ptr) + higher_half + page_size) 332 | } 333 | 334 | pub fn realloc(ptr voidptr, new_size u64) voidptr { 335 | if ptr == 0 { 336 | return malloc(new_size) 337 | } 338 | 339 | if u64(ptr) & u64(0xfff) == 0 { 340 | return big_realloc(ptr, new_size) 341 | } 342 | 343 | slab_hdr := &SlabHeader(u64(ptr) & ~u64(0xfff)) 344 | mut slab := slab_hdr.slab 345 | 346 | if new_size > slab.ent_size { 347 | mut new_ptr := malloc(new_size) 348 | unsafe { C.memcpy(new_ptr, ptr, slab.ent_size) } 349 | slab.sfree(ptr) 350 | return new_ptr 351 | } 352 | 353 | return ptr 354 | } 355 | 356 | fn big_realloc(ptr voidptr, new_size u64) voidptr { 357 | mut metadata := &MallocMetadata(u64(ptr) - page_size) 358 | 359 | if lib.div_roundup(metadata.size, page_size) == lib.div_roundup(new_size, page_size) { 360 | metadata.size = new_size 361 | return ptr 362 | } 363 | 364 | new_ptr := unsafe { C.malloc(new_size) } 365 | if new_ptr == 0 { 366 | return 0 367 | } 368 | 369 | if metadata.size > new_size { 370 | unsafe { C.memcpy(new_ptr, ptr, new_size) } 371 | } else { 372 | unsafe { C.memcpy(new_ptr, ptr, metadata.size) } 373 | } 374 | 375 | C.free(ptr) 376 | 377 | return new_ptr 378 | } 379 | 380 | pub fn calloc(a u64, b u64) voidptr { 381 | return unsafe { C.malloc(a * b) } 382 | } 383 | -------------------------------------------------------------------------------- /modules/term/term.v: -------------------------------------------------------------------------------- 1 | module term 2 | 3 | import klock 4 | import framebuffer 5 | import flanterm as _ 6 | 7 | __global ( 8 | flanterm_ctx voidptr 9 | term_write_lock klock.Lock 10 | ) 11 | 12 | pub fn initialise() { 13 | flanterm_ctx = unsafe { 14 | C.flanterm_fb_init(voidptr(malloc), voidptr(free), framebuffer_tag.address, 15 | framebuffer_width, framebuffer_height, framebuffer_tag.pitch, framebuffer_tag.red_mask_size, 16 | framebuffer_tag.red_mask_shift, framebuffer_tag.green_mask_size, framebuffer_tag.green_mask_shift, 17 | framebuffer_tag.blue_mask_size, framebuffer_tag.blue_mask_shift, nil, nil, 18 | nil, nil, nil, nil, nil, nil, 0, 0, 1, 0, 0, 0) 19 | } 20 | } 21 | 22 | pub fn write(s voidptr, len u64) { 23 | term_write_lock.acquire() 24 | C.flanterm_write(flanterm_ctx, s, len) 25 | term_write_lock.release() 26 | } 27 | -------------------------------------------------------------------------------- /v.mod: -------------------------------------------------------------------------------- 1 | Module { 2 | name: 'V-Unikernel' 3 | description: 'A V Unikernel' 4 | version: '0.0.0' 5 | license: 'GPL-2.0' 6 | dependencies: [] 7 | } 8 | --------------------------------------------------------------------------------