Compare commits

..

718 Commits

Author SHA1 Message Date
e780eaa45f meminfo: Print allocated physical memory percentage 2023-10-03 10:39:27 +03:00
44cb0af64f Shell: source $HOME/.shellrc if found on Shell startup 2023-10-03 10:39:27 +03:00
bb0989fdef Shell: Implement sourcing scripts 2023-10-03 10:24:10 +03:00
Bananymous
f0b6844feb meminfo: Add process command line to the output 2023-09-30 23:17:31 +03:00
Bananymous
b712c70c75 Kernel: Expose command line and environment to /proc 2023-09-30 23:01:33 +03:00
Bananymous
797ca65c66 Kernel: Add physical memory info to /proc/{pid}/meminfo 2023-09-30 22:11:45 +03:00
Bananymous
762b7a4276 Userspace: Add meminfo command that parses /proc/{pid}/meminfo 2023-09-30 21:20:53 +03:00
Bananymous
a511441f7e Kernel: /proc/{pid}/meminfo now reports per process memory usage 2023-09-30 21:20:18 +03:00
Bananymous
cd61d710df Kernel: Add procfs that contains only pids 2023-09-30 21:19:36 +03:00
Bananymous
f88ad7efcd Kernel: All process' memory areas can report their virtual mem usage 2023-09-30 21:15:46 +03:00
Bananymous
38320018dc LibC: Implement stpcpy since gcc seems to need it
gcc seems to optimize some calls to strcpy to stpcpy
2023-09-30 20:58:19 +03:00
Bananymous
d883d212b1 Kernel/LibC: dirent now contains file type 2023-09-30 20:46:57 +03:00
Bananymous
dedb2a2399 Kernel: RamInode verifies that you have not specified mode type
This is kinda weird behaviour, but it ensures the user cannot
create e.g. CharacterDevice with mode having IFLNK.

The Inode overrider is the only one setting the mode.
2023-09-30 20:46:57 +03:00
Bananymous
8604c55de8 Kernel: Add API for RamDirectoryInodes to delete containing inodes 2023-09-30 19:22:30 +03:00
Bananymous
e949e8550c Kernel: Rework RamInode API
RamInode is now a general RamInode with no data. RamFileInode is now
a inode for regular files. This is much cleaner and more intuitive
since there is no reason for most non-regular inodes to hold data
Vector.
2023-09-30 19:22:30 +03:00
Bananymous
eb5c6cf736 BAN: Remove endianness functions from Math
There is now a Endianness.h for these. The functions were super slow.
2023-09-29 19:38:07 +03:00
Bananymous
94ce2c97be Shell: Quick fix to not freeze for multiple seconds
When sync is writing to disk, it reserves whole disk to itself.
This commit makes Shell to read username only once from getpwuid().
We used to get username every time prompt was printed.
2023-09-29 19:20:48 +03:00
Bananymous
3f164c6b82 Userspace: Implement basic test for MAP_SHARED 2023-09-29 18:59:37 +03:00
Bananymous
f953f3d3ff Kernel: Implement MAP_SHARED for regular files
Every inode holds a weak pointer to shared file data. This contains
physical addresses of pages for inode file data. Physical addresses
are allocated and read on demand.

When last shared mapping is unmapped. The inodes shared data is freed
and written to the inode.
2023-09-29 18:59:20 +03:00
Bananymous
9fc75fe445 Kernel: Don't write to stat_loc on SYS_WAIT if it is null 2023-09-29 18:31:44 +03:00
Bananymous
7a5bb6a56b Userspace: Implement cat-mmap
This behaves exactly as cat, but uses mmap to read the file.
2023-09-29 17:24:55 +03:00
Bananymous
d54c6b7f6b LibC: Fix mmap()
mmap() did not pass fildes to the syscall structure.
2023-09-29 17:24:21 +03:00
Bananymous
db5d6a7f80 Kernel: Implement MAP_PRIVATE file mappings
mmap() now supports mapping files with MAP_PRIVATE.
2023-09-29 17:23:42 +03:00
Bananymous
4a92f44cf6 Kernel: Implement new abstract MemoryRegion
MemoryBackedRegion now inherits from this and is used for private
anonymous mappigs. This will make shared mappings and file backed
mappings much easier to implement.
2023-09-29 16:18:23 +03:00
Bananymous
376b9f7272 LibC: mmap returns MAP_FAILED instead of NULL 2023-09-29 10:38:08 +03:00
Bananymous
7e9e4c47ae LibELF: Optimize LoadableELF::clone() memory usage
We only clone mapped pages that have been marked as writeble.
Read/execute only pages will be exactly as in the file itself and
can be demand paged also :D
2023-09-29 02:05:12 +03:00
Bananymous
603fc200e6 Kernel: Add some sanity assertions/functions 2023-09-29 02:03:19 +03:00
Bananymous
c11e84b248 Kernel: Use the new on demand ELF structure
All executable files are now read from disk and paged on demand.
This was a big rewrite of the old ELF library but in the end
everything seems much cleaner, since all the old functionality was
not actually needed for execution.

I have to do some measurements, but I feel like memory usage dropped
quite a bit after this change.
2023-09-29 02:00:10 +03:00
Bananymous
be13120554 LibELF: Implement new ELF structure
This structure is used for demand pagable execution. It handles all
memory allocation and file reading.
2023-09-29 01:58:03 +03:00
Bananymous
9943edad5a LibELF: Add types for native executable 2023-09-29 01:56:57 +03:00
Bananymous
f4049be975 Kernel: Fix off by one error when calculating pages in range 2023-09-29 01:56:15 +03:00
Bananymous
6cf7e01fe9 Kernel: Don't map interrupt stack as userspace accessable 2023-09-28 21:58:24 +03:00
Bananymous
b51d2f5295 Kernel: mmap regions are now demand paged
mmap will not actually take any memory unless you use the given
memory.
2023-09-28 21:07:14 +03:00
Bananymous
49d941ad65 LibC: Fix a bug in malloc
You could not allocate with size equal to one of the pool sizes.
2023-09-28 21:05:27 +03:00
Bananymous
a66c3bdae5 Kernel: Remove duplicate code in VirtualRange::create_to_vaddr_range 2023-09-28 13:59:49 +03:00
Bananymous
547eabb403 Kernel: Reboot will now always succeed
If acpi reset fails, we forcefully trigger a triple fault to restart
the system.
2023-09-28 13:53:03 +03:00
Bananymous
79851394b3 Kernel/LibC/Userspace: Add SYS_POWEROFF + cli tool
You can now shutdown/reboot banan-os with the poweroff cli tool.

Reboot doesn't seem to work on qemu.
2023-09-28 12:36:47 +03:00
Bananymous
fcdc922343 Kernel: Enter ACPI mode with lai 2023-09-28 12:30:27 +03:00
Bananymous
0b11d76576 LibC: Add errno for unknown error 2023-09-28 12:06:17 +03:00
Bananymous
f7097398ca Kernel: Make tty overload correct has_data() function
This allows snake game to work again :)
2023-09-28 11:54:12 +03:00
Bananymous
85b1252b9e Userspace: Use printf length modifiers when printing 2023-09-28 11:49:31 +03:00
Bananymous
1cd12b5f16 LibC: Implement length modifiers to printf 2023-09-28 11:42:57 +03:00
Bananymous
c84b66d078 Shell: String leading and trailing whitespace from commands
This fixes a bug of inserting empty argument if command had trailing
whitespace
2023-09-28 10:28:49 +03:00
Bananymous
27adb9486b BAN: Update Endiannes API
Add functions to swap endiannes or convert host to big/little endian

This code should be very compiler friendly and should be optimized to
single bswap instruction on x86.
2023-09-28 01:22:14 +03:00
Bananymous
8d5369fafe Kernel: Add some functionality to disable TTY input/output
Userspace programs can call tty_ctrl() to disable/enable tty from
handling input and displaying output.

This API is probably going to change in the future to ioctl calls
but I'm not sure how ioctl is used and what functionality should it
have. I decided to create whole new function and syscall for now.

Next I will expose framebuffer in /dev/fb0 and then I can start work
on graphical environment! :D
2023-09-27 15:44:05 +03:00
Bananymous
feafc57b63 Kernel: Disable DiskCache sync messages 2023-09-27 14:12:21 +03:00
Bananymous
f9b347f9d9 BuildSystem: Rework calling qemu
I change always manually the serial/graphical. When running cmake
you can define variable QEMU_ACCEL that will be used as accelerator.

Also ninja has the following targets for running qemu
  1. qemu:            Run graphical qemu environment
  2. qemu-nographic:  Run qemu without graphical screen. You should
                      select 'serial only' from grub menu.
  3. qemu-debug:      Run qemu without accelerator and interrupt
                      debugger.
2023-09-27 13:49:01 +03:00
Bananymous
6e1825d6b4 Kernel: Add missing TRY() to font loading 2023-09-27 00:49:53 +03:00
Bananymous
ec2baeb276 Sync: Add some argument parsing to sync(1)
You can specify --block to make the program wait until sync is
complete.
2023-09-27 00:37:23 +03:00
Bananymous
6cb8bda6e1 LibC: add syncsync() to unistd.h
This is my own WELL NAMED (:D) function that takes a paramemeter
to make the sync operation synchronous.
2023-09-27 00:35:36 +03:00
Bananymous
05e57801e7 Kernel: SYS_SYNC now schedules sync to happen soon
You can pass non-zero argument to the syscall to block until the
sync has finished.
2023-09-27 00:34:00 +03:00
Bananymous
b924c85669 Kernel: DiskCache now requires sync to be called from kernel thread
This disables the scenario where user interrupts sync operation
possibly leaving the syncing in invalid state.
2023-09-27 00:32:13 +03:00
Bananymous
09c1aa44d8 Kernel: Allow creationg of empty processes and manual registration
You can now create kernel processes without any threads, add the
needed threads and only then register the process and its threads
to the scheduler.
2023-09-27 00:29:45 +03:00
Bananymous
1d470fb5ba Kernel: All syscalls now validate users pointers
We now validate pointers passed by the user, to forbid arbitary
memory read/write. Now the user is only allowed to pass in pointers
in their own mapped memory space (or null).
2023-09-25 22:07:12 +03:00
Bananymous
b4e4f7a6cc Kernel: Print more detailed output on ISR 2023-09-25 20:33:07 +03:00
Bananymous
55d30a7cc3 Kernel: Ext2 inodes are now stored in cache
This allows faster inode access and ensures working inodes when
opened in multiple places.
2023-09-25 20:31:40 +03:00
Bananymous
b62186441b BAN: Implement basic WeakPtr
This can be constructed from classes that inherit from Weakable
2023-09-25 19:43:10 +03:00
Bananymous
8caba1e774 Kernel: Ext2 filesystem now holds 10 preallocated block buffers
Inodes can query blocks from this buffer. This allows allocation of
blocks to not fail during normal operations. Also less stress on
kmalloc.
2023-09-25 14:32:25 +03:00
Bananymous
7bdb428938 Kernel: Fix ext2 block allocation
Redo ext2 block allocation. This is now much "cleaner" although
I'm not too fond of the macros.
2023-09-25 13:17:44 +03:00
Bananymous
3ea707c0e7 BuildSystem: Optimize image creation
We now use truncate to create disk image, since it doesn't require
writing zeroes to full disk. I also removed creation of third
partition as this was not currently used.
2023-09-25 13:15:55 +03:00
Bananymous
18d582c6ce Kernel: Hacky kmalloc quick fix
Remove GeneralAllocator from kmalloc as it is not CriticalScope safe.
This requires increasing kmalloc memory.
2023-09-25 13:13:57 +03:00
Bananymous
8b2bb95b81 Kernel: VirtualRange doesn't store physical addresses of pages
This was unnecessarry allocation, since the page table allready
contains virtual address -> physical address mappings.
2023-09-24 01:29:34 +03:00
Bananymous
2ef496a24a Kernel: all mapped ranges are now stored in one container
We just now have a flag if a mapping is unmappable
2023-09-23 23:45:26 +03:00
Bananymous
c0a89e8951 Kernel: Fully remove sys_alloc and sys_free
I could delete the whole FixedWidthAllocator as it was now obsolete.
GeneralAllocator is still used by kmalloc. Kmalloc cannot actually
use it since, GeneralAllocator depends on SpinLock and kmalloc runs
without interrupts.
2023-09-23 03:53:30 +03:00
Bananymous
fc953df281 Kernel/LibC: remove PATH resoltion from kernel
I have no idea why I had made PATH environment variable parsing
to be part of the kernel. Now the shell does the parsing and
environment syscall is no longer needed.
2023-09-23 03:08:14 +03:00
Bananymous
fe2dca16f0 Kernel/LibC: add flag to enable/disable sse support
SSE support is very experimental and causes GP. I decided to make
SSE not default until I get to fixing it :)
2023-09-23 02:28:25 +03:00
Bananymous
f662aa6da2 Kernel/LibC: userspace malloc now uses mmap to get memory
We could remove syscalls to allocate more memory. This was not
something the kernel should have done.
2023-09-23 02:26:23 +03:00
Bananymous
fee3677fb9 Kernel/LibC: add mmap for private anonymous mappings
This will be used by the userspace to get more memory. Currently
kernel handles all allocations, which is not preferable.
2023-09-22 23:01:14 +03:00
Bananymous
4818c6e3dd BuildSystem: Add cmake target for debugging qemu 2023-09-22 17:20:35 +03:00
Bananymous
971eb737c1 BAN: Fix LinkedList::pop_back() 2023-09-22 17:20:35 +03:00
Bananymous
9a3286ad57 Kernel: Add constexpr conditional debug prints 2023-09-22 17:20:35 +03:00
Bananymous
c9e09b840e Kernel: Add LAI as a dependency
I did not feel like implementing AML interpreter now, and wanted
everything AML has to offer. I will be writing my own AML interperter
at some point.
2023-09-22 17:20:35 +03:00
Bananymous
8136248a67 Kernel: Fix timer includes 2023-09-22 17:20:35 +03:00
Bananymous
0d67e46041 Kernel: Add config read/write api to PCI 2023-09-22 17:20:35 +03:00
Bananymous
bc1087f5a7 Kernel: Add pointer validation API to page table 2023-09-22 17:20:35 +03:00
Bananymous
3a9c6fc51a General: remove linecount.sh 2023-09-22 17:20:35 +03:00
Bananymous
7774f56ab6 Kernel: PCI devices can now create region for BAR
This creates either MEM or IO region for read/write access to PCI
device.
2023-09-22 17:20:35 +03:00
Bananymous
14a608effd 1000th COMMIT: Kernel: Add basic E1000 driver
This driver is only capable to read mac address and enable and read
link status
2023-09-22 17:20:28 +03:00
Bananymous
5fae3cec2a Kernel: Implement SYS_SYNC and add sync executable to userspace
You can (and have to) manually sync disk after writes to it.
2023-09-11 01:26:27 +03:00
Bananymous
b0c22b61ec Kernel: Writes to disk are not synchronous anymore
Implement "proper" DiskCache syncing
2023-09-11 01:25:16 +03:00
Bananymous
82b049204d Kernel: Print stack trace on isr 2023-09-11 01:20:55 +03:00
Bananymous
aa59142bfa Kernel: Fix ext2 file write 2023-09-11 01:20:39 +03:00
Bananymous
c55bb77ff5 BuildSystem: remove install-usb.sh since it is not used
This was dangerous script since it rewrote /dev/sda
2023-09-10 01:20:44 +03:00
Bananymous
9b4e2e1e21 Kernel: Add basic dd command
This only supports if, of, bs, count and status=progress
2023-09-10 01:19:47 +03:00
Bananymous
202c38a65a Kernel: Improve open() POSIX compatability
Also rename Process::sys_creat -> create_file since it is not
actually a syscall and only used by open as a healper.
2023-09-10 00:31:42 +03:00
Bananymous
720bc418a6 All: Clear lines with only whitspace in them 2023-09-10 00:31:42 +03:00
Bananymous
d77f455065 Kernel: Add basic ZeroDevice to /dev/zero 2023-09-10 00:31:42 +03:00
Bananymous
7e08f0fb66 Kernel: Start work on making inodes more thread safe
All inode operations are now locked and thread blocked
2023-09-10 00:31:42 +03:00
Bananymous
9e4a87021c Kernel: Fix directory permissions
We did not care about X bit in directories and instead used only the
R bit for search/read.
2023-09-08 11:46:53 +03:00
Bananymous
5887f6bcaa Kernel: Ext2 directories can allocate new blocks if needed 2023-09-08 10:29:26 +03:00
Bananymous
5d67559e33 Kernel: Fix bug in disk writing
I have used two weeks in locating a bug in my ext2 implementation
while the bug was actually in disk write. If you called write_sectors
on disk it would write the first sector_size bytes repeatedly to all
asked sectors and this corrupted the disk...
2023-09-08 02:43:08 +03:00
Bananymous
e631eb7a7a Kernel: Fix possible out-of-bounds disk access in ext2 2023-09-08 02:42:53 +03:00
Bananymous
64ff4c232a BuildSystem: Cleanup disk image creation 2023-09-08 02:41:39 +03:00
Bananymous
0ec4f970f7 LibC: fix fread implementation
fread() should read until either size*nitems bytes are read or eof
is reached.
2023-09-07 16:00:47 +03:00
Bananymous
afe95be42f Shell: Clean exit on ^D and use getchar()
Use getchar() instead of fread(&ch, 1, sizeof(char), stdin). This
is much cleaner.
2023-09-07 15:51:05 +03:00
Bananymous
14ac1c9904 Init: use read instead of fread()
This allows actually correct behaviour. My fread implementation is
flawed. It should not return on '\n'
2023-09-07 15:47:59 +03:00
Bananymous
7c11ea3694 Kernel: Cleanup TTY::read() 2023-09-07 15:27:21 +03:00
Bananymous
c1fd341698 Kernel: TTY now reads input byte by byte
This allows correct behaviour for character streams / keyboard
handling. Serial input can now send working ^C :D
2023-09-07 15:06:27 +03:00
Bananymous
0deab1be51 Kernel: Remove bitmap debug printing from ext2 inode allocation 2023-09-06 01:25:09 +03:00
Bananymous
5a623adaa6 Kernel: Correct inode links count 2023-09-05 14:46:56 +03:00
Bananymous
4363118d9d Snake render grid lines without extra space after last '#' 2023-09-05 14:35:53 +03:00
Bananymous
d613da4b6c BuildSystem: Don't set permissions on image creation
This is already handled by *new* tar ball sysroot
2023-09-05 14:35:53 +03:00
Bananymous
f46f5b2050 Snake: Reorder apple generation/snake body update
If new apple was generated to heads previous position after update,
it would not render.
2023-09-05 14:30:30 +03:00
Bananymous
d9c4114b5f Base: Add grub entries for serial-only boot
We can now fully boot with serial input and output :)
Qemu can be run with -nographic flag
2023-09-05 01:07:52 +03:00
Bananymous
ddd36af0f1 Kernel: Add /dev/tty symlink that targets the current tty 2023-09-05 01:07:52 +03:00
Bananymous
35fd30ee29 Kernel: Implement basic RamSymlinkInode 2023-09-05 01:07:52 +03:00
Bananymous
4a0652684c Kernel: You can now read serial output from the /dev/ttyS* 2023-09-05 01:07:52 +03:00
Bananymous
33c81f00b7 Kernel: Receive interrupts for COM1 and COM2 input
The input has to still be attached to terminal
2023-09-04 22:07:40 +03:00
Bananymous
55714b90cd Kernel: Rework whole Terminal structure
Serial monitors can now be used as a output. This requires editing
init code for the stdio opening. Serial input is not supported, so qemu
still needs graphical window for ps/2 keyboard.
2023-09-04 19:34:18 +03:00
Bananymous
9b47603a1d Kernel: Make serial devices to their own class 2023-09-04 14:57:05 +03:00
Bananymous
2e2a913412 Userspace: Implement basic snake game :) 2023-09-04 14:30:45 +03:00
Bananymous
42b85dc33b Kernel: Add ANSI support for hiding/showing cursor to TTY 2023-09-04 14:30:09 +03:00
Bananymous
a15ffcb071 LibC: add time() implementation 2023-09-04 13:52:58 +03:00
Bananymous
692b77fb8e LibC: Add rand() and srand() implementation
This code is from the wikipedia page for Permuted congruential generator
2023-09-04 13:52:13 +03:00
Bananymous
044378cfa3 Kernel: Add basic nanosleep, only millisecond percision 2023-09-04 12:59:50 +03:00
Bananymous
f1d4d5f995 Kernel: Add basic support for O_NONBLOCK (only for tty) 2023-09-04 12:57:52 +03:00
Bananymous
19d0fb6fcd Kernel: add basic fcntl() with couple of commands and no validation 2023-09-04 12:57:09 +03:00
Bananymous
7933265095 Kernel: Split ext2 implementation to multiple files 2023-09-01 15:10:23 +03:00
Bananymous
d810644018 Kernel: Start work on writable ext2 filesystem 2023-08-31 23:40:31 +03:00
Bananymous
a7bfc1c2ec Kernel: Add missing ififo() to Inode::Mode 2023-08-31 21:38:31 +03:00
Bananymous
104b2740bc BuildSystem: Add cmake target to validate main partition 2023-08-31 21:37:30 +03:00
Bananymous
65501837b7 Kernel: Stack trace dumping validates pointers before using them 2023-08-31 21:36:23 +03:00
Bananymous
3ed0a54847 Update README.md 2023-08-31 17:54:12 +03:00
Bananymous
cbd2519b9a Kernel: better handle kernel errors 2023-08-29 00:13:21 +03:00
Bananymous
e8a73f9696 BuildSystem: use -a with rsync
This allows cmake to not rebuild whole project every time
2023-08-28 11:38:17 +03:00
Bananymous
1a0d114861 LibC: Don't undef needed values in inttypes.h 2023-08-24 15:48:14 +03:00
Bananymous
5c3497681e BuildSystem: Add USES_TERMINAL when preparing sysroot
You were not able to enter sudo password on sysroot unpacking,
which caused ninja to hang
2023-08-23 20:48:40 +03:00
Bananymous
b05cf9ef09 BuildSystem: Base sysroot is now distributed as a tar ball
This allows file and directory permissions work as intended.

cmake is now filled with 'sudo' but with sudo timeout this should be
fine.
2023-08-23 10:38:21 +03:00
Bananymous
a74eb357a1 Shell: hostname is not parsed from /etc/hostname 2023-08-23 10:38:21 +03:00
Bananymous
8eb71084f0 Shell: use process groups more properly 2023-08-22 14:54:50 +03:00
Bananymous
ef1077fd7b Kernel: kill() with negative pid actually matches pgid 2023-08-22 14:54:15 +03:00
Bananymous
f1ba5c7e0f Kernel: Process keeps track if forked process has called exec*() 2023-08-22 14:53:46 +03:00
Bananymous
97ea4986af Kernel/LibC: implement proper getpgid and setpgid 2023-08-22 14:53:12 +03:00
Bananymous
25c021c15b Kernel: Add function to enumerate processes in session 2023-08-22 14:52:28 +03:00
Bananymous
2bf12a52d1 BAN: increase function size to 5 * sizeof(void*) 2023-08-22 14:52:03 +03:00
Bananymous
6ada36d3cb Shell/init: We now use pgrp instead of pid and init open terminal 2023-08-22 11:37:04 +03:00
Bananymous
42b90ae76c Kernel/LibC: Add {get,set}pgrp() 2023-08-22 11:36:33 +03:00
Bananymous
ccc61cb10c Kernel: Barebones implementation sessions and process groups 2023-08-22 11:35:40 +03:00
Bananymous
be5b81740e BAN: Add enum class Iteration for for_each loops 2023-08-22 11:30:53 +03:00
Bananymous
e7458ca10a BAN: Function call is now const 2023-08-22 11:30:40 +03:00
Bananymous
b10168eb1c LibC: make printf buffer only 128 bytes
Only reason for it to be big is if you want super large zero padding
for your number. This will eventually be handled without the buffer.
2023-08-18 16:49:27 +03:00
Bananymous
30463c9082 Id: Print username corresponding to uid/euid
Also print euid and egid if they differ from uid or gid.
2023-08-18 15:36:51 +03:00
Bananymous
dc7391dc89 BuildSystem: only apply stack usage warning to libc 2023-08-17 20:49:52 +03:00
Bananymous
eb98d70a0b Shell: command execution and parsing support piped commands
There is still problems with "blocking" builtin commands (time),
return value, ...
2023-08-17 12:05:38 +03:00
Bananymous
dcd8374b89 LibC: add fileno() and fdopen()
fdopen() doesn't currently care about mode and will have same mode
as the underlying file descriptor.
2023-08-17 12:03:59 +03:00
Bananymous
8e4216215e Kernel/LibC: add dup() syscall and function 2023-08-17 12:03:29 +03:00
Bananymous
5bd7099b96 Shell: add basic printf test 2023-08-16 10:49:34 +03:00
Bananymous
9a63d3b2da LibC: printf handles nan and inf 2023-08-16 10:49:34 +03:00
Bananymous
bf02330db9 LibC: math.h defines is*() macros and uses builtins for values 2023-08-16 10:41:55 +03:00
Bananymous
5806a6484f cat: use 1025 buffer. this allows reads to be 1024 byte
reading from disk currently returns ENOTSUP if buffer size is not
multiple of sector size.
2023-08-16 09:33:14 +03:00
Bananymous
0fa5401800 Shell: $? returns last return value 2023-08-15 09:17:46 +03:00
Bananymous
b30f4cbfb5 Userspace: Add basic whoami command 2023-08-15 09:03:51 +03:00
Bananymous
ba37183c9c Shell: '\u' in PS1 is replaced with username corresponding to euid 2023-08-14 14:55:23 +03:00
Bananymous
2f298a1979 Shell: ^A moves cursor to the beginning of line 2023-08-14 12:26:22 +03:00
Bananymous
8c282a5d83 Kernel: O_SEARCH doesn't require any permissions
Except read permissions for all directories in the path

This allows us to stat e.g. device files for which we don't have
the permissions.
2023-08-11 12:26:07 +03:00
Bananymous
d34c0a5abe LibC: cleanup fstatat 2023-08-11 12:25:15 +03:00
Bananymous
8f3348cf2b Kernel: open() now validates file access mode 2023-08-11 11:53:38 +03:00
Bananymous
38c0bc7bae Kernel: Remove unused syscall 2023-08-11 11:43:48 +03:00
Bananymous
313b00b11f Userspace: implement basic stat
I tried to format the output pretty much how linux does, except for
uid and git -> name resolution which is not implemented
2023-08-11 10:30:50 +03:00
Bananymous
165a379c73 LibC: Fix S_IF* macros and add macros for access/type masks 2023-08-11 10:30:50 +03:00
Bananymous
a7f37236bf Kernel: Zero initialize threads sse_storage 2023-08-11 00:26:43 +03:00
Bananymous
51532336b0 Kernel: Move structures in boot.S to .data section 2023-08-11 00:26:12 +03:00
Bananymous
03d4b47f63 Kernel: HPET is now used in legacy mode when PIC is forced 2023-08-10 22:01:30 +03:00
Bananymous
8b57edde6b Kernel: Fix slave PIC
we dont mask interrupt 2 in PIC since it corresponds to the
slave PIC. Also cleanup PIC code :)
2023-08-10 21:52:31 +03:00
Bananymous
778778fede Kernel: HPET doesn't use the legacy mapping anymore 2023-08-10 21:08:32 +03:00
Bananymous
f7449c4ab9 Kernel: APIC now uses MMIO namespace functions for mmio 2023-08-10 21:07:23 +03:00
Bananymous
fd2bcc9156 Kernel: IDT now sends EOI for every interrupt
EOI is sent before calling the interrupt handler. This should be fine
since all interrupts are processed with interrupts disabled
2023-08-09 09:57:02 +03:00
Bananymous
a5b1555725 Kernel: Validate HPET tick period 2023-08-09 09:50:38 +03:00
Bananymous
e74fdbc55b Kernel: Remove get_unix_timestamp from SystemTimer
Kernel now uses the percise real_time() and time_since_boot()
2023-08-09 08:57:50 +03:00
Bananymous
008c777a9f Kernel: Add PageTable::Flags::CacheDisable
Also fix multiple places where we were using uint8_t as page table
flags instead of PageTable::flags_t which we promoted to uint16_t
while back.
2023-08-06 23:59:30 +03:00
Bananymous
d8a9d4a24e Kerne: SystemTimer can now output the current real time 2023-08-04 16:06:47 +03:00
Bananymous
bc0e1fa898 Kernel: Rename TimerHandler to SystemTimer
I changed SystemTimer to only handle the "best" supported timer
it can initialize.
2023-08-04 16:06:47 +03:00
Bananymous
17f1737c9a Kernel: Add function to retrieve boot time as timespec 2023-08-04 16:06:47 +03:00
Bananymous
868444f043 Kernel: F11 now prints current time to debug output
This allows better timer percision and system freeze checks
2023-08-04 15:22:51 +03:00
Bananymous
fdae253695 Kernel: Add basic HPET support to replace PIT if exists
This works same way as the PIT implementation; calls Scheduler every
milli second.
2023-08-04 15:22:51 +03:00
Bananymous
d4adcff958 Kernel: Move sleep() implementation to TimerHandler 2023-08-04 15:15:00 +03:00
Bananymous
2c59c9a3cc Kernel: Add more structures to ACPI 2023-08-04 15:13:47 +03:00
Bananymous
3a59a64355 Kernel: Implement basic MMIO functions
This is equivalent to IO.h except for memory mapped io instead of
IO ports
2023-08-04 15:12:29 +03:00
Bananymous
9363c1cdaf Kernel: Move RTC to Timer directory
PIT ms counter seems to be off by multiple seconds/minute. I will
be probably changing to HPET for system time
2023-08-04 11:12:16 +03:00
Bananymous
198e6d7cf6 Kernel: Start work on abstracting Timers 2023-08-04 10:29:42 +03:00
Bananymous
07ee898f4f Kernel: Remove unnecessary timer check from Scheduler 2023-08-04 10:29:42 +03:00
Bananymous
6feb8a99d2 BAN: UniqPtr can now be constructed from other convertible UniqPtr 2023-08-04 10:29:42 +03:00
Bananymous
e57c1fc9fc Kernel: Threads are deleted sooner and cleaner
We now delete threads when
    1. it is marked as Terminated and is the current thread
    2. it tries to start execution in Terminated state

This allows us to never have thread executing in Terminated state
2023-08-04 10:29:42 +03:00
Bananymous
a11b5ae41f Kernel: align userspace stacks
I was getting #GP on sse instructions
2023-08-03 18:09:48 +03:00
Bananymous
c67a7cec5b LibC: fix typo 2023-08-02 22:10:12 +03:00
Bananymous
91f04ce250 Kernel: Change PageTable API
Getting free pages not reserves them, so you don't have to hold
the page table lock :)
2023-08-02 22:09:14 +03:00
Bananymous
926df2b276 Kernel: PageTable now supports reserved pages
You can now map pages as reserved, so that they will not be given
from get_free_page() or get_free_contiguous_pages().
2023-08-01 16:35:54 +03:00
Bananymous
9fe878bbec Shell: Print if the process exited because of a signal 2023-08-01 14:24:36 +03:00
Bananymous
217dbca7b7 Kernel: Cleanup returns from any kind on interrupts 2023-08-01 14:23:50 +03:00
Bananymous
13852e865c LibC: sys/wait.h now has proper exit status macros
We use the same format as basically every implementation
2023-08-01 10:32:04 +03:00
Bananymous
679d47131d BuildSystem: Edit build flags regarding sse and warnings 2023-07-31 22:31:17 +03:00
Bananymous
8b1bccb79b Kernel: store/load sse/mmx/fpu state on isr/irq/syscall
I haven't tested this yet but should be fine. This will be optimized
to only save state from threads that are using it
2023-07-31 22:28:57 +03:00
Bananymous
e86e755c51 Kernel: Generally improve signals 2023-07-31 22:28:18 +03:00
Bananymous
8ec6d4c9fc LibC: we don't parse syscall arguments in unistd
We just call Kernel::syscall() with 5 arguments from the variadic function.

This was a necessary addition since the syscall() function
used over 2 KiB of stack space.
2023-07-31 22:24:11 +03:00
Bananymous
08cdf88586 Kernel: Cleanup signal trampoline 2023-07-30 15:58:35 +03:00
Bananymous
6f7d97cf94 Kernel: Remove is_in_syscall from Thread 2023-07-30 14:49:51 +03:00
Bananymous
5e434f5131 Kernel: Hardware exceptions now sends signals to userspace 2023-07-30 14:34:15 +03:00
Bananymous
a152d0aac5 Kernel: raise() now force sends signal 2023-07-30 14:17:07 +03:00
Bananymous
879706e6e9 Kernel: Signals are not queued anymore
Posix doesn't require signal queing if you don't use sigqueue() which
we don't support. Process also has its own pending signal mask.
2023-07-29 16:54:31 +03:00
Bananymous
00f1f30a08 Kernel: Userspace sets the foreground process and Shell handles ^C 2023-07-28 18:10:36 +03:00
Bananymous
a5813f9ba5 Kernel: TTY now sends SIGINT on ^C to foreground process 2023-07-28 18:10:09 +03:00
Bananymous
5652af3384 Kernel: Mark reading section from StorageDevice as terminate critical 2023-07-28 18:09:35 +03:00
Bananymous
22cd9af8cc Kernel: Restructure process and thread termination
When we want to kill a process, we mark its threads as Terminating
or Terminated. If the thread is in critical section that has to be
finished, it will be in Terminating state until done. Once Scheduler
is trying to execute Terminated thread it will instead delete it.

Once processes last thread is marked Terminated, the processes will
turn it into a cleanup thread, that will allow blocks and memory
cleanup to be done.
2023-07-28 18:06:20 +03:00
Bananymous
a9cf9bceef Kernel: Rewrite DiskCache
We now cache only clean pages since I don't want to think about
syncing the disk later.
2023-07-27 22:22:39 +03:00
Bananymous
6c0f864a6e Kernel: Locks allow locking after locker is invalid
SpinLock and RecursiveSpinLock will now allow locking after the initial
locker is invalid. This allows us to kill threads even if they are holding
internal locks
2023-07-27 18:52:51 +03:00
Bananymous
e4509d9482 Kernel: DiskCache uses are now done without interrupts
This allows disk cache to not be invalid state after partial read/write
2023-07-27 18:52:51 +03:00
Bananymous
0f23e1f0f4 Kernel: Scheduler can now check if tid is valid
Tid can become invalid if the thread is already terminated
2023-07-27 18:34:06 +03:00
Bananymous
642a6aa4ad Kernel: Process::exit() unblocks the whole lock before blocking 2023-07-24 22:29:59 +03:00
Bananymous
432c296b7b Kernel: Implement SYS_GET_PID and SYS_TCSETPGID
we don't have consept of process groups yet
2023-07-24 22:29:59 +03:00
Bananymous
b576d373c4 Kernel: TTY now stores foreground process pid
this will be process pgid when we have process groups
2023-07-24 22:29:59 +03:00
Bananymous
522aa8e762 Kernel: add Thread::queue_signal() 2023-07-24 22:26:10 +03:00
Bananymous
146802fa4c Kernel: Scheduler can now block threads based on tid 2023-07-24 22:24:21 +03:00
Bananymous
cc8af25d73 Kernel: remove Semaphore::is_blocked
this will not mean anything after semaphores can spuriously wake up
2023-07-24 22:23:13 +03:00
Bananymous
f5f4bf58ad Kernel: add is_tty() to inode 2023-07-24 22:19:59 +03:00
Bananymous
3784da0d18 Kernel: you can now ask process if it is userspace process 2023-07-23 18:54:10 +03:00
Bananymous
9eb72f4392 Kernel: OpenFileDesctiptor can now return inode of fd 2023-07-23 18:52:33 +03:00
Bananymous
f7bf6d5e62 Kernel: Change signal kernel API
return rsp and rip are now stored per thread
2023-07-23 18:33:10 +03:00
Bananymous
adb14ba373 Kernel: Userspace signal handlers are now called one at a time
I added a syscall for telling the kernel when signal execution has
finished. We should send a random hash or id to the signal trampoline
that we would include in the syscall, so validity of signal exit can
be confirmed.
2023-07-23 13:34:53 +03:00
Bananymous
7391d91317 Kernel/LibC: add SYS_SIGNAL/signal() 2023-07-21 20:08:13 +03:00
Bananymous
2149cec29f Kernel: Make signals more POSIX 2023-07-21 20:01:12 +03:00
Bananymous
ad756c36fc Kernel/LibC: Add SYS_KILL/kill() 2023-07-21 19:27:38 +03:00
Bananymous
b56316e9da Kernel: Scheduler now sends queued signals 2023-07-21 19:27:10 +03:00
Bananymous
a989c44211 Kernel: Make signals thread specific 2023-07-21 19:00:59 +03:00
Bananymous
217e5f81cc Kernel: add default signal actions 2023-07-21 18:02:35 +03:00
Bananymous
5f2549b198 BuildSystem: Strip kernel. We will add the map once we use it 2023-07-21 15:47:31 +03:00
Bananymous
dcd4d0daeb Kernel/LibC: Add bareboness signals
You can now call raise() to raise a signal. Signal handlers are
not yet supported, but the handling works :)
2023-07-21 15:45:02 +03:00
Bananymous
faf4220b38 Kernel: kernel memory takes now full pml4e
This allows flags to work properly
2023-07-21 13:47:05 +03:00
Bananymous
193ddaa2f6 Kernel: remove PAGE_FLAGS_MASK as that was not correct anymore
After I added NXE bit, the flags mask is no longer valid
2023-07-21 12:01:50 +03:00
Bananymous
46eb27883a Kernel: Don't map kernel executable memory as writable 2023-07-21 11:17:39 +03:00
Bananymous
2db7cdb71e Kernel: Syscalls now get the interrupt stack 2023-07-21 11:04:16 +03:00
Bananymous
5411c5aa4a BAN: Fix function call in Optional 2023-07-21 10:57:46 +03:00
Bananymous
f8a1a10897 Kernel: add NullDevice to /dev/null 2023-07-20 00:06:22 +03:00
Bananymous
adbe13938e Kernel: move Device.h to its own directory 2023-07-19 23:55:38 +03:00
Bananymous
4d5b14753d Kernel: cleanup sys_exec()
We now scope everyting so desctructors get called
2023-07-19 23:20:39 +03:00
Bananymous
ba9fa00947 Kernel: fix memory leak in PageTable
PageTable used to leak 2 pages on creation and other two on deletion
2023-07-19 23:17:39 +03:00
Bananymous
98cedf155c Kernel: FixedWidthAllocator creation can now fail 2023-07-19 18:07:24 +03:00
Bananymous
88e3998664 Kernel: VirtualRange creation can fail 2023-07-19 17:56:26 +03:00
Bananymous
c0c0bbc1bf Kernel: SYS_FORK can now fail instead of panicing on error 2023-07-19 17:47:12 +03:00
Bananymous
650e1b4fc5 Kernel: Fix bug which made bochs unbootable
We could not boot if ATABus did not have valid primary device.
2023-07-13 15:53:09 +03:00
Bananymous
6c1ada8d0a Kernel: Enable global bit on kernel pages 2023-07-13 15:23:25 +03:00
Bananymous
7d00c2670f Kernel: Support execute disable bit
We will now map executable memory explicitly as executable.
2023-07-13 14:28:53 +03:00
Bananymous
bca7e9a1e8 Kernel: CPUID can detect wether cpu supports nxe bit 2023-07-13 14:24:58 +03:00
Bananymous
3748f0304f Kernel: Fix multiple bugs with terminal 2023-07-13 13:09:52 +03:00
Bananymous
2576bdbd14 Kernel: Fix ATA disk and partition numbering 2023-07-13 12:12:47 +03:00
Bananymous
e341a36287 Init: Use the new pwd.h api for user parsing 2023-07-13 12:01:41 +03:00
Bananymous
bba09a3cd0 LibC: add getpwname() and getpwuid() 2023-07-13 12:01:16 +03:00
Bananymous
985df3532b LibC: implement endpwent(), getpwent() and setpwent() 2023-07-13 11:22:09 +03:00
Bananymous
72041a52e8 Kernel: Ext2Inode::create_file actually sets the inode data
We used to just create inode without touching its memory.
2023-07-13 10:20:56 +03:00
Bananymous
891144dac1 BAN: Iterators are now single general type with CONST template
This allows us to use the same base class for iterator and
const_iterator which were practically the same code.
2023-07-12 13:35:21 +03:00
Bananymous
41e7b53903 BAN: add either_or and either_or_t
This allows you to select type depending on constexpr evaluatable
boolean
2023-07-12 13:34:31 +03:00
Bananymous
6b0920e8c0 BAN: Implement ConstIteratorDouble and add it to HashMap
This is same as IteratorDouble except it uses const_iterator and does
not return non-const references.
2023-07-12 11:41:05 +03:00
Bananymous
4285729d5c BAN: Generalize HashMapIterator to IteratorDouble
This iterator should be able to iterate any container within container
with type iterator defined.

This also fixed bug if first entry in outer container is empty container.
2023-07-12 09:29:05 +03:00
Bananymous
a9c10d0751 Base: add empty directories with .gitkeep to base/ 2023-07-11 08:02:28 +03:00
Bananymous
74c79c7eff Kernel: Rewrite whole device structure
We now have DevFileSystem which is derived from RamFileSystem. All
devices are RamInodes. We don't have separate DeviceManager anymore.
To iterate over devices, you can loop througn every inode in devfs.
2023-07-10 23:17:14 +03:00
Bananymous
9174a89971 BAN: Add iterators to HashMap 2023-07-10 23:16:41 +03:00
Bananymous
5c94a583bc Userspace: add basic 'touch' command 2023-07-10 16:38:15 +03:00
Bananymous
6e1fc2766f tee: indent with tabs 2023-07-10 16:18:08 +03:00
Bananymous
d3bb00cb55 Userspace: Add basic tee command 2023-07-10 16:07:53 +03:00
Bananymous
5a5656b2d3 Kenrel: RamInode now implements truncate() 2023-07-10 16:07:09 +03:00
Bananymous
1a1e584cba Kernel: OpenFileDescriptors can now store more than 8 bits of flags 2023-07-10 16:06:36 +03:00
Bananymous
65fa05f998 Kernel: add O_TRUNC
this is not supported by anything yet
2023-07-10 15:48:18 +03:00
Bananymous
2276fc95b8 Kernel: creat() mode now has to be access mode
We provide the S_IFREG in creat
2023-07-10 15:34:41 +03:00
Bananymous
1e173c178d Kernel: Ext2 fill now return ENOTSUP on write
We used to crash the kernel
2023-07-10 15:34:25 +03:00
Bananymous
773747cf9c Kernel: O_APPEND is now supported 2023-07-10 15:11:27 +03:00
Bananymous
4972284dde Kernel: open() and openat() now take mode as parameter
O_CREAT now tries to create file if O_CREAT is specified
2023-07-10 15:08:54 +03:00
Bananymous
45789fda08 Kernel: You can now read/write to RamInodes
RamFS should be stored on physical pages and not in kmalloc, but
that will be implemented later :)
2023-07-10 14:09:35 +03:00
Bananymous
3b5bc63d1b Kernel: Inode::create_file() now takes uid and gid as parameters 2023-07-10 13:32:10 +03:00
Bananymous
f1089e2b8a Kernel: start work on ram file system 2023-07-10 13:26:14 +03:00
Bananymous
6d93c1eb92 LibC: add NAME_MAX to limits.h
This is defined to 255 which is _XOPEN_NAME_MAX, smallest value
for XOPEN compliance
2023-07-10 11:48:11 +03:00
Bananymous
363c325c79 Kenrel: Fix inode comparison
We should not compare rdevs
2023-07-10 11:48:11 +03:00
Bananymous
583504ebe0 Kernel: Inode rename directory functions 2023-07-10 11:48:11 +03:00
Bananymous
b354b77f8b Kernel: Mark Ext2 classes final 2023-07-10 11:48:11 +03:00
Bananymous
74af46cb4a BAN: RefPtr can be constructed from other types 2023-07-10 11:48:11 +03:00
Bananymous
19dab08275 Kernel: add more functionality to PCI 2023-07-09 23:04:11 +03:00
Bananymous
3840fbf957 Kernel: Edit lock scopes and make string copy able to fail 2023-07-07 23:12:19 +03:00
Bananymous
78c091f7f8 Kernel: Move open file descriptors to their own class
This simplifies code a lot :)
2023-07-07 23:11:37 +03:00
Bananymous
274ecbba78 LibC: limits.h now defined OPEN_MAX 2023-07-07 23:08:49 +03:00
Bananymous
683c2a68cd Shell: $(...) expansion works now :) 2023-07-06 23:22:57 +03:00
Bananymous
ad98181069 Shell: you can call Shell -c ... to invoke the shell as interpreter 2023-07-06 23:22:49 +03:00
Bananymous
a549336530 Kernel/LibC: add basic dup2 2023-07-06 23:17:54 +03:00
Bananymous
4eb95c963d Kernel/LibC: Add basic pipe() syscall and command
You can now create pipes :)
2023-07-06 22:16:26 +03:00
Bananymous
22caacd2a9 LibC: add read() and write() to unistd 2023-07-06 22:15:55 +03:00
Bananymous
af30d537da Kernel: TTY now unblocks semaphore after read
This allows concurrent reads not fully reading the buffer not block
indefinately.
2023-07-06 21:32:34 +03:00
Bananymous
f1bd26fb92 Kernel: Add O_CLOEXEC 2023-07-06 20:00:33 +03:00
Bananymous
5c6bbcb62f Kernel: Remove spammy process/thread exit printing 2023-07-06 10:34:46 +03:00
Bananymous
21bd87bb07 Userspace: Shell now has 'env' for printing environment 2023-07-06 10:32:43 +03:00
Bananymous
79450df04c Userspace: Shell imporove 'time' command 2023-07-06 09:45:04 +03:00
Bananymous
7f8b7b811e Userspace: Shell now has time builtin 2023-07-06 00:39:04 +03:00
Bananymous
3c068aa0ae Kernel/LibC: add clock_gettime() for CLOCK_MONOTONIC
This gets the number of milliseconds since boot
2023-07-06 00:38:29 +03:00
Bananymous
86df258365 Kernel: rework the whole PageTable structure
We now have page table structure for kernel memory which is shared
between all processes.
2023-07-05 23:41:35 +03:00
Bananymous
d99e704728 LibC: Fix syscall SYS_READ and SYS_WRITE arguments 2023-06-19 10:38:29 +03:00
Bananymous
0d620f3e0f Kernel: Rewrite and optimize DiskCache
DiskCache now consists of PageCaches which are caches of contiguous
sectors. This allows the disk cache to be ordered and faster traversal.

We seem to have a problem somewhere during reading. The stack gets
corrupted.
2023-06-19 10:31:47 +03:00
Bananymous
4dce0f9074 Userspace: u8sum fix error message 2023-06-19 10:31:23 +03:00
Bananymous
54f89cba33 Userspace: Shell now processes $ arguments 2023-06-19 01:39:24 +03:00
Bananymous
de88f60d1a Userspace: Shell argument parsing now appriciates quotes 2023-06-19 01:07:00 +03:00
Bananymous
f7060970e6 Userspace: Shell argument parse now results in BAN::String 2023-06-19 00:34:44 +03:00
Bananymous
e7a98ac6cc Userspace: Shell now sets SHELL environment variable 2023-06-18 23:35:51 +03:00
Bananymous
10544db52e LibELF: We use BAN::Vector<uint8_t> as elf storage
This is made possible by the dynamic kmalloc
2023-06-18 23:29:23 +03:00
Bananymous
5e123031aa Kernel: kmalloc has now somewhat dynamic storage
Allocations bigger than PAGE_SIZE and those not forced to be identity
mapped are now done on a GeneralAllocator. This allows us to use kmalloc
for big allocations; bigger than the fixed 1 MiB storage.

This is still a hack and the whole kmalloc will have to be rewritten at
some point, but for now this does the job :D
2023-06-18 23:27:00 +03:00
Bananymous
388f530edd Kernel: Add GeneralAllocator::paddr_of
Yoy can now query physical address of a virtual address for general
allocator allocation
2023-06-18 23:25:51 +03:00
Bananymous
d354cccd37 Kernel: Add enum for ISR name to number 2023-06-18 23:24:27 +03:00
Bananymous
714305ef56 Kernel: General allocator takes first valid vaddr as parameter 2023-06-17 22:23:34 +03:00
Bananymous
f83ae1e9c6 Kernel: Move print during boot 2023-06-12 23:45:36 +03:00
Bananymous
c38e8de6b5 BAN: Optional can be constructed inplace 2023-06-12 23:45:36 +03:00
Bananymous
97638f7ade BAN: Add operator-> and operator* to Optional 2023-06-12 22:25:14 +03:00
Bananymous
326a30d1af Userspace: Add u8sum
This program caluculates the sum of bytes in file mod 256
2023-06-12 20:36:16 +03:00
Bananymous
32e1473c94 Kernel: Make disk IO blocking thread safe
This was causing a lot of deadlocks on vms without kvm accel
2023-06-12 18:57:47 +03:00
Bananymous
bf617036c7 Kernel: Rework syscall calling
I removed the intermediate function when calling syscalls. Now syscall
handler calls the current process automatically. Only exception is
sys_fork, since it needs a assembly trampoline for the new thread.
2023-06-12 14:16:48 +03:00
Bananymous
ce55422a24 Kernel: Remove Shell from kernel
This is now obsolete since we have a userspace Shell.
2023-06-12 02:04:52 +03:00
Bananymous
388cc7c3bb Base: add home directories for root and user 2023-06-12 02:03:13 +03:00
Bananymous
37f9404d93 BuildSystem: every file except /home/* is now owned by root 2023-06-12 02:03:13 +03:00
Bananymous
38dff41e25 Userspace: Shell processes PS1 '\~' as cwd and implement cd 2023-06-12 02:03:13 +03:00
Bananymous
d360340b9e Userspace: init now sets HOME environment variable and cd's into HOME 2023-06-12 02:02:52 +03:00
Bananymous
0f63cfa43f Kernel/LibC: add SYS_{SET,GET}_PWD and chdir, getpwd 2023-06-12 02:02:52 +03:00
Bananymous
537780ee1e Kernel: allocate thread stacks from 0x300000 onwards
I had a problem where thread stack was overlapping with elf loading
2023-06-12 01:02:19 +03:00
Bananymous
4ca99fcb4e Kernel: Fix bug in elf loading
We were allocating one extra page
2023-06-12 00:59:19 +03:00
Bananymous
eb7ee13f43 Userspace: init now default logins as user 2023-06-12 00:46:07 +03:00
Bananymous
b2de706693 Userspace: Shell now uses PS1 as prompt if set 2023-06-12 00:45:47 +03:00
Bananymous
6a8180470d Userspace: Add color to ls 2023-06-11 23:00:19 +03:00
Bananymous
12d56be5cc Userspace: init now sets user and group ids before running their shell 2023-06-11 22:37:00 +03:00
Bananymous
bb4d81a4fa Userspace: Add basic id that prints {,e}{uid,gid} of the current proc 2023-06-11 22:37:00 +03:00
Bananymous
b254ade69b Kernel: Add SYS_GET_{,E}{UID,GID} 2023-06-11 22:37:00 +03:00
Bananymous
ef4ebaa969 Kernel: Add syscalls for set{,e,re}{uid,gid} 2023-06-11 22:37:00 +03:00
Bananymous
99f8133b91 LibC: gid_t and id_t are now signed types
I have no idea why I had made them unisigned
2023-06-11 22:37:00 +03:00
Bananymous
51eb44bf40 Kernel/Userspace: Add basic init process
This process parses /etc/passwd and promps login screen.
When an username is entered, it will launch that users shell
2023-06-11 22:37:00 +03:00
Bananymous
a0be415e09 BAN: Add basic Optional 2023-06-11 21:00:25 +03:00
Bananymous
071da18fa3 LibC: add strchrnul()
this is a gnu libc extension
2023-06-11 20:18:03 +03:00
Bananymous
c62e820bcf Kernel: Add basic Credentials for the system
Now filesystem access/open, etc confirm that you have access for rwxs
2023-06-11 20:06:06 +03:00
Bananymous
46c34db6cb Kernel: GeneralAllocator and FixedWidth allocator invalidate TLB caches
We were getting random exceptions when reallocating same addressess and
this fixes that problem :)
2023-06-11 15:57:48 +03:00
Bananymous
25a2a4879c Userspace: add basic ls command 2023-06-11 03:38:44 +03:00
Bananymous
8be28012ee LibC: Reorder some syscalls 2023-06-11 03:29:22 +03:00
Bananymous
5aed186827 Kernel: Add SYS_OPENAT 2023-06-11 03:29:22 +03:00
Bananymous
91f812e17f Kernel: Exceptions will now enable interrupts before calling exit()
this allows scheduler verification on reschedule() not panic
2023-06-11 03:29:22 +03:00
Bananymous
f0b22c48b2 LibC: implement close 2023-06-11 03:29:22 +03:00
Bananymous
52c4eebd77 Kernel: Implement SYS_FSTAT 2023-06-11 03:29:22 +03:00
Bananymous
24f0d26fce LibC: Implement basic dirent functionality
We don't currently support seeking
2023-06-11 03:29:22 +03:00
Bananymous
825ec221b7 Kernel: we don't panic anymore on unrecognized syscall 2023-06-11 00:18:48 +03:00
Bananymous
e31080bce3 Kernel: allow open() call with O_SEARCH 2023-06-11 00:18:34 +03:00
Bananymous
7a5d5cabad Kernel: Add syscall for reading directory entries 2023-06-11 00:18:08 +03:00
Bananymous
f7de310889 LibC: add missing O_EXEC and O_SEARCH 2023-06-11 00:17:18 +03:00
Bananymous
e209ca7c82 Kernel: Rewrite directory listing so it can be integrated to libc 2023-06-11 00:17:18 +03:00
Bananymous
ee8de77a90 Userspace: fix return values of cat and echo 2023-06-10 17:34:10 +03:00
Bananymous
db49cbd6e2 Kernel: We now store the processes exit code 2023-06-10 17:31:56 +03:00
Bananymous
e001eecb7b Userspace: add exit to shell 2023-06-09 01:51:23 +03:00
Bananymous
7f34d00c95 Userspace: add echo 2023-06-09 01:50:18 +03:00
Bananymous
2c18adbddd Userspace: add ^L support for shell 2023-06-09 01:24:33 +03:00
Bananymous
97c7fc42d1 Kernel: SpinLocks now reschedule if they cannot aquire the lock
This allows us to not actually spin doing nothing while waiting for
another (not executing) to release the lock. This api won't probably
work when we get to SMP
2023-06-09 00:53:32 +03:00
Bananymous
7da0627f8e Kernel: Process::exit() now uses the new Scheduler::reschedule()
We use this new function while waiting for all blocking threads to
resume execution
2023-06-09 00:49:19 +03:00
Bananymous
27cef23823 Kernel: Scheduler now has reschedule()
This can be called from anywhere and just causes the scheduler to
schedule the next thread. This is more efficient and verbose version
of Scheduler::set_current_thread_sleeping(0), since we don't have
to wake other threads or do other verifications.
2023-06-09 00:47:17 +03:00
Bananymous
b7fc2dc3d0 Kenrel: Rename Scheduler::reschedule -> Scheduler::timer_reschedule 2023-06-09 00:41:43 +03:00
Bananymous
8af390e0f6 Kernel: Big commit. Rewrite ELF loading code
We now load ELF files to VirtualRanges instead of using kmalloc.
We have only a fixed 1 MiB kmalloc for big allocations and this
allows loading files even when they don't fit in there.

This caused me to rewrite the whole ELF loading process since the
loaded ELF is not in memory mapped by every process.

Virtual ranges allow you to zero out the memory and to copy into
them from arbitary byte buffers.
2023-06-09 00:37:43 +03:00
Bananymous
96d6453ea8 Kernel: PageTableScope locks the PageTable before disabling interrupts
This allows replacing some PageTableScopes with PageTable::lock()
2023-06-09 00:34:41 +03:00
Bananymous
2b9900e56e Kernel: get_free_contiguous_pages works with non-page aligned addresses
also fix bug in ordering
2023-06-06 02:03:23 +03:00
Bananymous
86f58f60cb LibC: implement setenv, unsetenv, putenv 2023-06-05 22:51:02 +03:00
Bananymous
064ce568c2 Kernel: add basic support for environment variables
exec functions will search files from PATH
2023-06-05 22:51:02 +03:00
Bananymous
6aff459e1c BAN: add StringView::contains(char) 2023-06-05 22:51:02 +03:00
Bananymous
0b1b4d8f7e Kernel: exec now has better posix errors 2023-06-05 21:12:08 +03:00
Bananymous
3fc2c3529a Shell: load old termios for process execution 2023-06-05 21:12:08 +03:00
Bananymous
b0e9ab0519 Kernel/LibC: pass environ pointer to process 2023-06-05 21:12:08 +03:00
Bananymous
668517a723 Kernel: TTY now actually flushes on ^D 2023-06-05 20:21:46 +03:00
Bananymous
649f08ec78 Kernel: verify that loaded elfs are executable 2023-06-05 19:29:32 +03:00
Bananymous
2f2c298c68 Shell: add bareboness utf8 support
This should work as long as TTY provides only valid utf8.
If the utf is invalid, assertion fails and the shell dies.
2023-06-05 18:55:22 +03:00
Bananymous
90e48970e6 Shell: we now support left/right arrows 2023-06-05 18:24:41 +03:00
Bananymous
480842a203 LibC: abort now prints 'abort()' and exits
we used to call assert in abort which then recursively called
abort again.
2023-06-05 18:23:19 +03:00
Bananymous
5425394880 Kernel: TTY now supports CSI s and u
These are commonly supported values to save and load cursor
position
2023-06-05 18:19:13 +03:00
Bananymous
a365813fa9 BuildSystem: disable nls from gcc 2023-06-05 17:55:47 +03:00
Bananymous
9d64dbd5c2 Kernel/LibC: add SYS_STAT and stat(), lstat() 2023-06-05 14:37:14 +03:00
Bananymous
30bb61a775 Base: revert bin and lib symlinks to relative paths
Absolute paths fucked up toolchain building and usespace linking,
since g++ was finding host libraries from /usr/lib.
2023-06-05 10:08:01 +03:00
Bananymous
1f36ed0cf9 Userspace: Start work on proper shell 2023-06-05 01:42:57 +03:00
Bananymous
d54c76f88a Base: /lib and /bin are now absolute symlinks 2023-06-04 18:12:05 +03:00
Bananymous
cbb9f47ee5 LibC: add wait and waitpid
Note that wait() doesn't work since only waiting for specified
pid is supported. wait() will just return -1 and set errno to
ECHILD.
2023-06-04 18:00:52 +03:00
Bananymous
b68d5a5833 Kernel: Add SYS_WAIT
This syscall waits for a given pid.
2023-06-04 18:00:52 +03:00
Bananymous
94d2090777 Kernel: fork() now clones current thread
This is how posix specifies thread cloning during fork
2023-06-04 17:40:37 +03:00
Bananymous
e97585daf9 Kernel: Process FixedWidthAllocators come now in 4 sizes 2023-06-04 01:26:43 +03:00
Bananymous
924fc2118c Kernel: Allocators are now stored in UniqPtr
This allows proper memory management, we had some memory leak
2023-06-04 01:25:57 +03:00
Bananymous
51f4c0c750 Kernel: make load_elf() its own function 2023-06-04 01:24:11 +03:00
Bananymous
37b93da650 Kernel: PhysicalRange maps its nodes to kernel vaddr space
This keeps the lower half of address space cleaner
2023-06-04 01:20:47 +03:00
Bananymous
35e739dcdd Kernel: reorder process exit steps 2023-06-04 01:19:04 +03:00
Bananymous
8352392b38 Kernel: You can specify first vaddr for getting free pages 2023-06-04 01:15:48 +03:00
Bananymous
413f05bfca BAN: Add UniqPtr 2023-06-04 00:39:20 +03:00
Bananymous
dc1aff58ed Kernel: PAGE_FLAG_MASK is now only 0xF
We don't care currenly about anything but the last few bits
2023-06-03 20:08:13 +03:00
Bananymous
9f75d9cfe5 Kernel: PageTable now has debug_dump
This dumps all the mapped pages and their flags
2023-06-03 20:08:13 +03:00
Bananymous
a42af7e973 Kernel: boot.S is back to 2 MiB pages
bochs doesn't seem to support 1 GiB pages
2023-06-03 18:53:36 +03:00
Bananymous
2ce244d303 BAN: Errors now includes assert.h 2023-06-03 18:53:05 +03:00
Bananymous
a775a920d0 BuildSystem: remove sse and sse2 from userspace 2023-06-03 16:05:32 +03:00
Bananymous
4f84faf392 LibC: printf string persision works now 2023-06-03 15:07:02 +03:00
Bananymous
a4cb5d8360 Kernel: Inode/Device detection is done with overridden bool functions 2023-06-03 13:28:15 +03:00
Bananymous
da7f09cf82 Kernel: Heap will return 0 if no free page is available 2023-06-03 02:55:31 +03:00
Bananymous
0166af472b Kernel: DiskCache will try to shrink_to_fit after cache cleanup 2023-06-03 02:55:22 +03:00
Bananymous
884d986bd6 Kernel: DiskCache won't crash when running out of kmalloc memory 2023-06-03 02:36:20 +03:00
Bananymous
59b807189f Kernel: add basic disk cache
ATADevices now add disk cache to themselves
2023-06-03 02:23:14 +03:00
Bananymous
fb1c7015b1 Kernel: Shell 'memory' now prints heap memory usage 2023-06-03 02:22:18 +03:00
Bananymous
d4123f62b2 Update README.md 2023-06-02 18:43:30 +03:00
Bananymous
a3f410d1a1 Userspace: create_program creates proper cmake files again 2023-06-02 18:42:25 +03:00
Bananymous
1d19a4bffe BuildSystem: all scripts have now bash shebang
I could not use the scripts on debian
2023-06-02 18:41:45 +03:00
Bananymous
271dd91292 BuildSystem: add rule to build libstdc++
We can now build libstdc++ and actually link with g++
2023-06-02 18:39:42 +03:00
Bananymous
9bd4d68f9c Kernel: Shell ls and stat now properly show symlinks 2023-06-02 18:22:56 +03:00
Bananymous
3c3c7826ef LibC: Add simple definition for realloc
The syscall just crashes the kernel currently. I will implement
this when needed
2023-06-02 17:56:13 +03:00
Bananymous
2207357b93 LibC: add __cxa_at_exit() for libc 2023-06-02 17:50:55 +03:00
Bananymous
3a69768eb0 LibC: remove select() declaration
This already comes from sys/select.h
2023-06-02 17:29:09 +03:00
Bananymous
afb29ff3ec LibC: rename [[noreturn]] to __attribute__((__noreturn__))
This compiles with C compiler
2023-06-02 17:28:36 +03:00
Bananymous
e6f0f891a6 LibC: stdlib.h doesn't seem to typedef wchar_t without __need_wchar_t 2023-06-02 17:27:31 +03:00
Bananymous
36e5aa4683 LibC: fix INFINITY definition typo 2023-06-02 17:27:14 +03:00
Bananymous
7738050105 LibC: fix DIR typedef for C code 2023-06-02 17:26:38 +03:00
Bananymous
4bf11ec349 LibC: complex.h undefs I before defining it 2023-06-02 17:13:09 +03:00
Bananymous
d821012eed LibC: mbstate_t is empty struct 2023-06-02 17:10:29 +03:00
Bananymous
35c6edd989 LibC: fix sig_atomic_t definition 2023-06-02 17:08:43 +03:00
Bananymous
633cb4f282 Kernel: VFS now has max link depth of 100 2023-06-02 12:50:40 +03:00
Bananymous
4d4d0e26a9 Kernel: Symlinks are now working
We still have to implement loop or depth detection
2023-06-02 11:43:46 +03:00
Bananymous
feea2d4024 BAN: Fix function call in Vector 2023-06-01 00:50:04 +03:00
Bananymous
0ffd2a5c1d Kernel: Shell can now list symlinks 2023-06-01 00:25:53 +03:00
Bananymous
232fdcb82c Kernel: add basic support for symlinks 2023-06-01 00:24:45 +03:00
Bananymous
0ccc23d544 Kernel: Shell opens standard files 2023-05-31 23:14:15 +03:00
Bananymous
789ca3db1a BuildSystem: cmake creates /usr/bin 2023-05-31 23:13:53 +03:00
Bananymous
cb359a05dc BuildSystem: link libraries when they change
This also fixed the need for manual linkin on firt build
2023-05-31 23:01:40 +03:00
Bananymous
14982c137a Userspace: make test program link against libc on change 2023-05-31 22:36:47 +03:00
Bananymous
0acab11620 LibC: add execl 2023-05-31 22:36:26 +03:00
Bananymous
02f0239016 Kernel: Cleanup exec code 2023-05-31 22:36:05 +03:00
Bananymous
ab61b49aca Kernel: Add SYS_EXEC syscall 2023-05-31 20:57:33 +03:00
Bananymous
4da1d6fd27 Kernel: Implement Process::exec() 2023-05-31 20:56:29 +03:00
Bananymous
909e847369 Kernel: Move userspace entry functions to Process instead of Thread 2023-05-31 19:31:10 +03:00
Bananymous
eafa09fecf Kernel: boot.S maps GiB as single pdpte 2023-05-31 00:51:15 +03:00
Bananymous
8175348284 Kernel: Fix comment 2023-05-31 00:51:15 +03:00
Bananymous
b2832cb47a Kernel: PageTable destructor works now
we are successfully booting higher half kernel now :)
2023-05-31 00:44:14 +03:00
Bananymous
9f499991c8 Kernel: PageTable::create_userspace() now works 2023-05-31 00:44:14 +03:00
Bananymous
9a416e8ae8 Kernel: kmalloc free error prints the pointer 2023-05-31 00:34:56 +03:00
Bananymous
911922c6a3 Kernel: RSDP location is now done with virtual addresses 2023-05-31 00:34:21 +03:00
Bananymous
1f2fd59ad5 Kernel: Physical range now calculates RAM with physical addresses 2023-05-31 00:33:44 +03:00
Bananymous
708d401d2c Kernel: Heap gets multiboot pointer with P2V 2023-05-30 23:57:44 +03:00
Bananymous
ed0dcacab3 Kernel: Move V2P and P2V to Memory/Types.h 2023-05-30 23:57:03 +03:00
Bananymous
e86050f343 Kernel: PageTable::map_range_at maps correctly the last page 2023-05-30 23:56:07 +03:00
Bananymous
57f7da6ce1 Kernel: Booting with higher half kernel gets to Heap initialization 2023-05-30 22:21:12 +03:00
Bananymous
93e6455171 Kernel: start work on higher half kernel 2023-05-30 08:00:17 +03:00
Bananymous
8f38780197 Toolchain: lib gcc is wuild with mcmodel=large 2023-05-30 07:59:41 +03:00
Bananymous
341f7e41e5 LibC: Fix some headers to make gcc build again 2023-05-30 01:17:45 +03:00
Bananymous
265fe9c62e Kernel: We now identity map full GiB in boot.S
The paging structure is pre-built so no unnecessary calculations are done
2023-05-30 00:08:52 +03:00
Bananymous
3b9d60d7cb Kernel: Remove unused includes of CriticalScope 2023-05-29 21:15:55 +03:00
Bananymous
278b873e89 Kernel: Remove unnecessary usages of PageTableScope
This should be used as few times as possible since it calls 'cli'
2023-05-29 21:11:29 +03:00
Bananymous
e640344d7a Kernel: Rename MMU to PageTable
This is more descriptive name for what it actually represents
2023-05-29 21:06:09 +03:00
Bananymous
7151bb86a8 Kernel/LibC: opening standard files is done in libc 2023-05-29 20:21:19 +03:00
Bananymous
2a34391b71 LibC: open() now just returns syscall(SYS_OPEN, ...)
errno is handled in syscall()
2023-05-29 20:19:17 +03:00
Bananymous
3d95cf02f3 Kernel: We can't lock the MMU lock in load()
It needs to be callable always by scheduler
2023-05-29 19:39:35 +03:00
Bananymous
dd3f34cb2c Kernel: Make RecursiveSpinLock thread safe
also SpinLock is now implemented with gcc builtins
2023-05-29 19:38:09 +03:00
Bananymous
0c316ebfb2 Kernel: Add SYS_SLEEP 2023-05-28 22:34:48 +03:00
Bananymous
282bf24f65 Kernel: fork() now copies allocations through FixedWidthAllocator 2023-05-28 21:34:35 +03:00
Bananymous
f964f6be8d Kernel: Move page macros to Types.h 2023-05-28 21:03:08 +03:00
Bananymous
0202ccec5f Kernel: ISR will now crash userspace process instead of panicing kernel 2023-05-28 20:53:10 +03:00
Bananymous
636c308993 Kernel: fork() now copies allocation done through GeneralAllocator 2023-05-28 20:37:39 +03:00
Bananymous
6fdbe6f9c2 Kernel: Add bareboness fork() function 2023-05-28 18:08:49 +03:00
Bananymous
c19f4c019a Kernel: Add invalidate() to MMU 2023-05-28 18:05:49 +03:00
Bananymous
83eb3dc0cb Kernel: fix MMU::map_page_at()
We used to only reassign if flags changed
2023-05-28 17:57:05 +03:00
Bananymous
481c8406f3 LibC: fputs uses fputc instead of putc 2023-05-28 17:48:34 +03:00
Bananymous
0129619d9a Kernel: Processes and Threads use VirtualRange memory allocations 2023-05-28 17:48:34 +03:00
Bananymous
e0479b291d Kernel: Move PhysicalRange to its own file and add VirtualRange 2023-05-28 17:48:34 +03:00
Bananymous
b847d7dfd5 Kernel: MMU::get() is now MMU::kernel
MMU is can now be locked with RecursiveSpinLock.

Scheduler now has get_current_tid() that works before the Scheduler
is initialized. This allows RecursiveSpinLock usage early on.
2023-05-28 16:18:18 +03:00
Bananymous
245dff8027 Shell: we now link BAN (we can't use it though) 2023-05-26 22:31:21 +03:00
Bananymous
fed690a7f2 Kernel: Directory listing is working again 2023-05-26 22:31:21 +03:00
Bananymous
54d981120d Kernel: kmalloc debug_dump is marked [[maybe_unused]] 2023-05-26 22:31:21 +03:00
Bananymous
f79250c4d4 LibC: Rewrite all the headers.
We now have more or less posix issue 2018 conforming libc headers.

This was a really time consuming and boring operation but it had to
be done.

Now we get to actually start implementing libc :)
2023-05-26 22:31:21 +03:00
Bananymous
78b62776d2 BAN: libban is now build into library dir 2023-05-26 22:31:21 +03:00
Bananymous
bda4614783 BAN: Errors.h can be included from userspace 2023-05-26 22:31:21 +03:00
Bananymous
0ab3332ad3 Userspace: Start work on shell 2023-05-26 22:31:21 +03:00
Bananymous
9e0abbc2f0 Kernel: Add bareboness possibility to set termios 2023-05-26 22:31:21 +03:00
Bananymous
496adb61a4 Buildsystem: Fix userspace link order 2023-05-26 22:31:21 +03:00
Bananymous
4a4a3bf184 Kernel/LibC: move file offset back to kernel
This makes keeping track of offsets easier and more proper
2023-05-26 22:31:21 +03:00
Bananymous
f33e78882e Kernel: Add argc and argv to process entry 2023-05-16 00:27:49 +03:00
Bananymous
0ff067bdb7 Kernel: Add MMUScope
This disables interrupts and loads specified mmu for the
scope it lives in
2023-05-16 00:26:39 +03:00
Bananymous
31ac3260ed Kernel: MMU keeps track of the current 2023-05-16 00:26:39 +03:00
Bananymous
d82c6c2337 LibC: fix bugs with printf 2023-05-15 22:47:08 +03:00
Bananymous
632b699475 BAN: add is_arithmetic and is_signed to Traits.h 2023-05-15 20:26:29 +03:00
Bananymous
85039020d3 Kernel: argc is passed as zero to userspace 2023-05-11 18:28:32 +03:00
Bananymous
1a0fdc5a44 LibC: printf now prints 0 as integer 2023-05-11 18:20:37 +03:00
Bananymous
fb1bab7c30 BuildSystem: add helper to create userspace programs 2023-05-11 18:10:06 +03:00
Bananymous
7eb43990ad BuildSystem: userspace has cmake target 2023-05-11 16:19:53 +03:00
Bananymous
53f4b5a9da LibC: add function declarations to sys/stat.h 2023-05-11 15:11:33 +03:00
Bananymous
1d4a6c3a42 LibC: add function declarations to dirent.h 2023-05-11 01:42:52 +03:00
Bananymous
40083e4aa1 LibC: add definitions to math.h 2023-05-11 01:40:42 +03:00
Bananymous
bd929bff07 LibC: add defines in stdio.h 2023-05-11 01:39:16 +03:00
Bananymous
cd4a0530fa LibC: add function declarations to unistd.h 2023-05-11 00:34:03 +03:00
Bananymous
273fdd2235 LibC: add function declarations to string.h 2023-05-11 00:34:03 +03:00
Bananymous
b20f2e8d31 LibC: add function declarations to math.h 2023-05-11 00:34:03 +03:00
Bananymous
e756cde2b1 LibC: define all errnos and strerror{name,desk}_np 2023-05-11 00:34:03 +03:00
Bananymous
de18d3e64d LibC: add function declarations to time.h 2023-05-11 00:34:03 +03:00
Bananymous
441999ba9f LibC: add more types to sys/types.h 2023-05-11 00:33:53 +03:00
Bananymous
dd046b1ace LibC: Add dummy signal.h 2023-05-10 23:20:27 +03:00
Bananymous
926aa238ab LibC: add toupper, tolower in ctype.h 2023-05-10 23:13:56 +03:00
Bananymous
01fa521a03 LibC: Add dummy setjmp.h 2023-05-10 23:00:53 +03:00
Bananymous
f31da19266 LibC: Add dummy locale.h 2023-05-10 22:58:07 +03:00
Bananymous
48edc38817 LibC: implement printf conversions e, E, f, F 2023-05-10 22:36:03 +03:00
Bananymous
ac12132ac0 LibC: add math.h with floorl 2023-05-10 22:35:42 +03:00
Bananymous
13fabcc1f1 BAN: add pow, log2, log10, log in math
These are implemented using x86 floating point assembly
2023-05-10 19:03:33 +03:00
Bananymous
67005a80be LibC: add working f modifier to printf
This is implementation will write out of bounds if the conversion
takes more than 1024 characters (either super large number or very
big percision).

Also we dont handle NaN and infinity cases
2023-05-10 15:43:42 +03:00
Bananymous
f43bfcb398 LibC: add better error string support 2023-05-10 02:22:31 +03:00
Bananymous
d5ce4c9d2c LibC: add probably functional *printf
I wrote a general printf function that takes an putc function
pointer. We can use this to implement all the printf family
functions. I haven't done thorough testing with this, but it seems
to be functional for the most part
2023-05-10 02:00:28 +03:00
Bananymous
1cf7ef3de6 Kernel: Remove offset from OpenFileDescriptor
This is now handled on the libc side. There might be reasons to
have it in kernel side, but for simplicity's sake I'm moving it
to libc for now :)
2023-05-09 20:31:22 +03:00
Bananymous
5248a3fe48 LibC: Fix bug in *printf 2023-05-09 20:30:12 +03:00
Bananymous
812e61ca70 Kernel: Add barebones GeneralAllocator for >4096B 2023-05-08 22:10:49 +03:00
Bananymous
2d0a5a9e15 Kernel: FixedWidthAllocator operates on MMU
Instead of Process* we use MMU& in FixedWidthAllocator since it is
everything it actually needs :)
2023-05-08 00:06:56 +03:00
Bananymous
f32d594879 Kernel: We add FixedWidthAllocators on demand
On SYS_ALLOC we will add a new FixedWidthAllocator if the old ones
are already full or we don't have one with proper size. This allows
arbitary number of allocations as long as you have enough memory
available :)

Next I will be writing a general allocator for allocations larger
than 4096 bytes which should make SYS_ALLOC syscall complete :)
2023-05-07 23:57:01 +03:00
Bananymous
c2ad76fe4f BAN: Error uses 64 bit error codes 2023-05-07 02:09:52 +03:00
Bananymous
10d9b72da1 LibC: syscall() now returns -1 on error and updates errno 2023-05-07 01:51:39 +03:00
Bananymous
2fe9af7165 Kernel/LibC: add free function for FixedWidthAllocator
I have to rework the syscall API and allocators in process. For
now this works well enough :)
2023-05-07 01:21:50 +03:00
Bananymous
0deda83d05 BuildSystem: linker -O2 doesn't do anything? hopefully 2023-05-06 19:58:08 +03:00
Bananymous
ff5bcd4416 Kernel: Add basic fixed width allocator for userspace
We have to move process stacks to the general heap and maybe map
kernel to higher half.
2023-05-06 19:58:08 +03:00
Bananymous
b65cd1d09b Kernel: Invalid physical addresses from heap are specified now by 0 2023-05-06 17:34:22 +03:00
Bananymous
bc35a561d3 Kernel: GDT tss selector is now 16 bit value 2023-05-06 17:34:22 +03:00
Bananymous
06bc807e34 Kernel: MMU can now provide info about virtual address space 2023-05-06 17:34:22 +03:00
Bananymous
6262e41de1 Kernel: ISRs now print pid and tid 2023-05-06 00:10:15 +03:00
Bananymous
0cb53efa01 Kernel: 64 bit MMU now properly identity maps kernel 2023-05-05 14:19:28 +03:00
Bananymous
4e859bedbc Kernel: TTY input process is now single instance
Process sends key events to the active (currently only) tty
2023-04-30 16:11:14 +03:00
Bananymous
f139fc2229 Kernel: namespace and function renames
MMU moved to namespace kernel
Kernel::Memory::Heap moved to just Kernel
MMU::map_{page,range} renamed to identity_map_{page,range}

Add MMU::get_page_flags
2023-04-28 14:48:38 +03:00
Bananymous
e48acbb03b Kernel: Add set_tss_stack() to 32 bit 2023-04-28 14:44:23 +03:00
Bananymous
d1155c968e Kernel: dprintln file name is now relative
This makes file names much shorter
2023-04-28 14:43:19 +03:00
Bananymous
88a2c60065 BAN: Add is_power_of_two to Math functions 2023-04-28 14:42:49 +03:00
Bananymous
5bfcf6783e LibC: add proper stdlib.h header
Function declarations taken from the posix specifications
2023-04-27 15:14:03 +03:00
Bananymous
94f8a657f1 LibC: add link to posix stdio.h 2023-04-27 14:16:25 +03:00
Bananymous
7fac2a7526 Userspace: Simple stdio test 2023-04-25 14:50:26 +03:00
Bananymous
46dcf98fc1 Kernel: Scheduler updates tss stack on thread execution 2023-04-25 14:49:50 +03:00
Bananymous
58ce907327 Kernel: Usespace threads now have an interrupt stack 2023-04-25 14:49:18 +03:00
Bananymous
6ecc8cac0e Kernel: TSS stack can be set after initialization 2023-04-25 14:48:12 +03:00
Bananymous
bd95f17426 Kernel: Stack pointer out of bounds now panics with a message 2023-04-25 13:40:55 +03:00
Bananymous
0718bea5a1 LibC: Fix some bugs 2023-04-25 13:27:01 +03:00
Bananymous
175f07cd2f Kernel: Fix bug in Process::create_userspace()
We used to write more than we needed to. This could lead to unwanted
page faults
2023-04-25 13:21:28 +03:00
Bananymous
7b19d6e479 LibC: fread() now does a single syscall 2023-04-25 12:38:08 +03:00
Bananymous
77c83e5552 Kernel: Fix possible dead lock in Process::read() 2023-04-23 14:46:18 +03:00
Bananymous
b15deb420f LibC: Write mostly functioning stdio 2023-04-23 14:32:37 +03:00
Bananymous
b38989d594 Kernel: Add ASSERTion to Process::exit()
This is just so I don't forget that exit can currently only
be called on the executing thread itself
2023-04-22 19:05:27 +03:00
Bananymous
79e6de325f Kernel: Process can now load 32 bit elf files on i386
We are page faulting on process exit but I'm investigating
the reason.
2023-04-22 19:03:30 +03:00
Bananymous
163d2e4ba8 LibELF: Add 32 bit support 2023-04-22 19:00:18 +03:00
Bananymous
4f8f3ddc29 Kernel: Fix 32 bit MMU 2023-04-22 18:22:39 +03:00
Bananymous
82a1a29260 Kernel: More proper paging in Elf loading 2023-04-22 18:17:44 +03:00
Bananymous
8a5608df91 Kernel: d{print,warn,error}ln(...) now has a spinlock 2023-04-22 17:58:51 +03:00
Bananymous
3f1c0ec91b Kernel: Process now frees up its pages on destruction 2023-04-22 16:54:46 +03:00
Bananymous
1406a75a92 Kernel: Cleanup process creation for userspace 2023-04-22 16:51:50 +03:00
Bananymous
8001493df3 Kernel: Fix some deadlocks in the Process 2023-04-22 16:19:57 +03:00
Bananymous
8c1f5bfe1e Kernel: Remove obsolete userspace stuff from kernel 2023-04-22 15:38:45 +03:00
Bananymous
ec8b9640e2 Kernel: Usespace programs are now ran through ELF files
only 64 bit elf files are supported for now.
2023-04-22 15:35:32 +03:00
Bananymous
4ae1332a43 LibELF: Header printing can now be turned off 2023-04-22 15:34:09 +03:00
Bananymous
10c884bba4 LibELF: ELF now has methods for accessing more attributes
You can now access the program headers and the memory itself
2023-04-22 15:31:05 +03:00
Bananymous
c15f031c3f LibC: puts() now just calls syscall(SYS_WRITE, ...) 2023-04-22 15:29:38 +03:00
Bananymous
1b4c744974 LibC: syscalls have now proper argument order 2023-04-22 15:29:15 +03:00
Bananymous
d9068eebb5 Kernel: kmalloc does not dump stack trace
dump_stack_trace() page faults and i dont feel like debugging this
now :)
2023-04-21 13:45:13 +03:00
Bananymous
3ad0d2328d Kernel: Don't call 'cli' on interrupt
This is automatically done because we are using interrupt gates
in the IDT
2023-04-21 11:18:08 +03:00
Bananymous
3f2beb4547 Kernel: Fix syscall return value in 32 bit 2023-04-21 11:08:02 +03:00
Bananymous
be14a6c239 Kernel: Stack pointer is validated when updated
This allows us not to fail stack pointer when in syscall since
interrupts use their own stack
2023-04-21 10:40:24 +03:00
Bananymous
3aa0eeb4a3 Kernel: Add barebones per process virtual addresses
We now assign every (userspace) process its own MMU which we load
in scheduler. This allows every process to have separate virtual
address space.

This is very hackish implementations but it works for now
2023-04-20 00:45:41 +03:00
Bananymous
b3eeb6412f Kernel: Add some bareboness functionality to map virtual addresses 2023-04-19 23:51:36 +03:00
Bananymous
d38470c8e2 Kernel: Rename MMU::{un,}allocate... to MMU::{un,}map
This is more appropriate name for the behaviour :D
2023-04-19 21:50:30 +03:00
Bananymous
a159c980ee Kernel: kmalloc will always print debug on failed alloc 2023-04-19 18:05:01 +03:00
Bananymous
a993d997ad LibELF: remove unused file 2023-04-19 17:32:12 +03:00
Bananymous
4475e3e184 Kernel: ATAController will fail to initialize in native mode 2023-04-19 17:29:36 +03:00
Bananymous
cf0320e47d Kernel: PCI devices now report their prog_if 2023-04-19 16:43:05 +03:00
Bananymous
cd03a95128 Kernel: Shell fix file reading
We were reading 8 bytes at a time instead of the 1024 we were
supposed to :DD
2023-04-19 14:09:23 +03:00
Bananymous
51e299c7e3 Kernel: Shell now has exit() command 2023-04-19 12:53:09 +03:00
Bananymous
6f65453fd4 Kernel: Fix Process::exit() and where it is called from
cksum doesn't seem to work anymore on big files. I have to look
into this. It locks up after couple of seconds...
2023-04-19 12:52:21 +03:00
Bananymous
67e0c21e0f Update README.md
Add cool badges :D
2023-04-19 00:46:42 +03:00
Bananymous
702016a6e3 LibC: exit() calls _fini() 2023-04-19 00:42:00 +03:00
Bananymous
d74ce4950c Kernel: We now launch Shell again on boot
Adding this just before push :D
2023-04-19 00:41:24 +03:00
Bananymous
59a682c720 Kernel: init2 is now launched as a process instead of thread
Also only process can now add threads to scheduler. Nobody should
have raw access to scheduler and everything should be through
Process::current() or irqs (reschedules)
2023-04-19 00:39:06 +03:00
Bananymous
7bd4593748 Kernel: Process is not reference counted any more
This was not necessary and it made things needlessly complicated
2023-04-19 00:34:18 +03:00
Bananymous
c5b006bf19 BAN: Add {TRY,MUST}_REF for references
It is annoying that we have to have separate macros for these but
I can't find a way to cleanly return lvalue reference from statement
expression. Currently we cast the reference to pointer and return
unreference the pointer outside of the expression.

This feature will probably not be used any time soon, but atleas
it is implemented if I need it one day
2023-04-19 00:31:15 +03:00
Bananymous
115c44630d BAN: TRY and MUST macros use rvalue references 2023-04-19 00:11:15 +03:00
Bananymous
1dc81abca4 BAN: Add specialization for ErrorOr<LValueReference>
ErrorOr can now return a reference :)
2023-04-18 22:02:47 +03:00
Bananymous
5aaf2128a8 BAN: Variant with reference now supports copy/assign 2023-04-18 20:21:23 +03:00
Bananymous
6aeac17072 BAN: Variant now works with references
References can be assigned with the set() method. Construction nor
assigment operators cannot be used with references to avoid ambiguity
with what assignment to reference does.

You can set the underlying reference with the set() method and access
it with the get() method.

The references are stored as pointers to the object under the hood
which means that size of a reference is sizeof pointer.
2023-04-18 19:53:34 +03:00
Bananymous
6d425182a2 BAN: Variant::set now copy/move assigns when possible 2023-04-18 19:10:22 +03:00
Bananymous
04ac23b67c BAN: Variant now has variadic template types 2023-04-18 18:29:48 +03:00
Bananymous
5494e2c125 Kernel: Heap allows us to take free pages.
The API is kinda weird and will probably be reworked in near future
but this will work for now :)
2023-04-18 10:18:15 +03:00
Bananymous
aba82564f5 Kernel: Panic wont print stacktrace if it has already paniced
This prevents stack trace dump to panic and loop
2023-04-18 10:18:15 +03:00
Bananymous
93abee9c7c Kernel: Map all ACPI tables on initialization
This makes their usage much easier and less error prone

They won't mapped be processes when we get to there, so this won't be
a problem
2023-04-18 10:18:15 +03:00
Bananymous
4034bef42e Scripts: remove disk build from install-usb.sh
You can still use the install-usb.sh script from the build directory.
2023-04-18 10:18:15 +03:00
Bananymous
821d065eba Kernel: Handle some Heap edge cases 2023-04-18 10:18:15 +03:00
Bananymous
2614437ba0 Kernel: Reorder boot initialization
We now create the TTY as soon as possible so we can show console
output without serial port.
2023-04-18 10:18:15 +03:00
Bananymous
1aac3a0425 Kernel: Heap implementation can now give free pages from all of RAM 2023-04-18 10:18:15 +03:00
Bananymous
a4568f9263 Kernel: Remove unused file 2023-04-18 10:18:15 +03:00
Bananymous
a180e72b6f Kernel: Start working on heap 2023-04-18 10:18:15 +03:00
Bananymous
2de64b592d Kernel: Kmalloc now has its memory statically allocated
We don't use the memory map given by bootloader since this automatically
maps the memory to a available space.
2023-04-18 10:18:15 +03:00
Bananymous
9c0f3dd996 Kernel: Move kmalloc and MMU to Memory directory 2023-04-18 10:18:15 +03:00
Bananymous
079df39ca8 LibELF: Start implementing elf library 2023-04-18 10:18:15 +03:00
Bananymous
60a99d1d23 Create LICENCE 2023-04-13 00:38:24 +03:00
Bananymous
fe87c08a02 LibC: add needed stubs to build executables with our compiler 2023-04-12 17:53:02 +03:00
Bananymous
8637959289 Kernel: We can create basic userspace processes
These are still allocated on the kernel memory
2023-04-12 17:52:36 +03:00
Bananymous
6be53668b9 Kernel: Scheduler can now terminate processes threads 2023-04-12 17:49:04 +03:00
Bananymous
d1b7249803 Kernel: Debug::dump_stack_trace now 'detects' repeating function 2023-04-12 01:32:41 +03:00
Bananymous
ff7c50c627 Kernel: kmalloc does not check for corruptiong unless it cannot allocate
We 'don't care' if kmalloc is corrupted unless it prevents us from
allocating memory. Scheduler should be catching stack overflows either
way and is much more efficient
2023-04-12 00:24:02 +03:00
Bananymous
12779cdef8 Kernel: Threads now use only 4 KiB stack :) 2023-04-12 00:22:08 +03:00
Bananymous
f5e676b2b7 Kernel: Fix TTY spinlock usage 2023-04-12 00:20:04 +03:00
Bananymous
8e5e5f819f Kernel: Shell TTY_PRINT is now function instead of macro
This makes functions uses way less stack
2023-04-12 00:18:58 +03:00
Bananymous
370a958379 BuildSystem: GCC will now complain on functions with 1 KiB stack 2023-04-12 00:18:06 +03:00
Bananymous
0ee7da92a3 BAN: Variant now aligns its data properly 2023-04-12 00:17:45 +03:00
Bananymous
a0bd3dc54f Kernel: kmalloc now detects corruption
Kmalloc checks if its nodes have corrupted. This was happening
because of stack overflow.
2023-04-11 23:36:46 +03:00
Bananymous
809eb2fe3e Kernel: Mark Scheduler::start() as noreturn as appropriate 2023-04-11 23:33:20 +03:00
Bananymous
7010d8614f Kernel: kernel doesn't allocate large blocks of data on stack
We used to allocate 1 KiB blocks on multiple places on stack. This
is a problem, since kernel stack shouldn't have to be too big
2023-04-11 23:31:58 +03:00
Bananymous
69f13f1896 Kernel: Scheduler will panic if it encounters stack overflow 2023-04-11 23:29:21 +03:00
Bananymous
bdaf7cddcb Kernel: Process now locks the mutex while modifying open files
This allows processes to be actually removed from the list instead
of dead locking
2023-04-11 23:28:16 +03:00
Bananymous
8d6db168d6 Kernel: remove message from BAN::Error
We don't store the error message anymore in BAN::Error.
Instead we store a error code that can be mapped into a string.
This allows BAN::Error to only take 4 bytes instead of 128.

We should also make some kernel initialization just panic instead
of returning errors since they are required for succesfull boot
anyway.
2023-04-11 23:25:21 +03:00
Bananymous
2fabe1949c BAN: Move RefPtr to its own file and create New.h
New.h contains definitions for placement new operators and
BAN::allocator and BAN::dealloctor
2023-04-10 21:07:25 +03:00
Bananymous
c660df14ec BuildSystem: Fix header copying to sysroot
We used to copy all headers everytime to sysroot which caused
rebuild of the whole os. Now we use the cmake command
'copy_directory_if_different' which seemed to fix this issue :)
2023-04-10 21:07:25 +03:00
Bananymous
e704968f96 Kernel: Threads can now be terminated mid execution 2023-04-10 21:07:25 +03:00
Bananymous
32359df939 Kernel: Add small wait in ATA driver before reading/writing
This allows bochs to boot again
2023-04-10 21:07:25 +03:00
Bananymous
641ed23380 Kernel: Fix framepointers on started threads 2023-04-10 21:07:25 +03:00
Bananymous
9f977488fa BuildSystem: cmake can now build out libc
I can't seem to get libc++ build to work...
2023-04-10 21:07:25 +03:00
Bananymous
ac0b22f9b9 LibC: remove old unused files 2023-04-07 02:26:44 +03:00
Bananymous
7752b02fb7 BuildSystem: remove now obsolete include directories from kernel build 2023-04-07 02:25:47 +03:00
Bananymous
7610670287 Add a screenshot to README 2023-04-06 21:01:27 +03:00
Bananymous
31a1b23fb7 General: Write basic README 2023-04-06 20:59:45 +03:00
Bananymous
91c8f9a596 Scripts: linecount does not count lines from toolchain/ 2023-04-06 20:31:10 +03:00
Bananymous
f70cd3ea77 BuildSystem: Cleanup cmake code
The buildsystem is kind of a mess. I will be writingn build
instructions soon.
2023-04-06 20:31:10 +03:00
Bananymous
5db5ff069a BuildSystem: you can now build the toolchain with cmake 2023-04-06 00:23:02 +03:00
Bananymous
b8d852ddb7 Update .gitignore 2023-04-06 00:02:47 +03:00
Bananymous
46eedbd1a4 BuildSystem: Create script for os specific toolchain 2023-04-06 00:02:13 +03:00
Bananymous
e760bafeeb LibC: add stubs for a lot of functions 2023-04-05 23:58:40 +03:00
Bananymous
12351d5cb6 LibC: sys/types uses 'typedef' instead of 'using' 2023-04-05 15:03:24 +03:00
Bananymous
e84f613c4d Kernel: Shell now somewhat functions again
I will not be fixing the shell implementation until I get to
userspace
2023-04-05 11:37:41 +03:00
Bananymous
5db4e5b4d5 Kernel: Fix TTY echo and canonical flag behaviour 2023-04-05 11:35:19 +03:00
Bananymous
b00dd8d68d Kernel: Fix ansi control sequence cursor movement 2023-04-05 03:07:52 +03:00
Bananymous
abbbf7ec15 Kernel: Add tty to process and make termios modifiable 2023-04-05 02:53:28 +03:00
Bananymous
22c72d8c70 LibC: Add errno ENOTTY 2023-04-05 02:47:37 +03:00
Bananymous
d0b1457f30 Kernel: TTY now supports clearing 2023-04-05 02:04:18 +03:00
Bananymous
a423cd8bb3 Kernel: Add partial support for shell
We don't handle arrow keys, and the tty does not know how to clear
the screeen :D
2023-04-05 01:30:58 +03:00
Bananymous
db076058b9 Kernel: Process can now initialize stdio
This allows the use of the fds STD{IN,OUT,ERR}_FILENO
2023-04-05 01:10:25 +03:00
Bananymous
fe10ea85db LibC: Add unistd.h with STD{IN,OUT,ERR}_FILENO definitions 2023-04-05 00:59:48 +03:00
Bananymous
a1100624bf Kernel: Start work on making tty a file
TTY is now a file that you can read from/write to. I still have
to port shell to use this new interface
2023-04-05 00:56:09 +03:00
Bananymous
28e1497f88 Kernel: add virtual write function to inode 2023-04-03 20:29:07 +03:00
Bananymous
8d6111641e Kernel: Fix keys in PS2Keymap 2023-04-03 20:25:23 +03:00
Bananymous
3ee20d1a84 Kernel: Fix typo 2023-04-03 19:56:55 +03:00
Bananymous
002c2d0aca BuildSystem: remove non-existent file from kernel CMakeLists.txt 2023-04-03 19:02:25 +03:00
Bananymous
de9f109f2a BAN: Add data() member function to Array 2023-04-03 17:00:52 +03:00
Bananymous
461a5774f8 Kernel: Device dev and rdev number is done more properly
Also hd* partitions are now 1 indexed instead of 0
2023-04-03 11:43:16 +03:00
Bananymous
914f718767 LibC: add device macros in sys/sysmacros.h 2023-04-03 10:59:15 +03:00
Bananymous
ebfd092075 Kernel: Cleaner partition parsing errors 2023-04-03 09:55:49 +03:00
Bananymous
e322826347 Kernel: Optimize scheduler idling
Now after each interrupt we will ask the scheduler to reschedule
if the current thread is the idle thread. This allows semaphore
unblocking to be practically instant when there is only one thread
executing.

Now disk reading is back to ~3 MB/s for single threaded process
2023-04-03 01:51:05 +03:00
Bananymous
3998c5f955 Kernel: ATA now uses irqs instead of polling
Reading is now much slower at ~500 kB/s it was around 3 MB/s.
This is probably mostly due semaphore blocking taking atleast
until next reschedule (1 ms itervals). This will be a problem
as long as we are using only single processor.

I could try to use {READ/WRITE}_MULTIPLE commands, but since
most of the disk reads are 2 sectors (inode block size) this
will at most double the speed.

Most efficient speed up would of course be caching disk access
data and inodes overall.
2023-04-03 00:03:38 +03:00
Bananymous
762d22ed28 Kernel: Move ATADevice to its own file from ATAController
The API is kinda weird since device reads/writes go from
ATADevice -> ATAController -> ATADevice
but this is for now atleast necessary since ATAController has(?)
to keep all devices from using the disks at the same time
2023-04-02 18:26:19 +03:00
Bananymous
f2362b2b78 Kernel: ATA controller waits now before read/write until disk is ready
Qemu used to freeze on disk writes but now it seems fine
2023-04-02 05:37:17 +03:00
Bananymous
471ac80420 BuildSystem: building for 32-bit works now 2023-04-02 05:03:17 +03:00
Bananymous
4a887fc706 Scipts: linecount doesn't count lines in build/ 2023-04-02 04:09:54 +03:00
Bananymous
e49d3c7bfe BuildSystem: We are now using cmake instead of plain make
I have been annoyed for a while since I had to build everything
when running the os since the buildsystem was really bad.

I wanted to rewrite the whole build system and changed to using cmake

:)
2023-04-02 04:07:27 +03:00
Bananymous
c5b83074ac LibC: Combine string.h functions definitions to single file 2023-04-02 00:00:29 +03:00
Bananymous
79090c2648 Kernel: cleanup includes
I went quickly went through all files since I found some weird
includes :D
2023-04-01 02:14:49 +03:00
Bananymous
7a6b1c8e47 Kernel: Fix traversing back from mount points 2023-04-01 01:54:35 +03:00
Bananymous
8988ce2766 Kernel: Add inodes '.' and '..' to device manager 2023-04-01 01:33:04 +03:00
Bananymous
dcde2ae6b4 Kernel: Reads return 0 bytes read at the end of device
We used to not have any idea if we where already at the end of device.
Also fixed couple of copy-paste errors from read->write
2023-04-01 00:55:07 +03:00
Bananymous
c62849a783 Kernel: Shell cleanup cat command 2023-04-01 00:54:39 +03:00
Bananymous
f453e8e170 Kernel: Shell 'time' prints the time even if command fails 2023-04-01 00:30:33 +03:00
Bananymous
990887891e Kernel: Process gets absolute paths for mount 2023-04-01 00:30:11 +03:00
Bananymous
5da801d12b Kernel: fix ext2 failed creation memory leak 2023-04-01 00:22:03 +03:00
Bananymous
3a4557d417 Kernel: Cleanup ATA device initialization 2023-03-31 00:58:57 +03:00
Bananymous
26d9a3e253 Kernel: Move DeviceManager to its own file 2023-03-30 22:39:45 +03:00
Bananymous
eef3631a5a Kernel: Improve locking in Process, VFS and ATAController
We used to block on all process access. This meant that shell
reading the keyboard input would block all VFS access making disk
accesses practically impossible. We now block only when it is
necessary :)
2023-03-30 22:02:16 +03:00
Bananymous
88ee35165f Kernel: Thread is no longer RefCounted
This makes developement with Scheduler much easier against compiler
optimizations. I could now remove the pragma GCC optimize stuff.
2023-03-30 19:16:51 +03:00
Bananymous
c8f05b4a7a Kernel: Add Semaphore to block threads 2023-03-30 18:46:33 +03:00
Bananymous
c32584cca0 BAN: LinkedList::remove now returns iterator to the element after 2023-03-30 18:46:19 +03:00
Bananymous
2995a36942 Kernel: root partition is now passed from the commandline 2023-03-30 18:46:19 +03:00
Bananymous
c1dbafc101 BAN: StringView::split(char, bool) is now thread safe
I had to duplicate some code, but this is better since I would like
to not use BAN::Function for something like this
2023-03-30 16:35:38 +03:00
Bananymous
3e8ab8271d Kernel: Shell can now mount partitions 2023-03-30 15:06:41 +03:00
Bananymous
5b3a00c64f Kernel: Inode::Mode is now a struct so we can have functions in it 2023-03-30 14:41:15 +03:00
Bananymous
0ce9fd8597 Kernel: StorageDevices and Controllers are now devices 2023-03-30 14:22:15 +03:00
Bananymous
c9badb5a1c Kernel: Add IFBLK, IFLNK, IFSOCK to Inode::Mode 2023-03-30 13:15:46 +03:00
Bananymous
a513bc5749 Kernel: All devices have atime, mtime, ctime at their creation 2023-03-30 13:15:46 +03:00
Bananymous
5d5487315f Kernel: Remove the mount test from VFS 2023-03-30 11:43:24 +03:00
Bananymous
3508df67b1 Kernel: fix stat command and device numbers 2023-03-30 10:43:08 +03:00
Bananymous
06ce1f0667 Kernel: Rewrite mounting code 2023-03-29 21:34:48 +03:00
Bananymous
f9c3ae7090 BAN: String add front() and back() helpers 2023-03-29 14:10:29 +03:00
Bananymous
1fb8c211f0 Kernel: Move Partition out of StorageDevice and rename functions 2023-03-29 13:23:01 +03:00
Bananymous
9c7670847e Kernel: Shell commands 'ls' and 'stat' recognize character devices 2023-03-29 11:56:33 +03:00
Bananymous
a24c2d9be2 Kernel: DeviceManager is now a 'FileSystem' so it can expose devices
Shell reads keyboard through /dev/input :)
2023-03-29 11:50:46 +03:00
Bananymous
f4db246658 LibC: add ENOBUFS errno 2023-03-29 10:58:25 +03:00
Bananymous
7f90079ea7 Kernel: Fix keymap numlock behaviour 2023-03-29 03:18:22 +03:00
Bananymous
f4b4987d43 Kernel: Remove obsolete KeyboardLayout/
This was used by the old input system. Currently keyboard layout is
hardcoded to finnish. But it will be reworked as something read from
the filesystem
2023-03-29 03:09:14 +03:00
Bananymous
7f88ba70d4 Kernel: Add linecount.sh script
This calculates the number of lines of code in the whole project :D
2023-03-29 03:06:57 +03:00
Bananymous
ac094a48d6 Kernel: Rework the whole input system
We now use Device abstraction that will allow us to provide devices
to userspace through /dev. Currently Shell reads from first and only
device (it being PS/2 Keyboard).
2023-03-29 03:05:16 +03:00
Bananymous
779912d8af BAN: Vector now takes optional argument for default value on resize 2023-03-28 23:10:36 +03:00
Bananymous
f205b8e883 BAN: Implement basic Circular Queue 2023-03-28 21:44:02 +03:00
Bananymous
f9a0412e78 Kernel: ACPI unmap_header does not do anything
We have to work with MMU mapping/unmapping to be user friendly
2023-03-28 02:56:44 +03:00
Bananymous
0ef318633c BAN: ScopeGuard can now be disabled (it wont call the function) 2023-03-28 01:15:13 +03:00
Bananymous
2f8c9746e3 Kernel: Move ACPI to its own file 2023-03-27 17:30:45 +03:00
Bananymous
6d6bef1b04 BAN: move placement new declaration to Move.h 2023-03-27 03:38:06 +03:00
Bananymous
3dab392296 Build System: Create base directory for the FS 2023-03-27 01:11:17 +03:00
Bananymous
f8a2c60c8d Kernel/BAN: move unix time conversion to BAN and add stat to Shell 2023-03-27 00:49:58 +03:00
Bananymous
770f7716a0 Kernel: Rework processes and VFS so we don't expose inodes
Everything is now done through a file descriptor.
2023-03-26 04:30:57 +03:00
Bananymous
a011c0384f LibC: add dirent.h 2023-03-25 02:08:33 +02:00
Bananymous
0d356c5bbc LibC: Add stat structure 2023-03-24 18:08:22 +02:00
Bananymous
d67de70126 Kernel: Process::working_directory() is now thread safe
I realized you cannot return a stirng view and it to be thread safe
2023-03-24 01:46:25 +02:00
Bananymous
6f334756c5 Kernel: Create RecursiveSpinLock and add it to Process
We now lock every function within Proccess, just to be sure.
Recursive lock allows us to use lock from the same thread even if
we already have the spinlock locked
2023-03-24 01:32:35 +02:00
Bananymous
310713d203 Kernel: Lock process functions instead of the ata controller
Process has to use locks at least on some functions so multithreaded
disk io is safe. This seemed to fix corrupted reads from disk
2023-03-24 01:17:39 +02:00
Bananymous
7d2ab53baa Kernel: Ext2FS now does allocations better
We only have to allocate at the beginning of the all functions and
can properly exit before any disk reads if we run out of memory.

This makes development little bit 'harder' since the {read,write}_block
user must allocate a buffer of atleast block_size bytes.

I also made disk access to cause kernel panic on error since the error
handling during file write is something I don't want to think now.
The filesystem can easily corrupt so, I feel like when disk io starts
to fail I'll come back to this.
2023-03-23 23:22:31 +02:00
Bananymous
2152b8b95f Kernel: Add possibiliity to create empty files on Ext2
Big rewrite for Ext2 for more easy and optimized code
2023-03-23 22:26:06 +02:00
Bananymous
8ac1ae1574 LibC: add more typedefs to sys/types 2023-03-23 19:24:12 +02:00
Bananymous
4fd21bc303 Kernel: Remove block group descriptor cache from ext2fs
This will make improving the fs easier since we need to only update
the values on the disk
2023-03-23 18:52:58 +02:00
Bananymous
15037bfc7a Kernel: Move get_unix_time to RTC namespace 2023-03-23 18:14:51 +02:00
Bananymous
5831c72aad LibC: add errno NAMETOOLONG 2023-03-23 14:48:42 +02:00
Bananymous
a063d041c9 BAN: char* is now formatted as string and not pointer 2023-03-23 14:29:35 +02:00
Bananymous
3572e9794a BAN: Modify Span constructors to keep constness correctly 2023-03-23 14:26:03 +02:00
Bananymous
cef6999dc7 BAN: Add is_const to traits 2023-03-23 13:28:57 +02:00
Bananymous
6ed9651176 Kernel: StorageDevice and Ext2 "support" writing 2023-03-23 13:04:13 +02:00
Bananymous
3efbe22a1b Kernel: Shell now prints unix time with 'date' command 2023-03-23 11:13:51 +02:00
Bananymous
96579b88cf Kernel: Cleanup GPT parsing code 2023-03-23 11:13:14 +02:00
Bananymous
2ec18855f2 Kernel: TTY buffer is resized on font size change
Shell also has better line wrapping. You still can't visually go
back to previous line, but atleas we now write from the beginning
of the line
2023-03-22 02:09:22 +02:00
Bananymous
b222581d18 Kernel: Reading from fd verifies that file is opened for reading 2023-03-22 01:55:58 +02:00
Bananymous
a8e3ee6f19 Kernel: Ext2 directory functions now fail on invalid blocks
Invalid blocks should only happen while writing to a file and
I think in that case we should just bail out instead of giving
you incomlete inode list or search result.
2023-03-22 01:55:57 +02:00
Bananymous
a083e588ba Kernel: cksum uses now a different crc32_table to match linux 'cksum' 2023-03-22 01:55:21 +02:00
Bananymous
9b500842a0 Kernel: Ext2 can now read from non-block-size aligned offsets 2023-03-21 19:19:17 +02:00
Bananymous
b21348379f Kernel: Remove obsolete Ext2FS::ext2_root_inode()
This was not used by anyone and the cast was wrong anyway
2023-03-21 18:19:48 +02:00
Bananymous
633055293e Kernel: Remove for_each_block from Ext2 2023-03-21 18:14:02 +02:00
Bananymous
ae9d618803 Kernel: Cleanup font parsing
We use now the LittleEndian<> wrapper for PSF2 header and no more
magic constants in code
2023-03-20 19:48:08 +02:00
Bananymous
9c744dfc44 BAN: Add wrappers for little/big endian numbers 2023-03-20 19:48:08 +02:00
Bananymous
faf1b661bb Kernel: prefs font does not allocate extra buffer 2023-03-20 19:48:01 +02:00
Bananymous
22e45278a2 Kernel: Fix PC Screen font parsing
I had misread the format and the parsing code was incorrect. I also
changed fonts to store unicode codepoints as 32 bit integers, so
every character can be represented
2023-03-20 14:52:42 +02:00
Bananymous
43f4657566 Kernel: Font parsing uses Spans now 2023-03-20 13:35:54 +02:00
Bananymous
70f2908056 BAN: Implement basic Span
This is wrapper over contiguous block of memory e.g. Vector
2023-03-20 13:34:26 +02:00
Bananymous
ef381d0600 BAN: Add iterators to all containers with contiguous memory 2023-03-20 13:26:42 +02:00
Bananymous
cfa87526a7 BAN: Add implementation for basic iterator for contiguous memory 2023-03-20 13:15:38 +02:00
Bananymous
39b560fde3 Kernel: Add basic mounting to VFS. 2023-03-19 05:51:25 +02:00
Bananymous
0c582b4490 LibC: add errno ENOTEMPTY 2023-03-19 05:43:40 +02:00
Bananymous
61caf566fc LibC: add errno EEXISTS 2023-03-19 04:17:39 +02:00
Bananymous
76d5364a55 Kernel: Add comparison operator for inodes 2023-03-19 03:34:23 +02:00
Bananymous
5224df321e Create README.md 2023-03-18 04:05:59 +02:00
1370 changed files with 178100 additions and 123262 deletions

13
.clangd
View File

@@ -1,13 +0,0 @@
Diagnostics:
Suppress: target_unsupported_type
CompileFlags:
Remove: [
-fstrict-volatile-bitfields,
-fno-tree-loop-distribute-patterns
]
Add: [
-D__banan_os__,
-D__arch__=x86_64,
-D__x86_64__
]

6
.gitignore vendored
View File

@@ -2,5 +2,7 @@
.idea/ .idea/
build/ build/
base/ base/
script/fakeroot-context *.tar.*
tools/update-image-perms toolchain/*/
!base-sysroot.tar.gz

4
.gitmodules vendored
View File

@@ -0,0 +1,4 @@
[submodule "kernel/lai"]
path = kernel/lai
url = https://github.com/managarm/lai.git
ignore = untracked

View File

@@ -1,8 +0,0 @@
# .pre-commit-config.yaml
exclude: '.patch$'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0 # this is optional, use `pre-commit autoupdate` to get the latest rev!
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace

View File

@@ -1,38 +0,0 @@
{
"configurations": [
{
"name": "banan-os",
"includePath": [
"${workspaceFolder}/BAN/include",
"${workspaceFolder}/kernel/include",
"${workspaceFolder}/userspace/libraries/*/include"
],
"defines": [
"__arch=x86_64",
"__enable_sse=1"
],
"compilerPath": "${workspaceFolder}/toolchain/local/bin/x86_64-banan_os-gcc",
"cStandard": "c17",
"cppStandard": "gnu++20",
"intelliSenseMode": "linux-gcc-x64"
},
{
"name": "banan-os-kernel",
"includePath": [
"${workspaceFolder}/BAN/include",
"${workspaceFolder}/kernel/include",
"${workspaceFolder}/userspace/libraries/*/include"
],
"defines": [
"__arch=x86_64",
"__is_kernel",
"__enable_sse=1"
],
"compilerPath": "${workspaceFolder}/toolchain/local/bin/x86_64-banan_os-gcc",
"cStandard": "c17",
"cppStandard": "gnu++20",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}

10
.vscode/settings.json vendored
View File

@@ -1,10 +0,0 @@
{
"cmake.configureOnOpen": false,
"editor.tabSize": 4,
"editor.insertSpaces": false,
"editor.detectIndentation": false,
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/build",
"-header-insertion=never"
]
}

3
BAN/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.a
*.d
*.o

View File

@@ -1,22 +0,0 @@
#include <BAN/Assert.h>
#if __is_kernel
#include <kernel/Panic.h>
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
{
Kernel::panic_impl(location, msg);
}
#else
#include <BAN/Debug.h>
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
{
derrorln("{}: {}", location, msg);
__builtin_trap();
}
#endif

262
BAN/BAN/String.cpp Normal file
View File

@@ -0,0 +1,262 @@
#include <BAN/Errors.h>
#include <BAN/Math.h>
#include <BAN/Move.h>
#include <BAN/New.h>
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <string.h>
namespace BAN
{
String::String()
{
MUST(copy_impl(""sv));
}
String::String(const String& other)
{
MUST(copy_impl(other.sv()));
}
String::String(String&& other)
{
move_impl(move(other));
}
String::String(StringView other)
{
MUST(copy_impl(other));
}
String::~String()
{
BAN::deallocator(m_data);
}
String& String::operator=(const String& other)
{
MUST(copy_impl(other.sv()));
return *this;
}
String& String::operator=(String&& other)
{
BAN::deallocator(m_data);
move_impl(move(other));
return *this;
}
String& String::operator=(StringView other)
{
MUST(copy_impl(other));
return *this;
}
ErrorOr<void> String::push_back(char ch)
{
TRY(ensure_capacity(m_size + 2));
m_data[m_size] = ch;
m_size++;
m_data[m_size] = '\0';
return {};
}
ErrorOr<void> String::insert(char ch, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1 + 1));
memmove(m_data + index + 1, m_data + index, m_size - index);
m_data[index] = ch;
m_size += 1;
m_data[m_size] = '\0';
return {};
}
ErrorOr<void> String::insert(StringView other, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + other.size() + 1));
memmove(m_data + index + other.size(), m_data + index, m_size - index);
memcpy(m_data + index, other.data(), other.size());
m_size += other.size();
m_data[m_size] = '\0';
return {};
}
ErrorOr<void> String::append(StringView other)
{
TRY(ensure_capacity(m_size + other.size() + 1));
memcpy(m_data + m_size, other.data(), other.size());
m_size += other.size();
m_data[m_size] = '\0';
return {};
}
ErrorOr<void> String::append(const String& string)
{
TRY(append(string.sv()));
return {};
}
void String::pop_back()
{
ASSERT(m_size > 0);
m_size--;
m_data[m_size] = '\0';
}
void String::remove(size_type index)
{
erase(index, 1);
}
void String::erase(size_type index, size_type count)
{
ASSERT(index + count <= m_size);
memmove(m_data + index, m_data + index + count, m_size - index - count);
m_size -= count;
m_data[m_size] = '\0';
}
void String::clear()
{
m_size = 0;
m_data[0] = '\0';
}
char String::operator[](size_type index) const
{
ASSERT(index < m_size);
return m_data[index];
}
char& String::operator[](size_type index)
{
ASSERT(index < m_size);
return m_data[index];
}
bool String::operator==(const String& other) const
{
if (m_size != other.m_size)
return false;
return memcmp(m_data, other.m_data, m_size) == 0;
}
bool String::operator==(StringView other) const
{
if (m_size != other.size())
return false;
return memcmp(m_data, other.data(), m_size) == 0;
}
bool String::operator==(const char* other) const
{
for (size_type i = 0; i <= m_size; i++)
if (m_data[i] != other[i])
return false;
return true;
}
ErrorOr<void> String::resize(size_type size, char ch)
{
if (size < m_size)
{
m_data[size] = '\0';
m_size = size;
}
else if (size > m_size)
{
TRY(ensure_capacity(size + 1));
for (size_type i = m_size; i < size; i++)
m_data[i] = ch;
m_data[size] = '\0';
m_size = size;
}
m_size = size;
return {};
}
ErrorOr<void> String::reserve(size_type size)
{
TRY(ensure_capacity(size));
return {};
}
ErrorOr<void> String::shrink_to_fit()
{
size_type temp = m_capacity;
m_capacity = 0;
auto error_or = ensure_capacity(m_size);
if (error_or.is_error())
{
m_capacity = temp;
return error_or;
}
return {};
}
StringView String::sv() const
{
return StringView(*this);
}
bool String::empty() const
{
return m_size == 0;
}
String::size_type String::size() const
{
return m_size;
}
String::size_type String::capacity() const
{
return m_capacity;
}
const char* String::data() const
{
return m_data;
}
ErrorOr<void> String::ensure_capacity(size_type size)
{
if (m_capacity >= size)
return {};
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
void* new_data = BAN::allocator(new_cap);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
if (m_data)
memcpy(new_data, m_data, m_size + 1);
BAN::deallocator(m_data);
m_data = (char*)new_data;
m_capacity = new_cap;
return {};
}
ErrorOr<void> String::copy_impl(StringView other)
{
TRY(ensure_capacity(other.size() + 1));
memcpy(m_data, other.data(), other.size());
m_size = other.size();
m_data[m_size] = '\0';
return {};
}
void String::move_impl(String&& other)
{
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
}

View File

@@ -1,11 +1,180 @@
#include <BAN/String.h> #include <BAN/String.h>
#include <BAN/StringView.h> #include <BAN/StringView.h>
#include <BAN/Vector.h>
#include <string.h>
namespace BAN namespace BAN
{ {
StringView::StringView()
{ }
StringView::StringView(const String& other) StringView::StringView(const String& other)
: StringView(other.data(), other.size()) : StringView(other.data(), other.size())
{ } { }
StringView::StringView(const char* string, size_type len)
{
if (len == size_type(-1))
len = strlen(string);
m_data = string;
m_size = len;
}
char StringView::operator[](size_type index) const
{
ASSERT(index < m_size);
return m_data[index];
}
bool StringView::operator==(const String& other) const
{
if (m_size != other.size())
return false;
return memcmp(m_data, other.data(), m_size) == 0;
}
bool StringView::operator==(StringView other) const
{
if (m_size != other.m_size)
return false;
return memcmp(m_data, other.m_data, m_size) == 0;
}
bool StringView::operator==(const char* other) const
{
if (memcmp(m_data, other, m_size))
return false;
return other[m_size] == '\0';
}
StringView StringView::substring(size_type index, size_type len) const
{
ASSERT(index <= m_size);
if (len == size_type(-1))
len = m_size - index;
ASSERT(len <= m_size - index); // weird order to avoid overflow
StringView result;
result.m_data = m_data + index;
result.m_size = len;
return result;
}
ErrorOr<Vector<StringView>> StringView::split(char delim, bool allow_empties)
{
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result;
TRY(result.reserve(count));
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start != m_size)
TRY(result.push_back(this->substring(start)));
return result;
}
ErrorOr<Vector<StringView>> StringView::split(bool(*comp)(char), bool allow_empties)
{
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result;
TRY(result.reserve(count));
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start != m_size)
TRY(result.push_back(this->substring(start)));
return result;
}
char StringView::back() const
{
ASSERT(m_size > 0);
return m_data[m_size - 1];
}
char StringView::front() const
{
ASSERT(m_size > 0);
return m_data[0];
}
bool StringView::contains(char ch) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
return true;
return false;
}
StringView::size_type StringView::count(char ch) const
{
size_type result = 0;
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
result++;
return result;
}
bool StringView::empty() const
{
return m_size == 0;
}
StringView::size_type StringView::size() const
{
return m_size;
}
const char* StringView::data() const
{
return m_data;
}
} }

View File

@@ -1,18 +1,26 @@
cmake_minimum_required(VERSION 3.26)
project(BAN CXX)
set(BAN_SOURCES set(BAN_SOURCES
BAN/Assert.cpp
BAN/New.cpp BAN/New.cpp
BAN/String.cpp
BAN/StringView.cpp BAN/StringView.cpp
BAN/Time.cpp BAN/Time.cpp
) )
add_custom_target(ban-headers
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
USES_TERMINAL
)
add_library(ban ${BAN_SOURCES}) add_library(ban ${BAN_SOURCES})
target_link_options(ban PRIVATE -nolibc) add_dependencies(ban headers libc-install)
banan_link_library(ban libc)
set_target_properties(ban PROPERTIES OUTPUT_NAME libban) add_custom_target(ban-install
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/libban.a ${BANAN_LIB}/
# set SONAME as cmake doesn't set it for some reason?? DEPENDS ban
set_target_properties(ban PROPERTIES LINK_FLAGS "-Wl,-soname,libban.so") BYPRODUCTS ${BANAN_LIB}/libban.a
USES_TERMINAL
banan_install_headers(ban) )
install(TARGETS ban OPTIONAL)

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <BAN/Errors.h>
#include <BAN/Iterators.h> #include <BAN/Iterators.h>
#include <BAN/Span.h> #include <BAN/Span.h>
@@ -18,78 +19,85 @@ namespace BAN
using const_iterator = ConstIteratorSimple<T, Array>; using const_iterator = ConstIteratorSimple<T, Array>;
public: public:
constexpr Array() = default; Array();
constexpr Array(const T&); Array(const T&);
iterator begin() { return iterator(m_data); } iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + size()); } iterator end() { return iterator(m_data + size()); }
const_iterator begin() const { return const_iterator(m_data); } const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + size()); } const_iterator end() const { return const_iterator(m_data + size()); }
constexpr const T& operator[](size_type) const; const T& operator[](size_type) const;
constexpr T& operator[](size_type); T& operator[](size_type);
constexpr const T& back() const; const T& back() const;
constexpr T& back(); T& back();
constexpr const T& front() const; const T& front() const;
constexpr T& front(); T& front();
Span<T> span() { return Span(m_data, size()); } Span<T> span() { return Span(m_data, size()); }
Span<const T> span() const { return Span(m_data, size()); } const Span<T> span() const { return Span(m_data, size()); }
constexpr size_type size() const; constexpr size_type size() const;
constexpr const T* data() const { return m_data; } const T* data() const { return m_data; }
constexpr T* data() { return m_data; } T* data() { return m_data; }
private: private:
T m_data[S] {}; T m_data[S];
}; };
template<typename T, size_t S> template<typename T, size_t S>
constexpr Array<T, S>::Array(const T& value) Array<T, S>::Array()
{
for (size_type i = 0; i < S; i++)
m_data[i] = T();
}
template<typename T, size_t S>
Array<T, S>::Array(const T& value)
{ {
for (size_type i = 0; i < S; i++) for (size_type i = 0; i < S; i++)
m_data[i] = value; m_data[i] = value;
} }
template<typename T, size_t S> template<typename T, size_t S>
constexpr const T& Array<T, S>::operator[](size_type index) const const T& Array<T, S>::operator[](size_type index) const
{ {
ASSERT(index < S); ASSERT(index < S);
return m_data[index]; return m_data[index];
} }
template<typename T, size_t S> template<typename T, size_t S>
constexpr T& Array<T, S>::operator[](size_type index) T& Array<T, S>::operator[](size_type index)
{ {
ASSERT(index < S); ASSERT(index < S);
return m_data[index]; return m_data[index];
} }
template<typename T, size_t S> template<typename T, size_t S>
constexpr const T& Array<T, S>::back() const const T& Array<T, S>::back() const
{ {
ASSERT(S != 0); ASSERT(S != 0);
return m_data[S - 1]; return m_data[S - 1];
} }
template<typename T, size_t S> template<typename T, size_t S>
constexpr T& Array<T, S>::back() T& Array<T, S>::back()
{ {
ASSERT(S != 0); ASSERT(S != 0);
return m_data[S - 1]; return m_data[S - 1];
} }
template<typename T, size_t S> template<typename T, size_t S>
constexpr const T& Array<T, S>::front() const const T& Array<T, S>::front() const
{ {
ASSERT(S != 0); ASSERT(S != 0);
return m_data[0]; return m_data[0];
} }
template<typename T, size_t S> template<typename T, size_t S>
constexpr T& Array<T, S>::front() T& Array<T, S>::front()
{ {
ASSERT(S != 0); ASSERT(S != 0);
return m_data[0]; return m_data[0];

View File

@@ -1,14 +1,11 @@
#pragma once #pragma once
#define __ban_assert_stringify_helper(s) #s #if defined(__is_kernel)
#define __ban_assert_stringify(s) __ban_assert_stringify_helper(s) #include <kernel/Panic.h>
#define ASSERT(cond) do { if (!(cond)) Kernel::panic("ASSERT("#cond") failed"); } while (false)
#define ASSERT(cond) \ #define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
(__builtin_expect(!(cond), 0) \ #else
? __ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT(" #cond ") failed") \ #include <assert.h>
: (void)0) #define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
#define ASSERT_NOT_REACHED() do { assert(false && "ASSERT_NOT_REACHED() failed"); __builtin_unreachable(); } while (false)
#define ASSERT_NOT_REACHED() \ #endif
__ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT_NOT_REACHED() reached")
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg);

View File

@@ -1,99 +0,0 @@
#pragma once
#include <BAN/Traits.h>
namespace BAN
{
enum MemoryOrder
{
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
memory_order_acquire = __ATOMIC_ACQUIRE,
memory_order_release = __ATOMIC_RELEASE,
memory_order_acq_rel = __ATOMIC_ACQ_REL,
memory_order_seq_cst = __ATOMIC_SEQ_CST,
};
template<typename T> concept atomic_c = is_integral_v<T> || is_pointer_v<T>;
template<typename T> concept atomic_lockfree_c = (is_integral_v<T> || is_pointer_v<T>) && __atomic_always_lock_free(sizeof(T), 0);
template<atomic_lockfree_c T, atomic_c U>
inline void atomic_store(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { __atomic_store_n(&obj, value, mem_order); }
template<atomic_lockfree_c T>
inline T atomic_load(T& obj, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_load_n(&obj, mem_order); }
template<atomic_lockfree_c T, atomic_c U>
inline T atomic_exchange(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_exchange_n(&obj, value, mem_order); }
template<atomic_lockfree_c T, atomic_lockfree_c U, atomic_c V>
inline bool atomic_compare_exchange(T& obj, U& expected, V value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_compare_exchange_n(&obj, &expected, value, false, mem_order, mem_order); }
#define DECL_ATOMIC_INLINE template<atomic_lockfree_c T, atomic_c U> inline
DECL_ATOMIC_INLINE T atomic_add_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_add_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_sub_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_sub_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_and_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_and_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_xor_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_xor_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_or_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_or_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_nand_fetch(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_nand_fetch(&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_add (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_add (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_sub (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_sub (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_and (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_and (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_xor (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_xor (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_or (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_or (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_nand(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_nand(&obj, value, mem_order); }
#undef DECL_ATOMIC_INLINE
template<atomic_lockfree_c T, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
class Atomic
{
Atomic(const Atomic&) = delete;
Atomic(Atomic&&) = delete;
Atomic& operator=(const Atomic&) volatile = delete;
Atomic& operator=(Atomic&&) volatile = delete;
public:
constexpr Atomic() : m_value(0) {}
constexpr Atomic(T val) : m_value(val) {}
inline T load(MemoryOrder mem_order = MEM_ORDER) const volatile { return atomic_load(m_value, mem_order); }
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { atomic_store(m_value, val, mem_order); }
inline T operator=(T val) volatile { store(val); return val; }
inline operator T() const volatile { return load(); }
inline T operator+=(T val) volatile { return atomic_add_fetch(m_value, val, MEM_ORDER); }
inline T operator-=(T val) volatile { return atomic_sub_fetch(m_value, val, MEM_ORDER); }
inline T operator&=(T val) volatile { return atomic_and_fetch(m_value, val, MEM_ORDER); }
inline T operator^=(T val) volatile { return atomic_xor_fetch(m_value, val, MEM_ORDER); }
inline T operator|=(T val) volatile { return atomic_or_fetch(m_value, val, MEM_ORDER); }
inline T operator--() volatile { return atomic_sub_fetch(m_value, 1, MEM_ORDER); }
inline T operator++() volatile { return atomic_add_fetch(m_value, 1, MEM_ORDER); }
inline T operator--(int) volatile { return atomic_fetch_sub(m_value, 1, MEM_ORDER); }
inline T operator++(int) volatile { return atomic_fetch_add(m_value, 1, MEM_ORDER); }
inline bool compare_exchange(T& expected, T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_compare_exchange(m_value, expected, desired, mem_order); }
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_exchange(m_value, desired, mem_order); };
inline T add_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_add_fetch (m_value, val, mem_order); }
inline T sub_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_sub_fetch (m_value, val, mem_order); }
inline T and_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_and_fetch (m_value, val, mem_order); }
inline T xor_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_xor_fetch (m_value, val, mem_order); }
inline T or_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_or_fetch (m_value, val, mem_order); }
inline T nand_fetch(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_nand_fetch(m_value, val, mem_order); }
inline T fetch_add (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_add (m_value, val, mem_order); }
inline T fetch_sub (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_sub (m_value, val, mem_order); }
inline T fetch_and (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_and (m_value, val, mem_order); }
inline T fetch_xor (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_xor (m_value, val, mem_order); }
inline T fetch_or (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_or (m_value, val, mem_order); }
inline T fetch_nand(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_nand(m_value, val, mem_order); }
private:
T m_value;
};
}

View File

@@ -1,12 +0,0 @@
#pragma once
namespace BAN
{
template<typename To, typename From>
constexpr To bit_cast(const From& from)
{
return __builtin_bit_cast(To, from);
}
}

View File

@@ -1,124 +0,0 @@
#pragma once
#include <BAN/Span.h>
#include <stdint.h>
namespace BAN
{
template<bool CONST>
class ByteSpanGeneral
{
public:
using value_type = maybe_const_t<CONST, uint8_t>;
using size_type = size_t;
public:
ByteSpanGeneral() = default;
ByteSpanGeneral(value_type* data, size_type size)
: m_data(data)
, m_size(size)
{ }
template<bool SRC_CONST>
ByteSpanGeneral(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
: m_data(other.data())
, m_size(other.size())
{ }
template<bool SRC_CONST>
ByteSpanGeneral(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
: m_data(other.data())
, m_size(other.size())
{
other.clear();
}
template<typename T>
ByteSpanGeneral(const Span<T>& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
: m_data(other.data())
, m_size(other.size())
{ }
template<typename T>
ByteSpanGeneral(Span<T>&& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
: m_data(other.data())
, m_size(other.size())
{
other.clear();
}
template<bool SRC_CONST>
ByteSpanGeneral& operator=(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
{
m_data = other.data();
m_size = other.size();
return *this;
}
template<bool SRC_CONST>
ByteSpanGeneral& operator=(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
{
m_data = other.data();
m_size = other.size();
other.clear();
return *this;
}
template<typename S>
static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v<S>)
{
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
}
template<typename S>
S& as() const requires(!CONST || is_const_v<S>)
{
ASSERT(m_data);
ASSERT(m_size >= sizeof(S));
return *reinterpret_cast<S*>(m_data);
}
template<typename S>
Span<S> as_span() const requires(!CONST || is_const_v<S>)
{
ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
}
[[nodiscard]] ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1)) const
{
ASSERT(m_data);
ASSERT(m_size >= offset);
if (length == size_type(-1))
length = m_size - offset;
ASSERT(m_size >= offset + length);
return ByteSpanGeneral(m_data + offset, length);
}
value_type& operator[](size_type offset) const
{
ASSERT(offset < m_size);
return m_data[offset];
}
value_type* data() const { return m_data; }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
void clear()
{
m_data = nullptr;
m_size = 0;
}
private:
value_type* m_data { nullptr };
size_type m_size { 0 };
friend class ByteSpanGeneral<!CONST>;
};
using ByteSpan = ByteSpanGeneral<false>;
using ConstByteSpan = ByteSpanGeneral<true>;
}

View File

@@ -1,8 +1,6 @@
#pragma once #pragma once
#include <BAN/Assert.h> #include <BAN/Assert.h>
#include <BAN/Move.h>
#include <BAN/PlacementNew.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
@@ -24,21 +22,13 @@ namespace BAN
void push(const T&); void push(const T&);
void push(T&&); void push(T&&);
template<typename... Args> template<typename... Args>
void emplace(Args&&... args) requires is_constructible_v<T, Args...>; void emplace(Args&&... args);
void pop(); void pop();
const T& front() const; const T& front() const;
T& front(); T& front();
const T& back() const;
T& back();
const T& operator[](size_t index) const;
T& operator[](size_t index);
void clear();
size_type size() const { return m_size; } size_type size() const { return m_size; }
bool empty() const { return size() == 0; } bool empty() const { return size() == 0; }
bool full() const { return size() == capacity(); } bool full() const { return size() == capacity(); }
@@ -58,7 +48,8 @@ namespace BAN
template<typename T, size_t S> template<typename T, size_t S>
CircularQueue<T, S>::~CircularQueue() CircularQueue<T, S>::~CircularQueue()
{ {
clear(); for (size_type i = 0; i < m_size; i++)
element_at((m_first + i) % capacity())->~T();
} }
template<typename T, size_t S> template<typename T, size_t S>
@@ -75,7 +66,7 @@ namespace BAN
template<typename T, size_t S> template<typename T, size_t S>
template<typename... Args> template<typename... Args>
void CircularQueue<T, S>::emplace(Args&&... args) requires is_constructible_v<T, Args...> void CircularQueue<T, S>::emplace(Args&&... args)
{ {
ASSERT(!full()); ASSERT(!full());
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...); new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
@@ -105,42 +96,6 @@ namespace BAN
return *element_at(m_first); return *element_at(m_first);
} }
template<typename T, size_t S>
const T& CircularQueue<T, S>::back() const
{
ASSERT(!empty());
return *element_at((m_first + m_size - 1) % capacity());
}
template<typename T, size_t S>
T& CircularQueue<T, S>::back()
{
ASSERT(!empty());
return *element_at((m_first + m_size - 1) % capacity());
}
template<typename T, size_t S>
const T& CircularQueue<T, S>::operator[](size_t index) const
{
ASSERT(index < m_size);
return *element_at((m_first + index) % capacity());
}
template<typename T, size_t S>
T& CircularQueue<T, S>::operator[](size_t index)
{
ASSERT(index < m_size);
return *element_at((m_first + index) % capacity());
}
template<typename T, size_t S>
void CircularQueue<T, S>::clear()
{
for (size_type i = 0; i < m_size; i++)
element_at((m_first + i) % capacity())->~T();
m_size = 0;
}
template<typename T, size_t S> template<typename T, size_t S>
const T* CircularQueue<T, S>::element_at(size_type index) const const T* CircularQueue<T, S>::element_at(size_type index) const
{ {

View File

@@ -1,61 +0,0 @@
#pragma once
#if __is_kernel
#include <kernel/Debug.h>
#else
#include <BAN/Formatter.h>
#include <stdio.h>
#define __debug_putchar [](int c) { putc_unlocked(c, stddbg); }
#define dprintln(...) \
do { \
flockfile(stddbg); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar,"\n"); \
fflush(stddbg); \
funlockfile(stddbg); \
} while (false)
#define dwarnln(...) \
do { \
flockfile(stddbg); \
BAN::Formatter::print(__debug_putchar, "\e[33m"); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
fflush(stddbg); \
funlockfile(stddbg); \
} while(false)
#define derrorln(...) \
do { \
flockfile(stddbg); \
BAN::Formatter::print(__debug_putchar, "\e[31m"); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
fflush(stddbg); \
funlockfile(stddbg); \
} while(false)
#define dprintln_if(cond, ...) \
do { \
if constexpr(cond) \
dprintln(__VA_ARGS__); \
} while(false)
#define dwarnln_if(cond, ...) \
do { \
if constexpr(cond) \
dwarnln(__VA_ARGS__); \
} while(false)
#define derrorln_if(cond, ...) \
do { \
if constexpr(cond) \
derrorln(__VA_ARGS__); \
} while(false)
#endif

View File

@@ -45,12 +45,6 @@ namespace BAN
#endif #endif
} }
template<integral T>
constexpr T little_endian_to_host(T value)
{
return host_to_little_endian(value);
}
template<integral T> template<integral T>
constexpr T host_to_big_endian(T value) constexpr T host_to_big_endian(T value)
{ {
@@ -61,28 +55,13 @@ namespace BAN
#endif #endif
} }
template<integral T>
constexpr T big_endian_to_host(T value)
{
return host_to_big_endian(value);
}
template<integral T> template<integral T>
struct LittleEndian struct LittleEndian
{ {
constexpr LittleEndian()
: raw(0)
{ }
constexpr LittleEndian(T value)
: raw(host_to_little_endian(value))
{ }
constexpr operator T() const constexpr operator T() const
{ {
return host_to_little_endian(raw); return host_to_little_endian(raw);
} }
private: private:
T raw; T raw;
}; };
@@ -90,36 +69,12 @@ namespace BAN
template<integral T> template<integral T>
struct BigEndian struct BigEndian
{ {
constexpr BigEndian()
: raw(0)
{ }
constexpr BigEndian(T value)
: raw(host_to_big_endian(value))
{ }
constexpr operator T() const constexpr operator T() const
{ {
return host_to_big_endian(raw); return host_to_big_endian(raw);
} }
private: private:
T raw; T raw;
}; };
template<integral T>
using NetworkEndian = BigEndian<T>;
template<integral T>
constexpr T host_to_network_endian(T value)
{
return host_to_big_endian(value);
}
template<integral T>
constexpr T network_endian_to_host(T value)
{
return big_endian_to_host(value);
}
} }

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <BAN/Formatter.h> #include <BAN/Formatter.h>
#include <BAN/NoCopyMove.h> #include <BAN/StringView.h>
#include <BAN/Variant.h> #include <BAN/Variant.h>
#include <errno.h> #include <errno.h>
@@ -10,16 +10,16 @@
#ifdef __is_kernel #ifdef __is_kernel
#include <kernel/Panic.h> #include <kernel/Panic.h>
#include <kernel/Errors.h> #include <kernel/Errors.h>
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); }) #define MUST(expr) ({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); }) #define MUST_REF(expr) *({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
#else #else
#include <BAN/Debug.h> #include <assert.h>
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } e.release_value(); }) #define MUST(expr) ({ auto&& e = expr; assert(!e.is_error()); e.release_value(); })
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } &e.release_value(); }) #define MUST_REF(expr) *({ auto&& e = expr; assert(!e.is_error()); &e.release_value(); })
#endif #endif
#define TRY(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); e.release_value(); }) #define TRY(expr) ({ auto&& e = expr; if (e.is_error()) return e.release_error(); e.release_value(); })
#define TRY_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); &e.release_value(); }) #define TRY_REF(expr) *({ auto&& e = expr; if (e.is_error()) return e.release_error(); &e.release_value(); })
namespace BAN namespace BAN
{ {
@@ -37,14 +37,7 @@ namespace BAN
{ {
return Error((uint64_t)error | kernel_error_mask); return Error((uint64_t)error | kernel_error_mask);
} }
#else
template<size_t N>
consteval static Error from_literal(const char (&message)[N])
{
return Error(message);
}
#endif #endif
static Error from_errno(int error) static Error from_errno(int error)
{ {
return Error(error); return Error(error);
@@ -62,43 +55,27 @@ namespace BAN
} }
#endif #endif
constexpr uint64_t get_error_code() const { return m_error_code; } uint64_t get_error_code() const { return m_error_code; }
const char* get_message() const BAN::StringView get_message() const
{ {
#ifdef __is_kernel #ifdef __is_kernel
if (m_error_code & kernel_error_mask) if (m_error_code & kernel_error_mask)
return Kernel::error_string(kernel_error()); return Kernel::error_string(kernel_error());
#else
if (m_message)
return m_message;
#endif #endif
if (auto* desc = strerrordesc_np(m_error_code)) return strerror(m_error_code);
return desc;
return "Unknown error";
} }
private: private:
constexpr Error(uint64_t error) Error(uint64_t error)
: m_error_code(error) : m_error_code(error)
{} {}
#ifndef __is_kernel uint64_t m_error_code;
constexpr Error(const char* message)
: m_message(message)
{}
#endif
uint64_t m_error_code { 0 };
#ifndef __is_kernel
const char* m_message { nullptr };
#endif
}; };
template<typename T> template<typename T>
class [[nodiscard]] ErrorOr class [[nodiscard]] ErrorOr
{ {
BAN_NON_COPYABLE(ErrorOr);
public: public:
ErrorOr(const T& value) ErrorOr(const T& value)
: m_data(value) : m_data(value)
@@ -112,14 +89,6 @@ namespace BAN
ErrorOr(Error&& error) ErrorOr(Error&& error)
: m_data(move(error)) : m_data(move(error))
{} {}
ErrorOr(ErrorOr&& other)
: m_data(move(other.m_data))
{}
ErrorOr& operator=(ErrorOr&& other)
{
m_data = move(other.m_data);
return *this;
}
bool is_error() const { return m_data.template has<Error>(); } bool is_error() const { return m_data.template has<Error>(); }
const Error& error() const { return m_data.template get<Error>(); } const Error& error() const { return m_data.template get<Error>(); }

View File

@@ -10,19 +10,22 @@ namespace BAN::Formatter
struct ValueFormat; struct ValueFormat;
template<typename F>
static void print(F putc, const char* format);
template<typename F, typename Arg, typename... Args>
static void print(F putc, const char* format, Arg&& arg, Args&&... args);
template<typename F, typename... Args> template<typename F, typename... Args>
concept PrintableArguments = requires(F putc, Args&&... args, const ValueFormat& format) static void println(F putc, const char* format, Args&&... args);
{
(print_argument(putc, BAN::forward<Args>(args), format), ...);
};
template<typename F, typename T> template<typename F, typename T>
inline void print_argument(F putc, T value, const ValueFormat& format); static void print_argument(F putc, T value, const ValueFormat& format);
namespace detail namespace detail
{ {
template<typename F, typename T> template<typename F, typename T>
inline size_t parse_format_and_print_argument(F putc, const char* format, T&& arg); static size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
} }
/* /*
@@ -36,12 +39,11 @@ namespace BAN::Formatter
int base = 10; int base = 10;
int percision = 3; int percision = 3;
int fill = 0; int fill = 0;
char fill_char = '0';
bool upper = false; bool upper = false;
}; };
template<typename F> template<typename F>
inline void print(F putc, const char* format) void print(F putc, const char* format)
{ {
while (*format) while (*format)
{ {
@@ -50,8 +52,8 @@ namespace BAN::Formatter
} }
} }
template<typename F, typename Arg, typename... Args> requires PrintableArguments<F, Arg, Args...> template<typename F, typename Arg, typename... Args>
inline void print(F putc, const char* format, Arg&& arg, Args&&... args) void print(F putc, const char* format, Arg&& arg, Args&&... args)
{ {
while (*format && *format != '{') while (*format && *format != '{')
{ {
@@ -69,7 +71,7 @@ namespace BAN::Formatter
} }
template<typename F, typename... Args> template<typename F, typename... Args>
inline void println(F putc, const char* format, Args&&... args) void println(F putc, const char* format, Args&&... args)
{ {
print(putc, format, args...); print(putc, format, args...);
putc('\n'); putc('\n');
@@ -79,7 +81,7 @@ namespace BAN::Formatter
{ {
template<typename F, typename Arg> template<typename F, typename Arg>
inline size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument) size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument)
{ {
ValueFormat value_format; ValueFormat value_format;
@@ -92,12 +94,6 @@ namespace BAN::Formatter
if (!format[i] || format[i] == '}') if (!format[i] || format[i] == '}')
break; break;
if (format[i] == ' ')
{
value_format.fill_char = ' ';
i++;
}
if ('0' <= format[i] && format[i] <= '9') if ('0' <= format[i] && format[i] <= '9')
{ {
int fill = 0; int fill = 0;
@@ -147,7 +143,7 @@ namespace BAN::Formatter
return i + 1; return i + 1;
} }
inline char value_to_base_char(uint8_t value, int base, bool upper) static char value_to_base_char(uint8_t value, int base, bool upper)
{ {
if (base <= 10) if (base <= 10)
return value + '0'; return value + '0';
@@ -161,12 +157,11 @@ namespace BAN::Formatter
} }
template<typename F, typename T> template<typename F, typename T>
inline void print_integer(F putc, T value, const ValueFormat& format) void print_integer(F putc, T value, const ValueFormat& format)
{ {
if (value == 0) if (value == 0)
{ {
for (int i = 0; i < format.fill - 1; i++) for (int i = 0; i < format.fill || i < 1; i++)
putc(format.fill_char);
putc('0'); putc('0');
return; return;
} }
@@ -193,7 +188,7 @@ namespace BAN::Formatter
} }
while (ptr >= buffer + sizeof(buffer) - format.fill) while (ptr >= buffer + sizeof(buffer) - format.fill)
*(--ptr) = format.fill_char; *(--ptr) = '0';
if (sign) if (sign)
*(--ptr) = '-'; *(--ptr) = '-';
@@ -202,16 +197,12 @@ namespace BAN::Formatter
} }
template<typename F, typename T> template<typename F, typename T>
inline void print_floating(F putc, T value, const ValueFormat& format) void print_floating(F putc, T value, const ValueFormat& format)
{ {
if (value < 0)
{
putc('-');
return print_floating(putc, -value, format);
}
int64_t int_part = (int64_t)value; int64_t int_part = (int64_t)value;
T frac_part = value - (T)int_part; T frac_part = value - (T)int_part;
if (frac_part < 0)
frac_part = -frac_part;
print_integer(putc, int_part, format); print_integer(putc, int_part, format);
@@ -229,7 +220,7 @@ namespace BAN::Formatter
} }
template<typename F> template<typename F>
inline void print_pointer(F putc, void* ptr, const ValueFormat& format) void print_pointer(F putc, void* ptr, const ValueFormat& format)
{ {
uintptr_t value = (uintptr_t)ptr; uintptr_t value = (uintptr_t)ptr;
print(putc, "0x"); print(putc, "0x");
@@ -245,13 +236,13 @@ namespace BAN::Formatter
*/ */
template<typename F, integral T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); } template<typename F, integral T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
template<typename F, floating_point T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); } template<typename F, floating_point T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); }
template<typename F, pointer T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); } template<typename F, pointer T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
template<typename F> inline void print_argument(F putc, char value, const ValueFormat&) { putc(value); } template<typename F> void print_argument(F putc, char value, const ValueFormat&) { putc(value); }
template<typename F> inline void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); } template<typename F> void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); }
template<typename F> inline void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); } template<typename F> void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); }
template<typename F> inline void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); } template<typename F> void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
} }

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <BAN/Traits.h>
#include <stddef.h> #include <stddef.h>
namespace BAN namespace BAN
@@ -15,6 +14,5 @@ namespace BAN
class StringView; class StringView;
template<typename> class Vector; template<typename> class Vector;
template<typename> class LinkedList; template<typename> class LinkedList;
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
} }

View File

@@ -2,7 +2,7 @@
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/PlacementNew.h> #include <BAN/New.h>
namespace BAN namespace BAN
{ {
@@ -20,19 +20,19 @@ namespace BAN
new (m_storage) CallablePointer(function); new (m_storage) CallablePointer(function);
} }
template<typename Own> template<typename Own>
Function(Ret(Own::*function)(Args...), Own& owner) Function(Ret(Own::*function)(Args...), Own* owner)
{ {
static_assert(sizeof(CallableMember<Own>) <= m_size); static_assert(sizeof(CallableMember<Own>) <= m_size);
new (m_storage) CallableMember<Own>(function, owner); new (m_storage) CallableMember<Own>(function, owner);
} }
template<typename Own> template<typename Own>
Function(Ret(Own::*function)(Args...) const, const Own& owner) Function(Ret(Own::*function)(Args...) const, const Own* owner)
{ {
static_assert(sizeof(CallableMemberConst<Own>) <= m_size); static_assert(sizeof(CallableMemberConst<Own>) <= m_size);
new (m_storage) CallableMemberConst<Own>(function, owner); new (m_storage) CallableMemberConst<Own>(function, owner);
} }
template<typename Lambda> template<typename Lambda>
Function(Lambda lambda) requires requires(Lambda lamda, Args&&... args) { { lambda(forward<Args>(args)...) } -> BAN::same_as<Ret>; } Function(Lambda lambda)
{ {
static_assert(sizeof(CallableLambda<Lambda>) <= m_size); static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
new (m_storage) CallableLambda<Lambda>(lambda); new (m_storage) CallableLambda<Lambda>(lambda);
@@ -91,36 +91,36 @@ namespace BAN
template<typename Own> template<typename Own>
struct CallableMember : public CallableBase struct CallableMember : public CallableBase
{ {
CallableMember(Ret(Own::*function)(Args...), Own& owner) CallableMember(Ret(Own::*function)(Args...), Own* owner)
: m_owner(owner) : m_owner(owner)
, m_function(function) , m_function(function)
{ } { }
virtual Ret call(Args... args) const override virtual Ret call(Args... args) const override
{ {
return (m_owner.*m_function)(forward<Args>(args)...); return (m_owner->*m_function)(forward<Args>(args)...);
} }
private: private:
Own& m_owner; Own* m_owner = nullptr;
Ret(Own::*m_function)(Args...) = nullptr; Ret(Own::*m_function)(Args...) = nullptr;
}; };
template<typename Own> template<typename Own>
struct CallableMemberConst : public CallableBase struct CallableMemberConst : public CallableBase
{ {
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own& owner) CallableMemberConst(Ret(Own::*function)(Args...) const, const Own* owner)
: m_owner(owner) : m_owner(owner)
, m_function(function) , m_function(function)
{ } { }
virtual Ret call(Args... args) const override virtual Ret call(Args... args) const override
{ {
return (m_owner.*m_function)(forward<Args>(args)...); return (m_owner->*m_function)(forward<Args>(args)...);
} }
private: private:
const Own& m_owner; const Own* m_owner = nullptr;
Ret(Own::*m_function)(Args...) const = nullptr; Ret(Own::*m_function)(Args...) const = nullptr;
}; };
@@ -141,7 +141,7 @@ namespace BAN
}; };
private: private:
static constexpr size_t m_size = sizeof(void*) * 8; static constexpr size_t m_size = sizeof(void*) * 5;
alignas(CallableBase) uint8_t m_storage[m_size] { 0 }; alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
}; };

View File

@@ -1,73 +0,0 @@
#pragma once
#include <BAN/Optional.h>
#include <BAN/String.h>
#include <string.h>
namespace BAN
{
struct GUID
{
uint32_t component1 { 0 };
uint16_t component2 { 0 };
uint16_t component3 { 0 };
uint8_t component45[8] { };
bool operator==(const GUID& other) const
{
return memcmp(this, &other, sizeof(GUID)) == 0;
}
BAN::ErrorOr<BAN::String> to_string() const
{
char buffer[37];
char* ptr = buffer;
const auto append_hex_nibble =
[&ptr](uint8_t nibble)
{
if (nibble < 10)
*ptr++ = '0' + nibble;
else
*ptr++ = 'A' + nibble - 10;
};
const auto append_hex_byte =
[&append_hex_nibble](uint8_t byte)
{
append_hex_nibble(byte >> 4);
append_hex_nibble(byte & 0xF);
};
append_hex_byte((component1 >> 24) & 0xFF);
append_hex_byte((component1 >> 16) & 0xFF);
append_hex_byte((component1 >> 8) & 0xFF);
append_hex_byte((component1 >> 0) & 0xFF);
*ptr++ = '-';
append_hex_byte((component2 >> 8) & 0xFF);
append_hex_byte((component2 >> 0) & 0xFF);
*ptr++ = '-';
append_hex_byte((component3 >> 8) & 0xFF);
append_hex_byte((component3 >> 0) & 0xFF);
*ptr++ = '-';
append_hex_byte(component45[0]);
append_hex_byte(component45[1]);
*ptr++ = '-';
append_hex_byte(component45[2]);
append_hex_byte(component45[3]);
append_hex_byte(component45[4]);
append_hex_byte(component45[5]);
append_hex_byte(component45[6]);
append_hex_byte(component45[7]);
*ptr = '\0';
BAN::String guid;
TRY(guid.append(buffer));
return BAN::move(guid);
}
};
static_assert(sizeof(GUID) == 16);
}

View File

@@ -7,6 +7,9 @@
namespace BAN namespace BAN
{ {
template<typename Container>
class HashMapIterator;
template<typename Key, typename T, typename HASH = BAN::hash<Key>> template<typename Key, typename T, typename HASH = BAN::hash<Key>>
class HashMap class HashMap
{ {
@@ -14,17 +17,11 @@ namespace BAN
struct Entry struct Entry
{ {
template<typename... Args> template<typename... Args>
Entry(const Key& key, Args&&... args) requires is_constructible_v<T, Args...> Entry(const Key& key, Args&&... args)
: key(key) : key(key)
, value(forward<Args>(args)...) , value(forward<Args>(args)...)
{} {}
template<typename... Args>
Entry(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
: key(BAN::move(key))
, value(forward<Args>(args)...)
{}
Key key; Key key;
T value; T value;
}; };
@@ -45,27 +42,10 @@ namespace BAN
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&); HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&); HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); } ErrorOr<void> insert(const Key&, const T&);
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); } ErrorOr<void> insert(const Key&, T&&);
ErrorOr<iterator> insert(Key&& key, const T& value) { return emplace(move(key), value); }
ErrorOr<iterator> insert(Key&& key, T&& value) { return emplace(move(key), move(value)); }
ErrorOr<iterator> insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); }
ErrorOr<iterator> insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); }
ErrorOr<iterator> insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); }
ErrorOr<iterator> insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); }
template<typename... Args> template<typename... Args>
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<void> emplace(const Key&, Args&&...);
{ return emplace(Key(key), forward<Args>(args)...); }
template<typename... Args>
ErrorOr<iterator> emplace(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
template<typename... Args>
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
{ return emplace_or_assign(Key(key), forward<Args>(args)...); }
template<typename... Args>
ErrorOr<iterator> emplace_or_assign(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); } iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); } iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
@@ -75,14 +55,11 @@ namespace BAN
ErrorOr<void> reserve(size_type); ErrorOr<void> reserve(size_type);
void remove(const Key&); void remove(const Key&);
void remove(iterator it);
void clear(); void clear();
T& operator[](const Key&); T& operator[](const Key&);
const T& operator[](const Key&) const; const T& operator[](const Key&) const;
iterator find(const Key& key);
const_iterator find(const Key& key) const;
bool contains(const Key&) const; bool contains(const Key&) const;
bool empty() const; bool empty() const;
@@ -92,8 +69,6 @@ namespace BAN
ErrorOr<void> rebucket(size_type); ErrorOr<void> rebucket(size_type);
LinkedList<Entry>& get_bucket(const Key&); LinkedList<Entry>& get_bucket(const Key&);
const LinkedList<Entry>& get_bucket(const Key&) const; const LinkedList<Entry>& get_bucket(const Key&) const;
Vector<LinkedList<Entry>>::iterator get_bucket_iterator(const Key&);
Vector<LinkedList<Entry>>::const_iterator get_bucket_iterator(const Key&) const;
private: private:
Vector<LinkedList<Entry>> m_buckets; Vector<LinkedList<Entry>> m_buckets;
@@ -140,36 +115,29 @@ namespace BAN
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
template<typename... Args> ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, const T& value)
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{ {
ASSERT(!contains(key)); return insert(key, move(T(value)));
TRY(rebucket(m_size + 1)); }
auto bucket_it = get_bucket_iterator(key); template<typename Key, typename T, typename HASH>
TRY(bucket_it->emplace_back(move(key), forward<Args>(args)...)); ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, T&& value)
m_size++; {
return emplace(key, move(value));
return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1));
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
template<typename... Args> template<typename... Args>
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<void> HashMap<Key, T, HASH>::emplace(const Key& key, Args&&... args)
{ {
if (empty()) ASSERT(!contains(key));
return emplace(move(key), forward<Args>(args)...); TRY(rebucket(m_size + 1));
auto& bucket = get_bucket(key);
auto bucket_it = get_bucket_iterator(key); auto result = bucket.emplace_back(key, forward<Args>(args)...);
for (auto entry_it = bucket_it->begin(); entry_it != bucket_it->end(); entry_it++) if (result.is_error())
{ return Error::from_errno(ENOMEM);
if (entry_it->key != key) m_size++;
continue; return {};
entry_it->value = T(forward<Args>(args)...);
return iterator(m_buckets.end(), bucket_it, entry_it);
}
return emplace(move(key), forward<Args>(args)...);
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
@@ -182,16 +150,17 @@ namespace BAN
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(const Key& key) void HashMap<Key, T, HASH>::remove(const Key& key)
{ {
auto it = find(key); if (empty()) return;
if (it != end()) auto& bucket = get_bucket(key);
remove(it); for (auto it = bucket.begin(); it != bucket.end(); it++)
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(iterator it)
{ {
it.outer_current()->remove(it.inner_current()); if (it->key == key)
{
bucket.remove(it);
m_size--; m_size--;
return;
}
}
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
@@ -209,7 +178,7 @@ namespace BAN
for (Entry& entry : bucket) for (Entry& entry : bucket)
if (entry.key == key) if (entry.key == key)
return entry.value; return entry.value;
ASSERT_NOT_REACHED(); ASSERT(false);
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
@@ -220,37 +189,18 @@ namespace BAN
for (const Entry& entry : bucket) for (const Entry& entry : bucket)
if (entry.key == key) if (entry.key == key)
return entry.value; return entry.value;
ASSERT_NOT_REACHED(); ASSERT(false);
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::iterator HashMap<Key, T, HASH>::find(const Key& key)
{
if (empty())
return end();
auto bucket_it = get_bucket_iterator(key);
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
if (it->key == key)
return iterator(m_buckets.end(), bucket_it, it);
return end();
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::const_iterator HashMap<Key, T, HASH>::find(const Key& key) const
{
if (empty())
return end();
auto bucket_it = get_bucket_iterator(key);
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
if (it->key == key)
return const_iterator(m_buckets.end(), bucket_it, it);
return end();
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
bool HashMap<Key, T, HASH>::contains(const Key& key) const bool HashMap<Key, T, HASH>::contains(const Key& key) const
{ {
return find(key) != end(); if (empty()) return false;
const auto& bucket = get_bucket(key);
for (const Entry& entry : bucket)
if (entry.key == key)
return true;
return false;
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
@@ -273,14 +223,18 @@ namespace BAN
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2); size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
Vector<LinkedList<Entry>> new_buckets; Vector<LinkedList<Entry>> new_buckets;
TRY(new_buckets.resize(new_bucket_count)); if (new_buckets.resize(new_bucket_count).is_error())
return Error::from_errno(ENOMEM);
// NOTE: We have to copy the old entries to the new entries and not move
// since we might run out of memory half way through.
for (auto& bucket : m_buckets) for (auto& bucket : m_buckets)
{ {
for (auto it = bucket.begin(); it != bucket.end();) for (Entry& entry : bucket)
{ {
size_type new_bucket_index = HASH()(it->key) % new_buckets.size(); size_type bucket_index = HASH()(entry.key) % new_buckets.size();
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it); if (new_buckets[bucket_index].push_back(entry).is_error())
return Error::from_errno(ENOMEM);
} }
} }
@@ -291,29 +245,17 @@ namespace BAN
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
{ {
return *get_bucket_iterator(key); ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return m_buckets[index];
} }
template<typename Key, typename T, typename HASH> template<typename Key, typename T, typename HASH>
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
{
return *get_bucket_iterator(key);
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key)
{ {
ASSERT(!m_buckets.empty()); ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size(); auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index); return m_buckets[index];
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::const_iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key) const
{
ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index);
} }
} }

View File

@@ -2,8 +2,6 @@
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <BAN/Hash.h> #include <BAN/Hash.h>
#include <BAN/Iterators.h>
#include <BAN/LinkedList.h>
#include <BAN/Math.h> #include <BAN/Math.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/Vector.h> #include <BAN/Vector.h>
@@ -11,22 +9,24 @@
namespace BAN namespace BAN
{ {
template<typename T, typename HASH>
class HashSetIterator;
template<typename T, typename HASH = hash<T>> template<typename T, typename HASH = hash<T>>
class HashSet class HashSet
{ {
public: public:
using value_type = T; using value_type = T;
using size_type = size_t; using size_type = hash_t;
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>; using const_iterator = HashSetIterator<T, HASH>;
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
public: public:
HashSet() = default; HashSet() = default;
HashSet(const HashSet&); HashSet(const HashSet<T, HASH>&);
HashSet(HashSet&&); HashSet(HashSet<T, HASH>&&);
HashSet& operator=(const HashSet&); HashSet<T, HASH>& operator=(const HashSet<T, HASH>&);
HashSet& operator=(HashSet&&); HashSet<T, HASH>& operator=(HashSet<T, HASH>&&);
ErrorOr<void> insert(const T&); ErrorOr<void> insert(const T&);
ErrorOr<void> insert(T&&); ErrorOr<void> insert(T&&);
@@ -35,10 +35,8 @@ namespace BAN
ErrorOr<void> reserve(size_type); ErrorOr<void> reserve(size_type);
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); } const_iterator begin() const { return const_iterator(this, m_buckets.begin()); }
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); } const_iterator end() const { return const_iterator(this, m_buckets.end()); }
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
bool contains(const T&) const; bool contains(const T&) const;
@@ -47,23 +45,55 @@ namespace BAN
private: private:
ErrorOr<void> rebucket(size_type); ErrorOr<void> rebucket(size_type);
LinkedList<T>& get_bucket(const T&); Vector<T>& get_bucket(const T&);
const LinkedList<T>& get_bucket(const T&) const; const Vector<T>& get_bucket(const T&) const;
private: private:
Vector<LinkedList<T>> m_buckets; Vector<Vector<T>> m_buckets;
size_type m_size = 0; size_type m_size = 0;
friend class HashSetIterator<T, HASH>;
}; };
template<typename T, typename HASH> template<typename T, typename HASH>
HashSet<T, HASH>::HashSet(const HashSet& other) class HashSetIterator
{
public:
HashSetIterator() = default;
HashSetIterator(const HashSetIterator<T, HASH>&);
HashSetIterator<T, HASH>& operator++();
HashSetIterator<T, HASH> operator++(int);
const T& operator*() const;
const T* operator->() const;
bool operator==(const HashSetIterator<T, HASH>&) const;
bool operator!=(const HashSetIterator<T, HASH>&) const;
operator bool() const { return m_owner && m_current_bucket; }
private:
HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket);
void find_next();
private:
const HashSet<T, HASH>* m_owner = nullptr;
Vector<Vector<T>>::const_iterator m_current_bucket;
Vector<T>::const_iterator m_current_key;
friend class HashSet<T, HASH>;
};
template<typename T, typename HASH>
HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
: m_buckets(other.m_buckets) : m_buckets(other.m_buckets)
, m_size(other.m_size) , m_size(other.m_size)
{ {
} }
template<typename T, typename HASH> template<typename T, typename HASH>
HashSet<T, HASH>::HashSet(HashSet&& other) HashSet<T, HASH>::HashSet(HashSet<T, HASH>&& other)
: m_buckets(move(other.m_buckets)) : m_buckets(move(other.m_buckets))
, m_size(other.m_size) , m_size(other.m_size)
{ {
@@ -71,7 +101,7 @@ namespace BAN
} }
template<typename T, typename HASH> template<typename T, typename HASH>
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet& other) HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet<T, HASH>& other)
{ {
clear(); clear();
m_buckets = other.m_buckets; m_buckets = other.m_buckets;
@@ -80,7 +110,7 @@ namespace BAN
} }
template<typename T, typename HASH> template<typename T, typename HASH>
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet&& other) HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet<T, HASH>&& other)
{ {
clear(); clear();
m_buckets = move(other.m_buckets); m_buckets = move(other.m_buckets);
@@ -111,12 +141,12 @@ namespace BAN
void HashSet<T, HASH>::remove(const T& key) void HashSet<T, HASH>::remove(const T& key)
{ {
if (empty()) return; if (empty()) return;
auto& bucket = get_bucket(key); Vector<T>& bucket = get_bucket(key);
for (auto it = bucket.begin(); it != bucket.end(); it++) for (size_type i = 0; i < bucket.size(); i++)
{ {
if (*it == key) if (bucket[i] == key)
{ {
bucket.remove(it); bucket.remove(i);
m_size--; m_size--;
break; break;
} }
@@ -162,17 +192,20 @@ namespace BAN
if (m_buckets.size() >= bucket_count) if (m_buckets.size() >= bucket_count)
return {}; return {};
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2); size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
Vector<LinkedList<T>> new_buckets; Vector<Vector<T>> new_buckets;
if (new_buckets.resize(new_bucket_count).is_error()) if (new_buckets.resize(new_bucket_count).is_error())
return Error::from_errno(ENOMEM); return Error::from_errno(ENOMEM);
for (auto& bucket : m_buckets) // NOTE: We have to copy the old keys to the new keys and not move
// since we might run out of memory half way through.
for (Vector<T>& bucket : m_buckets)
{ {
for (auto it = bucket.begin(); it != bucket.end();) for (T& key : bucket)
{ {
size_type new_bucket_index = HASH()(*it) % new_buckets.size(); size_type bucket_index = HASH()(key) % new_buckets.size();
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it); if (new_buckets[bucket_index].push_back(key).is_error())
return Error::from_errno(ENOMEM);
} }
} }
@@ -181,7 +214,7 @@ namespace BAN
} }
template<typename T, typename HASH> template<typename T, typename HASH>
LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) Vector<T>& HashSet<T, HASH>::get_bucket(const T& key)
{ {
ASSERT(!m_buckets.empty()); ASSERT(!m_buckets.empty());
size_type index = HASH()(key) % m_buckets.size(); size_type index = HASH()(key) % m_buckets.size();
@@ -189,11 +222,83 @@ namespace BAN
} }
template<typename T, typename HASH> template<typename T, typename HASH>
const LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) const const Vector<T>& HashSet<T, HASH>::get_bucket(const T& key) const
{ {
ASSERT(!m_buckets.empty()); ASSERT(!m_buckets.empty());
size_type index = HASH()(key) % m_buckets.size(); size_type index = HASH()(key) % m_buckets.size();
return m_buckets[index]; return m_buckets[index];
} }
template<typename T, typename HASH>
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
{
ASSERT(*this);
if (m_current_key == m_current_bucket->end())
m_current_bucket++;
else
m_current_key++;
find_next();
return *this;
}
template<typename T, typename HASH>
HashSetIterator<T, HASH> HashSetIterator<T, HASH>::operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
template<typename T, typename HASH>
const T& HashSetIterator<T, HASH>::operator*() const
{
ASSERT(m_owner && m_current_bucket && m_current_key);
return *m_current_key;
}
template<typename T, typename HASH>
const T* HashSetIterator<T, HASH>::operator->() const
{
return &**this;
}
template<typename T, typename HASH>
bool HashSetIterator<T, HASH>::operator==(const HashSetIterator<T, HASH>& other) const
{
if (!m_owner || m_owner != other.m_owner)
return false;
return m_current_bucket == other.m_current_bucket
&& m_current_key == other.m_current_key;
}
template<typename T, typename HASH>
bool HashSetIterator<T, HASH>::operator!=(const HashSetIterator<T, HASH>& other) const
{
return !(*this == other);
}
template<typename T, typename HASH>
HashSetIterator<T, HASH>::HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket)
: m_owner(owner)
, m_current_bucket(bucket)
{
if (m_current_bucket != m_owner->m_buckets.end())
m_current_key = m_current_bucket->begin();
find_next();
}
template<typename T, typename HASH>
void HashSetIterator<T, HASH>::find_next()
{
ASSERT(m_owner && m_current_bucket);
while (m_current_bucket != m_owner->m_buckets.end())
{
if (m_current_key && m_current_key != m_current_bucket->end())
return;
m_current_bucket++;
m_current_key = m_current_bucket->begin();
}
m_current_key = typename Vector<T>::const_iterator();
}
} }

View File

@@ -1,89 +0,0 @@
#pragma once
#include <BAN/Iterators.h>
#include <BAN/Swap.h>
#include <BAN/Traits.h>
namespace BAN
{
namespace detail
{
template<typename It, typename Comp>
void heapify_up(It begin, size_t index, Comp comp)
{
size_t parent = (index - 1) / 2;
while (parent < index)
{
if (comp(*(begin + index), *(begin + parent)))
break;
swap(*(begin + parent), *(begin + index));
index = parent;
parent = (index - 1) / 2;
}
}
template<typename It, typename Comp>
void heapify_down(It begin, size_t index, size_t len, Comp comp)
{
for (;;)
{
const size_t lchild = 2 * index + 1;
const size_t rchild = 2 * index + 2;
size_t child = 0;
if (lchild < len && !comp(*(begin + lchild), *(begin + index)))
{
if (rchild < len && !comp(*(begin + rchild), *(begin + lchild)))
child = rchild;
else
child = lchild;
}
else if (rchild < len && !comp(*(begin + rchild), *(begin + index)))
child = rchild;
else
break;
swap(*(begin + child), *(begin + index));
index = child;
}
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void make_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
size_t index = (len - 2) / 2;
while (index < len)
detail::heapify_down(begin, index--, len, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void push_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
detail::heapify_up(begin, len - 1, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void pop_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
swap(*begin, *(begin + len - 1));
detail::heapify_down(begin, 0, len - 1, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void sort_heap(It begin, It end, Comp comp = {})
{
while (begin != end)
pop_heap(begin, end--, comp);
}
}

View File

@@ -1,79 +0,0 @@
#pragma once
#include <BAN/Endianness.h>
#include <BAN/Formatter.h>
#include <BAN/Hash.h>
namespace BAN
{
struct IPv4Address
{
constexpr IPv4Address()
: IPv4Address(0)
{ }
constexpr IPv4Address(uint32_t u32_address)
{
raw = u32_address;
}
constexpr IPv4Address(uint8_t oct1, uint8_t oct2, uint8_t oct3, uint8_t oct4)
{
octets[0] = oct1;
octets[1] = oct2;
octets[2] = oct3;
octets[3] = oct4;
}
constexpr bool operator==(const IPv4Address& other) const
{
return raw == other.raw;
}
constexpr IPv4Address mask(const IPv4Address& other) const
{
return IPv4Address(raw & other.raw);
}
union
{
uint8_t octets[4];
uint32_t raw;
} __attribute__((packed));
};
static_assert(sizeof(IPv4Address) == 4);
template<>
struct hash<IPv4Address>
{
constexpr hash_t operator()(IPv4Address ipv4) const
{
return hash<uint32_t>()(ipv4.raw);
}
};
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const IPv4Address& ipv4, const ValueFormat&)
{
ValueFormat format {
.base = 10,
.percision = 0,
.fill = 0,
.upper = false,
};
print_argument(putc, ipv4.octets[0], format);
for (size_t i = 1; i < 4; i++)
{
putc('.');
print_argument(putc, ipv4.octets[i], format);
}
}
}

View File

@@ -1,180 +1,92 @@
#pragma once #pragma once
#include <BAN/Assert.h> #include <BAN/Assert.h>
#include <BAN/Traits.h>
#include <stddef.h>
namespace BAN namespace BAN
{ {
template<typename It>
constexpr It next(It it, size_t count)
{
for (size_t i = 0; i < count; i++)
++it;
return it;
}
template<typename It>
requires requires(It it, size_t n) { requires is_same_v<decltype(it + n), It>; }
constexpr It next(It it, size_t count)
{
return it + count;
}
template<typename It>
constexpr It prev(It it, size_t count)
{
for (size_t i = 0; i < count; i++)
--it;
return it;
}
template<typename It>
requires requires(It it, size_t n) { requires is_same_v<decltype(it - n), It>; }
constexpr It prev(It it, size_t count)
{
return it - count;
}
template<typename It>
constexpr size_t distance(It it1, It it2)
{
size_t dist = 0;
while (it1 != it2)
{
++it1;
++dist;
}
return dist;
}
template<typename It>
requires requires(It it1, It it2) { requires is_integral_v<decltype(it2 - it1)>; }
constexpr size_t distance(It it1, It it2)
{
return it2 - it1;
}
template<typename T, typename Container, bool CONST> template<typename T, typename Container, bool CONST>
class IteratorSimpleGeneral class IteratorSimpleGeneral
{ {
public: public:
using value_type = T; IteratorSimpleGeneral() = default;
public:
constexpr IteratorSimpleGeneral() = default;
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>> template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
constexpr IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other) IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
: m_pointer(other.m_pointer) : m_pointer(other.m_pointer)
, m_valid(other.m_valid)
{ {
} }
constexpr const T& operator*() const const T& operator*() const
{ {
ASSERT(m_pointer); ASSERT(m_pointer);
return *m_pointer; return *m_pointer;
} }
template<bool CONST2 = CONST> template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T&> operator*() enable_if_t<!CONST2, T&> operator*()
{ {
ASSERT(*this);
ASSERT(m_pointer); ASSERT(m_pointer);
return *m_pointer; return *m_pointer;
} }
constexpr const T* operator->() const const T* operator->() const
{ {
ASSERT(*this);
ASSERT(m_pointer); ASSERT(m_pointer);
return m_pointer; return m_pointer;
} }
template<bool CONST2 = CONST> template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T*> operator->() enable_if_t<!CONST2, T*> operator->()
{ {
ASSERT(*this);
ASSERT(m_pointer); ASSERT(m_pointer);
return m_pointer; return m_pointer;
} }
constexpr IteratorSimpleGeneral& operator++() IteratorSimpleGeneral& operator++()
{ {
ASSERT(*this);
ASSERT(m_pointer); ASSERT(m_pointer);
++m_pointer; ++m_pointer;
return *this; return *this;
} }
constexpr IteratorSimpleGeneral operator++(int) IteratorSimpleGeneral operator++(int)
{ {
auto temp = *this; auto temp = *this;
++(*this); ++(*this);
return temp; return temp;
} }
constexpr IteratorSimpleGeneral& operator--() IteratorSimpleGeneral& operator--()
{ {
ASSERT(*this);
ASSERT(m_pointer); ASSERT(m_pointer);
--m_pointer; return --m_pointer;
return *this;
} }
constexpr IteratorSimpleGeneral operator--(int) IteratorSimpleGeneral operator--(int)
{ {
auto temp = *this; auto temp = *this;
--(*this); --(*this);
return temp; return temp;
} }
constexpr size_t operator-(const IteratorSimpleGeneral& other) const bool operator==(const IteratorSimpleGeneral& other) const
{ {
ASSERT(*this && other);
return m_pointer - other.m_pointer;
}
constexpr IteratorSimpleGeneral operator+(size_t offset) const
{
return IteratorSimpleGeneral(m_pointer + offset);
}
constexpr IteratorSimpleGeneral operator-(size_t offset) const
{
return IteratorSimpleGeneral(m_pointer - offset);
}
constexpr bool operator<(const IteratorSimpleGeneral& other) const
{
ASSERT(*this);
return m_pointer < other.m_pointer;
}
constexpr bool operator==(const IteratorSimpleGeneral& other) const
{
ASSERT(*this);
return m_pointer == other.m_pointer; return m_pointer == other.m_pointer;
} }
constexpr bool operator!=(const IteratorSimpleGeneral& other) const bool operator!=(const IteratorSimpleGeneral& other) const
{ {
ASSERT(*this);
return !(*this == other); return !(*this == other);
} }
constexpr explicit operator bool() const operator bool() const
{ {
return m_valid; return m_pointer;
} }
private: private:
constexpr IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer) IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
: m_pointer(pointer) : m_pointer(pointer)
, m_valid(true)
{ {
} }
private: private:
maybe_const_t<CONST, T>* m_pointer = nullptr; maybe_const_t<CONST, T>* m_pointer = nullptr;
bool m_valid = false;
friend IteratorSimpleGeneral<T, Container, !CONST>; friend IteratorSimpleGeneral<T, Container, !CONST>;
friend Container; friend Container;
@@ -190,19 +102,17 @@ namespace BAN
using InnerIterator = either_or_t<CONST, typename Inner::const_iterator, typename Inner::iterator>; using InnerIterator = either_or_t<CONST, typename Inner::const_iterator, typename Inner::iterator>;
using OuterIterator = either_or_t<CONST, typename Outer::const_iterator, typename Outer::iterator>; using OuterIterator = either_or_t<CONST, typename Outer::const_iterator, typename Outer::iterator>;
using value_type = T;
public: public:
constexpr IteratorDoubleGeneral() = default; IteratorDoubleGeneral() = default;
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>> template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
constexpr IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other) IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other)
: m_outer_end(other.m_outer_end) : m_outer_end(other.m_outer_end)
, m_outer_current(other.m_outer_current) , m_outer_current(other.m_outer_current)
, m_inner_current(other.m_inner_current) , m_inner_current(other.m_inner_current)
{ {
} }
constexpr const T& operator*() const const T& operator*() const
{ {
ASSERT(*this); ASSERT(*this);
ASSERT(m_outer_current != m_outer_end); ASSERT(m_outer_current != m_outer_end);
@@ -210,7 +120,7 @@ namespace BAN
return m_inner_current.operator*(); return m_inner_current.operator*();
} }
template<bool CONST2 = CONST> template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T&> operator*() enable_if_t<!CONST2, T&> operator*()
{ {
ASSERT(*this); ASSERT(*this);
ASSERT(m_outer_current != m_outer_end); ASSERT(m_outer_current != m_outer_end);
@@ -218,7 +128,7 @@ namespace BAN
return m_inner_current.operator*(); return m_inner_current.operator*();
} }
constexpr const T* operator->() const const T* operator->() const
{ {
ASSERT(*this); ASSERT(*this);
ASSERT(m_outer_current != m_outer_end); ASSERT(m_outer_current != m_outer_end);
@@ -226,7 +136,7 @@ namespace BAN
return m_inner_current.operator->(); return m_inner_current.operator->();
} }
template<bool CONST2 = CONST> template<bool CONST2 = CONST>
constexpr enable_if_t<!CONST2, T*> operator->() enable_if_t<!CONST2, T*> operator->()
{ {
ASSERT(*this); ASSERT(*this);
ASSERT(m_outer_current != m_outer_end); ASSERT(m_outer_current != m_outer_end);
@@ -234,7 +144,7 @@ namespace BAN
return m_inner_current.operator->(); return m_inner_current.operator->();
} }
constexpr IteratorDoubleGeneral& operator++() IteratorDoubleGeneral& operator++()
{ {
ASSERT(*this); ASSERT(*this);
ASSERT(m_outer_current != m_outer_end); ASSERT(m_outer_current != m_outer_end);
@@ -243,37 +153,37 @@ namespace BAN
find_valid_or_end(); find_valid_or_end();
return *this; return *this;
} }
constexpr IteratorDoubleGeneral operator++(int) IteratorDoubleGeneral operator++(int)
{ {
auto temp = *this; auto temp = *this;
++(*this); ++(*this);
return temp; return temp;
} }
constexpr bool operator==(const IteratorDoubleGeneral& other) const bool operator==(const IteratorDoubleGeneral& other) const
{ {
ASSERT(*this && other); if (!*this || !other)
return false;
if (m_outer_end != other.m_outer_end) if (m_outer_end != other.m_outer_end)
return false; return false;
if (m_outer_current != other.m_outer_current) if (m_outer_current != other.m_outer_current)
return false; return false;
if (m_outer_current == m_outer_end) if (m_outer_current == m_outer_end)
return true; return true;
ASSERT(m_inner_current && other.m_inner_current);
return m_inner_current == other.m_inner_current; return m_inner_current == other.m_inner_current;
} }
constexpr bool operator!=(const IteratorDoubleGeneral& other) const bool operator!=(const IteratorDoubleGeneral& other) const
{ {
return !(*this == other); return !(*this == other);
} }
constexpr explicit operator bool() const operator bool() const
{ {
return !!m_outer_current; return m_outer_end && m_outer_current;
} }
private: private:
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current) IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
: m_outer_end(outer_end) : m_outer_end(outer_end)
, m_outer_current(outer_current) , m_outer_current(outer_current)
{ {
@@ -284,15 +194,7 @@ namespace BAN
} }
} }
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current, const InnerIterator& inner_current) void find_valid_or_end()
: m_outer_end(outer_end)
, m_outer_current(outer_current)
, m_inner_current(inner_current)
{
find_valid_or_end();
}
constexpr void find_valid_or_end()
{ {
while (m_inner_current == m_outer_current->end()) while (m_inner_current == m_outer_current->end())
{ {
@@ -303,9 +205,6 @@ namespace BAN
} }
} }
constexpr OuterIterator outer_current() { return m_outer_current; }
constexpr InnerIterator inner_current() { return m_inner_current; }
private: private:
OuterIterator m_outer_end; OuterIterator m_outer_end;
OuterIterator m_outer_current; OuterIterator m_outer_current;

View File

@@ -1,156 +0,0 @@
#pragma once
#include <BAN/Traits.h>
#include <stdint.h>
namespace BAN
{
template<typename T>
class numeric_limits
{
public:
numeric_limits() = delete;
static inline constexpr T max()
{
if constexpr(is_same_v<T, char>)
return __SCHAR_MAX__;
if constexpr(is_same_v<T, signed char>)
return __SCHAR_MAX__;
if constexpr(is_same_v<T, unsigned char>)
return (T)__SCHAR_MAX__ * 2 + 1;
if constexpr(is_same_v<T, short>)
return __SHRT_MAX__;
if constexpr(is_same_v<T, int>)
return __INT_MAX__;
if constexpr(is_same_v<T, long>)
return __LONG_MAX__;
if constexpr(is_same_v<T, long long>)
return __LONG_LONG_MAX__;
if constexpr(is_same_v<T, unsigned short>)
return (T)__SHRT_MAX__ * 2 + 1;
if constexpr(is_same_v<T, unsigned int>)
return (T)__INT_MAX__ * 2 + 1;
if constexpr(is_same_v<T, unsigned long>)
return (T)__LONG_MAX__ * 2 + 1;
if constexpr(is_same_v<T, unsigned long long>)
return (T)__LONG_LONG_MAX__ * 2 + 1;
if constexpr(is_same_v<T, float>)
return __FLT_MAX__;
if constexpr(is_same_v<T, double>)
return __DBL_MAX__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MAX__;
}
static inline constexpr T min()
{
if constexpr(is_signed_v<T> && is_integral_v<T>)
return -max() - 1;
if constexpr(is_unsigned_v<T> && is_integral_v<T>)
return 0;
if constexpr(is_same_v<T, float>)
return __FLT_MIN__;
if constexpr(is_same_v<T, double>)
return __DBL_MIN__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MIN__;
}
static inline constexpr bool has_infinity()
{
if constexpr(is_same_v<T, float>)
return __FLT_HAS_INFINITY__;
if constexpr(is_same_v<T, double>)
return __DBL_HAS_INFINITY__;
if constexpr(is_same_v<T, long double>)
return __LDBL_HAS_INFINITY__;
return false;
}
static inline constexpr T infinity() requires(has_infinity())
{
if constexpr(is_same_v<T, float>)
return __builtin_inff();
if constexpr(is_same_v<T, double>)
return __builtin_inf();
if constexpr(is_same_v<T, long double>)
return __builtin_infl();
}
static inline constexpr bool has_quiet_NaN()
{
if constexpr(is_same_v<T, float>)
return __FLT_HAS_QUIET_NAN__;
if constexpr(is_same_v<T, double>)
return __DBL_HAS_QUIET_NAN__;
if constexpr(is_same_v<T, long double>)
return __LDBL_HAS_QUIET_NAN__;
return false;
}
static inline constexpr T quiet_NaN() requires(has_quiet_NaN())
{
if constexpr(is_same_v<T, float>)
return __builtin_nanf("");
if constexpr(is_same_v<T, double>)
return __builtin_nan("");
if constexpr(is_same_v<T, long double>)
return __builtin_nanl("");
}
static inline constexpr int max_exponent2()
{
static_assert(__FLT_RADIX__ == 2);
if constexpr(is_same_v<T, float>)
return __FLT_MAX_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MAX_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MAX_EXP__;
return 0;
}
static inline constexpr int max_exponent10()
{
if constexpr(is_same_v<T, float>)
return __FLT_MAX_10_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MAX_10_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MAX_10_EXP__;
return 0;
}
static inline constexpr int min_exponent2()
{
static_assert(__FLT_RADIX__ == 2);
if constexpr(is_same_v<T, float>)
return __FLT_MIN_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MIN_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MIN_EXP__;
return 0;
}
static inline constexpr int min_exponent10()
{
if constexpr(is_same_v<T, float>)
return __FLT_MIN_10_EXP__;
if constexpr(is_same_v<T, double>)
return __DBL_MIN_10_EXP__;
if constexpr(is_same_v<T, long double>)
return __LDBL_MIN_10_EXP__;
return 0;
}
};
}

View File

@@ -3,7 +3,6 @@
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/New.h> #include <BAN/New.h>
#include <BAN/PlacementNew.h>
namespace BAN namespace BAN
{ {
@@ -22,11 +21,11 @@ namespace BAN
public: public:
LinkedList() = default; LinkedList() = default;
LinkedList(const LinkedList<T>& other) requires is_copy_constructible_v<T> { *this = other; } LinkedList(const LinkedList<T>& other) { *this = other; }
LinkedList(LinkedList<T>&& other) { *this = move(other); } LinkedList(LinkedList<T>&& other) { *this = move(other); }
~LinkedList() { clear(); } ~LinkedList() { clear(); }
LinkedList<T>& operator=(const LinkedList<T>&) requires is_copy_constructible_v<T>; LinkedList<T>& operator=(const LinkedList<T>&);
LinkedList<T>& operator=(LinkedList<T>&&); LinkedList<T>& operator=(LinkedList<T>&&);
ErrorOr<void> push_back(const T&); ErrorOr<void> push_back(const T&);
@@ -34,16 +33,14 @@ namespace BAN
ErrorOr<void> insert(iterator, const T&); ErrorOr<void> insert(iterator, const T&);
ErrorOr<void> insert(iterator, T&&); ErrorOr<void> insert(iterator, T&&);
template<typename... Args> template<typename... Args>
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>; ErrorOr<void> emplace_back(Args&&...);
template<typename... Args> template<typename... Args>
ErrorOr<void> emplace(iterator, Args&&...) requires is_constructible_v<T, Args...>; ErrorOr<void> emplace(iterator, Args&&...);
void pop_back(); void pop_back();
iterator remove(iterator); iterator remove(iterator);
void clear(); void clear();
iterator move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter);
iterator begin() { return iterator(m_data, empty()); } iterator begin() { return iterator(m_data, empty()); }
const_iterator begin() const { return const_iterator(m_data, empty()); } const_iterator begin() const { return const_iterator(m_data, empty()); }
iterator end() { return iterator(m_last, true); } iterator end() { return iterator(m_last, true); }
@@ -67,11 +64,7 @@ namespace BAN
Node* prev; Node* prev;
}; };
template<typename... Args> ErrorOr<Node*> allocate_node() const;
ErrorOr<Node*> allocate_node(Args&&...) const;
Node* remove_node(iterator);
void insert_node(iterator, Node*);
Node* m_data = nullptr; Node* m_data = nullptr;
Node* m_last = nullptr; Node* m_last = nullptr;
@@ -122,7 +115,7 @@ namespace BAN
}; };
template<typename T> template<typename T>
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other) requires is_copy_constructible_v<T> LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
{ {
clear(); clear();
for (const T& elem : other) for (const T& elem : other)
@@ -143,31 +136,6 @@ namespace BAN
return *this; return *this;
} }
template<typename T>
LinkedList<T>::Node* LinkedList<T>::remove_node(iterator iter)
{
ASSERT(!empty() && iter);
Node* node = iter.m_current;
Node* prev = node->prev;
Node* next = node->next;
(prev ? prev->next : m_data) = next;
(next ? next->prev : m_last) = prev;
m_size--;
return node;
}
template<typename T>
void LinkedList<T>::insert_node(iterator iter, Node* node)
{
Node* next = iter.m_past_end ? nullptr : iter.m_current;
Node* prev = next ? next->prev : m_last;
node->next = next;
node->prev = prev;
(prev ? prev->next : m_data) = node;
(next ? next->prev : m_last) = node;
m_size++;
}
template<typename T> template<typename T>
ErrorOr<void> LinkedList<T>::push_back(const T& value) ErrorOr<void> LinkedList<T>::push_back(const T& value)
{ {
@@ -189,31 +157,44 @@ namespace BAN
template<typename T> template<typename T>
ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value) ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value)
{ {
Node* new_node = TRY(allocate_node(move(value))); Node* next = iter.m_past_end ? nullptr : iter.m_current;
insert_node(iter, new_node); Node* prev = next ? next->prev : m_last;
Node* new_node = TRY(allocate_node());
new (&new_node->value) T(move(value));
new_node->next = next;
new_node->prev = prev;
(prev ? prev->next : m_data) = new_node;
(next ? next->prev : m_last) = new_node;
m_size++;
return {}; return {};
} }
template<typename T> template<typename T>
template<typename... Args> template<typename... Args>
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args)
{ {
return emplace(end(), forward<Args>(args)...); return emplace(end(), forward<Args>(args)...);
} }
template<typename T> template<typename T>
template<typename... Args> template<typename... Args>
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args)
{ {
Node* new_node = TRY(allocate_node(forward<Args>(args)...)); Node* next = iter.m_past_end ? nullptr : iter.m_current;
insert_node(iter, new_node); Node* prev = next ? next->prev : m_last;
Node* new_node = TRY(allocate_node());
new (&new_node->value) T(forward<Args>(args)...);
new_node->next = next;
new_node->prev = prev;
(prev ? prev->next : m_data) = new_node;
(next ? next->prev : m_last) = new_node;
m_size++;
return {}; return {};
} }
template<typename T> template<typename T>
void LinkedList<T>::pop_back() void LinkedList<T>::pop_back()
{ {
ASSERT(!empty());
remove(iterator(m_last, false)); remove(iterator(m_last, false));
} }
@@ -221,10 +202,14 @@ namespace BAN
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter) LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
{ {
ASSERT(!empty() && iter); ASSERT(!empty() && iter);
Node* node = remove_node(iter); Node* node = iter.m_current;
Node* prev = node->prev;
Node* next = node->next; Node* next = node->next;
node->value.~T(); node->value.~T();
BAN::deallocator(node); BAN::deallocator(node);
(prev ? prev->next : m_data) = next;
(next ? next->prev : m_last) = prev;
m_size--;
return next ? iterator(next, false) : iterator(m_last, true); return next ? iterator(next, false) : iterator(m_last, true);
} }
@@ -244,16 +229,6 @@ namespace BAN
m_size = 0; m_size = 0;
} }
template<typename T>
LinkedList<T>::iterator LinkedList<T>::move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter)
{
ASSERT(!empty() && src_iter);
Node* node = remove_node(src_iter);
iterator ret = node->next ? iterator(node->next, false) : iterator(m_last, true);
dest_list.insert_node(dest_iter, node);
return ret;
}
template<typename T> template<typename T>
const T& LinkedList<T>::back() const const T& LinkedList<T>::back() const
{ {
@@ -308,13 +283,11 @@ namespace BAN
} }
template<typename T> template<typename T>
template<typename... Args> ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node() const
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node(Args&&... args) const
{ {
Node* node = (Node*)BAN::allocator(sizeof(Node)); Node* node = (Node*)BAN::allocator(sizeof(Node));
if (node == nullptr) if (node == nullptr)
return Error::from_errno(ENOMEM); return Error::from_errno(ENOMEM);
new (&node->value) T(forward<Args>(args)...);
return node; return node;
} }

View File

@@ -1,47 +0,0 @@
#pragma once
#include <BAN/Formatter.h>
namespace BAN
{
struct MACAddress
{
uint8_t address[6];
constexpr bool operator==(const MACAddress& other) const
{
return
address[0] == other.address[0] &&
address[1] == other.address[1] &&
address[2] == other.address[2] &&
address[3] == other.address[3] &&
address[4] == other.address[4] &&
address[5] == other.address[5];
}
};
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const MACAddress& mac, const ValueFormat&)
{
ValueFormat format {
.base = 16,
.percision = 0,
.fill = 2,
.upper = true,
};
print_argument(putc, mac.address[0], format);
for (size_t i = 1; i < 6; i++)
{
putc(':');
print_argument(putc, mac.address[i], format);
}
}
}

View File

@@ -1,20 +1,13 @@
#pragma once #pragma once
#include <BAN/Limits.h>
#include <BAN/Numbers.h>
#include <BAN/Traits.h> #include <BAN/Traits.h>
#include <float.h> #include <stddef.h>
#include <stdint.h>
namespace BAN::Math namespace BAN::Math
{ {
template<typename T>
inline constexpr T abs(T x)
{
return x < 0 ? -x : x;
}
template<typename T> template<typename T>
inline constexpr T min(T a, T b) inline constexpr T min(T a, T b)
{ {
@@ -36,11 +29,12 @@ namespace BAN::Math
template<integral T> template<integral T>
inline constexpr T gcd(T a, T b) inline constexpr T gcd(T a, T b)
{ {
T t;
while (b) while (b)
{ {
T temp = b; t = b;
b = a % b; b = a % b;
a = temp; a = t;
} }
return a; return a;
} }
@@ -58,408 +52,54 @@ namespace BAN::Math
} }
template<integral T> template<integral T>
inline constexpr bool is_power_of_two(T x) inline constexpr bool is_power_of_two(T value)
{ {
if (x == 0) if (value == 0)
return false; return false;
return (x & (x - 1)) == 0; return (value & (value - 1)) == 0;
}
template<integral T>
__attribute__((always_inline))
inline constexpr bool will_multiplication_overflow(T a, T b)
{
T dummy;
return __builtin_mul_overflow(a, b, &dummy);
}
template<integral T>
__attribute__((always_inline))
inline constexpr bool will_addition_overflow(T a, T b)
{
T dummy;
return __builtin_add_overflow(a, b, &dummy);
}
template<typename T>
requires is_same_v<T, unsigned int> || is_same_v<T, unsigned long> || is_same_v<T, unsigned long long>
inline constexpr T ilog2(T x)
{
if constexpr(is_same_v<T, unsigned int>)
return sizeof(T) * 8 - __builtin_clz(x) - 1;
if constexpr(is_same_v<T, unsigned long>)
return sizeof(T) * 8 - __builtin_clzl(x) - 1;
return sizeof(T) * 8 - __builtin_clzll(x) - 1;
}
// This is ugly but my clangd does not like including
// intrinsic headers at all
#if !defined(__SSE__) || !defined(__SSE2__)
#pragma GCC push_options
#ifndef __SSE__
#pragma GCC target("sse")
#endif
#ifndef __SSE2__
#pragma GCC target("sse2")
#endif
#define BAN_MATH_POP_OPTIONS
#endif
template<floating_point T>
inline constexpr T floor(T x)
{
if constexpr(is_same_v<T, float>)
return __builtin_floorf(x);
if constexpr(is_same_v<T, double>)
return __builtin_floor(x);
if constexpr(is_same_v<T, long double>)
return __builtin_floorl(x);
} }
template<floating_point T> template<floating_point T>
inline constexpr T ceil(T x) inline constexpr T log2(T value)
{ {
if constexpr(is_same_v<T, float>) T result;
return __builtin_ceilf(x); asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"((T)1.0) : "st(1)");
if constexpr(is_same_v<T, double>) return result;
return __builtin_ceil(x);
if constexpr(is_same_v<T, long double>)
return __builtin_ceill(x);
} }
template<floating_point T> template<floating_point T>
inline constexpr T round(T x) inline constexpr T log10(T value)
{ {
if (x == (T)0.0) constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
return x; T result;
if (x > (T)0.0) asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"(INV_LOG_2_10) : "st(1)");
return floor<T>(x + (T)0.5); return result;
return ceil<T>(x - (T)0.5);
} }
template<floating_point T> template<floating_point T>
inline constexpr T trunc(T x) inline constexpr T log(T value, T base)
{ {
if constexpr(is_same_v<T, float>) return log2(value) / log2(base);
return __builtin_truncf(x);
if constexpr(is_same_v<T, double>)
return __builtin_trunc(x);
if constexpr(is_same_v<T, long double>)
return __builtin_truncl(x);
} }
template<floating_point T> template<floating_point T>
inline constexpr T rint(T x) inline constexpr T pow(T base, T exp)
{ {
asm("frndint" : "+t"(x)); T result;
return x; asm volatile(
} "fyl2x;"
template<floating_point T>
inline constexpr T fmod(T a, T b)
{
asm(
"1:"
"fprem;"
"fnstsw %%ax;"
"testb $4, %%ah;"
"jne 1b;"
: "+t"(a)
: "u"(b)
: "ax", "cc"
);
return a;
}
template<floating_point T>
inline constexpr T remainder(T a, T b)
{
asm(
"1:"
"fprem1;"
"fnstsw %%ax;"
"testb $4, %%ah;"
"jne 1b;"
: "+t"(a)
: "u"(b)
: "ax", "cc"
);
return a;
}
template<floating_point T>
static T modf(T x, T* iptr)
{
const T frac = BAN::Math::fmod<T>(x, (T)1.0);
*iptr = x - frac;
return frac;
}
template<floating_point T>
inline constexpr T frexp(T num, int* exp)
{
if (num == (T)0.0)
{
*exp = 0;
return (T)0.0;
}
T e;
asm("fxtract" : "+t"(num), "=u"(e));
*exp = (int)e + 1;
return num / (T)2.0;
}
template<floating_point T>
inline constexpr T copysign(T x, T y)
{
if ((x < (T)0.0) != (y < (T)0.0))
x = -x;
return x;
}
namespace detail
{
template<floating_point T>
inline constexpr T fyl2x(T x, T y)
{
asm("fyl2x" : "+t"(x) : "u"(y) : "st(1)");
return x;
}
}
template<floating_point T>
inline constexpr T log(T x)
{
return detail::fyl2x<T>(x, numbers::ln2_v<T>);
}
template<floating_point T>
inline constexpr T log2(T x)
{
return detail::fyl2x<T>(x, 1.0);
}
template<floating_point T>
inline constexpr T log10(T x)
{
return detail::fyl2x<T>(x, numbers::lg2_v<T>);
}
template<floating_point T>
inline constexpr T logb(T x)
{
static_assert(FLT_RADIX == 2);
return log2<T>(x);
}
template<floating_point T>
inline constexpr T exp2(T x)
{
if (abs(x) <= (T)1.0)
{
asm("f2xm1" : "+t"(x));
return x + (T)1.0;
}
asm(
"fld1;" "fld1;"
"fld %%st(1);" "fld %%st(1);"
"fprem;" "fprem;"
"f2xm1;" "f2xm1;"
"faddp;" "faddp;"
"fscale;" "fscale;"
"fstp %%st(1);" "fxch %%st(1);"
: "+t"(x) "fstp %%st;"
: "=t"(result)
: "0"(base), "u"(exp)
); );
return result;
return x;
} }
template<floating_point T>
inline constexpr T exp(T x)
{
return exp2<T>(x * numbers::log2e_v<T>);
}
template<floating_point T>
inline constexpr T pow(T x, T y)
{
if (x == (T)0.0)
return (T)0.0;
return exp2<T>(y * log2<T>(x));
}
template<floating_point T>
inline constexpr T scalbn(T x, int n)
{
asm("fscale" : "+t"(x) : "u"(static_cast<T>(n)));
return x;
}
template<floating_point T>
inline constexpr T ldexp(T x, int y)
{
const bool exp_sign = y < 0;
if (exp_sign)
y = -y;
T exp = (T)1.0;
T mult = (T)2.0;
while (y)
{
if (y & 1)
exp *= mult;
mult *= mult;
y >>= 1;
}
if (exp_sign)
exp = (T)1.0 / exp;
return x * exp;
}
template<floating_point T>
inline constexpr T sqrt(T x)
{
if constexpr(BAN::is_same_v<T, float>)
{
using v4sf = float __attribute__((vector_size(16)));
return __builtin_ia32_sqrtss((v4sf) { x, 0.0f, 0.0f, 0.0f })[0];
}
else if constexpr(BAN::is_same_v<T, double>)
{
using v2df = double __attribute__((vector_size(16)));
return __builtin_ia32_sqrtsd((v2df) { x, 0.0 })[0];
}
else if constexpr(BAN::is_same_v<T, long double>)
{
asm("fsqrt" : "+t"(x));
return x;
}
}
template<floating_point T>
inline constexpr T cbrt(T value)
{
return pow<T>(value, (T)1.0 / (T)3.0);
}
template<floating_point T>
inline constexpr T sin(T x)
{
asm("fsin" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr T cos(T x)
{
asm("fcos" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr void sincos(T x, T& sin, T& cos)
{
asm("fsincos" : "=t"(cos), "=u"(sin) : "0"(x));
}
template<floating_point T>
inline constexpr T tan(T x)
{
T one, ret;
asm("fptan" : "=t"(one), "=u"(ret) : "0"(x));
return ret;
}
template<floating_point T>
inline constexpr T atan2(T y, T x)
{
asm("fpatan" : "+t"(x) : "u"(y) : "st(1)");
return x;
}
template<floating_point T>
inline constexpr T atan(T x)
{
return atan2<T>(x, (T)1.0);
}
template<floating_point T>
inline constexpr T asin(T x)
{
if (x == (T)0.0)
return (T)0.0;
if (x == (T)1.0)
return +numbers::pi_v<T> / (T)2.0;
if (x == (T)-1.0)
return -numbers::pi_v<T> / (T)2.0;
return (T)2.0 * atan<T>(x / ((T)1.0 + sqrt<T>((T)1.0 - x * x)));
}
template<floating_point T>
inline constexpr T acos(T x)
{
if (x == (T)0.0)
return numbers::pi_v<T> / (T)2.0;
if (x == (T)1.0)
return (T)0.0;
if (x == (T)-1.0)
return numbers::pi_v<T>;
return (T)2.0 * atan<T>(sqrt<T>((T)1.0 - x * x) / ((T)1.0 + x));
}
template<floating_point T>
inline constexpr T sinh(T x)
{
return (exp<T>(x) - exp<T>(-x)) / (T)2.0;
}
template<floating_point T>
inline constexpr T cosh(T x)
{
return (exp<T>(x) + exp<T>(-x)) / (T)2.0;
}
template<floating_point T>
inline constexpr T tanh(T x)
{
const T exp_px = exp<T>(+x);
const T exp_nx = exp<T>(-x);
return (exp_px - exp_nx) / (exp_px + exp_nx);
}
template<floating_point T>
inline constexpr T asinh(T x)
{
return log<T>(x + sqrt<T>(x * x + (T)1.0));
}
template<floating_point T>
inline constexpr T acosh(T x)
{
return log<T>(x + sqrt<T>(x * x - (T)1.0));
}
template<floating_point T>
inline constexpr T atanh(T x)
{
return (T)0.5 * log<T>(((T)1.0 + x) / ((T)1.0 - x));
}
template<floating_point T>
inline constexpr T hypot(T x, T y)
{
return sqrt<T>(x * x + y * y);
}
#ifdef BAN_MATH_POP_OPTIONS
#undef BAN_MATH_POP_OPTIONS
#pragma GCC pop_options
#endif
} }

View File

@@ -27,3 +27,6 @@ namespace BAN
} }
} }
inline void* operator new(size_t, void* addr) { return addr; }
inline void* operator new[](size_t, void* addr) { return addr; }

View File

@@ -9,12 +9,10 @@
namespace BAN namespace BAN
{ {
#if defined(__is_kernel) #if defined(__is_kernel)
static constexpr void*(*allocator)(size_t) = kmalloc; static constexpr void*(&allocator)(size_t) = kmalloc;
static constexpr void*(*reallocator)(void*, size_t) = nullptr; static constexpr void(&deallocator)(void*) = kfree;
static constexpr void(*deallocator)(void*) = kfree;
#else #else
static constexpr void*(*allocator)(size_t) = malloc; static constexpr void*(&allocator)(size_t) = malloc;
static constexpr void*(*reallocator)(void*, size_t) = realloc; static constexpr void(&deallocator)(void*) = free;
static constexpr void(*deallocator)(void*) = free;
#endif #endif
} }

View File

@@ -1,28 +0,0 @@
#pragma once
#include <BAN/Traits.h>
namespace BAN::numbers
{
template<floating_point T> inline constexpr T e_v = 2.71828182845904523536;
template<floating_point T> inline constexpr T log2e_v = 1.44269504088896340736;
template<floating_point T> inline constexpr T lge_v = 0.43429448190325182765;
template<floating_point T> inline constexpr T lg2_v = 0.30102999566398119521;
template<floating_point T> inline constexpr T ln2_v = 0.69314718055994530942;
template<floating_point T> inline constexpr T ln10_v = 2.30258509299404568402;
template<floating_point T> inline constexpr T pi_v = 3.14159265358979323846;
template<floating_point T> inline constexpr T sqrt2_v = 1.41421356237309504880;
template<floating_point T> inline constexpr T sqrt3_v = 1.73205080756887729353;
inline constexpr double e = e_v<double>;
inline constexpr double log2e = log2e_v<double>;
inline constexpr double lge = lge_v<double>;
inline constexpr double lg2 = lge_v<double>;
inline constexpr double ln2 = ln2_v<double>;
inline constexpr double ln10 = ln10_v<double>;
inline constexpr double pi = pi_v<double>;
inline constexpr double sqrt2 = sqrt2_v<double>;
inline constexpr double sqrt3 = sqrt3_v<double>;
}

View File

@@ -2,7 +2,6 @@
#include <BAN/Assert.h> #include <BAN/Assert.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/PlacementNew.h>
#include <stdint.h> #include <stdint.h>
@@ -13,74 +12,64 @@ namespace BAN
class Optional class Optional
{ {
public: public:
constexpr Optional(); Optional();
constexpr Optional(Optional&&); Optional(const T&);
constexpr Optional(const Optional&); Optional(T&&);
constexpr Optional(const T&); template<typename... Args>
constexpr Optional(T&&); Optional(Args&&...);
~Optional(); ~Optional();
constexpr Optional& operator=(Optional&&); Optional& operator=(const Optional&);
constexpr Optional& operator=(const Optional&); Optional& operator=(Optional&&);
template<typename... Args> template<typename... Args>
constexpr Optional& emplace(Args&&...) requires is_constructible_v<T, Args...>; Optional& emplace(Args&&...);
constexpr T* operator->(); T* operator->();
constexpr const T* operator->() const; const T* operator->() const;
constexpr T& operator*(); T& operator*();
constexpr const T& operator*() const; const T& operator*() const;
constexpr bool has_value() const; bool has_value() const;
constexpr T release_value(); T&& release_value();
constexpr T& value(); const T& value() const;
constexpr const T& value() const; T& value();
constexpr T& value_or(T&);
constexpr const T& value_or(const T&) const;
constexpr void clear(); void clear();
private: private:
alignas(T) uint8_t m_storage[sizeof(T)] {}; alignas(T) uint8_t m_storage[sizeof(T)];
bool m_has_value { false }; bool m_has_value { false };
}; };
template<typename T> template<typename T>
constexpr Optional<T>::Optional() Optional<T>::Optional()
: m_has_value(false) : m_has_value(false)
{} {}
template<typename T> template<typename T>
constexpr Optional<T>::Optional(Optional<T>&& other) Optional<T>::Optional(const T& value)
: m_has_value(other.has_value())
{
if (other.has_value())
new (m_storage) T(move(other.release_value()));
}
template<typename T>
constexpr Optional<T>::Optional(const Optional<T>& other)
: m_has_value(other.has_value())
{
if (other.has_value())
new (m_storage) T(other.value());
}
template<typename T>
constexpr Optional<T>::Optional(const T& value)
: m_has_value(true) : m_has_value(true)
{ {
new (m_storage) T(value); new (m_storage) T(value);
} }
template<typename T> template<typename T>
constexpr Optional<T>::Optional(T&& value) Optional<T>::Optional(T&& value)
: m_has_value(true) : m_has_value(true)
{ {
new (m_storage) T(move(value)); new (m_storage) T(BAN::move(value));
}
template<typename T>
template<typename... Args>
Optional<T>::Optional(Args&&... args)
: m_has_value(true)
{
new (m_storage) T(BAN::forward<Args>(args)...);
} }
template<typename T> template<typename T>
@@ -90,111 +79,97 @@ namespace BAN
} }
template<typename T> template<typename T>
constexpr Optional<T>& Optional<T>::operator=(Optional&& other) Optional<T>& Optional<T>::operator=(const Optional& other)
{ {
clear(); clear();
m_has_value = other.has_value();
if (other.has_value()) if (other.has_value())
new (m_storage) T(move(other.release_value())); {
m_has_value = true;
new (m_storage) T(other.value());
}
return *this; return *this;
} }
template<typename T> template<typename T>
constexpr Optional<T>& Optional<T>::operator=(const Optional& other) Optional<T>& Optional<T>::operator=(Optional&& other)
{ {
clear(); clear();
m_has_value = other.has_value();
if (other.has_value()) if (other.has_value())
new (m_storage) T(other.value()); {
m_has_value = true;
new (m_storage) T(BAN::move(other.release_value()));
}
return *this; return *this;
} }
template<typename T> template<typename T>
template<typename... Args> template<typename... Args>
constexpr Optional<T>& Optional<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...> Optional<T>& Optional<T>::emplace(Args&&... args)
{ {
clear(); clear();
m_has_value = true; m_has_value = true;
new (m_storage) T(forward<Args>(args)...); new (m_storage) T(BAN::forward<Args>(args)...);
return *this; return *this;
} }
template<typename T> template<typename T>
constexpr T* Optional<T>::operator->() T* Optional<T>::operator->()
{ {
ASSERT(has_value()); ASSERT(has_value());
return &value(); return &value();
} }
template<typename T> template<typename T>
constexpr const T* Optional<T>::operator->() const const T* Optional<T>::operator->() const
{ {
ASSERT(has_value()); ASSERT(has_value());
return &value(); return &value();
} }
template<typename T> template<typename T>
constexpr T& Optional<T>::operator*() T& Optional<T>::operator*()
{ {
ASSERT(has_value()); ASSERT(has_value());
return value(); return value();
} }
template<typename T> template<typename T>
constexpr const T& Optional<T>::operator*() const const T& Optional<T>::operator*() const
{ {
ASSERT(has_value()); ASSERT(has_value());
return value(); return value();
} }
template<typename T> template<typename T>
constexpr bool Optional<T>::has_value() const bool Optional<T>::has_value() const
{ {
return m_has_value; return m_has_value;
} }
template<typename T> template<typename T>
constexpr T Optional<T>::release_value() T&& Optional<T>::release_value()
{ {
ASSERT(has_value()); ASSERT(has_value());
T released_value = move(value());
value().~T();
m_has_value = false; m_has_value = false;
return move(released_value); return BAN::move((T&)m_storage);
} }
template<typename T> template<typename T>
constexpr T& Optional<T>::value() const T& Optional<T>::value() const
{ {
ASSERT(has_value()); ASSERT(has_value());
return *reinterpret_cast<T*>(&m_storage); return (const T&)m_storage;
} }
template<typename T> template<typename T>
constexpr const T& Optional<T>::value() const T& Optional<T>::value()
{ {
ASSERT(has_value()); ASSERT(has_value());
return *reinterpret_cast<const T*>(&m_storage); return (T&)m_storage;
} }
template<typename T> template<typename T>
constexpr T& Optional<T>::value_or(T& empty) void Optional<T>::clear()
{
if (!has_value())
return empty;
return value();
}
template<typename T>
constexpr const T& Optional<T>::value_or(const T& empty) const
{
if (!has_value())
return empty;
return value();
}
template<typename T>
constexpr void Optional<T>::clear()
{ {
if (m_has_value) if (m_has_value)
value().~T(); value().~T();

View File

@@ -1,10 +0,0 @@
#pragma once
#if __has_include(<new>)
#include <new>
#else
#include <stddef.h>
inline void* operator new(size_t, void* addr) { return addr; }
inline void* operator new[](size_t, void* addr) { return addr; }
#endif

View File

@@ -1,64 +0,0 @@
#pragma once
#include "BAN/Errors.h"
#include <BAN/Vector.h>
#include <BAN/Heap.h>
namespace BAN
{
template<typename T, typename Comp = less<T>>
class PriorityQueue
{
public:
PriorityQueue() = default;
PriorityQueue(Comp comp)
: m_comp(comp)
{ }
ErrorOr<void> push(const T& value)
{
TRY(m_data.push_back(value));
push_heap(m_data.begin(), m_data.end());
return {};
}
ErrorOr<void> push(T&& value)
{
TRY(m_data.push_back(move(value)));
push_heap(m_data.begin(), m_data.end());
return {};
}
template<typename... Args>
ErrorOr<void> emplace(Args&&... args) requires is_constructible_v<T, Args...>
{
TRY(m_data.emplace_back(forward<Args>(args)...));
push_heap(m_data.begin(), m_data.end());
return {};
}
void pop()
{
pop_heap(m_data.begin(), m_data.end());
m_data.pop_back();
}
BAN::ErrorOr<void> reserve(Vector<T>::size_type size)
{
return m_data.reserve(size);
}
T& top() { return m_data.front(); }
const T& top() const { return m_data.front(); }
bool empty() const { return m_data.empty(); }
Vector<T>::size_type size() const { return m_data.size(); }
Vector<T>::size_type capacity() const { return m_data.capacity(); }
private:
Comp m_comp;
Vector<T> m_data;
};
}

View File

@@ -5,7 +5,6 @@
#include <BAN/Math.h> #include <BAN/Math.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/New.h> #include <BAN/New.h>
#include <BAN/PlacementNew.h>
namespace BAN namespace BAN
{ {
@@ -31,7 +30,7 @@ namespace BAN
ErrorOr<void> push(T&&); ErrorOr<void> push(T&&);
ErrorOr<void> push(const T&); ErrorOr<void> push(const T&);
template<typename... Args> template<typename... Args>
ErrorOr<void> emplace(Args&&...) requires is_constructible_v<T, Args...>; ErrorOr<void> emplace(Args&&...);
ErrorOr<void> reserve(size_type); ErrorOr<void> reserve(size_type);
ErrorOr<void> shrink_to_fit(); ErrorOr<void> shrink_to_fit();
@@ -45,7 +44,6 @@ namespace BAN
void clear(); void clear();
bool empty() const; bool empty() const;
size_type capacity() const;
size_type size() const; size_type size() const;
const T& front() const; const T& front() const;
@@ -131,7 +129,7 @@ namespace BAN
template<typename T> template<typename T>
template<typename... Args> template<typename... Args>
ErrorOr<void> Queue<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<void> Queue<T>::emplace(Args&&... args)
{ {
TRY(ensure_capacity(m_size + 1)); TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(forward<Args>(args)...); new (m_data + m_size) T(forward<Args>(args)...);
@@ -187,12 +185,6 @@ namespace BAN
return m_size == 0; return m_size == 0;
} }
template<typename T>
typename Queue<T>::size_type Queue<T>::capacity() const
{
return m_capacity;
}
template<typename T> template<typename T>
typename Queue<T>::size_type Queue<T>::size() const typename Queue<T>::size_type Queue<T>::size() const
{ {

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <BAN/Atomic.h>
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/NoCopyMove.h> #include <BAN/NoCopyMove.h>
@@ -23,36 +22,24 @@ namespace BAN
void ref() const void ref() const
{ {
uint32_t old = m_ref_count.fetch_add(1, MemoryOrder::memory_order_relaxed); ASSERT(m_ref_count > 0);
ASSERT(old > 0); m_ref_count++;
}
bool try_ref() const
{
uint32_t expected = m_ref_count.load(MemoryOrder::memory_order_relaxed);
for (;;)
{
if (expected == 0)
return false;
if (m_ref_count.compare_exchange(expected, expected + 1, MemoryOrder::memory_order_acquire))
return true;
}
} }
void unref() const void unref() const
{ {
uint32_t old = m_ref_count.fetch_sub(1); ASSERT(m_ref_count > 0);
ASSERT(old > 0); m_ref_count--;
if (old == 1) if (m_ref_count == 0)
delete static_cast<const T*>(this); delete (const T*)this;
} }
protected: protected:
RefCounted() = default; RefCounted() = default;
virtual ~RefCounted() { ASSERT(m_ref_count == 0); } ~RefCounted() { ASSERT(m_ref_count == 0); }
private: private:
mutable Atomic<uint32_t> m_ref_count = 1; mutable uint32_t m_ref_count = 1;
}; };
template<typename T> template<typename T>
@@ -76,9 +63,8 @@ namespace BAN
return ptr; return ptr;
} }
// NOTE: don't use is_constructible_v<T, Args...> as RefPtr<T> is allowed with friends
template<typename... Args> template<typename... Args>
static ErrorOr<RefPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); } static ErrorOr<RefPtr> create(Args&&... args)
{ {
T* pointer = new T(forward<Args>(args)...); T* pointer = new T(forward<Args>(args)...);
if (pointer == nullptr) if (pointer == nullptr)
@@ -138,11 +124,8 @@ namespace BAN
T* operator->() { return ptr(); } T* operator->() { return ptr(); }
const T* operator->() const { return ptr(); } const T* operator->() const { return ptr(); }
bool operator==(RefPtr other) const { return m_pointer == other.m_pointer; }
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
bool empty() const { return m_pointer == nullptr; } bool empty() const { return m_pointer == nullptr; }
explicit operator bool() const { return m_pointer; } operator bool() const { return m_pointer; }
void clear() void clear()
{ {

View File

@@ -1,165 +0,0 @@
#pragma once
#include <BAN/Heap.h>
#include <BAN/Math.h>
#include <BAN/Swap.h>
#include <BAN/Traits.h>
#include <BAN/Vector.h>
namespace BAN::sort
{
template<typename It, typename Comp = less<it_value_type_t<It>>>
void exchange_sort(It begin, It end, Comp comp = {})
{
for (It lhs = begin; lhs != end; ++lhs)
for (It rhs = next(lhs, 1); rhs != end; ++rhs)
if (!comp(*lhs, *rhs))
swap(*lhs, *rhs);
}
namespace detail
{
template<typename It, typename Comp>
It partition(It begin, It end, Comp comp)
{
It pivot = prev(end, 1);
It it1 = begin;
for (It it2 = begin; it2 != pivot; ++it2)
{
if (comp(*it2, *pivot))
{
swap(*it1, *it2);
++it1;
}
}
swap(*it1, *pivot);
return it1;
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void quick_sort(It begin, It end, Comp comp = {})
{
if (distance(begin, end) <= 1)
return;
It mid = detail::partition(begin, end, comp);
quick_sort(begin, mid, comp);
quick_sort(++mid, end, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void insertion_sort(It begin, It end, Comp comp = {})
{
if (distance(begin, end) <= 1)
return;
for (It it1 = next(begin, 1); it1 != end; ++it1)
{
auto x = move(*it1);
It it2 = it1;
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
*it2 = move(*prev(it2, 1));
*it2 = move(x);
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void heap_sort(It begin, It end, Comp comp = {})
{
make_heap(begin, end, comp);
sort_heap(begin, end, comp);
}
namespace detail
{
template<typename It, typename Comp>
void intro_sort_impl(It begin, It end, size_t max_depth, Comp comp)
{
if (distance(begin, end) <= 16)
return insertion_sort(begin, end, comp);
if (max_depth == 0)
return heap_sort(begin, end, comp);
It mid = detail::partition(begin, end, comp);
intro_sort_impl(begin, mid, max_depth - 1, comp);
intro_sort_impl(++mid, end, max_depth - 1, comp);
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void intro_sort(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
detail::intro_sort_impl(begin, end, 2 * Math::ilog2(len), comp);
}
namespace detail
{
template<unsigned_integral T>
consteval T lsb_index(T value)
{
for (T result = 0;; result++)
if (value & (1 << result))
return result;
}
}
template<typename It, size_t radix = 256>
requires is_unsigned_v<it_value_type_t<It>> && (radix > 0 && (radix & (radix - 1)) == 0)
BAN::ErrorOr<void> radix_sort(It begin, It end)
{
using value_type = it_value_type_t<It>;
const size_t len = distance(begin, end);
if (len <= 1)
return {};
Vector<value_type> temp;
TRY(temp.resize(len));
Vector<size_t> counts;
TRY(counts.resize(radix));
constexpr size_t mask = radix - 1;
constexpr size_t shift = detail::lsb_index(radix);
for (size_t s = 0; s < sizeof(value_type) * 8; s += shift)
{
for (auto& cnt : counts)
cnt = 0;
for (It it = begin; it != end; ++it)
counts[(*it >> s) & mask]++;
for (size_t i = 0; i < radix - 1; i++)
counts[i + 1] += counts[i];
for (It it = end; it != begin;)
{
--it;
temp[--counts[(*it >> s) & mask]] = *it;
}
for (size_t j = 0; j < temp.size(); j++)
*next(begin, j) = temp[j];
}
return {};
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void sort(It begin, It end, Comp comp = {})
{
return intro_sort(begin, end, comp);
}
}

View File

@@ -14,89 +14,119 @@ namespace BAN
public: public:
using value_type = T; using value_type = T;
using size_type = size_t; using size_type = size_t;
using iterator = IteratorSimple<value_type, Span>; using iterator = IteratorSimple<T, Span>;
using const_iterator = ConstIteratorSimple<value_type, Span>; using const_iterator = ConstIteratorSimple<T, Span>;
private:
template<typename S>
static inline constexpr bool can_init_from_v = is_same_v<value_type, const S> || is_same_v<value_type, S>;
public: public:
Span() = default; Span() = default;
Span(value_type* data, size_type size) Span(T*, size_type);
: m_data(data) Span(Span<T>&);
, m_size(size)
{ }
template<typename S> template<typename S>
Span(const Span<S>& other) requires can_init_from_v<S> requires(is_same_v<T, const S>)
: m_data(other.m_data) Span(const Span<S>&);
, m_size(other.m_size)
{ }
template<typename S>
Span(Span<S>&& other) requires can_init_from_v<S>
: m_data(other.m_data)
, m_size(other.m_size)
{
other.clear();
}
template<typename S>
Span& operator=(const Span<S>& other) requires can_init_from_v<S>
{
m_data = other.m_data;
m_size = other.m_size;
return *this;
}
template<typename S>
Span& operator=(Span<S>&& other) requires can_init_from_v<S>
{
m_data = other.m_data;
m_size = other.m_size;
return *this;
}
iterator begin() { return iterator(m_data); } iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + m_size); } iterator end() { return iterator(m_data + m_size); }
const_iterator begin() const { return const_iterator(m_data); } const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + m_size); } const_iterator end() const { return const_iterator(m_data + m_size); }
value_type& operator[](size_type index) const T& operator[](size_type);
const T& operator[](size_type) const;
T* data();
const T* data() const;
bool empty() const;
size_type size() const;
void clear();
Span slice(size_type, size_type = ~size_type(0));
private:
T* m_data = nullptr;
size_type m_size = 0;
};
template<typename T>
Span<T>::Span(T* data, size_type size)
: m_data(data)
, m_size(size)
{ {
}
template<typename T>
Span<T>::Span(Span& other)
: m_data(other.data())
, m_size(other.size())
{
}
template<typename T>
template<typename S>
requires(is_same_v<T, const S>)
Span<T>::Span(const Span<S>& other)
: m_data(other.data())
, m_size(other.size())
{
}
template<typename T>
T& Span<T>::operator[](size_type index)
{
ASSERT(m_data);
ASSERT(index < m_size); ASSERT(index < m_size);
return m_data[index]; return m_data[index];
} }
value_type* data() const template<typename T>
const T& Span<T>::operator[](size_type index) const
{
ASSERT(m_data);
ASSERT(index < m_size);
return m_data[index];
}
template<typename T>
T* Span<T>::data()
{ {
return m_data; return m_data;
} }
bool empty() const { return m_size == 0; } template<typename T>
size_type size() const { return m_size; } const T* Span<T>::data() const
{
return m_data;
}
void clear() template<typename T>
bool Span<T>::empty() const
{
return m_size == 0;
}
template<typename T>
typename Span<T>::size_type Span<T>::size() const
{
return m_size;
}
template<typename T>
void Span<T>::clear()
{ {
m_data = nullptr; m_data = nullptr;
m_size = 0; m_size = 0;
} }
Span slice(size_type start, size_type length = ~size_type(0)) const template<typename T>
Span<T> Span<T>::slice(size_type start, size_type length)
{ {
ASSERT(m_data);
ASSERT(start <= m_size); ASSERT(start <= m_size);
if (length == ~size_type(0)) if (length == ~size_type(0))
length = m_size - start; length = m_size - start;
ASSERT(m_size - start >= length); ASSERT(start + length <= m_size);
return Span(m_data + start, length); return Span(m_data + start, m_size - start - length);
} }
Span<const value_type> as_const() const { return *this; }
private:
value_type* m_data = nullptr;
size_type m_size = 0;
friend class Span<const value_type>;
};
} }

View File

@@ -1,11 +1,10 @@
#pragma once #pragma once
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <BAN/ForwardList.h>
#include <BAN/Formatter.h> #include <BAN/Formatter.h>
#include <BAN/Hash.h> #include <BAN/Hash.h>
#include <BAN/Iterators.h> #include <BAN/Iterators.h>
#include <BAN/New.h>
#include <BAN/StringView.h>
namespace BAN namespace BAN
{ {
@@ -14,320 +13,85 @@ namespace BAN
{ {
public: public:
using size_type = size_t; using size_type = size_t;
using value_type = char;
using iterator = IteratorSimple<char, String>; using iterator = IteratorSimple<char, String>;
using const_iterator = ConstIteratorSimple<char, String>; using const_iterator = ConstIteratorSimple<char, String>;
static constexpr size_type sso_capacity = 15;
public: public:
String() {} String();
String(const String& other) { *this = other; } String(const String&);
String(String&& other) { *this = move(other); } String(String&&);
String(StringView other) { *this = other; } String(StringView);
~String() { clear(); } ~String();
template<typename... Args> template<typename... Args>
static BAN::ErrorOr<String> formatted(const char* format, Args&&... args) static String formatted(const char* format, const Args&... args);
String& operator=(const String&);
String& operator=(String&&);
String& operator=(StringView);
ErrorOr<void> push_back(char);
ErrorOr<void> insert(char, size_type);
ErrorOr<void> insert(StringView, size_type);
ErrorOr<void> append(StringView);
ErrorOr<void> append(const String&);
void pop_back();
void remove(size_type);
void erase(size_type, size_type);
void clear();
const_iterator begin() const { return const_iterator(m_data); }
iterator begin() { return iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + m_size); }
iterator end() { return iterator(m_data + m_size); }
char front() const { ASSERT(!empty()); return m_data[0]; }
char& front() { ASSERT(!empty()); return m_data[0]; }
char back() const { ASSERT(!empty()); return m_data[m_size - 1]; }
char& back() { ASSERT(!empty()); return m_data[m_size - 1]; }
char operator[](size_type) const;
char& operator[](size_type);
bool operator==(const String&) const;
bool operator==(StringView) const;
bool operator==(const char*) const;
ErrorOr<void> resize(size_type, char = '\0');
ErrorOr<void> reserve(size_type);
ErrorOr<void> shrink_to_fit();
StringView sv() const;
bool empty() const;
size_type size() const;
size_type capacity() const;
const char* data() const;
private:
ErrorOr<void> ensure_capacity(size_type);
ErrorOr<void> copy_impl(StringView);
void move_impl(String&&);
private:
char* m_data = nullptr;
size_type m_capacity = 0;
size_type m_size = 0;
};
template<typename... Args>
String String::formatted(const char* format, const Args&... args)
{ {
size_type length = 0;
BAN::Formatter::print([&](char) { length++; }, format, BAN::forward<Args>(args)...);
String result; String result;
TRY(result.reserve(length)); BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, args...);
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward<Args>(args)...);
return result; return result;
} }
String& operator=(const String& other)
{
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size() + 1);
m_size = other.size();
return *this;
}
String& operator=(String&& other)
{
clear();
if (other.has_sso())
memcpy(data(), other.data(), other.size() + 1);
else
{
m_storage.general_storage = other.m_storage.general_storage;
m_has_sso = false;
}
m_size = other.m_size;
other.m_size = 0;
other.m_storage.sso_storage = SSOStorage();
other.m_has_sso = true;
return *this;
}
String& operator=(StringView other)
{
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size());
m_size = other.size();
data()[m_size] = '\0';
return *this;
}
ErrorOr<void> push_back(char c)
{
TRY(ensure_capacity(m_size + 1));
data()[m_size] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> insert(char c, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1));
memmove(data() + index + 1, data() + index, m_size - index);
data()[index] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> insert(StringView str, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + str.size()));
memmove(data() + index + str.size(), data() + index, m_size - index);
memcpy(data() + index, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
ErrorOr<void> append(StringView str)
{
TRY(ensure_capacity(m_size + str.size()));
memcpy(data() + m_size, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
void pop_back()
{
ASSERT(m_size > 0);
m_size--;
data()[m_size] = '\0';
}
void remove(size_type index)
{
ASSERT(index < m_size);
memmove(data() + index, data() + index + 1, m_size - index);
m_size--;
data()[m_size] = '\0';
}
void clear()
{
if (!has_sso())
{
deallocator(m_storage.general_storage.data);
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
}
m_size = 0;
data()[m_size] = '\0';
}
const_iterator begin() const { return const_iterator(data()); }
iterator begin() { return iterator(data()); }
const_iterator end() const { return const_iterator(data() + size()); }
iterator end() { return iterator(data() + size()); }
char front() const { ASSERT(m_size > 0); return data()[0]; }
char& front() { ASSERT(m_size > 0); return data()[0]; }
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
bool operator==(const String& str) const
{
if (size() != str.size())
return false;
for (size_type i = 0; i < m_size; i++)
if (data()[i] != str.data()[i])
return false;
return true;
}
bool operator==(StringView str) const
{
if (size() != str.size())
return false;
for (size_type i = 0; i < m_size; i++)
if (data()[i] != str.data()[i])
return false;
return true;
}
bool operator==(const char* cstr) const
{
for (size_type i = 0; i < m_size; i++)
if (data()[i] != cstr[i])
return false;
if (cstr[size()] != '\0')
return false;
return true;
}
ErrorOr<void> resize(size_type new_size, char init_c = '\0')
{
if (m_size == new_size)
return {};
// expanding
if (m_size < new_size)
{
TRY(ensure_capacity(new_size));
memset(data() + m_size, init_c, new_size - m_size);
m_size = new_size;
data()[m_size] = '\0';
return {};
}
m_size = new_size;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> reserve(size_type new_size)
{
TRY(ensure_capacity(new_size));
return {};
}
ErrorOr<void> shrink_to_fit()
{
if (has_sso())
return {};
if (fits_in_sso())
{
char* data = m_storage.general_storage.data;
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
memcpy(this->data(), data, m_size + 1);
deallocator(data);
return {};
}
GeneralStorage& storage = m_storage.general_storage;
if (storage.capacity == m_size)
return {};
char* new_data = (char*)allocator(m_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
memcpy(new_data, storage.data, m_size);
deallocator(storage.data);
storage.capacity = m_size;
storage.data = new_data;
return {};
}
StringView sv() const { return StringView(data(), size()); }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
size_type capacity() const
{
if (has_sso())
return sso_capacity;
return m_storage.general_storage.capacity;
}
char* data()
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
const char* data() const
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
private:
ErrorOr<void> ensure_capacity(size_type new_size)
{
if (m_size >= new_size)
return {};
if (has_sso() && fits_in_sso(new_size))
return {};
char* new_data = (char*)allocator(new_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
if (m_size)
memcpy(new_data, data(), m_size + 1);
if (has_sso())
{
m_storage.general_storage = GeneralStorage();
m_has_sso = false;
}
else
deallocator(m_storage.general_storage.data);
auto& storage = m_storage.general_storage;
storage.capacity = new_size;
storage.data = new_data;
return {};
}
bool has_sso() const { return m_has_sso; }
bool fits_in_sso() const { return fits_in_sso(m_size); }
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
private:
struct SSOStorage
{
char data[sso_capacity + 1] {};
};
struct GeneralStorage
{
size_type capacity { 0 };
char* data { nullptr };
};
private:
union {
SSOStorage sso_storage;
GeneralStorage general_storage;
} m_storage { .sso_storage = SSOStorage() };
size_type m_size : sizeof(size_type) * 8 - 1 { 0 };
size_type m_has_sso : 1 { true };
};
template<> template<>
struct hash<String> struct hash<String>
{ {
@@ -353,9 +117,10 @@ namespace BAN::Formatter
{ {
template<typename F> template<typename F>
void print_argument(F putc, const String& string, const ValueFormat& format) void print_argument(F putc, const String& string, const ValueFormat&)
{ {
print_argument(putc, string.sv(), format); for (String::size_type i = 0; i < string.size(); i++)
putc(string[i]);
} }
} }

View File

@@ -1,11 +1,8 @@
#pragma once #pragma once
#include <BAN/Formatter.h>
#include <BAN/ForwardList.h> #include <BAN/ForwardList.h>
#include <BAN/Hash.h> #include <BAN/Formatter.h>
#include <BAN/Iterators.h> #include <BAN/Iterators.h>
#include <BAN/Optional.h>
#include <BAN/Vector.h>
namespace BAN namespace BAN
{ {
@@ -14,245 +11,55 @@ namespace BAN
{ {
public: public:
using size_type = size_t; using size_type = size_t;
using value_type = char;
using const_iterator = ConstIteratorSimple<char, StringView>; using const_iterator = ConstIteratorSimple<char, StringView>;
public: public:
constexpr StringView() {} StringView();
constexpr StringView(const char* string, size_type len = -1)
{
if (len == size_type(-1))
for (len = 0; string[len];)
len++;
m_data = string;
m_size = len;
}
StringView(const String&); StringView(const String&);
StringView(const char*, size_type = -1);
constexpr const_iterator begin() const { return const_iterator(m_data); } const_iterator begin() const { return const_iterator(m_data); }
constexpr const_iterator end() const { return const_iterator(m_data + m_size); } const_iterator end() const { return const_iterator(m_data + m_size); }
constexpr char operator[](size_type index) const char operator[](size_type) const;
{
ASSERT(index < m_size);
return m_data[index];
}
constexpr bool operator==(StringView other) const bool operator==(const String&) const;
{ bool operator==(StringView) const;
if (m_size != other.m_size) bool operator==(const char*) const;
return false;
for (size_type i = 0; i < m_size; i++)
if (m_data[i] != other.m_data[i])
return false;
return true;
}
constexpr bool operator==(const char* other) const StringView substring(size_type, size_type = -1) const;
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] != other[i])
return false;
return other[m_size] == '\0';
}
constexpr StringView substring(size_type index, size_type len = -1) const ErrorOr<Vector<StringView>> split(char, bool = false);
{ ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool = false);
ASSERT(index <= m_size);
if (len == size_type(-1))
len = m_size - index;
ASSERT(len <= m_size - index); // weird order to avoid overflow
StringView result;
result.m_data = m_data + index;
result.m_size = len;
return result;
}
ErrorOr<Vector<StringView>> split(char delim, bool allow_empties = false) const char back() const;
{ char front() const;
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result; bool contains(char) const;
TRY(result.reserve(count)); size_type count(char) const;
size_type start = 0; bool empty() const;
for (size_type i = 0; i < m_size; i++) size_type size() const;
{
if (m_data[i] == delim)
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start < m_size || (start == m_size && allow_empties))
TRY(result.push_back(this->substring(start)));
return result;
}
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool allow_empties = false) const const char* data() const;
{
size_type count = 0;
{
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
count++;
start = i + 1;
}
}
if (start != m_size)
count++;
}
Vector<StringView> result;
TRY(result.reserve(count));
size_type start = 0;
for (size_type i = 0; i < m_size; i++)
{
if (comp(m_data[i]))
{
if (allow_empties || start != i)
TRY(result.push_back(this->substring(start, i - start)));
start = i + 1;
}
}
if (start < m_size || (start == m_size && allow_empties))
TRY(result.push_back(this->substring(start)));
return result;
}
constexpr char back() const
{
ASSERT(m_size > 0);
return m_data[m_size - 1];
}
constexpr char front() const
{
ASSERT(m_size > 0);
return m_data[0];
}
BAN::Optional<size_type> find(char ch) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
return i;
return {};
}
BAN::Optional<size_type> find(bool(*comp)(char)) const
{
for (size_type i = 0; i < m_size; i++)
if (comp(m_data[i]))
return i;
return {};
}
BAN::Optional<size_type> rfind(char ch) const
{
for (size_type i = m_size; i > 0; i--)
if (m_data[i - 1] == ch)
return i - 1;
return {};
}
BAN::Optional<size_type> rfind(bool(*comp)(char)) const
{
for (size_type i = m_size; i > 0; i--)
if (comp(m_data[i - 1]))
return i - 1;
return {};
}
constexpr bool starts_with(BAN::StringView target) const
{
if (target.size() > m_size)
return false;
for (size_type i = 0; i < target.size(); i++)
if (m_data[i] != target[i])
return false;
return true;
}
constexpr bool contains(char ch) const
{
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
return true;
return false;
}
constexpr size_type count(char ch) const
{
size_type result = 0;
for (size_type i = 0; i < m_size; i++)
if (m_data[i] == ch)
result++;
return result;
}
constexpr bool empty() const { return m_size == 0; }
constexpr size_type size() const { return m_size; }
constexpr const char* data() const { return m_data; }
private: private:
const char* m_data = nullptr; const char* m_data = nullptr;
size_type m_size = 0; size_type m_size = 0;
}; };
template<>
struct hash<StringView>
{
hash_t operator()(StringView string) const
{
constexpr hash_t FNV_offset_basis = 0x811c9dc5;
constexpr hash_t FNV_prime = 0x01000193;
hash_t hash = FNV_offset_basis;
for (StringView::size_type i = 0; i < string.size(); i++)
{
hash *= FNV_prime;
hash ^= (uint8_t)string[i];
}
return hash;
}
};
} }
inline constexpr BAN::StringView operator""_sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); } inline BAN::StringView operator""sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
namespace BAN::Formatter namespace BAN::Formatter
{ {
template<typename F> template<typename F>
void print_argument(F putc, const StringView& sv, const ValueFormat& format) void print_argument(F putc, const StringView& sv, const ValueFormat&)
{ {
for (StringView::size_type i = 0; i < sv.size(); i++) for (StringView::size_type i = 0; i < sv.size(); i++)
putc(sv[i]); putc(sv[i]);
for (int i = sv.size(); i < format.fill; i++)
putc(' ');
} }
} }

View File

@@ -1,16 +0,0 @@
#pragma once
#include <BAN/Move.h>
namespace BAN
{
template<typename T>
void swap(T& lhs, T& rhs)
{
T tmp = move(lhs);
lhs = move(rhs);
rhs = move(tmp);
}
}

View File

@@ -3,10 +3,10 @@
namespace BAN namespace BAN
{ {
template<typename T> struct remove_reference { using type = T; }; template<typename T> struct remove_refenrece { using type = T; };
template<typename T> struct remove_reference<T&> { using type = T; }; template<typename T> struct remove_refenrece<T&> { using type = T; };
template<typename T> struct remove_reference<T&&> { using type = T; }; template<typename T> struct remove_refenrece<T&&> { using type = T; };
template<typename T> using remove_reference_t = typename remove_reference<T>::type; template<typename T> using remove_reference_t = typename remove_refenrece<T>::type;
template<typename T> struct remove_const { using type = T; }; template<typename T> struct remove_const { using type = T; };
template<typename T> struct remove_const<const T> { using type = T; }; template<typename T> struct remove_const<const T> { using type = T; };
@@ -34,36 +34,18 @@ namespace BAN
template<typename T1, typename T2> struct either_or<true, T1, T2> { using type = T1; }; template<typename T1, typename T2> struct either_or<true, T1, T2> { using type = T1; };
template<bool B, typename T1, typename T2> using either_or_t = typename either_or<B, T1, T2>::type; template<bool B, typename T1, typename T2> using either_or_t = typename either_or<B, T1, T2>::type;
template<typename T, T V> struct integral_constant { static constexpr T value = V; }; struct true_type { static constexpr bool value = true; };
template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value; struct false_type { static constexpr bool value = false; };
using true_type = integral_constant<bool, true>;
using false_type = integral_constant<bool, false>;
template<typename T, typename S> struct is_same : false_type {}; template<typename T, typename S> struct is_same : false_type {};
template<typename T> struct is_same<T, T> : true_type {}; template<typename T> struct is_same<T, T> : true_type {};
template<typename T, typename S> inline constexpr bool is_same_v = is_same<T, S>::value; template<typename T, typename S> inline constexpr bool is_same_v = is_same<T, S>::value;
template<typename T, typename S> concept same_as = BAN::is_same_v<T, S>;
template<typename T> struct is_lvalue_reference : false_type {}; template<typename T> struct is_lvalue_reference : false_type {};
template<typename T> struct is_lvalue_reference<T&> : true_type {}; template<typename T> struct is_lvalue_reference<T&> : true_type {};
template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value; template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>; template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>;
template<typename T, typename... Args> struct is_constructible { static constexpr bool value = __is_constructible(T, Args...); };
template<typename T, typename... Args> inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
template<typename T> struct is_default_constructible { static constexpr bool value = is_constructible_v<T>; };
template<typename T> inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;
template<typename T> struct is_copy_constructible { static constexpr bool value = is_constructible_v<T, const T&>; };
template<typename T> inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;
template<typename T> struct is_move_constructible { static constexpr bool value = is_constructible_v<T, T&&>; };
template<typename T> inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
template<typename T> struct is_trivially_copyable { static constexpr bool value = __is_trivially_copyable(T); };
template<typename T> inline constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; }; template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value; template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value;
template<typename T> concept integral = is_integral_v<T>; template<typename T> concept integral = is_integral_v<T>;
@@ -90,61 +72,13 @@ namespace BAN
template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; }; template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; };
template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value; template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); };
template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
template<typename T> struct is_pod { static constexpr bool value = __is_pod(T); };
template<typename T> inline constexpr bool is_pod_v = is_pod<T>::value;
namespace detail namespace detail
{ {
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); }; template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
template<typename T> struct is_signed<T, false> : false_type {}; template<typename T> struct is_signed<T, false> : false_type {};
template<typename T, bool = is_arithmetic_v<T>> struct is_unsigned { static constexpr bool value = T(0) < T(-1); };
template<typename T> struct is_unsigned<T, false> : false_type {};
} }
template<typename T> struct is_signed : detail::is_signed<T> {}; template<typename T> struct is_signed : detail::is_signed<T> {};
template<typename T> inline constexpr bool is_signed_v = is_signed<T>::value; template<typename T> inline constexpr bool is_signed_v = is_signed<T>::value;
template<typename T> concept signed_integral = is_signed_v<T> && is_integral_v<T>;
template<typename T> struct is_unsigned : detail::is_unsigned<T> {};
template<typename T> inline constexpr bool is_unsigned_v = is_unsigned<T>::value;
template<typename T> concept unsigned_integral = is_unsigned_v<T> && is_integral_v<T>;
#define __BAN_TRAITS_MAKE_UNSIGNED_CV(__type) \
template<> struct make_unsigned<__type> { using type = unsigned __type; }; \
template<> struct make_unsigned<const __type> { using type = unsigned const __type; }; \
template<> struct make_unsigned<volatile __type> { using type = unsigned volatile __type; }; \
template<> struct make_unsigned<const volatile __type> { using type = unsigned const volatile __type; };
template<typename T> requires is_arithmetic_v<T> struct make_unsigned { using type = T; };
__BAN_TRAITS_MAKE_UNSIGNED_CV(char)
__BAN_TRAITS_MAKE_UNSIGNED_CV(short)
__BAN_TRAITS_MAKE_UNSIGNED_CV(int)
__BAN_TRAITS_MAKE_UNSIGNED_CV(long)
__BAN_TRAITS_MAKE_UNSIGNED_CV(long long)
template<typename T> using make_unsigned_t = typename make_unsigned<T>::type;
#undef __BAN_TRAITS_MAKE_UNSIGNED_CV
#define __BAN_TRAITS_MAKE_SIGNED_CV(__type) \
template<> struct make_signed<unsigned __type> { using type = __type; }; \
template<> struct make_signed<unsigned const __type> { using type = const __type; }; \
template<> struct make_signed<unsigned volatile __type> { using type = volatile __type; }; \
template<> struct make_signed<unsigned const volatile __type> { using type = const volatile __type; };
template<typename T> requires is_arithmetic_v<T> struct make_signed { using type = T; };
__BAN_TRAITS_MAKE_SIGNED_CV(char)
__BAN_TRAITS_MAKE_SIGNED_CV(short)
__BAN_TRAITS_MAKE_SIGNED_CV(int)
__BAN_TRAITS_MAKE_SIGNED_CV(long)
__BAN_TRAITS_MAKE_SIGNED_CV(long long)
template<typename T> using make_signed_t = typename make_signed<T>::type;
#undef __BAN_TRAITS_MAKE_SIGNED_CV
template<typename T> struct it_value_type { using value_type = T::value_type; };
template<typename T> struct it_value_type<T*> { using value_type = T; };
template<typename T> using it_value_type_t = typename it_value_type<T>::value_type;
template<typename T> struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; template<typename T> struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } };
template<typename T> struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } }; template<typename T> struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } };

View File

@@ -18,24 +18,23 @@ namespace BAN::UTF8
return 3; return 3;
if ((first_byte & 0xF8) == 0xF0) if ((first_byte & 0xF8) == 0xF0)
return 4; return 4;
return UTF8::invalid; return 0;
} }
template<typename T> requires (sizeof(T) == 1) constexpr uint32_t to_codepoint(uint8_t* bytes)
constexpr uint32_t to_codepoint(const T* bytes)
{ {
uint32_t length = byte_length(bytes[0]); uint32_t length = byte_length(bytes[0]);
for (uint32_t i = 1; i < length; i++) for (uint32_t i = 1; i < length; i++)
if (((uint8_t)bytes[i] & 0xC0) != 0x80) if ((bytes[i] & 0xC0) != 0x80)
return UTF8::invalid; return UTF8::invalid;
switch (length) switch (length)
{ {
case 1: return (((uint8_t)bytes[0] & 0x80) != 0x00) ? UTF8::invalid : (uint8_t)bytes[0]; case 1: return ((bytes[0] & 0x80) != 0x00) ? UTF8::invalid : bytes[0];
case 2: return (((uint8_t)bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x1F) << 6) | ((uint8_t)bytes[1] & 0x3F); case 2: return ((bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
case 3: return (((uint8_t)bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x0F) << 12) | (((uint8_t)bytes[1] & 0x3F) << 6) | ((uint8_t)bytes[2] & 0x3F); case 3: return ((bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) | (bytes[2] & 0x3F);
case 4: return (((uint8_t)bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x07) << 18) | (((uint8_t)bytes[1] & 0x3F) << 12) | (((uint8_t)bytes[2] & 0x3F) << 6) | ((uint8_t)bytes[3] & 0x3F); case 4: return ((bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) | ((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F);
} }
return UTF8::invalid; return UTF8::invalid;
@@ -76,8 +75,6 @@ namespace BAN::UTF8
} }
} }
*ptr = '\0';
return true; return true;
} }

View File

@@ -33,9 +33,8 @@ namespace BAN
return uniq; return uniq;
} }
// NOTE: don't use is_constructible_v<T, Args...> as UniqPtr<T> is allowed with friends
template<typename... Args> template<typename... Args>
static BAN::ErrorOr<UniqPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); } static BAN::ErrorOr<UniqPtr> create(Args&&... args)
{ {
UniqPtr uniq; UniqPtr uniq;
uniq.m_pointer = new T(BAN::forward<Args>(args)...); uniq.m_pointer = new T(BAN::forward<Args>(args)...);

View File

@@ -3,7 +3,6 @@
#include <BAN/Assert.h> #include <BAN/Assert.h>
#include <BAN/Math.h> #include <BAN/Math.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/PlacementNew.h>
#include <string.h> #include <string.h>
@@ -43,10 +42,9 @@ namespace BAN
void destruct(size_t index, uint8_t* data) void destruct(size_t index, uint8_t* data)
{ {
if (index == 0) if (index == 0)
{
if constexpr(!is_lvalue_reference_v<T>) if constexpr(!is_lvalue_reference_v<T>)
reinterpret_cast<T*>(data)->~T(); reinterpret_cast<T*>(data)->~T();
} else;
else if constexpr(sizeof...(Ts) > 0) else if constexpr(sizeof...(Ts) > 0)
destruct<Ts...>(index - 1, data); destruct<Ts...>(index - 1, data);
else else
@@ -126,7 +124,6 @@ namespace BAN
Variant(Variant&& other) Variant(Variant&& other)
: m_index(other.m_index) : m_index(other.m_index)
{ {
if (other.has_value())
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage); detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
other.clear(); other.clear();
} }
@@ -134,7 +131,6 @@ namespace BAN
Variant(const Variant& other) Variant(const Variant& other)
: m_index(other.m_index) : m_index(other.m_index)
{ {
if (other.has_value())
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage); detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
} }
@@ -159,12 +155,11 @@ namespace BAN
Variant& operator=(Variant&& other) Variant& operator=(Variant&& other)
{ {
if (m_index == other.m_index && m_index != invalid_index()) if (m_index == other.m_index)
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage); detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
else else
{ {
clear(); clear();
if (other.has_value())
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage); detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
m_index = other.m_index; m_index = other.m_index;
} }
@@ -174,12 +169,11 @@ namespace BAN
Variant& operator=(const Variant& other) Variant& operator=(const Variant& other)
{ {
if (m_index == other.m_index && m_index != invalid_index()) if (m_index == other.m_index)
detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage); detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
else else
{ {
clear(); clear();
if (other.has_value())
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage); detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
m_index = other.m_index; m_index = other.m_index;
} }
@@ -220,14 +214,6 @@ namespace BAN
return m_index == detail::index<T, Ts...>(); return m_index == detail::index<T, Ts...>();
} }
template<typename T, typename... Args>
void emplace(Args&&... args) requires (can_have<T>() && is_constructible_v<T, Args...>)
{
clear();
m_index = detail::index<T, Ts...>();
new (m_storage) T(BAN::forward<Args>(args)...);
}
template<typename T> template<typename T>
void set(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>) void set(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
{ {
@@ -290,16 +276,6 @@ namespace BAN
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage); return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
} }
bool has_value() const
{
return m_index != invalid_index();
}
explicit operator bool() const
{
return has_value();
}
void clear() void clear()
{ {
if (m_index != invalid_index()) if (m_index != invalid_index())

View File

@@ -5,9 +5,7 @@
#include <BAN/Math.h> #include <BAN/Math.h>
#include <BAN/Move.h> #include <BAN/Move.h>
#include <BAN/New.h> #include <BAN/New.h>
#include <BAN/PlacementNew.h>
#include <BAN/Span.h> #include <BAN/Span.h>
#include <BAN/Swap.h>
namespace BAN namespace BAN
{ {
@@ -35,9 +33,9 @@ namespace BAN
ErrorOr<void> push_back(T&&); ErrorOr<void> push_back(T&&);
ErrorOr<void> push_back(const T&); ErrorOr<void> push_back(const T&);
template<typename... Args> template<typename... Args>
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>; ErrorOr<void> emplace_back(Args&&...);
template<typename... Args> template<typename... Args>
ErrorOr<void> emplace(size_type, Args&&...) requires is_constructible_v<T, Args...>; ErrorOr<void> emplace(size_type, Args&&...);
ErrorOr<void> insert(size_type, T&&); ErrorOr<void> insert(size_type, T&&);
ErrorOr<void> insert(size_type, const T&); ErrorOr<void> insert(size_type, const T&);
@@ -56,7 +54,7 @@ namespace BAN
bool contains(const T&) const; bool contains(const T&) const;
Span<T> span() { return Span(m_data, m_size); } Span<T> span() { return Span(m_data, m_size); }
Span<const T> span() const { return Span(m_data, m_size); } const Span<T> span() const { return Span(m_data, m_size); }
const T& operator[](size_type) const; const T& operator[](size_type) const;
T& operator[](size_type); T& operator[](size_type);
@@ -66,10 +64,7 @@ namespace BAN
const T& front() const; const T& front() const;
T& front(); T& front();
void reverse(); ErrorOr<void> resize(size_type, const T& = T());
ErrorOr<void> resize(size_type) requires is_default_constructible_v<T>;
ErrorOr<void> resize(size_type, const T&) requires is_copy_constructible_v<T>;
ErrorOr<void> reserve(size_type); ErrorOr<void> reserve(size_type);
ErrorOr<void> shrink_to_fit(); ErrorOr<void> shrink_to_fit();
@@ -141,13 +136,10 @@ namespace BAN
template<typename T> template<typename T>
Vector<T>& Vector<T>::operator=(const Vector<T>& other) Vector<T>& Vector<T>::operator=(const Vector<T>& other)
{ {
clear();
MUST(ensure_capacity(other.size())); MUST(ensure_capacity(other.size()));
for (size_type i = 0; i < BAN::Math::min(size(), other.size()); i++) for (size_type i = 0; i < other.size(); i++)
m_data[i] = other.m_data[i];
for (size_type i = size(); i < other.size(); i++)
new (m_data + i) T(other[i]); new (m_data + i) T(other[i]);
for (size_type i = other.size(); i < size(); i++)
m_data[i].~T();
m_size = other.m_size; m_size = other.m_size;
return *this; return *this;
} }
@@ -169,7 +161,7 @@ namespace BAN
template<typename T> template<typename T>
template<typename... Args> template<typename... Args>
ErrorOr<void> Vector<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<void> Vector<T>::emplace_back(Args&&... args)
{ {
TRY(ensure_capacity(m_size + 1)); TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(forward<Args>(args)...); new (m_data + m_size) T(forward<Args>(args)...);
@@ -179,7 +171,7 @@ namespace BAN
template<typename T> template<typename T>
template<typename... Args> template<typename... Args>
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args) requires is_constructible_v<T, Args...> ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args)
{ {
ASSERT(index <= m_size); ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1)); TRY(ensure_capacity(m_size + 1));
@@ -305,28 +297,7 @@ namespace BAN
} }
template<typename T> template<typename T>
void Vector<T>::reverse() ErrorOr<void> Vector<T>::resize(size_type size, const T& value)
{
for (size_type i = 0; i < m_size / 2; i++)
BAN::swap(m_data[i], m_data[m_size - i - 1]);
}
template<typename T>
ErrorOr<void> Vector<T>::resize(size_type size) requires is_default_constructible_v<T>
{
TRY(ensure_capacity(size));
if (size < m_size)
for (size_type i = size; i < m_size; i++)
m_data[i].~T();
if (size > m_size)
for (size_type i = m_size; i < size; i++)
new (m_data + i) T();
m_size = size;
return {};
}
template<typename T>
ErrorOr<void> Vector<T>::resize(size_type size, const T& value) requires is_copy_constructible_v<T>
{ {
TRY(ensure_capacity(size)); TRY(ensure_capacity(size));
if (size < m_size) if (size < m_size)
@@ -381,35 +352,10 @@ namespace BAN
template<typename T> template<typename T>
ErrorOr<void> Vector<T>::ensure_capacity(size_type size) ErrorOr<void> Vector<T>::ensure_capacity(size_type size)
{ {
static_assert(alignof(T) <= alignof(max_align_t), "over aligned types not supported");
if (m_capacity >= size) if (m_capacity >= size)
return {}; return {};
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
const size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2); T* new_data = (T*)BAN::allocator(new_cap * sizeof(T));
if constexpr (BAN::is_trivially_copyable_v<T>)
{
if constexpr (BAN::reallocator)
{
auto* new_data = static_cast<T*>(BAN::reallocator(m_data, new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
m_data = new_data;
}
else
{
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
memcpy(new_data, m_data, m_size * sizeof(T));
BAN::deallocator(m_data);
m_data = new_data;
}
}
else
{
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
if (new_data == nullptr) if (new_data == nullptr)
return Error::from_errno(ENOMEM); return Error::from_errno(ENOMEM);
for (size_type i = 0; i < m_size; i++) for (size_type i = 0; i < m_size; i++)
@@ -419,8 +365,6 @@ namespace BAN
} }
BAN::deallocator(m_data); BAN::deallocator(m_data);
m_data = new_data; m_data = new_data;
}
m_capacity = new_cap; m_capacity = new_cap;
return {}; return {};
} }

View File

@@ -2,10 +2,6 @@
#include <BAN/RefPtr.h> #include <BAN/RefPtr.h>
#if __is_kernel
#include <kernel/Lock/SpinLock.h>
#endif
namespace BAN namespace BAN
{ {
@@ -15,37 +11,22 @@ namespace BAN
template<typename T> template<typename T>
class WeakPtr; class WeakPtr;
// FIXME: Write this without using locks...
template<typename T> template<typename T>
class WeakLink : public RefCounted<WeakLink<T>> class WeakLink : public RefCounted<WeakLink<T>>
{ {
public: public:
RefPtr<T> try_lock() const RefPtr<T> lock() { ASSERT(m_ptr); return raw_ptr(); }
{ T* raw_ptr() { return m_ptr; }
#if __is_kernel
Kernel::SpinLockGuard _(m_weak_lock);
#endif
if (m_ptr && m_ptr->try_ref())
return RefPtr<T>::adopt(m_ptr);
return nullptr;
}
bool valid() const { return m_ptr; } bool valid() const { return m_ptr; }
void invalidate() void invalidate() { m_ptr = nullptr; }
{
#if __is_kernel
Kernel::SpinLockGuard _(m_weak_lock);
#endif
m_ptr = nullptr;
}
private: private:
WeakLink(T* ptr) : m_ptr(ptr) {} WeakLink(T* ptr) : m_ptr(ptr) {}
private: private:
T* m_ptr; T* m_ptr;
#if __is_kernel
mutable Kernel::SpinLock m_weak_lock;
#endif
friend class RefPtr<WeakLink<T>>; friend class RefPtr<WeakLink<T>>;
}; };
@@ -53,7 +34,7 @@ namespace BAN
class Weakable class Weakable
{ {
public: public:
virtual ~Weakable() ~Weakable()
{ {
if (m_link) if (m_link)
m_link->invalidate(); m_link->invalidate();
@@ -99,10 +80,10 @@ namespace BAN
return *this; return *this;
} }
RefPtr<T> lock() const RefPtr<T> lock()
{ {
if (m_link) if (m_link->valid())
return m_link->try_lock(); return m_link->lock();
return nullptr; return nullptr;
} }
@@ -110,8 +91,6 @@ namespace BAN
bool valid() const { return m_link && m_link->valid(); } bool valid() const { return m_link && m_link->valid(); }
explicit operator bool() const { return valid(); }
private: private:
WeakPtr(const RefPtr<WeakLink<T>>& link) WeakPtr(const RefPtr<WeakLink<T>>& link)
: m_link(link) : m_link(link)

View File

@@ -1,54 +1,118 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.26)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "banan-os") if(DEFINED ENV{BANAN_ARCH})
message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os") set(BANAN_ARCH $ENV{BANAN_ARCH})
endif () else()
set(BANAN_ARCH x86_64)
endif()
project(banan-os CXX C ASM) set(TOOLCHAIN_PREFIX ${CMAKE_SOURCE_DIR}/toolchain/local)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++)
set(CMAKE_CXX_COMPILER_WORKS True)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-gcc)
set(CMAKE_C_COMPILER_WORKS True)
if(NOT EXISTS ${CMAKE_CXX_COMPILER})
set(CMAKE_CXX_COMPILER g++)
endif()
if(DEFINED QEMU_ACCEL)
set(QEMU_ACCEL -accel ${QEMU_ACCEL})
endif()
add_compile_options(-mno-sse -mno-sse2)
add_compile_definitions(__enable_sse=0)
project(banan-os CXX)
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
set(BANAN_SYSROOT ${CMAKE_BINARY_DIR}/sysroot)
set(BANAN_INCLUDE ${BANAN_SYSROOT}/usr/include) set(BANAN_INCLUDE ${BANAN_SYSROOT}/usr/include)
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib) set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin) set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
set(BANAN_ETC ${BANAN_SYSROOT}/usr/etc)
set(BANAN_SHARE ${BANAN_SYSROOT}/usr/share)
set(BANAN_BOOT ${BANAN_SYSROOT}/boot) set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
set(DISK_IMAGE_PATH ${CMAKE_BINARY_DIR}/banan-os.img)
set(CMAKE_INSTALL_BINDIR ${BANAN_BIN})
set(CMAKE_INSTALL_SBINDIR ${BANAN_BIN})
set(CMAKE_INSTALL_LIBDIR ${BANAN_LIB})
set(CMAKE_INSTALL_INCLUDEDIR ${BANAN_INCLUDE})
set(CMAKE_INSTALL_SYSCONF ${BANAN_ETC})
set(CMAKE_INSTALL_MESSAGE NEVER)
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY True)
set(CMAKE_STATIC_LIBRARY_PREFIX "")
set(CMAKE_SHARED_LIBRARY_PREFIX "")
set(BUILD_SHARED_LIBS True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# include headers of ${library} to ${target}
function(banan_include_headers target library)
target_include_directories(${target} PUBLIC $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
endfunction()
# include headers and link ${library} to ${target}
function(banan_link_library target library)
target_link_libraries(${target} PUBLIC ${library})
banan_include_headers(${target} ${library})
endfunction()
# add install step for all header files of target
function(banan_install_headers target)
file(GLOB_RECURSE headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/include *.h)
foreach(header ${headers})
get_filename_component(subdirectory ${header} DIRECTORY)
install(FILES include/${header} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${subdirectory})
endforeach()
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
endfunction()
add_subdirectory(kernel) add_subdirectory(kernel)
add_subdirectory(bootloader)
add_subdirectory(BAN) add_subdirectory(BAN)
add_subdirectory(libc)
add_subdirectory(LibELF)
add_subdirectory(userspace) add_subdirectory(userspace)
add_custom_target(sysroot
COMMAND mkdir -p ${BANAN_SYSROOT}
COMMAND cd ${BANAN_SYSROOT} && sudo tar xf ${BANAN_BASE_SYSROOT}
USES_TERMINAL
)
add_custom_target(headers
DEPENDS kernel-headers
DEPENDS ban-headers
DEPENDS libc-headers
DEPENDS libelf-headers
)
add_custom_target(toolchain
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" PREFIX="${TOOLCHAIN_PREFIX}" ARCH="${BANAN_ARCH}" ${CMAKE_SOURCE_DIR}/toolchain/build.sh
DEPENDS headers
USES_TERMINAL
)
add_custom_target(libstdc++
COMMAND ${CMAKE_COMMAND} -E env LIBSTDCPP="1" ${CMAKE_SOURCE_DIR}/toolchain/build.sh
DEPENDS libc-install
USES_TERMINAL
)
add_custom_target(image
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image.sh
DEPENDS kernel-install
DEPENDS ban-install
DEPENDS libc-install
DEPENDS userspace-install
DEPENDS libelf-install
USES_TERMINAL
)
add_custom_target(image-full
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image-full.sh
DEPENDS kernel-install
DEPENDS ban-install
DEPENDS libc-install
DEPENDS userspace-install
DEPENDS libelf-install
USES_TERMINAL
)
add_custom_target(check-fs
COMMAND ${CMAKE_COMMAND} -E env DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/check-fs.sh
USES_TERMINAL
)
add_custom_target(qemu
COMMAND ${CMAKE_COMMAND} -E env BANAN_ARCH="${BANAN_ARCH}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/qemu.sh -serial stdio ${QEMU_ACCEL}
DEPENDS image
USES_TERMINAL
)
add_custom_target(qemu-nographic
COMMAND ${CMAKE_COMMAND} -E env BANAN_ARCH="${BANAN_ARCH}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/qemu.sh -nographic ${QEMU_ACCEL}
DEPENDS image
USES_TERMINAL
)
add_custom_target(qemu-debug
COMMAND ${CMAKE_COMMAND} -E env BANAN_ARCH="${BANAN_ARCH}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/qemu.sh -serial stdio -d int -no-reboot
DEPENDS image
USES_TERMINAL
)
add_custom_target(bochs
COMMAND ${CMAKE_COMMAND} -E env DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/bochs.sh
DEPENDS image
USES_TERMINAL
)

View File

13
LibELF/CMakeLists.txt Normal file
View File

@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.26)
project(LibELF CXX)
add_custom_target(libelf-headers
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
USES_TERMINAL
)
add_custom_target(libelf-install
DEPENDS libelf-headers
)

406
LibELF/LibELF/ELF.cpp Normal file
View File

@@ -0,0 +1,406 @@
#include <BAN/ScopeGuard.h>
#include <LibELF/ELF.h>
#include <LibELF/Values.h>
#ifdef __is_kernel
#include <kernel/FS/VirtualFileSystem.h>
#include <kernel/Memory/PageTableScope.h>
#include <kernel/Process.h>
#endif
#include <fcntl.h>
#define ELF_PRINT_HEADERS 0
#ifdef __is_kernel
extern uint8_t g_kernel_end[];
using namespace Kernel;
#endif
namespace LibELF
{
#ifdef __is_kernel
BAN::ErrorOr<BAN::UniqPtr<ELF>> ELF::load_from_file(BAN::RefPtr<Inode> inode)
{
BAN::Vector<uint8_t> buffer;
TRY(buffer.resize(inode->size()));
TRY(inode->read(0, buffer.data(), inode->size()));
ELF* elf_ptr = new ELF(BAN::move(buffer));
if (elf_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto elf = BAN::UniqPtr<ELF>::adopt(elf_ptr);
TRY(elf->load());
return BAN::move(elf);
}
#else
BAN::ErrorOr<ELF*> ELF::load_from_file(BAN::StringView file_path)
{
ELF* elf = nullptr;
{
BAN::Vector<uint8_t> data;
int fd = TRY(Kernel::Process::current().open(file_path, O_RDONLY));
BAN::ScopeGuard _([fd] { MUST(Kernel::Process::current().close(fd)); });
struct stat st;
TRY(Kernel::Process::current().fstat(fd, &st));
TRY(data.resize(st.st_size));
TRY(Kernel::Process::current().read(fd, data.data(), data.size()));
elf = new ELF(BAN::move(data));
ASSERT(elf);
}
if (auto res = elf->load(); res.is_error())
{
delete elf;
return res.error();
}
return elf;
}
#endif
BAN::ErrorOr<void> ELF::load()
{
if (m_data.size() < EI_NIDENT)
{
dprintln("Too small ELF file");
return BAN::Error::from_errno(EINVAL);
}
if (m_data[EI_MAG0] != ELFMAG0 ||
m_data[EI_MAG1] != ELFMAG1 ||
m_data[EI_MAG2] != ELFMAG2 ||
m_data[EI_MAG3] != ELFMAG3)
{
dprintln("Invalid ELF header");
return BAN::Error::from_errno(EINVAL);
}
if (m_data[EI_DATA] != ELFDATA2LSB)
{
dprintln("Only little-endian is supported");
return BAN::Error::from_errno(EINVAL);
}
if (m_data[EI_VERSION] != EV_CURRENT)
{
dprintln("Invalid ELF version");
return BAN::Error::from_errno(EINVAL);
}
if (m_data[EI_CLASS] == ELFCLASS64)
{
if (m_data.size() <= sizeof(Elf64FileHeader))
{
dprintln("Too small ELF file");
return BAN::Error::from_errno(EINVAL);
}
auto& header = file_header64();
if (!parse_elf64_file_header(header))
return BAN::Error::from_errno(EINVAL);
for (size_t i = 0; i < header.e_phnum; i++)
{
auto& program_header = program_header64(i);
if (!parse_elf64_program_header(program_header))
return BAN::Error::from_errno(EINVAL);
}
for (size_t i = 1; i < header.e_shnum; i++)
{
auto& section_header = section_header64(i);
if (!parse_elf64_section_header(section_header))
return BAN::Error::from_errno(EINVAL);
}
}
else if (m_data[EI_CLASS] == ELFCLASS32)
{
if (m_data.size() <= sizeof(Elf32FileHeader))
{
dprintln("Too small ELF file");
return BAN::Error::from_errno(EINVAL);
}
auto& header = file_header32();
if (!parse_elf32_file_header(header))
return BAN::Error::from_errno(EINVAL);
for (size_t i = 0; i < header.e_phnum; i++)
{
auto& program_header = program_header32(i);
if (!parse_elf32_program_header(program_header))
return BAN::Error::from_errno(EINVAL);
}
for (size_t i = 1; i < header.e_shnum; i++)
{
auto& section_header = section_header32(i);
if (!parse_elf32_section_header(section_header))
return BAN::Error::from_errno(EINVAL);
}
}
return {};
}
bool ELF::is_x86_32() const { return m_data[EI_CLASS] == ELFCLASS32; }
bool ELF::is_x86_64() const { return m_data[EI_CLASS] == ELFCLASS64; }
/*
64 bit ELF
*/
const char* ELF::lookup_section_name64(uint32_t offset) const
{
return lookup_string64(file_header64().e_shstrndx, offset);
}
const char* ELF::lookup_string64(size_t table_index, uint32_t offset) const
{
if (table_index == SHN_UNDEF)
return nullptr;
auto& section_header = section_header64(table_index);
return (const char*)m_data.data() + section_header.sh_offset + offset;
}
bool ELF::parse_elf64_file_header(const Elf64FileHeader& header)
{
if (header.e_type != ET_EXEC)
{
dprintln("Only executable files are supported");
return false;
}
if (header.e_version != EV_CURRENT)
{
dprintln("Invalid ELF version");
return false;
}
return true;
}
bool ELF::parse_elf64_program_header(const Elf64ProgramHeader& header)
{
#if ELF_PRINT_HEADERS
dprintln("program header");
dprintln(" type {H}", header.p_type);
dprintln(" flags {H}", header.p_flags);
dprintln(" offset {H}", header.p_offset);
dprintln(" vaddr {H}", header.p_vaddr);
dprintln(" paddr {H}", header.p_paddr);
dprintln(" filesz {}", header.p_filesz);
dprintln(" memsz {}", header.p_memsz);
dprintln(" align {}", header.p_align);
#endif
(void)header;
return true;
}
bool ELF::parse_elf64_section_header(const Elf64SectionHeader& header)
{
#if ELF_PRINT_HEADERS
if (auto* name = lookup_section_name64(header.sh_name))
dprintln("{}", name);
switch (header.sh_type)
{
case SHT_NULL:
dprintln(" SHT_NULL");
break;
case SHT_PROGBITS:
dprintln(" SHT_PROGBITS");
break;
case SHT_SYMTAB:
for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++)
{
auto& symbol = ((const Elf64Symbol*)(m_data.data() + header.sh_offset))[i];
if (auto* name = lookup_string64(header.sh_link, symbol.st_name))
dprintln(" {}", name);
}
break;
case SHT_STRTAB:
dprintln(" SHT_STRTAB");
break;
case SHT_RELA:
dprintln(" SHT_RELA");
break;
case SHT_NOBITS:
dprintln(" SHT_NOBITS");
break;
case SHT_REL:
dprintln(" SHT_REL");
break;
case SHT_SHLIB:
dprintln(" SHT_SHLIB");
break;
case SHT_DYNSYM:
dprintln(" SHT_DYNSYM");
break;
default:
ASSERT(false);
}
#endif
(void)header;
return true;
}
const Elf64FileHeader& ELF::file_header64() const
{
ASSERT(is_x86_64());
return *(const Elf64FileHeader*)m_data.data();
}
const Elf64ProgramHeader& ELF::program_header64(size_t index) const
{
ASSERT(is_x86_64());
const auto& file_header = file_header64();
ASSERT(index < file_header.e_phnum);
return *(const Elf64ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index);
}
const Elf64SectionHeader& ELF::section_header64(size_t index) const
{
ASSERT(is_x86_64());
const auto& file_header = file_header64();
ASSERT(index < file_header.e_shnum);
return *(const Elf64SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index);
}
/*
32 bit ELF
*/
const char* ELF::lookup_section_name32(uint32_t offset) const
{
return lookup_string32(file_header32().e_shstrndx, offset);
}
const char* ELF::lookup_string32(size_t table_index, uint32_t offset) const
{
if (table_index == SHN_UNDEF)
return nullptr;
auto& section_header = section_header32(table_index);
return (const char*)m_data.data() + section_header.sh_offset + offset;
}
bool ELF::parse_elf32_file_header(const Elf32FileHeader& header)
{
if (header.e_type != ET_EXEC)
{
dprintln("Only executable files are supported");
return false;
}
if (header.e_version != EV_CURRENT)
{
dprintln("Invalid ELF version");
return false;
}
return true;
}
bool ELF::parse_elf32_program_header(const Elf32ProgramHeader& header)
{
#if ELF_PRINT_HEADERS
dprintln("program header");
dprintln(" type {H}", header.p_type);
dprintln(" flags {H}", header.p_flags);
dprintln(" offset {H}", header.p_offset);
dprintln(" vaddr {H}", header.p_vaddr);
dprintln(" paddr {H}", header.p_paddr);
dprintln(" filesz {}", header.p_filesz);
dprintln(" memsz {}", header.p_memsz);
dprintln(" align {}", header.p_align);
#endif
(void)header;
return true;
}
bool ELF::parse_elf32_section_header(const Elf32SectionHeader& header)
{
#if ELF_PRINT_HEADERS
if (auto* name = lookup_section_name32(header.sh_name))
dprintln("{}", name);
switch (header.sh_type)
{
case SHT_NULL:
dprintln(" SHT_NULL");
break;
case SHT_PROGBITS:
dprintln(" SHT_PROGBITS");
break;
case SHT_SYMTAB:
for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++)
{
auto& symbol = ((const Elf32Symbol*)(m_data.data() + header.sh_offset))[i];
if (auto* name = lookup_string32(header.sh_link, symbol.st_name))
dprintln(" {}", name);
}
break;
case SHT_STRTAB:
dprintln(" SHT_STRTAB");
break;
case SHT_RELA:
dprintln(" SHT_RELA");
break;
case SHT_NOBITS:
dprintln(" SHT_NOBITS");
break;
case SHT_REL:
dprintln(" SHT_REL");
break;
case SHT_SHLIB:
dprintln(" SHT_SHLIB");
break;
case SHT_DYNSYM:
dprintln(" SHT_DYNSYM");
break;
default:
ASSERT(false);
}
#endif
(void)header;
return true;
}
const Elf32FileHeader& ELF::file_header32() const
{
ASSERT(is_x86_32());
return *(const Elf32FileHeader*)m_data.data();
}
const Elf32ProgramHeader& ELF::program_header32(size_t index) const
{
ASSERT(is_x86_32());
const auto& file_header = file_header32();
ASSERT(index < file_header.e_phnum);
return *(const Elf32ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index);
}
const Elf32SectionHeader& ELF::section_header32(size_t index) const
{
ASSERT(is_x86_32());
const auto& file_header = file_header32();
ASSERT(index < file_header.e_shnum);
return *(const Elf32SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index);
}
}

View File

@@ -0,0 +1,297 @@
#include <BAN/ScopeGuard.h>
#include <kernel/Memory/Heap.h>
#include <kernel/LockGuard.h>
#include <LibELF/LoadableELF.h>
#include <LibELF/Values.h>
namespace LibELF
{
using namespace Kernel;
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr<Inode> inode)
{
auto* elf_ptr = new LoadableELF(page_table, inode);
if (elf_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
TRY(elf->initialize());
return BAN::move(elf);
}
LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr<Inode> inode)
: m_inode(inode)
, m_page_table(page_table)
{
}
LoadableELF::~LoadableELF()
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
continue;
case PT_LOAD:
{
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
for (size_t i = 0; i < pages; i++)
{
paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE);
if (paddr != 0)
Heap::get().release_page(paddr);
}
m_page_table.unmap_range(start, pages * PAGE_SIZE);
break;
}
default:
ASSERT_NOT_REACHED();
}
}
}
BAN::ErrorOr<void> LoadableELF::initialize()
{
if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader))
{
dprintln("Too small file");
return BAN::Error::from_errno(ENOEXEC);
}
size_t nread = TRY(m_inode->read(0, &m_file_header, sizeof(m_file_header)));
ASSERT(nread == sizeof(m_file_header));
if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 ||
m_file_header.e_ident[EI_MAG1] != ELFMAG1 ||
m_file_header.e_ident[EI_MAG2] != ELFMAG2 ||
m_file_header.e_ident[EI_MAG3] != ELFMAG3)
{
dprintln("Invalid magic in header");
return BAN::Error::from_errno(ENOEXEC);
}
if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB)
{
dprintln("Only little-endian is supported");
return BAN::Error::from_errno(ENOEXEC);
}
if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT)
{
dprintln("Invalid version");
return BAN::Error::from_errno(ENOEXEC);
}
#if ARCH(i386)
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32)
#elif ARCH(x86_64)
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64)
#endif
{
dprintln("Not in native format");
return BAN::Error::from_errno(EINVAL);
}
if (m_file_header.e_type != ET_EXEC)
{
dprintln("Only executable files are supported");
return BAN::Error::from_errno(EINVAL);
}
if (m_file_header.e_version != EV_CURRENT)
{
dprintln("Unsupported version");
return BAN::Error::from_errno(EINVAL);
}
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
TRY(m_program_headers.resize(m_file_header.e_phnum));
for (size_t i = 0; i < m_file_header.e_phnum; i++)
{
TRY(m_inode->read(m_file_header.e_phoff + m_file_header.e_phentsize * i, &m_program_headers[i], m_file_header.e_phentsize));
const auto& pheader = m_program_headers[i];
if (pheader.p_type != PT_NULL && pheader.p_type != PT_LOAD)
{
dprintln("Unsupported program header type {}", pheader.p_type);
return BAN::Error::from_errno(ENOTSUP);
}
if (pheader.p_memsz < pheader.p_filesz)
{
dprintln("Invalid program header");
return BAN::Error::from_errno(EINVAL);
}
m_virtual_page_count += BAN::Math::div_round_up<size_t>((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE);
}
return {};
}
vaddr_t LoadableELF::entry_point() const
{
return m_file_header.e_entry;
}
bool LoadableELF::contains(vaddr_t address) const
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
continue;
case PT_LOAD:
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
return true;
break;
default:
ASSERT_NOT_REACHED();
}
}
return false;
}
void LoadableELF::reserve_address_space()
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
break;
case PT_LOAD:
{
vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
ASSERT(m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE));
break;
}
default:
ASSERT_NOT_REACHED();
}
}
}
BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
break;
case PT_LOAD:
{
if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
continue;
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
if (program_header.p_flags & LibELF::PF_W)
flags |= PageTable::Flags::ReadWrite;
if (program_header.p_flags & LibELF::PF_X)
flags |= PageTable::Flags::Execute;
vaddr_t vaddr = address & PAGE_ADDR_MASK;
paddr_t paddr = Heap::get().take_free_page();
if (paddr == 0)
return BAN::Error::from_errno(ENOMEM);
m_page_table.map_page_at(paddr, vaddr, flags);
m_physical_page_count++;
memset((void*)vaddr, 0x00, PAGE_SIZE);
if (vaddr / PAGE_SIZE < BAN::Math::div_round_up<size_t>(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE))
{
size_t vaddr_offset = 0;
if (vaddr < program_header.p_vaddr)
vaddr_offset = program_header.p_vaddr - vaddr;
size_t file_offset = 0;
if (vaddr > program_header.p_vaddr)
file_offset = vaddr - program_header.p_vaddr;
size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
TRY(m_inode->read(program_header.p_offset + file_offset, (void*)(vaddr + vaddr_offset), bytes));
}
return {};
}
default:
ASSERT_NOT_REACHED();
}
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
{
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
if (elf_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader));
TRY(elf->m_program_headers.resize(m_program_headers.size()));
memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader));
elf->reserve_address_space();
ASSERT(&PageTable::current() == &m_page_table);
LockGuard _(m_page_table);
ASSERT(m_page_table.is_page_free(0));
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
break;
case PT_LOAD:
{
if (!(program_header.p_flags & LibELF::PF_W))
continue;
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
if (program_header.p_flags & LibELF::PF_W)
flags |= PageTable::Flags::ReadWrite;
if (program_header.p_flags & LibELF::PF_X)
flags |= PageTable::Flags::Execute;
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
for (size_t i = 0; i < pages; i++)
{
if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0)
continue;
paddr_t paddr = Heap::get().take_free_page();
if (paddr == 0)
return BAN::Error::from_errno(ENOMEM);
m_page_table.map_page_at(paddr, 0, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
memcpy((void*)0, (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
m_page_table.unmap_page(0);
new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
elf->m_physical_page_count++;
}
break;
}
default:
ASSERT_NOT_REACHED();
}
}
return elf;
}
}

View File

@@ -0,0 +1,89 @@
#pragma once
#ifdef __is_kernel
#include <kernel/FS/Inode.h>
#include <kernel/Memory/VirtualRange.h>
#endif
#include <BAN/StringView.h>
#include <BAN/UniqPtr.h>
#include <BAN/Vector.h>
#include <kernel/Arch.h>
#include "Types.h"
namespace LibELF
{
class ELF
{
public:
#ifdef __is_kernel
static BAN::ErrorOr<BAN::UniqPtr<ELF>> load_from_file(BAN::RefPtr<Kernel::Inode>);
#else
static BAN::ErrorOr<BAN::UniqPtr<ELF>> load_from_file(BAN::StringView);
#endif
const Elf64FileHeader& file_header64() const;
const Elf64ProgramHeader& program_header64(size_t) const;
const Elf64SectionHeader& section_header64(size_t) const;
const char* lookup_section_name64(uint32_t) const;
const char* lookup_string64(size_t, uint32_t) const;
#if ARCH(x86_64)
const Elf64FileHeader& file_header_native() const { return file_header64(); }
const Elf64ProgramHeader& program_header_native(size_t index) const { return program_header64(index); }
const Elf64SectionHeader& section_header_native(size_t index) const { return section_header64(index); }
const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name64(offset); }
const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string64(table_index, offset); }
bool is_native() const { return is_x86_64(); }
#endif
const Elf32FileHeader& file_header32() const;
const Elf32ProgramHeader& program_header32(size_t) const;
const Elf32SectionHeader& section_header32(size_t) const;
const char* lookup_section_name32(uint32_t) const;
const char* lookup_string32(size_t, uint32_t) const;
#if ARCH(i386)
const Elf32FileHeader& file_header_native() const { return file_header32(); }
const Elf32ProgramHeader& program_header_native(size_t index) const { return program_header32(index); }
const Elf32SectionHeader& section_header_native(size_t index) const { return section_header32(index); }
const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name32(offset); }
const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string32(table_index, offset); }
bool is_native() const { return is_x86_32(); }
#endif
const uint8_t* data() const { return m_data.data(); }
bool is_x86_32() const;
bool is_x86_64() const;
private:
//#ifdef __is_kernel
// ELF(BAN::UniqPtr<Kernel::VirtualRange>&& storage, size_t size)
// : m_storage(BAN::move(storage))
// , m_data((const uint8_t*)m_storage->vaddr(), size)
// {}
//#else
ELF(BAN::Vector<uint8_t>&& data)
: m_data(BAN::move(data))
{}
//#endif
BAN::ErrorOr<void> load();
bool parse_elf64_file_header(const Elf64FileHeader&);
bool parse_elf64_program_header(const Elf64ProgramHeader&);
bool parse_elf64_section_header(const Elf64SectionHeader&);
bool parse_elf32_file_header(const Elf32FileHeader&);
bool parse_elf32_program_header(const Elf32ProgramHeader&);
bool parse_elf32_section_header(const Elf32SectionHeader&);
private:
//#ifdef __is_kernel
// BAN::UniqPtr<Kernel::VirtualRange> m_storage;
// BAN::Span<const uint8_t> m_data;
//#else
const BAN::Vector<uint8_t> m_data;
//#endif
};
}

View File

@@ -0,0 +1,52 @@
#pragma once
#ifndef __is_kernel
#error "This is kernel only header"
#endif
#include <BAN/UniqPtr.h>
#include <BAN/Vector.h>
#include <kernel/FS/Inode.h>
#include <kernel/Memory/PageTable.h>
#include <LibELF/Types.h>
namespace LibELF
{
class LoadableELF
{
BAN_NON_COPYABLE(LoadableELF);
BAN_NON_MOVABLE(LoadableELF);
public:
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
~LoadableELF();
Kernel::vaddr_t entry_point() const;
bool contains(Kernel::vaddr_t address) const;
void reserve_address_space();
BAN::ErrorOr<void> load_page_to_memory(Kernel::vaddr_t address);
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> clone(Kernel::PageTable&);
size_t virtual_page_count() const { return m_virtual_page_count; }
size_t physical_page_count() const { return m_physical_page_count; }
private:
LoadableELF(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
BAN::ErrorOr<void> initialize();
private:
BAN::RefPtr<Kernel::Inode> m_inode;
Kernel::PageTable& m_page_table;
ElfNativeFileHeader m_file_header;
BAN::Vector<ElfNativeProgramHeader> m_program_headers;
size_t m_virtual_page_count = 0;
size_t m_physical_page_count = 0;
};
}

View File

@@ -80,16 +80,6 @@ namespace LibELF
Elf32Word p_align; Elf32Word p_align;
}; };
struct Elf32Dynamic
{
Elf32Sword d_tag;
union
{
Elf32Word d_val;
Elf32Addr d_ptr;
} d_un;
};
using Elf64Addr = uint64_t; using Elf64Addr = uint64_t;
using Elf64Off = uint64_t; using Elf64Off = uint64_t;
using Elf64Half = uint16_t; using Elf64Half = uint16_t;
@@ -165,29 +155,21 @@ namespace LibELF
Elf64Xword p_align; Elf64Xword p_align;
}; };
struct Elf64Dynamic
{
Elf64Sxword d_tag;
union
{
Elf64Xword d_val;
Elf64Addr d_ptr;
} d_un;
};
#if ARCH(i686) #if ARCH(i386)
using ElfNativeAddr = Elf32Addr; using ElfNativeAddr = Elf32Addr;
using ElfNativeOff = Elf32Off; using ElfNativeOff = Elf32Off;
using ElfNativeHalf = Elf32Half; using ElfNativeHalf = Elf32Half;
using ElfNativeWord = Elf32Word; using ElfNativeWord = Elf32Word;
using ElfNativeSword = Elf32Sword; using ElfNativeSword = Elf32Sword;
using ElfNativeXword = Elf32Xword;
using ElfNativeSxword = Elf32Sxword;
using ElfNativeFileHeader = Elf32FileHeader; using ElfNativeFileHeader = Elf32FileHeader;
using ElfNativeSectionHeader = Elf32SectionHeader; using ElfNativeSectionHeader = Elf32SectionHeader;
using ElfNativeSymbol = Elf32Symbol; using ElfNativeSymbol = Elf32Symbol;
using ElfNativeRelocation = Elf32Relocation; using ElfNativeRelocation = Elf32Relocation;
using ElfNativeRelocationA = Elf32RelocationA; using ElfNativeRelocationA = Elf32RelocationA;
using ElfNativeProgramHeader = Elf32ProgramHeader; using ElfNativeProgramHeader = Elf32ProgramHeader;
using ElfNativeDynamic = Elf32Dynamic;
#elif ARCH(x86_64) #elif ARCH(x86_64)
using ElfNativeAddr = Elf64Addr; using ElfNativeAddr = Elf64Addr;
using ElfNativeOff = Elf64Off; using ElfNativeOff = Elf64Off;
@@ -202,7 +184,6 @@ namespace LibELF
using ElfNativeRelocation = Elf64Relocation; using ElfNativeRelocation = Elf64Relocation;
using ElfNativeRelocationA = Elf64RelocationA; using ElfNativeRelocationA = Elf64RelocationA;
using ElfNativeProgramHeader = Elf64ProgramHeader; using ElfNativeProgramHeader = Elf64ProgramHeader;
using ElfNativeDynamic = Elf64Dynamic;
#endif #endif
} }

View File

@@ -0,0 +1,140 @@
#pragma once
namespace LibELF
{
enum ELF_Ident
{
ELFMAG0 = 0x7F,
ELFMAG1 = 'E',
ELFMAG2 = 'L',
ELFMAG3 = 'F',
ELFCLASSNONE = 0,
ELFCLASS32 = 1,
ELFCLASS64 = 2,
ELFDATANONE = 0,
ELFDATA2LSB = 1,
ELFDATA2MSB = 2,
};
enum ELF_EI
{
EI_MAG0 = 0,
EI_MAG1 = 1,
EI_MAG2 = 2,
EI_MAG3 = 3,
EI_CLASS = 4,
EI_DATA = 5,
EI_VERSION = 6,
EI_OSABI = 7,
EI_ABIVERSION = 8,
EI_NIDENT = 16,
};
enum ELF_ET
{
ET_NONE = 0,
ET_REL = 1,
ET_EXEC = 2,
ET_DYN = 3,
ET_CORE = 4,
ET_LOOS = 0xfe00,
ET_HIOS = 0xfeff,
ET_LOPROC = 0xff00,
ET_HIPROC = 0xffff,
};
enum ELF_EV
{
EV_NONE = 0,
EV_CURRENT = 1,
};
enum ELF_SHT
{
SHT_NULL = 0,
SHT_PROGBITS = 1,
SHT_SYMTAB = 2,
SHT_STRTAB = 3,
SHT_RELA = 4,
SHT_NOBITS = 8,
SHT_REL = 9,
SHT_SHLIB = 10,
SHT_DYNSYM = 11,
SHT_LOOS = 0x60000000,
SHT_HIOS = 0x6FFFFFFF,
SHT_LOPROC = 0x70000000,
SHT_HIPROC = 0x7FFFFFFF,
};
enum ELF_SHF
{
SHF_WRITE = 0x1,
SHF_ALLOC = 0x2,
SHF_EXECINSTR = 0x4,
SHF_MASKOS = 0x0F000000,
SHF_MASKPROC = 0xF0000000,
};
enum ELF_SHN
{
SHN_UNDEF = 0,
SHN_LOPROC = 0xFF00,
SHN_HIPROC = 0xFF1F,
SHN_LOOS = 0xFF20,
SHN_HIOS = 0xFF3F,
SHN_ABS = 0xFFF1,
SHN_COMMON = 0xFFF2,
};
enum ELF_STB
{
STB_LOCAL = 0,
STB_GLOBAL = 1,
STB_WEAK = 2,
STB_LOOS = 10,
STB_HIOS = 12,
STB_LOPROC = 13,
STB_HIPROC = 15,
};
enum ELF_STT
{
STT_NOTYPE = 0,
STT_OBJECT = 1,
STT_FUNC = 2,
STT_SECTION = 3,
STT_FILE = 4,
STT_LOOS = 10,
STT_HIOS = 12,
STT_LOPROC = 13,
STT_HIPROC = 15,
};
enum ELF_PT
{
PT_NULL = 0,
PT_LOAD = 1,
PT_DYNAMIC = 2,
PT_INTERP = 3,
PT_NOTE = 4,
PT_SHLIB = 5,
PT_PHDR = 6,
PT_LOOS = 0x60000000,
PT_HIOS = 0x6FFFFFFF,
PT_LOPROC = 0x70000000,
PT_HIPROC = 0x7FFFFFFF,
};
enum ELF_PF
{
PF_X = 0x1,
PF_W = 0x2,
PF_R = 0x4,
PF_MASKOS = 0x00FF0000,
PF_MASKPROC = 0xFF000000,
};
}

1
PreLoad.cmake Normal file
View File

@@ -0,0 +1 @@
set(CMAKE_GENERATOR "Ninja" CACHE INTERNAL "" FORCE)

132
README.md
View File

@@ -1,77 +1,8 @@
[![](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fbananymous.com%2Fbanan-os%2Ftokei.json&query=%24.lines&label=total%20lines)](https://git.bananymous.com/Bananymous/banan-os) ![license](https://img.shields.io/github/license/bananymous/banan-os)
[![](https://img.shields.io/github/commit-activity/m/Bananymous/banan-os)](https://git.bananymous.com/Bananymous/banan-os)
[![](https://img.shields.io/github/license/bananymous/banan-os)](https://git.bananymous.com/Bananymous/banan-os/src/branch/main/LICENSE)
[![](https://img.shields.io/discord/1242165176032297040?logo=discord&label=discord)](https://discord.gg/ehjGySwYdK)
# banan-os # banan-os
This is my hobby operating system written in C++. Currently supports x86\_64 and i686 architectures. This is my hobby operating system written in C++. Currently supports only x86_64 architecture. We have a read-only ext2 filesystem, read-write ramfs, IDE disk drivers in ATA PIO mode, userspace processes, executable loading from ELF format, linear VBE graphics and multithreaded processing on single core.
You can find a live demo [here](https://bananymous.com/banan-os)
If you want to try out DOOM, you should first enter the GUI environment using the `start-gui` command. Then you can run `doom` in the GUI terminal.
### Features
#### General
- [x] Ring3 userspace
- [x] SMP (multiprocessing)
- [x] Linear framebuffer (VESA and GOP)
- [x] Network stack
- [x] ELF executable loading
- [x] AML interpreter (partial)
- [x] Basic graphical environment
- [x] Terminal emulator
- [x] Status bar
- [x] Program launcher
- [ ] Some nice apps
- [x] ELF dynamic linking
- [x] copy-on-write memory
- [x] file mappings
- [ ] anonymous mappings
#### Drivers
- [x] NVMe disks
- [x] ATA (IDE, SATA) disks
- [x] E1000 and E1000E NICs
- [x] RTL8111/8168/8211/8411 NICs
- [x] PS2 keyboard (all scancode sets)
- [x] PS2 mouse
- [x] USB
- [x] xHCI
- [ ] EHCI
- [ ] OHCI
- [ ] UHCI
- [x] Keyboard
- [x] Mouse
- [x] Mass storage
- [x] Hubs
- [ ] ...
- [ ] virtio devices (network, storage)
#### Network
- [x] ARP
- [x] ICMP
- [x] IPv4
- [x] UDP
- [x] TCP (partial and buggy)
- [x] Unix domain sockets
- [ ] SSL
#### Filesystems
- [x] Virtual filesystem
- [x] Ext2
- [x] FAT12/16/32
- [x] Dev
- [x] Ram
- [x] Proc
- [ ] Sys
- [ ] 9P
#### Bootloader support
- [x] GRUB
- [x] Custom BIOS bootloader
- [ ] Custom UEFI bootloader
![screenshot from qemu running banan-os](assets/banan-os.png) ![screenshot from qemu running banan-os](assets/banan-os.png)
@@ -81,61 +12,44 @@ Each major component and library has its own subdirectory (kernel, userspace, li
## Building ## Building
### Needed packages There does not exist a complete list of needed packages for building. From the top of my head I can say that *cmake*, *ninja*, *make*, *grub*, *rsync* and emulator (*qemu* or *bochs*) are needed.
#### apt (tested on ubuntu 22.04) You can and *should* pass cmake variable QEMU_ACCEL set to proper accelerator to cmake commands. For example on Linux this means adding -DQEMU_ACCEL=kvm to the end of all cmake commands.
```# apt install build-essential git ninja-build texinfo bison flex libgmp-dev libmpfr-dev libmpc-dev parted qemu-system-x86 cpu-checker```
#### pacman Create the build directory and cofigure cmake
```# pacman -S --needed base-devel git wget cmake ninja parted qemu-system-x86``` ```sh
mkdir build
cd build
### Compilation cmake ..
```
To build the toolchain for this os. You can run the following command. To build the toolchain for this os. You can run the following command.
> ***NOTE:*** The following step has to be done only once. This might take a long time since we are compiling binutils and gcc. > ***NOTE:*** The following step has to be done only once. This might take a long time since we are compiling binutils and gcc.
```sh ```sh
./bos toolchain ninja toolchain
cmake --fresh .. # We need to reconfigure cmake to use the new compiler
ninja libstdc++
``` ```
To build the os itself you can run one of the following commands. You will need root access for disk image creation/modification. To build the os itself you can run either of the following commands. You will need root access since the sysroot has "proper" permissions.
```sh ```sh
./bos qemu ninja qemu
./bos qemu-nographic ninja bochs
./bos qemu-debug
./bos bochs
``` ```
You can also build the kernel or disk image without running it: You can also build the kernel or disk image without running it:
```sh ```sh
./bos kernel ninja kernel
./bos image ninja image
``` ```
To build for other architectures set environment variable BANAN\_ARCH=*arch* (e.g. BANAN\_ARCH=i686). If you have corrupted your disk image or want to create new one, you can either manually delete *banan-os.img* and cmake will automatically create you a new one or you can run the following command.
To change the bootloader you can set environment variable BANAN\_BOOTLOADER; supported values are BANAN (my custom bootloader) and GRUB.
To run with UEFI set environment variable BANAN\_UEFI\_BOOT=1. You will also have to set OVMF\_PATH to the correct OVMF (default */usr/share/ovmf/x64/OVMF.fd*).
To build an image with no physical root filesystem, but an initrd set environment variable BANAN\_INITRD=1. This can be used when testing on hardware with unsupported USB controller.
If you have corrupted your disk image or want to create new one, you can either manually delete *build/banan-os.img* and build system will automatically create you a new one or you can run the following command.
```sh ```sh
./bos image-full ninja image-full
``` ```
I have also created shell completion script for zsh. You can either copy the file in _script/shell-completion/zsh/\_bos_ to _/usr/share/zsh/site-functions/_ or add the _script/shell-completion/zsh_ to your fpath in _.zshrc_. > ***NOTE*** ```ninja clean``` has to be ran with root permissions, since it deletes the root filesystem.
## Contributing ### Contributing
As the upstream is hosted on my server https://git.bananymous.com/Bananymous/banan-os, merging contributions is not as trivial as it would be on GitHub. You can still send PRs in GitHub in which case I should be able to download the diff and apply it manually. If you want, I can also provide you an account to my git server. In this case please contact me ([email](mailto:oskari.alaranta@bananymous.com), [discord](https://discord.gg/ehjGySwYdK)). Currently I don't accept contributions to this repository unless explicitly told otherwise. This is a learning project for me and I want to do everything myself. Feel free to fork/clone this repo and tinker with it yourself.
As this is mostly a learning experience for me, I would appreciate if you first contacted me about adding new features (email, discord, issue, ...). If you send a PR about something I was planning on doing myself and you didn't ask me, I will probably just close it. Bug fixes are always welcome!
Commit message should be formatted followingly
1. First line is of the form "_Subject: Description_", where _Subject_ tells the area touched (Kernel, Shell, BuildSystem, ...) and _Description_ is brief description of the change done. First line should fit fully in 72 characters.
2. Body of the message should further describe the change and reasoning behind the change.
All commits should pass the pre-commit hook defined in _.pre-commit-config.yaml_. For instructions on how to setup pre-commit, please see https://pre-commit.com/#install.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

View File

@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -e
BOCHS_CONFIG_FILE=bochsrc BOCHS_CONFIG_FILE=bochsrc
COM1_TERMINAL=kitty COM1_TERMINAL=kitty
@@ -13,7 +14,7 @@ COM1_DEVICE=$(cat $COM1_DEVICE_FILE)
rm $COM1_DEVICE_FILE rm $COM1_DEVICE_FILE
cat > $BOCHS_CONFIG_FILE << EOF cat > $BOCHS_CONFIG_FILE << EOF
ata0-master: type=disk, path=$BANAN_DISK_IMAGE_PATH, status=inserted ata0-master: type=disk, path=$DISK_IMAGE_PATH, status=inserted
boot: disk boot: disk
clock: sync=realtime, time0=local clock: sync=realtime, time0=local
display_library: x, options="gui_debug" display_library: x, options="gui_debug"

View File

@@ -1,3 +0,0 @@
cmake_minimum_required(VERSION 3.26)
add_subdirectory(bios)

View File

@@ -1,20 +0,0 @@
cmake_minimum_required(VERSION 3.26)
project(bootloader ASM)
set(BOOTLOADER_SOURCES
a20_line.S
boot.S
command_line.S
disk.S
elf.S
ext2.S
framebuffer.S
memory_map.S
utils.S
)
add_executable(bootloader ${BOOTLOADER_SOURCES})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
target_link_options(bootloader PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
target_link_options(bootloader PRIVATE -nostdlib)

View File

@@ -1,168 +0,0 @@
.code16
.section .stage2
# checks whether A20 line is enabled or disabled
# return
# ax: 1 if enabled, 0 otherwise
check_a20:
pushf
pushw %si
pushw %di
pushw %ds
pushw %es
cli
xorw %ax, %ax
movw %ax, %es
notw %ax
movw %ax, %ds
movw $0x0500, %di
movw $0x0510, %si
movb %es:(%di), %al
pushw %ax
movb %ds:(%si), %al
pushw %ax
movb $0x00, %es:(%di)
movb $0xFF, %ds:(%si)
cmpb $0xFF, %es:(%di)
pop %ax
movb %al, %ds:(%si)
pop %ax
movb %al, %es:(%di)
movw $0, %ax
je .check_a20_done
movw $1, %ax
.check_a20_done:
popw %es
popw %ds
popw %di
popw %si
popf
ret
# Try to enable A20 using PS2 controller
enable_a20_ps2:
pushf
pushw %ax
cli
# disable first port
call .enable_a20_ps2_wait1
movb $0xAD, %al
outb %al, $0x64
# read controller output
call .enable_a20_ps2_wait1
movb $0xD0, %al
outb %al, $0x64
call .enable_a20_ps2_wait2
inb $0x60, %al
pushw %ax
# write controller output
call .enable_a20_ps2_wait1
movb $0xD1, %al
outb %al, $0x64
call .enable_a20_ps2_wait1
popw %ax
orw $2, %ax
outb %al, $0x60
# enable first port
call .enable_a20_ps2_wait1
movb $0xAE, %al
outb %al, $0x64
call .enable_a20_ps2_wait1
popw %ax
popf
ret
.enable_a20_ps2_wait1:
inb $0x64, %al
test $2, %al
jnz .enable_a20_ps2_wait1
ret
.enable_a20_ps2_wait2:
inb $0x64, %al
test $1, %al
jnz .enable_a20_ps2_wait1
ret
# Check if A20 line is disabled. If it is, try to enable it
.global enable_a20
enable_a20:
pushw %ax
pushw %si
call check_a20
testw %ax, %ax
jnz .enable_a20_done
movw $a20_line_disabled_msg, %si
call puts; call print_newline
# Try to enable A20 line using bios interrupt
movw $0x2401, %ax
int $0x15
call check_a20
testw %ax, %ax
jnz .enable_a20_done
# Try to enable A20 line using ps2 controller
call enable_a20_ps2
call check_a20
testw %ax, %ax
jnz .enable_a20_done
# Try to enable A20 line using fast A20 gate
inb $0x92, %al
testb $2, %al
jnz .enable_a20_fast_done
orb $2, %al
outb %al, $0x92
.enable_a20_fast_done:
call check_a20
testw %ax, %ax
jnz .enable_a20_done
movw $a20_could_not_enable_msg, %si
call print_and_halt
.enable_a20_done:
movw $a20_line_enabled_msg, %si
call puts; call print_newline
popw %si
popw %ax
ret
.section .data
a20_line_disabled_msg:
.asciz "A20 line disabled. Trying to enable it"
a20_line_enabled_msg:
.asciz "A20 line enabled"
a20_could_not_enable_msg:
.asciz "Could not enable A20 line"

View File

@@ -1,176 +0,0 @@
.include "common.S"
.code16
#########################################
#
# STAGE 1 BOOTLOADER
#
# its sole purpose is to load stage2 from
# bios boot partition
#
#########################################
.section .stage1
.global stage1_main
stage1_main:
# setup segments and stack
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movl $0x7C00, %esp
# save boot disk number
call read_stage2_into_memory
jmp stage2_main
.global print_and_halt
print_and_halt:
call puts
halt:
hlt
jmp halt
#########################################
#
# STAGE 2 BOOTLOADER
#
#########################################
.section .stage2
stage2_main:
# clear screen and enter 80x25 text mode
movb $0x03, %al
movb $0x00, %ah
int $0x10
# print hello message
movw $hello_msg, %si
call puts; call print_newline
lgdt gdtr
call enter_unreal_mode
movw $unreal_enter_msg, %si
call puts; call print_newline
call enable_a20
call get_memory_map
call print_newline
call read_user_command_line
call print_newline
movw $start_kernel_load_msg, %si
call puts; call print_newline
call print_memory_map
call find_root_disk
call find_root_partition
call print_root_partition_info
call print_newline
call has_ext2_filesystem
testb %al, %al
jz print_and_halt
call ext2_find_kernel
movl $ext2_inode_read_bytes, %esi
call elf_read_kernel_to_memory
call vesa_set_video_mode
cli
# kernel entry point
movl %eax, %ecx
# setup kernel parameters
movl $0xD3C60CFF, %eax
movl $banan_boot_info, %ebx
# setup protected mode
movl %cr0, %edx
orb $1, %dl
movl %edx, %cr0
# jump to protected mode
ljmpl $GDT_CODE32, $protected_mode
.code32
protected_mode:
# setup protected mode segments
movw $GDT_DATA32, %dx
movw %dx, %ds
movw %dx, %es
movw %dx, %fs
movw %dx, %gs
movw %dx, %ss
# jump to kernel entry
jmp *%ecx
.code16
enter_unreal_mode:
cli
pushw %ds
movl %cr0, %eax
orb $1, %al
movl %eax, %cr0
ljmpl $GDT_CODE16, $.enter_unreal_mode_pmode
.enter_unreal_mode_pmode:
movw $GDT_DATA32, %bx
movw %bx, %ds
andb $0xFE, %al
movl %eax, %cr0
ljmpl $0x00, $.enter_unreal_mode_unreal
.enter_unreal_mode_unreal:
popw %ds
sti
ret
.section .data
hello_msg:
.asciz "This is banan-os bootloader"
unreal_enter_msg:
.asciz "Entered unreal mode"
start_kernel_load_msg:
.asciz "Starting to load kernel"
gdt:
.quad 0x0000000000000000
.quad 0x008F9A000000FFFF # 16-bit code
.quad 0x00CF92000000FFFF # 32-bit data
.quad 0x00CF9A000000FFFF # 32-bit code
gdtr:
.short . - gdt - 1
.long gdt
banan_boot_info:
boot_command_line:
.long command_line
boot_framebuffer:
.long framebuffer
boot_memory_map:
.long memory_map
boot_kernel_paddr:
.long 0

View File

@@ -1,87 +0,0 @@
.code16
.section .stage2
# fills command line buffer
# NO REGISTERS SAVED
.global read_user_command_line
read_user_command_line:
# print initial command line
movw $command_line_enter_msg, %si
call puts
movw $command_line_buffer, %si
call puts
# prepare registers for input
movw $command_line_enter_msg, %si
movw $command_line_buffer, %di
.read_user_command_line_goto_end:
cmpb $0, (%di)
jz .read_user_command_line_loop
incw %di
jmp .read_user_command_line_goto_end
.read_user_command_line_loop:
call getc
cmpb $'\b', %al
je .read_user_command_line_backspace
cmpb $0x7F, %al
je .read_user_command_line_backspace
# Not sure if some BIOSes return '\n' as enter, but check it just in case
cmpb $'\r', %al
je .read_user_command_line_done
cmpb $'\n', %al
je .read_user_command_line_done
pushw %ax
call isprint
testb %al, %al
jz .read_user_command_line_loop
popw %ax
# put byte to buffer
movb %al, (%di)
incw %di
# print byte
call putc
jmp .read_user_command_line_loop
.read_user_command_line_backspace:
# don't do anything if at the beginning
cmpw $command_line_buffer, %di
je .read_user_command_line_loop
# decrement buffer pointer
decw %di
# erase byte in display
call print_backspace
jmp .read_user_command_line_loop
.read_user_command_line_done:
# null terminate command line
movb $0, (%di)
call print_newline
ret
.section .data
command_line_enter_msg:
.asciz "cmdline: "
.global command_line
command_line:
# 100 character command line
command_line_buffer:
.ascii "root=/dev/sda2"
.skip 100 - (. - command_line_buffer)

View File

@@ -1,3 +0,0 @@
.set GDT_CODE16, 0x08
.set GDT_DATA32, 0x10
.set GDT_CODE32, 0x18

View File

@@ -1,518 +0,0 @@
# FIXME: don't assume 512 byte sectors
.set SECTOR_SIZE_SHIFT, 9
.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT
.code16
.section .stage1
.global stage2_start
.global stage2_end
# check that drive has int13 ext
# dl: drive number
# returns only if drive does have the extension
drive_has_int13_ext:
pusha
movb $0x41, %ah
movw $0x55AA, %bx
int $0x13
jc .drive_has_int13_ext_no_int13_ext
popa
ret
.drive_has_int13_ext_no_int13_ext:
mov $no_int13_ext_msg, %si
jmp print_and_halt
# read sectors from disk
# bx:eax: lba start
# cx: lba count (has to less than 0x80)
# dl: drive number
# ds:di: physical address
# returns only on success
.global read_from_disk
read_from_disk:
pusha
call drive_has_int13_ext
# prepare disk read packet
movw $disk_address_packet, %si
movb $0x10, 0x00(%si) # packet size
movb $0x00, 0x01(%si) # always 0
movw %cx, 0x02(%si) # lba count
movw %di, 0x04(%si) # offset
movw %ds, 0x06(%si) # segment
movl %eax, 0x08(%si) # 32 bit lower lba
movw %bx, 0x0C(%si) # 16 bit upper lba
movw $0, 0x0E(%si) # zero
# issue read command
mov $0x42, %ah
int $0x13
jc .read_from_disk_failed
popa
ret
.read_from_disk_failed:
mov $read_from_disk_msg, %si
jmp print_and_halt
# Reads GPT header into gpt_header buffer
# dl: drive number
# return:
# ax: 1 if has GPT header, 0 otherwise
.global read_gpt_header
read_gpt_header:
pushw %bx
pushw %cx
pushw %di
xorw %bx, %bx
movl $1, %eax
movw $1, %cx
movw $gpt_header, %di
call read_from_disk
xorw %bx, %bx
movw $1, %ax
# check if header starts with 'EFI PART'
cmpl $0x20494645, (gpt_header + 0)
cmovnew %bx, %ax
cmpl $0x54524150, (gpt_header + 4)
cmovnew %bx, %ax
popw %di
popw %cx
popw %bx
ret
# Find bios boot partition from boot drive
# returns:
# bx:eax: first lba
# cx: sector count
find_stage2_partition:
# read boot disk GPT header
movb (boot_disk_number), %dl
call read_gpt_header
testb %al, %al
jz .find_stage2_partition_not_gpt
# eax := entry_count
movl (gpt_header + 80), %eax
test %eax, %eax
jz .find_stage2_partition_not_found
# edx:eax := eax * entry_size
mull (gpt_header + 84)
test %edx, %edx
jnz .find_stage2_partition_too_big_entries
# FIXME: read one entry array section at a time
# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
movl %eax, %ecx
shrl $SECTOR_SIZE_SHIFT, %ecx
# start lba
movl (gpt_header + 72), %eax
movw (gpt_header + 76), %bx
movw $gpt_entry_data, %di
movw $bios_boot_guid, %si
movb (boot_disk_number), %dl
call read_from_disk
# NOTE: 'only' 0xFFFF partitions supported,
# although read will fail with more than 0x80
movw (gpt_header + 80), %cx
.find_stage2_partition_loop_gpt_entries:
pushw %cx
movw $16, %cx
call memcmp
popw %cx
testb %al, %al
jnz .find_stage2_partition_found
# add entry size to entry pointer
addw (gpt_header + 84), %di
loop .find_stage2_partition_loop_gpt_entries
# fall through to not found case
.find_stage2_partition_not_found:
movw $no_bios_boot_partition_msg, %si
jmp print_and_halt
.find_stage2_partition_not_gpt:
movw $not_gpt_partition_msg, %si
jmp print_and_halt
.find_stage2_partition_too_big_entries:
movw $too_gpt_big_entries_msg, %si
jmp print_and_halt
.find_stage2_partition_found:
# first lba
movl 32(%di), %eax
movw 36(%di), %bx
# count := last lba - first lba + 1
movl 40(%di), %ecx
subl %eax, %ecx
incl %ecx
ret
# reads stage2 into memory
# dl: boot drive number
# returns only on success
.global read_stage2_into_memory
read_stage2_into_memory:
movb %dl, (boot_disk_number)
# push stage2 sector count
movl $stage2_end, %eax
subl $stage2_start, %eax
addl $(SECTOR_SIZE - 1), %eax
movl $SECTOR_SIZE, %ecx
xorl %edx, %edx
divl %ecx
pushl %eax
call find_stage2_partition
movb (boot_disk_number), %dl
popl %ecx # FIXME: validate that partition has enough sectors
movw $stage2_start, %di
call read_from_disk
ret
# 21686148-6449-6E6F-744E-656564454649
.align 4
bios_boot_guid:
.long 0x21686148 # little endian
.word 0x6449 # little endian
.word 0x6E6F # little endian
.word 0x4E74 # big endian
.quad 0x494645646565 # big endian
boot_disk_number:
.skip 1
read_from_disk_msg:
.asciz "read error"
no_int13_ext_msg:
.asciz "no INT13 ext"
no_bios_boot_partition_msg:
.asciz "no bios boot"
too_gpt_big_entries_msg:
.asciz "too big GPT array"
not_gpt_partition_msg:
.asciz "not GPT"
.section .stage2
# check if drive exists
# dl: drive number
# return:
# al: 1 if disk is usable, 0 otherwise
drive_exists:
pusha
movb $0x48, %ah
movw $disk_drive_parameters, %si
movw $0x1A, (disk_drive_parameters) # set buffer size
int $0x13
jc .drive_exists_nope
popa
movb $1, %al
ret
.drive_exists_nope:
popa
movb $0, %al
ret
# find root disk and populate root_disk_drive_number field
# NO REGISTERS SAVED
.global find_root_disk
find_root_disk:
movb $0x80, %dl
.find_root_disk_loop:
call drive_exists
testb %al, %al
jz .find_root_disk_not_found
# read GPT header
xorw %bx, %bx
movl $1, %eax
movw $1, %cx
movw $gpt_header, %di
call read_from_disk
# confirm header (starts with 'EFI PART')
cmpl $0x20494645, (gpt_header + 0)
jne .find_root_disk_next_disk
cmpl $0x54524150, (gpt_header + 4)
jne .find_root_disk_next_disk
# compare disk GUID
movw $root_disk_guid, %si
movw $(gpt_header + 56), %di
movw $16, %cx
call memcmp
testb %al, %al
jz .find_root_disk_next_disk
movw $root_disk_found_msg, %si
call puts; call print_newline
movb %dl, (root_disk_drive_number)
ret
.find_root_disk_next_disk:
incb %dl
jmp .find_root_disk_loop
.find_root_disk_not_found:
movw $root_disk_not_found_msg, %si
jmp print_and_halt
# finds root partition from root disk
# fills root_partition_entry data structure
# NOTE: assumes GPT header is in `gpt_header`
# NO REGISTERS SAVED
# return:
# dl: drive number
# ecx: sector count (capped at 0xFFFFFFFF)
# bx:eax: first sector
.global find_root_partition
find_root_partition:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
# esp + 0: 8 byte entry array lba
movl (gpt_header + 72), %eax
movl %eax, 0(%esp)
movl (gpt_header + 76), %eax
movl %eax, 4(%esp)
# FIXME: check that bits 48-63 are zero
# esp + 8: 4 byte entries per sector
xorl %edx, %edx
movl $SECTOR_SIZE, %eax
divl (gpt_header + 84)
movl %eax, 8(%esp)
# esp + 12: 4 byte entries remaining
movl (gpt_header + 80), %eax
testl %eax, %eax
jz .find_root_partition_not_found
movl %eax, 12(%esp)
.find_root_partition_read_entry_section:
movl 0(%esp), %eax
movl 4(%esp), %ebx
movw $1, %cx
movb (root_disk_drive_number), %dl
movw $sector_buffer, %di
call read_from_disk
# ecx: min(entries per section, entries remaining)
movl 8(%esp), %ecx
cmpl 12(%esp), %ecx
jae .find_root_partition_got_entry_count
movl 12(%esp), %ecx
.find_root_partition_got_entry_count:
# update entries remaining
subl %ecx, 12(%esp)
# si: entry pointer
movw $sector_buffer, %si
.find_root_partition_loop_entries:
# temporarily save cx in dx
movw %cx, %dx
# check that entry is used
movw $16, %cx
movw $zero_guid, %di
call memcmp
test %al, %al
jnz .find_root_partition_next_entry
# compare entry guid to root guid
movw $16, %cx
addw $16, %si
movw $root_partition_guid, %di
call memcmp
subw $16, %si
testb %al, %al
jnz .find_root_partition_found
.find_root_partition_next_entry:
# restore cx
movw %dx, %cx
# entry pointer += entry size
addw (gpt_header + 84), %si
loop .find_root_partition_loop_entries
# entry not found in this sector
# increment 8 byte entry array lba
incl 0(%esp)
adcl $0, 4(%esp)
# loop to read next section if entries remaining
cmpl $0, 12(%esp)
jnz .find_root_partition_read_entry_section
.find_root_partition_not_found:
movw $root_partition_not_found_msg, %si
jmp print_and_halt
.find_root_partition_found:
# copy entry to buffer
movw $root_partition_entry, %di
movw $128, %cx
rep movsb
movw $root_partition_found_msg, %si
call puts; call print_newline
# ebx:eax := last lba
movl (root_partition_entry + 44), %ebx
movl (root_partition_entry + 40), %eax
# ebx:eax -= first lba - 1
subl (root_partition_entry + 36), %ebx
movl (root_partition_entry + 32), %ecx
decl %ecx
subl %ecx, %eax
sbbl $0, %ebx
# ecx: min(partition count, 0xFFFFFFFF)
movl $0xFFFFFFFF, %edx
movl %eax, %ecx
testl %ebx, %ebx
cmovnzl %edx, %ecx
# ebx:eax := first lba
# FIXME: confirm ebx bits 16:31 are zero
movl (root_partition_entry + 36), %ebx
movl (root_partition_entry + 32), %eax
movb (root_disk_drive_number), %dl
leavel
ret
# print information about root partition
.global print_root_partition_info
print_root_partition_info:
pushw %ax
pushw %bx
pushw %cx
pushw %si
movw $root_partition_info_start_msg, %si
call puts;
movw $16, %bx
movw $2, %cx
movw (root_partition_entry + 38), %ax; call print_number
movw (root_partition_entry + 36), %ax; call print_number
movw (root_partition_entry + 34), %ax; call print_number
movw (root_partition_entry + 32), %ax; call print_number
movb $'-', %al; call putc
movb $'>', %al; call putc
movw (root_partition_entry + 46), %ax; call print_number
movw (root_partition_entry + 44), %ax; call print_number
movw (root_partition_entry + 42), %ax; call print_number
movw (root_partition_entry + 40), %ax; call print_number
call print_newline
popw %si
popw %cx
popw %bx
popw %ax
ret
.section .data
# These will be patched during bootloader installation
root_disk_guid:
.ascii "root disk guid "
root_partition_guid:
.ascii "root part guid "
zero_guid:
.skip 16, 0
root_disk_found_msg:
.asciz "Root disk found!"
root_disk_not_found_msg:
.asciz "Root disk not found"
root_partition_found_msg:
.asciz "Root partition found!"
root_partition_not_found_msg:
.asciz "Root partition not found"
root_partition_info_start_msg:
.asciz "Root partition: "
.section .bss
.align SECTOR_SIZE
gpt_header:
.skip SECTOR_SIZE
gpt_entry_data:
.skip SECTOR_SIZE
sector_buffer:
.skip SECTOR_SIZE
disk_address_packet:
.skip 16
disk_drive_parameters:
.skip 0x1A
.skip 2 # padding
root_disk_drive_number:
.skip 1
.skip 3 # padding
root_partition_entry:
.skip 128

View File

@@ -1,320 +0,0 @@
.set SECTOR_SIZE, 512
# file header field offsets
.set e_type, 16
.set e_machine, 18
.set e_version, 20
.set e_entry, 24
.set e32_phoff, 28
.set e32_shoff, 32
.set e32_flags, 36
.set e32_ehsize, 40
.set e32_phentsize, 42
.set e32_phnum, 44
.set e32_shentsize, 46
.set e32_shnum, 48
.set e32_shstrndx, 50
.set e64_phoff, 32
.set e64_shoff, 40
.set e64_flags, 48
.set e64_ehsize, 52
.set e64_phentsize, 54
.set e64_phnum, 56
.set e64_shentsize, 58
.set e64_shnum, 60
.set e64_shstrndx, 62
# e_ident offsets
.set EI_CLASS, 4
.set EI_DATA, 5
.set EI_VERSION, 6
# e_ident constants
.set ELFMAGIC, 0x464C457F
.set ELFCLASS32, 1
.set ELFCLASS64, 2
.set ELFDATA2LSB, 1
.set EV_CURRENT, 1
# e_type constants
.set ET_EXEC, 2
# program header field offsets
.set p_type, 0
.set p32_offset, 4
.set p32_vaddr, 8
.set p32_paddr, 12
.set p32_filesz, 16
.set p32_memsz, 20
.set p32_flags, 24
.set p32_align, 28
.set p64_flags, 4
.set p64_offset, 8
.set p64_vaddr, 16
.set p64_paddr, 24
.set p64_filesz, 32
.set p64_memsz, 40
.set p64_align, 48
# p_type constants
.set PT_NULL, 0
.set PT_LOAD, 1
# mask for entry point and segment loading
.set LOAD_MASK, 0x07FFFFFF
.code16
.section .stage2
# Validate file header stored in elf_file_header
# returns only on success
elf_validate_file_header:
cmpl $ELFMAGIC, (elf_file_header)
jne .elf_validate_file_header_invalid_magic
cmpb $ELFCLASS32, (elf_file_header + EI_CLASS)
je .elf_validate_file_header_class_valid
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
je .elf_validate_file_header_class_valid
jmp .elf_validate_file_header_invalid_class
.elf_validate_file_header_class_valid:
cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA)
jne .elf_validate_file_header_only_little_endian_supported
cmpb $EV_CURRENT, (elf_file_header + EI_VERSION)
jne .elf_validate_file_header_not_current_version
cmpl $EV_CURRENT, (elf_file_header + e_version)
jne .elf_validate_file_header_not_current_version
cmpw $ET_EXEC, (elf_file_header + e_type)
jne .elf_validate_file_header_not_executable
ret
.elf_validate_file_header_invalid_magic:
movw $elf_validate_file_header_invalid_magic_msg, %si
jmp print_and_halt
.elf_validate_file_header_invalid_class:
movw $elf_validate_file_header_invalid_class_msg, %si
jmp print_and_halt
.elf_validate_file_header_only_little_endian_supported:
movw $elf_validate_file_header_only_little_endian_supported_msg, %si
jmp print_and_halt
.elf_validate_file_header_not_current_version:
movw $elf_validate_file_header_not_current_version_msg, %si
jmp print_and_halt
.elf_validate_file_header_not_executable:
movw $elf_validate_file_header_not_executable_msg, %si
jmp print_and_halt
# reads memory specified by 32 bit elf_program_header to memory
elf_read_program_header32_to_memory:
pushal
pushl %ebp
movl %esp, %ebp
# memset p_filesz -> p_memsz to 0
movl (elf_program_header + p32_filesz), %ebx
movl (elf_program_header + p32_vaddr), %edi
andl $LOAD_MASK, %edi
addl %ebx, %edi
movl (elf_program_header + p32_memsz), %ecx
subl %ebx, %ecx
xorb %al, %al; call memset32
# read file specified in program header to memory
movl (elf_program_header + p32_offset), %eax
movl (elf_program_header + p32_vaddr), %edi
andl $LOAD_MASK, %edi
movl (elf_program_header + p32_filesz), %ecx
call *%esi
leavel
popal
ret
# reads memory specified by 64 bit elf_program_header to memory
elf_read_program_header64_to_memory:
pushal
pushl %ebp
movl %esp, %ebp
# memset p_filesz -> p_memsz to 0
movl (elf_program_header + p64_filesz), %ebx
movl (elf_program_header + p64_vaddr), %edi
andl $LOAD_MASK, %edi
addl %ebx, %edi
movl (elf_program_header + p64_memsz), %ecx
subl %ebx, %ecx
xorb %al, %al; call memset32
# read file specified in program header to memory
movl (elf_program_header + p64_offset), %eax
movl (elf_program_header + p64_vaddr), %edi
andl $LOAD_MASK, %edi
movl (elf_program_header + p64_filesz), %ecx
call *%esi
leavel
popal
ret
# read callback format
# eax: first byte
# ecx: byte count
# edi: buffer
# returns only on success
# reads kernel to memory
# esi: callback for reading from kernel image
# return:
# eax: kernel entry address
.global elf_read_kernel_to_memory
elf_read_kernel_to_memory:
pushal
pushl %ebp
movl %esp, %ebp
subl $2, %esp
# read start of file header
movl $0, %eax
movl $24, %ecx
movl $elf_file_header, %edi
call *%esi
call elf_validate_file_header
# determine file header size
movl $52, %ecx
movl $64, %edx
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
cmovel %edx, %ecx
# read full file header
movl $0, %eax
movl $elf_file_header, %edi
call *%esi
# verify that e_phoff fits in 32 bits
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
jne .elf_read_kernel_to_memory_valid_offset
cmpl $0, (elf_file_header + e64_phoff + 4)
jnz .elf_read_kernel_to_memory_unsupported_offset
.elf_read_kernel_to_memory_valid_offset:
# read architecture phentsize and phnum to fixed locations
movw (elf_file_header + e32_phentsize), %ax
movw (elf_file_header + e32_phnum), %bx
movl (elf_file_header + e32_phoff), %ecx
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
cmovew (elf_file_header + e64_phentsize), %ax
cmovew (elf_file_header + e64_phnum), %bx
cmovel (elf_file_header + e64_phoff), %ecx
movw %ax, (elf_file_header_phentsize)
movw %bx, (elf_file_header_phnum)
movl %ecx, (elf_file_header_phoff)
# current program header
movw $0, -2(%ebp)
.elf_read_kernel_to_memory_loop_program_headers:
movw -2(%ebp), %cx
cmpw (elf_file_header_phnum), %cx
jae .elf_read_kernel_to_memory_done
# eax := program_header_index * e_phentsize + e_phoff
xorl %eax, %eax
movw %cx, %ax
xorl %ebx, %ebx
movw (elf_file_header_phentsize), %bx
mull %ebx
addl (elf_file_header_phoff), %eax
jc .elf_read_kernel_to_memory_unsupported_offset
# determine program header size
movl $32, %ecx
movl $56, %edx
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
cmovel %edx, %ecx
# read program header
movl $elf_program_header, %edi
call *%esi
# test if program header is NULL header
cmpl $PT_NULL, (elf_program_header + p_type)
je .elf_read_kernel_to_memory_null_program_header
# confirm that the program header is loadable
cmpl $PT_LOAD, (elf_program_header + p_type)
jne .elf_read_kernel_to_memory_not_loadable_header
# read program header to memory
movl $elf_read_program_header32_to_memory, %eax
movl $elf_read_program_header64_to_memory, %ebx
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
cmovel %ebx, %eax
call *%eax
.elf_read_kernel_to_memory_null_program_header:
incw -2(%ebp)
jmp .elf_read_kernel_to_memory_loop_program_headers
.elf_read_kernel_to_memory_done:
leavel
popal
# set kernel entry address
movl (elf_file_header + e_entry), %eax
andl $LOAD_MASK, %eax
ret
.elf_read_kernel_to_memory_unsupported_offset:
movw $elf_read_kernel_to_memory_unsupported_offset_msg, %si
jmp print_and_halt
.elf_read_kernel_to_memory_not_loadable_header:
movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si
jmp print_and_halt
.section .data
elf_validate_file_header_invalid_magic_msg:
.asciz "ELF: file has invalid ELF magic"
elf_validate_file_header_invalid_class_msg:
.asciz "ELF: file has invalid ELF class"
elf_validate_file_header_only_little_endian_supported_msg:
.asciz "ELF: file is not in little endian format"
elf_validate_file_header_not_current_version_msg:
.asciz "ELF: file is not in current ELF version"
elf_validate_file_header_not_executable_msg:
.asciz "ELF: file is not an executable"
elf_read_kernel_to_memory_unsupported_offset_msg:
.asciz "ELF: unsupported offset (only 32 bit offsets supported)"
elf_read_kernel_to_memory_not_loadable_header_msg:
.asciz "ELF: kernel contains non-loadable program header"
.section .bss
elf_file_header:
.skip 64
elf_file_header_phentsize:
.skip 2
elf_file_header_phnum:
.skip 2
elf_file_header_phoff:
.skip 4 # NOTE: only 32 bit offsets are supported
elf_program_header:
.skip 56

View File

@@ -1,749 +0,0 @@
# FIXME: don't assume 512 byte sectors
.set SECTOR_SHIFT, 9
.set SECTOR_SIZE, 1 << SECTOR_SHIFT
.set EXT2_MAX_BLOCK_SIZE, 4096
.set EXT2_SUPERBLOCK_SIZE, 264
.set EXT2_BGD_SHIFT, 5
.set EXT2_BGD_SIZE, 1 << EXT2_BGD_SHIFT
.set EXT2_INODE_SIZE_MAX, 256
.set EXT2_ROOT_INO, 2
.set EXT2_GOOD_OLD_REV, 0
# inode types
.set EXT2_S_IMASK, 0xF000
.set EXT2_S_IFDIR, 0x4000
.set EXT2_S_IFREG, 0x8000
# superblock offsets
.set s_first_data_block, 20
.set s_log_block_size, 24
.set s_inodes_per_group, 40
.set s_magic, 56
.set s_rev_level, 76
.set s_inode_size, 88
# block group descriptor offsets
.set bg_inode_table, 8
# inode offsets
.set i_mode, 0
.set i_size, 4
.set i_block, 40
.code16
.section .stage2
# checks whether partition contains ext2 filesystem.
# fills ext2_superblock_buffer
# dl: drive number
# ecx: sector count
# bx:eax: first sector
# return:
# al: 1 if is ext2, 0 otherwise
# si: error message on error
.global has_ext2_filesystem
has_ext2_filesystem:
pushl %ecx
pushw %bx
pushw %di
# fill ext2_partition_first_sector
movw $0, (ext2_partition_first_sector + 6)
movw %bx, (ext2_partition_first_sector + 4)
movl %eax, (ext2_partition_first_sector + 0)
# fill ext2_drive_number
movb %dl, (ext2_drive_number)
cmpl $3, %ecx
jb .has_ext2_filesystem_does_not_fit
# one sector
movw $1, %cx
# from byte offset 1024
addl $(1024 / SECTOR_SIZE), %eax
adcw $0, %bx
# into sector buffer
movw $ext2_block_buffer, %di
call read_from_disk
# copy superblock to its buffer
movw $ext2_block_buffer, %si
movw $ext2_superblock_buffer, %di
movw $EXT2_SUPERBLOCK_SIZE, %cx
rep movsb
# verify magic
cmpw $0xEF53, (ext2_superblock_buffer + s_magic)
jne .has_ext2_filesystem_invalid_magic
# verify block size
# verify shift fits in one byte
movl (ext2_superblock_buffer + s_log_block_size), %ecx
testl $0xFFFFFF00, %ecx
jnz .has_ext2_filesystem_unsupported_block_size
# verify 1024 << s_log_block_size <= EXT2_MAX_BLOCK_SIZE
movl $1024, %eax
shll %cl, %eax
cmpl $EXT2_MAX_BLOCK_SIZE, %eax
ja .has_ext2_filesystem_unsupported_block_size
# fill block size and shift
movl %eax, (ext2_block_size)
addl $10, %ecx
movl %ecx, (ext2_block_shift)
# fill inode size
movl $128, %eax
cmpl $EXT2_GOOD_OLD_REV, (ext2_superblock_buffer + s_rev_level)
cmovnel (ext2_superblock_buffer + s_inode_size), %eax
movl %eax, (ext2_inode_size)
movb $1, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_does_not_fit:
movw $root_partition_does_not_fit_ext2_filesystem_msg, %si
movb $0, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_invalid_magic:
movw $root_partition_has_invalid_ext2_magic_msg, %si
movb $0, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_unsupported_block_size:
movw $root_partition_has_unsupported_ext2_block_size_msg, %si
movb $0, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_done:
popw %di
popw %bx
popl %ecx
ret
# reads block in to ext2_block_buffer
# eax: block number
ext2_read_block:
pushal
# ecx := sectors_per_block := block_size / sector_size
movl (ext2_block_size), %ecx
shrl $SECTOR_SHIFT, %ecx
# ebx:eax := block * sectors_per_block + (ext2_partition_first_sector)
xorl %ebx, %ebx
mull %ecx
addl (ext2_partition_first_sector + 0), %eax
adcl (ext2_partition_first_sector + 4), %ebx
movw $ext2_block_buffer, %di
movb (ext2_drive_number), %dl
call read_from_disk
popal
ret
# reads block group descrtiptor into ext2_block_group_descriptor
# eax: block group
ext2_read_block_group_descriptor:
pushal
# ebx := bgd_block_byte_offset := (s_first_data_block + 1) * block_size
# := (s_first_data_block + 1) << ext2_block_shift
movl (ext2_superblock_buffer + s_first_data_block), %ebx
incl %ebx
movb (ext2_block_shift), %cl
shll %cl, %ebx
# eax := bgd_byte_offset := bgd_block_byte_offset + EXT2_BGD_SIZE * block_group;
# := bgd_block_byte_offset + (block_group << EXT2_BGD_SHIFT)
movb $EXT2_BGD_SHIFT, %cl
shll %cl, %eax
addl %ebx, %eax
# eax: bgd_block := bgd_byte_offset / block_size
# ebx: bgd_offset := bgd_byte_offset % block_size
xorl %edx, %edx
divl (ext2_block_size)
movl %edx, %ebx
call ext2_read_block
# esi := &ext2_block_buffer + bgd_offset := ebx + &ext2_block_buffer
# edi := &ext2_block_group_descriptor_buffer
movl %ebx, %esi
addl $ext2_block_buffer, %esi
movl $ext2_block_group_descriptor_buffer, %edi
movw $EXT2_BGD_SIZE, %cx
rep movsb
popal
ret
# reads inode into ext2_inode_buffer
# eax: ino
ext2_read_inode:
pushal
# eax := block_group = (ino - 1) / s_inodes_per_group
# ebx := inode_index = (ino - 1) % s_inodes_per_group
xorl %edx, %edx
decl %eax
divl (ext2_superblock_buffer + s_inodes_per_group)
movl %edx, %ebx
call ext2_read_block_group_descriptor
# eax := inode_table_block := (inode_index * inode_size) / block_size
# ebx := inode_table_offset := (inode_index * inode_size) % block_size
movl %ebx, %eax
mull (ext2_inode_size)
divl (ext2_block_size)
movl %edx, %ebx
# eax := filesystem_block := eax + bg_inode_table
addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax
movb (ext2_drive_number), %dl
call ext2_read_block
# copy inode memory
# esi := inode_table_offset + ext2_block_buffer := edx + ext2_block_buffer
movl %ebx, %esi
addl $ext2_block_buffer, %esi
# edi := ext2_inode_buffer
movl $ext2_inode_buffer, %edi
# ecx := inode_size
movl (ext2_inode_size), %ecx
rep movsb
# reset indirect cache to zero
movl $0, (ext2_inode_indirect_number)
.ext2_read_inode_done:
popal
ret
# gets block index from n'th data block in inode stored in ext2_inode_buffer
# eax: data block index
# return:
# eax: block index
ext2_data_block_index:
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi
pushl %edi
# ebx := max_data_blocks := (file_size + block_size - 1) / block_size
# := (i_size + ext2_block_size - 1) >> ext2_block_shift
# cl := ext2_block_shift
movl (ext2_inode_buffer + i_size), %ebx
addl (ext2_block_size), %ebx
decl %ebx
movb (ext2_block_shift), %cl
shrl %cl, %ebx
# verify data block is within bounds
cmpl %ebx, %eax
jae .ext2_data_block_index_out_of_bounds
# check if this is direct block access
cmpl $12, %eax
jb .ext2_data_block_index_direct
subl $12, %eax
# cl := indices_per_block_shift := ext2_block_shift - 2
# ebx := comp
subb $2, %cl
movl $1, %ebx
shll %cl, %ebx
# check if this is singly indirect block access
cmpl %ebx, %eax
jb .ext2_data_block_index_singly_indirect
subl %ebx, %eax
shll %cl, %ebx
# check if this is doubly indirect block access
cmpl %ebx, %eax
jb .ext2_data_block_index_doubly_indirect
subl %ebx, %eax
shll %cl, %ebx
# check if this is triply indirect block access
cmpl %ebx, %eax
jb .ext2_data_block_index_triply_indirect
# otherwise this is invalid access
jmp .ext2_data_block_index_invalid
.ext2_data_block_index_direct:
movl $(ext2_inode_buffer + i_block), %esi
movl (%esi, %eax, 4), %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_singly_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 12 * 4), %eax
movw $1, %cx
jmp .ext2_data_block_index_indirect
.ext2_data_block_index_doubly_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 13 * 4), %eax
movw $2, %cx
jmp .ext2_data_block_index_indirect
.ext2_data_block_index_triply_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 14 * 4), %eax
movw $3, %cx
jmp .ext2_data_block_index_indirect
# eax := current block
# ebx := index
# cx := depth
.ext2_data_block_index_indirect:
# edx := cache index := (index & ~(block_size / 4 - 1)) | depth
# := (index & -(block_size >> 2)) | depth
movl (ext2_block_size), %edx
shrl $2, %edx
negl %edx
andl %ebx, %edx
orw %cx, %dx
# check whether this block is already cached
cmpl $0, (ext2_inode_indirect_number)
je .ext2_data_block_index_indirect_no_cache
cmpl %edx, (ext2_inode_indirect_number)
je .ext2_data_block_index_indirect_cached
.ext2_data_block_index_indirect_no_cache:
# update cache block number, will be cached when found
movl %edx, (ext2_inode_indirect_number)
# eax := current block
# ebx := index
# cx := depth
.ext2_data_block_index_indirect_loop:
call ext2_read_block
# store depth and index
pushw %cx
pushl %ebx
cmpw $1, %cx
jbe .ext2_data_block_index_no_shift
# cl := shift
movb (ext2_block_shift), %al
subb $2, %al
decb %cl
mulb %cl
movb %al, %cl
# ebx := ebx >> shift
shrl %cl, %ebx
.ext2_data_block_index_no_shift:
# edx := index of next block (ebx & (block_size / 4 - 1))
movl (ext2_block_size), %edx
shrl $2, %edx
decl %edx
andl %ebx, %edx
# eax := next block
movl $ext2_block_buffer, %esi
movl (%esi, %edx, 4), %eax
# restore depth and index
popl %ebx
popw %cx
loop .ext2_data_block_index_indirect_loop
# cache last read block
movw $ext2_block_buffer, %si
movw $ext2_inode_indirect_buffer, %di
movw (ext2_block_size), %cx
rep movsb
jmp .ext2_data_block_index_done
.ext2_data_block_index_out_of_bounds:
movw $ext2_data_block_index_out_of_bounds_msg, %si
call puts; call print_newline
movl $0, %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_invalid:
movw $ext2_data_block_index_invalid_msg, %si
call puts; call print_newline
movl $0, %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_indirect_cached:
movl $ext2_inode_indirect_buffer, %esi
movl (ext2_block_size), %edx
shrl $2, %edx
decl %edx
andl %edx, %ebx
movl (%esi, %ebx, 4), %eax
.ext2_data_block_index_done:
popl %edi
popl %esi
popl %edx
popl %ecx
popl %ebx
ret
# read bytes from inode (implements read callback)
# eax: first byte
# ecx: byte count
# edi: buffer
# returns only on success
.global ext2_inode_read_bytes
ext2_inode_read_bytes:
pushal
pushl %ebp
movl %esp, %ebp
subl $8, %esp
# save read info
movl %eax, 0(%esp)
movl %ecx, 4(%esp)
# eax := first_byte / block_size
# edx := first_byte % block_size
# when edx == 0, no partial read needed
xorl %edx, %edx
divl (ext2_block_size)
testl %edx, %edx
jz .ext2_inode_read_bytes_no_partial_start
# get data block index and read block
call ext2_data_block_index
call ext2_read_block
# ecx := byte count (min(block_size - edx, remaining_bytes))
movl (ext2_block_size), %ecx
subl %edx, %ecx
cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx
# update remaining read info
addl %ecx, 0(%esp)
subl %ecx, 4(%esp)
# esi := start sector data (block_buffer + index * SECTOR_SIZE)
movl $ext2_block_buffer, %esi
addl %edx, %esi
call memcpy32
# check if all sectors are read
cmpl $0, 4(%esp)
je .ext2_inode_read_bytes_done
.ext2_inode_read_bytes_no_partial_start:
# eax := data block index (byte_start / block_size)
movl 0(%esp), %eax
movb (ext2_block_shift), %cl
shrl %cl, %eax
# get data block index and read block
call ext2_data_block_index
call ext2_read_block
# calculate bytes to copy (min(block_size, remaining_bytes))
movl (ext2_block_size), %ecx
cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx
# update remaining read info
addl %ecx, 0(%esp)
subl %ecx, 4(%esp)
movl $ext2_block_buffer, %esi
call memcpy32
# read next block if more sectors remaining
cmpl $0, 4(%esp)
jnz .ext2_inode_read_bytes_no_partial_start
.ext2_inode_read_bytes_done:
leavel
popal
ret
# find inode in inside directory inode stored in ext2_inode_buffer
# store the found inode in ext2_inode_buffer
# si: name string
# cx: name length
# return:
# eax: ino if inode was found, 0 otherwise
ext2_directory_find_inode:
pushl %ebx
pushw %cx
pushw %dx
pushw %si
pushw %di
pushl %ebp
movl %esp, %ebp
subl $8, %esp
# 0(%esp) := name length
movw %cx, 0(%esp)
# 2(%esp) := name string
movw %si, 2(%esp)
# verify that the name is <= 0xFF bytes
cmpw $0xFF, %cx
ja .ext2_directory_find_inode_not_found
# ebx := max data blocks: ceil(i_size / block_size)
movl (ext2_inode_buffer + i_size), %ebx
addl (ext2_block_size), %ebx
decl %ebx
movb (ext2_block_shift), %cl
shrl %cl, %ebx
jz .ext2_directory_find_inode_not_found
# 4(%esp) := current block
movl $0, 4(%esp)
.ext2_directory_find_inode_block_read_loop:
# get next block index
movl 4(%esp), %eax
call ext2_data_block_index
test %eax, %eax
jz .ext2_directory_find_inode_next_block
# read current block
call ext2_read_block
# dx := current entry pointer
movw $ext2_block_buffer, %si
.ext2_directory_find_inode_loop_entries:
# temporarily store entry pointer in dx
movw %si, %dx
# check if name length matches
# cx := name length
movw 0(%esp), %cx
cmpb 6(%si), %cl
jne .ext2_directory_find_inode_next_entry
# si := entry name
addw $8, %si
# di := asked name
movw 2(%esp), %di
# check if name matches
call memcmp
test %al, %al
# NOTE: dx contains entry pointer
jnz .ext2_directory_find_inode_found
.ext2_directory_find_inode_next_entry:
# restore si
movw %dx, %si
# go to next entry if this block contains one
addw 4(%si), %si
movw $ext2_block_buffer, %di
addw (ext2_block_size), %di
cmpw %di, %si
jb .ext2_directory_find_inode_loop_entries
.ext2_directory_find_inode_next_block:
incl 4(%esp)
cmpl %ebx, 4(%esp)
jb .ext2_directory_find_inode_block_read_loop
.ext2_directory_find_inode_not_found:
xorb %al, %al
jmp .ext2_directory_find_inode_done
.ext2_directory_find_inode_found:
# extract ino and read it to ext2_inode_buffer
movw %dx, %si
movl 0(%si), %eax
call ext2_read_inode
.ext2_directory_find_inode_done:
leavel
popw %di
popw %si
popw %dx
popw %cx
popl %ebx
ret
# search for kernel file from filesystem
# returns only on success
.global ext2_find_kernel
ext2_find_kernel:
pushl %eax
pushw %cx
pushw %di
pushw %si
movl $EXT2_ROOT_INO, %eax
call ext2_read_inode
movw $kernel_path, %di
.ext2_find_kernel_loop:
movw (%di), %si
# check if this list is done
testw %si, %si
jz .ext2_find_kernel_loop_done
# check that current part is directory
movw (ext2_inode_buffer + i_mode), %ax
andw $EXT2_S_IMASK, %ax
cmpw $EXT2_S_IFDIR, %ax
jne .ext2_find_kernel_part_not_dir
# prepare registers for directory finding
movw 0(%si), %cx
addw $2, %si
# print search path
pushw %si
movw $ext2_looking_for_msg, %si
call puts
popw %si
call puts; call print_newline
# search current directory for this file
call ext2_directory_find_inode
testl %eax, %eax
jz .ext2_find_kernel_part_not_found
# loop to next part
addw $2, %di
jmp .ext2_find_kernel_loop
.ext2_find_kernel_loop_done:
# check that kernel is a regular file
movw (ext2_inode_buffer + i_mode), %ax
andw $EXT2_S_IMASK, %ax
cmpw $EXT2_S_IFREG, %ax
jne .ext2_find_kernel_not_reg
movw $ext2_kernel_found_msg, %si
call puts; call print_newline
popw %si
popw %di
popw %cx
popl %eax
ret
.ext2_find_kernel_part_not_dir:
movw $ext2_part_not_dir_msg, %si
jmp print_and_halt
.ext2_find_kernel_part_not_found:
movw $ext2_part_not_found_msg, %si
jmp print_and_halt
.ext2_find_kernel_not_reg:
movw $ext2_kernel_not_reg_msg, %si
jmp print_and_halt
.section .data
kernel_path:
.short kernel_path1
.short kernel_path2
.short 0
kernel_path1:
.short 4
.asciz "boot"
kernel_path2:
.short 15
.asciz "banan-os.kernel"
root_partition_does_not_fit_ext2_filesystem_msg:
.asciz "Root partition is too small to contain ext2 filesystem"
root_partition_has_invalid_ext2_magic_msg:
.asciz "Root partition doesn't contain ext2 magic number"
root_partition_has_unsupported_ext2_block_size_msg:
.asciz "Root partition has unsupported ext2 block size (1 KiB, 2 KiB and 4 KiB are supported)"
ext2_part_not_dir_msg:
.asciz "inode in root path is not directory"
ext2_part_not_found_msg:
.asciz " not found"
ext2_kernel_not_reg_msg:
.asciz "kernel is not a regular file"
ext2_kernel_found_msg:
.asciz "kernel found!"
ext2_data_block_index_out_of_bounds_msg:
.asciz "data block index out of bounds"
ext2_data_block_index_invalid_msg:
.asciz "data block index is invalid"
ext2_looking_for_msg:
.asciz "looking for "
.section .bss
.align SECTOR_SIZE
ext2_block_buffer:
.skip EXT2_MAX_BLOCK_SIZE
ext2_inode_indirect_buffer:
.skip EXT2_MAX_BLOCK_SIZE
ext2_inode_indirect_number:
.skip 4
ext2_partition_first_sector:
.skip 8
ext2_drive_number:
.skip 1
.skip 3 # padding
# NOTE: fits in 2 bytes
ext2_inode_size:
.skip 4
ext2_block_size:
.skip 4
ext2_block_shift:
.skip 4
ext2_superblock_buffer:
.skip EXT2_SUPERBLOCK_SIZE
ext2_block_group_descriptor_buffer:
.skip EXT2_BGD_SIZE
ext2_inode_buffer:
.skip EXT2_INODE_SIZE_MAX

View File

@@ -1,218 +0,0 @@
.code16
.section .stage2
# kernel framebuffer information format
# .align 8
# .long 0xBABAB007
# .long -(0xBABAB007 + width + height + bpp)
# .long width (2 bytes used, 4 bytes for ease of calculation)
# .long height (2 bytes used, 4 bytes for ease of calculation)
# .long bpp (1 bytes used, 4 bytes for ease of calculation)
# scan memory 0x100000 -> 0x200000 for framebuffer information
# return:
# ax: target width
# bx: target height
# cx: target bpp
vesa_scan_kernel_image:
pushl %edx
pushl %esi
movl $0x100000, %esi
.vesa_scan_kernel_image_loop:
# check magic
cmpl $0xBABAB007, (%esi)
jne .vesa_scan_kernel_image_next_addr
# check checksum
movl 0x00(%esi), %edx
addl 0x04(%esi), %edx
addl 0x08(%esi), %edx
addl 0x0C(%esi), %edx
addl 0x10(%esi), %edx
testl %edx, %edx
jnz .vesa_scan_kernel_image_next_addr
# set return registers
movw 0x08(%esi), %ax
movw 0x0C(%esi), %bx
movw 0x10(%esi), %cx
jmp .vesa_scan_kernel_image_done
.vesa_scan_kernel_image_next_addr:
addl $8, %esi
cmpl $0x200000, %esi
jb .vesa_scan_kernel_image_loop
# zero out return registers
xorw %ax, %ax
xorw %bx, %bx
xorw %cx, %cx
.vesa_scan_kernel_image_done:
popl %esi
popl %edx
ret
# Find suitable video mode and save it in (vesa_target_mode)
vesa_find_video_mode:
pushal
pushl %ebp
movl %esp, %ebp
subl $6, %esp
# clear target mode and frame buffer
movw $0, (vesa_target_mode)
movl $0, (framebuffer + 0)
movl $0, (framebuffer + 4)
movl $0, (framebuffer + 8)
movl $0, (framebuffer + 12)
movw $0, (framebuffer + 16)
call vesa_scan_kernel_image
testw %ax, %ax
jz .vesa_find_video_mode_loop_modes_done
# save arguments in stack
movw %ax, -2(%ebp)
movw %bx, -4(%ebp)
movw %cx, -6(%ebp)
# get vesa information
movw $0x4F00, %ax
movw $vesa_info_buffer, %di
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error
# confirm that response starts with 'VESA'
cmpl $0x41534556, (vesa_info_buffer)
jne .vesa_error
# confirm that version is atleast 2.0
cmpw $0x0200, (vesa_info_buffer + 0x04)
jb .vesa_unsupported_version
movl (vesa_info_buffer + 0x0E), %esi
.vesa_find_video_mode_loop_modes:
cmpw $0xFFFF, (%esi)
je .vesa_find_video_mode_loop_modes_done
# get info of next mode
movw $0x4F01, %ax
movw (%esi), %cx
movw $vesa_mode_info_buffer, %di
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error
# check whether in graphics mode
testb $0x10, (vesa_mode_info_buffer + 0)
jz .vesa_find_video_mode_next_mode
# compare mode's dimensions
movw -2(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x12)
jne .vesa_find_video_mode_next_mode
movw -4(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x14)
jne .vesa_find_video_mode_next_mode
movb -6(%ebp), %al; cmpb %al, (vesa_mode_info_buffer + 0x19)
jne .vesa_find_video_mode_next_mode
# set address, pitch, type
movl (vesa_mode_info_buffer + 0x28), %esi
movl %esi, (framebuffer + 0)
movw (vesa_mode_info_buffer + 0x10), %ax
movw %ax, (framebuffer + 4)
movb $1, (framebuffer + 17)
# set width, height, bpp
movw -2(%ebp), %ax; movw %ax, (framebuffer + 8)
movw -4(%ebp), %ax; movw %ax, (framebuffer + 12)
movw -6(%ebp), %ax; movb %al, (framebuffer + 16)
movw %cx, (vesa_target_mode)
jmp .vesa_find_video_mode_loop_modes_done
.vesa_find_video_mode_next_mode:
addl $2, %esi
jmp .vesa_find_video_mode_loop_modes
.vesa_find_video_mode_loop_modes_done:
leavel
popal
ret
.vesa_unsupported:
movw $vesa_unsupported_msg, %si
jmp print_and_halt
.vesa_unsupported_version:
movw $vesa_unsupported_version_msg, %si
jmp print_and_halt
.vesa_error:
movw $vesa_error_msg, %si
jmp print_and_halt
# scan for video mode in kernel memory and set the correct one.
# when video mode is not found or does not exists,
# set it to 80x25 text mode to clear the screen.
.global vesa_set_video_mode
vesa_set_video_mode:
pushw %ax
pushw %bx
call vesa_find_video_mode
movw (vesa_target_mode), %bx
testw %bx, %bx
jz .vesa_set_target_mode_generic
movw $0x4F02, %ax
orw $0x4000, %bx
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
jmp .set_video_done
.vesa_set_target_mode_generic:
movb $0x03, %al
movb $0x00, %ah
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
.set_video_done:
popw %bx
popw %ax
ret
.section .data
vesa_error_msg:
.asciz "VESA error"
vesa_unsupported_msg:
.asciz "VESA unsupported"
vesa_unsupported_version_msg:
.asciz "VESA unsupported version"
vesa_success_msg:
.asciz "VESA success"
.section .bss
vesa_info_buffer:
.skip 512
vesa_mode_info_buffer:
.skip 256
vesa_target_mode:
.skip 2
.global framebuffer
.align 8
framebuffer:
.skip 4 # address
.skip 4 # pitch
.skip 4 # width
.skip 4 # height
.skip 1 # bpp
.skip 1 # type

View File

@@ -1,17 +0,0 @@
ENTRY(stage1_main)
SECTIONS
{
. = 0x7C00;
.stage1 : { *(.stage1) }
. = ALIGN(512);
stage2_start = .;
.stage2 : { *(.stage2) }
. = ALIGN(512);
.data : { *(.data) }
stage2_end = .;
. = ALIGN(512);
.bss : { *(.bss) }
}

View File

@@ -1,132 +0,0 @@
.code16
.section .stage2
# fills memory map data structure
# doesn't return on error
# NO REGISTERS SAVED
.global get_memory_map
get_memory_map:
movl $0, (memory_map_entry_count)
movl $0x0000E820, %eax
movl $0x534D4150, %edx
xorl %ebx, %ebx
movl $20, %ecx
movw $memory_map_entries, %di
clc
int $0x15
# If first call returs with CF set, the call failed
jc .get_memory_map_error
.get_memory_map_rest:
cmpl $0x534D4150, %eax
jne .get_memory_map_error
# FIXME: don't assume BIOS to always return 20 bytes
cmpl $20, %ecx
jne .get_memory_map_error
# increment entry count
incl (memory_map_entry_count)
# increment entry pointer
addw %cx, %di
# BIOS can indicate end of list by 0 in ebx
testl %ebx, %ebx
jz .get_memory_map_done
movl $0x0000E820, %eax
movl $0x534D4150, %edx
clc
int $0x15
# BIOS can indicate end of list by setting CF
jnc .get_memory_map_rest
.get_memory_map_done:
ret
.get_memory_map_error:
movw $memory_map_error_msg, %si
jmp print_and_halt
# print memory map from memory_map_entries
# NO REGISTERS SAVED
.global print_memory_map
print_memory_map:
movw $memory_map_msg, %si
call puts
call print_newline
movl (memory_map_entry_count), %edx
movw $memory_map_entries, %si
movw $16, %bx
movw $4, %cx
.loop_memory_map:
movb $' ', %al
call putc; call putc; call putc; call putc
movw 0x06(%si), %ax
call print_number
movw 0x04(%si), %ax
call print_number
movw 0x02(%si), %ax
call print_number
movw 0x00(%si), %ax
call print_number
movb $',', %al
call putc
movb $' ', %al
call putc
movw 0x0E(%si), %ax
call print_number
movw 0x0C(%si), %ax
call print_number
movw 0x0A(%si), %ax
call print_number
movw 0x08(%si), %ax
call print_number
movb $',', %al
call putc
movb $' ', %al
call putc
movw 0x12(%si), %ax
call print_number
movw 0x10(%si), %ax
call print_number
call print_newline
addw $20, %si
decl %edx
jnz .loop_memory_map
ret
.section .data
memory_map_msg:
.asciz "memmap:"
memory_map_error_msg:
.asciz "Failed to get memory map"
.section .bss
.global memory_map
memory_map:
memory_map_entry_count:
.skip 4
# 100 entries should be enough...
memory_map_entries:
.skip 20 * 100

View File

@@ -1,385 +0,0 @@
.include "common.S"
.set SCREEN_WIDTH, 80
.set SCREEN_HEIGHT, 25
.code16
.section .stage1
# prints character to screen
# al: ascii character to print
.global putc
putc:
pushw %ax
pushw %bx
movb $0x0E, %ah
xorb %bh, %bh
int $0x10
popw %bx
popw %ax
ret
# prints null terminated string to screen
# ds:si: string address
.global puts
puts:
pushw %si
pushw %bx
pushw %ax
movb $0x0E, %ah
xorb %bh, %bh
.puts_loop:
lodsb
test %al, %al
jz .puts_done
int $0x10
jmp .puts_loop
.puts_done:
popw %ax
popw %bx
popw %si
ret
# compares memory between addresses
# si: ptr1
# di: ptr2
# cx: bytes count
# return:
# al: 1 if equal, 0 otherwise
.global memcmp
memcmp:
# NOTE: using pusha + popa to save space
pusha
cld
repe cmpsb
popa
setzb %al
ret
.section .stage2
# read a character from keyboard
# return:
# al: ascii
# ah: bios scan code
.global getc
getc:
movb $0x00, %ah
int $0x16
ret
# prints newline to screen
.global print_newline
print_newline:
pushw %ax
movb $'\r', %al
call putc
movb $'\n', %al
call putc
pop %ax
ret
# prints backspace to screen, can go back a line
.global print_backspace
print_backspace:
pushw %ax
pushw %bx
pushw %cx
pushw %dx
# get cursor position
movb $0x03, %ah
movb $0x00, %bh
int $0x10
# don't do anyting if on first row
testb %dh, %dh
jz .print_backspace_done
# go one line up if on first column
test %dl, %dl
jz .print_backspace_go_line_up
# otherwise decrease column
decb %dl
jmp .print_backspace_do_print
.print_backspace_go_line_up:
# decrease row and set column to the last one
decb %dh
movb $(SCREEN_WIDTH - 1), %dl
.print_backspace_do_print:
# set cursor position
movb $0x02, %ah
int $0x10
# print 'empty' character (space)
mov $' ', %al
call putc
# set cursor position
movb $0x02, %ah
int $0x10
.print_backspace_done:
popw %dx
popw %cx
popw %bx
popw %ax
ret
# print number to screen
# ax: number to print
# bx: number base
# cx: min width (zero pads if shorter)
.global print_number
print_number:
pusha
pushl %ebp
movl %esp, %ebp
# save min width
subl $4, %esp
movw %cx, (%esp)
movw $print_number_buffer, %si
xorw %cx, %cx
.print_number_fill_loop:
# fill buffer with all remainders ax % bx
xorw %dx, %dx
divw %bx
movb %dl, (%si)
incw %si
incw %cx
testw %ax, %ax
jnz .print_number_fill_loop
# check if zero pad is required
cmpw (%esp), %cx
jae .print_number_print_loop
# dx: saved number count
# cx: zero pad count
movw %cx, %dx
movw (%esp), %cx
subw %dx, %cx
movb $'0', %al
.print_number_pad_zeroes:
call putc
loop .print_number_pad_zeroes
# restore number count
movw %dx, %cx
.print_number_print_loop:
decw %si
movb (%si), %al
cmpb $10, %al
jae .print_number_hex
addb $'0', %al
jmp .print_number_do_print
.print_number_hex:
addb $('a' - 10), %al
.print_number_do_print:
call putc
loop .print_number_print_loop
leavel
popa
ret
# prints 8 bit hexadecimal number to screen
# al: number to print
.global print_hex8
print_hex8:
pushw %ax
pushw %bx
pushw %cx
movw $16, %bx
movw $2, %cx
andw $0xFF, %ax
call print_number
popw %cx
popw %bx
popw %ax
ret
# prints 16 bit hexadecimal number to screen
# ax: number to print
.global print_hex16
print_hex16:
pushw %bx
pushw %cx
movw $16, %bx
movw $4, %cx
call print_number
popw %cx
popw %bx
ret
# prints 32 bit hexadecimal number to screen
# eax: number to print
.global print_hex32
print_hex32:
pushl %eax
pushw %dx
movw %ax, %dx
shrl $16, %eax;
call print_hex16
movw %dx, %ax
call print_hex16
popw %dx
popl %eax
ret
# prints 64 bit hexadecimal number to screen
# edx:eax: number to print
.global print_hex64
print_hex64:
xchgl %eax, %edx
call print_hex32
xchgl %eax, %edx
call print_hex32
ret
# test if character is printable ascii
# al: character to test
# return:
# al: 1 if is printable, 0 otherwise
.global isprint
isprint:
subb $0x20, %al
cmpb $(0x7E - 0x20), %al
ja .isprint_not_printable
movb $1, %al
ret
.isprint_not_printable:
movb $0, %al
ret
# memset with 32 bit registers
# edi: destination address
# ecx: bytes count
# al: value to set
# return:
# edi: destination address + bytes count
# ecx: 0
# other: preserved
.global memset32
memset32:
testl %ecx, %ecx
jz .memset32_done
pushf; cli
pushw %es
pushl %eax
pushl %ebx
pushl %edx
movl %cr0, %ebx
orb $1, %bl
movl %ebx, %cr0
ljmpl $GDT_CODE32, $.memset32_pmode32
.code32
.memset32_pmode32:
movw $GDT_DATA32, %dx
movw %dx, %es
rep stosb %es:(%edi)
ljmpl $GDT_CODE16, $.memset32_pmode16
.code16
.memset32_pmode16:
andb $0xFE, %bl
movl %ebx, %cr0
ljmpl $0x00, $.memset32_rmode16
.memset32_rmode16:
popl %edx
popl %ebx
popl %eax
popw %es
popf
.memset32_done:
ret
# memcpy with 32 bit registers
# esi: source address
# edi: destination address
# ecx: bytes count
# return:
# esi: source address + bytes count
# edi: destination address + bytes count
# ecx: 0
# other: preserved
.global memcpy32
memcpy32:
testl %ecx, %ecx
jz .memcpy32_done
pushf; cli
pushw %ds
pushw %es
pushl %ebx
pushl %edx
movl %cr0, %ebx
orb $1, %bl
movl %ebx, %cr0
ljmpl $GDT_CODE32, $.memcpy32_pmode32
.code32
.memcpy32_pmode32:
movw $GDT_DATA32, %dx
movw %dx, %ds
movw %dx, %es
rep movsb %ds:(%esi), %es:(%edi)
ljmpl $GDT_CODE16, $.memcpy32_pmode16
.code16
.memcpy32_pmode16:
andb $0xFE, %bl
movl %ebx, %cr0
ljmpl $0x00, $.memcpy32_rmode16
.memcpy32_rmode16:
popl %edx
popl %ebx
popw %es
popw %ds
popf
.memcpy32_done:
ret
.section .bss
# enough for base 2 printing
print_number_buffer:
.skip 16

View File

@@ -1 +0,0 @@
build/

View File

@@ -1,22 +0,0 @@
cmake_minimum_required(VERSION 3.26)
if (NOT DEFINED ENV{BANAN_ARCH})
message(FATAL_ERROR "environment variable BANAN_ARCH not defined")
endif ()
set(BANAN_ARCH $ENV{BANAN_ARCH})
project(banan_os-bootloader-installer CXX)
set(SOURCES
crc32.cpp
ELF.cpp
GPT.cpp
GUID.cpp
main.cpp
)
add_executable(banan_os-bootloader-installer ${SOURCES})
target_compile_options(banan_os-bootloader-installer PRIVATE -O2 -std=c++20)
target_compile_definitions(banan_os-bootloader-installer PRIVATE __arch=${BANAN_ARCH})
target_include_directories(banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../userspace/libraries/LibELF/include)
target_include_directories(banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../kernel/include)

View File

@@ -1,142 +0,0 @@
#include "ELF.h"
#include <LibELF/Values.h>
#include <cassert>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
using namespace LibELF;
ELFFile::ELFFile(std::string_view path)
: m_path(path)
{
m_fd = open(m_path.c_str(), O_RDONLY);
if (m_fd == -1)
{
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
if (fstat(m_fd, &m_stat) == -1)
{
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ, MAP_PRIVATE, m_fd, 0);
if (mmap_addr == MAP_FAILED)
{
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
if (!validate_elf_header())
return;
m_success = true;
}
ELFFile::~ELFFile()
{
if (m_mmap)
munmap(m_mmap, m_stat.st_size);
m_mmap = nullptr;
if (m_fd != -1)
close(m_fd);
m_fd = -1;
}
const ElfNativeFileHeader& ELFFile::elf_header() const
{
return *reinterpret_cast<LibELF::ElfNativeFileHeader*>(m_mmap);
}
bool ELFFile::validate_elf_header() const
{
if (m_stat.st_size < sizeof(ElfNativeFileHeader))
{
std::cerr << m_path << " is too small to be a ELF executable" << std::endl;
return false;
}
const auto& elf_header = this->elf_header();
if (
elf_header.e_ident[EI_MAG0] != ELFMAG0 ||
elf_header.e_ident[EI_MAG1] != ELFMAG1 ||
elf_header.e_ident[EI_MAG2] != ELFMAG2 ||
elf_header.e_ident[EI_MAG3] != ELFMAG3
)
{
std::cerr << m_path << " doesn't have an ELF magic number" << std::endl;
return false;
}
#if ARCH(x86_64)
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
#elif ARCH(i686)
if (elf_header.e_ident[EI_CLASS] != ELFCLASS32)
#endif
{
std::cerr << m_path << " architecture doesn't match" << std::endl;
return false;
}
if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB)
{
std::cerr << m_path << " is not in little endian format" << std::endl;
return false;
}
if (elf_header.e_ident[EI_VERSION] != EV_CURRENT)
{
std::cerr << m_path << " has unsupported version" << std::endl;
return false;
}
if (elf_header.e_type != ET_EXEC)
{
std::cerr << m_path << " is not an executable ELF file" << std::endl;
return false;
}
return true;
}
const ElfNativeSectionHeader& ELFFile::section_header(std::size_t index) const
{
const auto& elf_header = this->elf_header();
assert(index < elf_header.e_shnum);
const uint8_t* section_array_start = m_mmap + elf_header.e_shoff;
return *reinterpret_cast<const ElfNativeSectionHeader*>(section_array_start + index * elf_header.e_shentsize);
}
std::string_view ELFFile::section_name(const ElfNativeSectionHeader& section_header) const
{
const auto& elf_header = this->elf_header();
assert(elf_header.e_shstrndx != SHN_UNDEF);
const auto& section_string_table = this->section_header(elf_header.e_shstrndx);
const char* string_table_start = reinterpret_cast<const char*>(m_mmap + section_string_table.sh_offset);
return string_table_start + section_header.sh_name;
}
std::optional<std::span<const uint8_t>> ELFFile::find_section(std::string_view name) const
{
const auto& elf_header = this->elf_header();
for (std::size_t i = 0; i < elf_header.e_shnum; i++)
{
const auto& section_header = this->section_header(i);
auto section_name = this->section_name(section_header);
if (section_name != name)
continue;
return std::span<const uint8_t>(m_mmap + section_header.sh_offset, section_header.sh_size);
}
return {};
}

View File

@@ -1,36 +0,0 @@
#pragma once
#include <LibELF/Types.h>
#include <cstdint>
#include <optional>
#include <span>
#include <string_view>
#include <string>
#include <sys/stat.h>
class ELFFile
{
public:
ELFFile(std::string_view path);
~ELFFile();
const LibELF::ElfNativeFileHeader& elf_header() const;
std::optional<std::span<const uint8_t>> find_section(std::string_view name) const;
bool success() const { return m_success; }
std::string_view path() const { return m_path; }
private:
const LibELF::ElfNativeSectionHeader& section_header(std::size_t index) const;
std::string_view section_name(const LibELF::ElfNativeSectionHeader&) const;
bool validate_elf_header() const;
private:
const std::string m_path;
bool m_success { false };
int m_fd { -1 };
struct stat m_stat { };
uint8_t* m_mmap { nullptr };
};

View File

@@ -1,250 +0,0 @@
#include "crc32.h"
#include "GPT.h"
#include <cassert>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
// FIXME: don't assume 512 byte sectors
#define SECTOR_SIZE 512
GPTFile::GPTFile(std::string_view path)
: m_path(path)
{
m_fd = open(m_path.c_str(), O_RDWR);
if (m_fd == -1)
{
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
if (fstat(m_fd, &m_stat) == -1)
{
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
if (mmap_addr == MAP_FAILED)
{
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
if (!validate_gpt_header())
return;
m_success = true;
}
GPTFile::~GPTFile()
{
if (m_mmap)
munmap(m_mmap, m_stat.st_size);
m_mmap = nullptr;
if (m_fd != -1)
close(m_fd);
m_fd = -1;
}
MBR& GPTFile::mbr()
{
return *reinterpret_cast<MBR*>(m_mmap);
}
const GPTHeader& GPTFile::gpt_header() const
{
return *reinterpret_cast<GPTHeader*>(m_mmap + SECTOR_SIZE);
}
bool GPTFile::install_stage1(std::span<const uint8_t> stage1)
{
auto& mbr = this->mbr();
if (stage1.size() > sizeof(mbr.boot_code))
{
std::cerr << m_path << ": can't fit " << stage1.size() << " bytes of boot code in mbr (max is " << sizeof(mbr.boot_code) << ")" << std::endl;
return false;
}
// copy boot code
memcpy(mbr.boot_code, stage1.data(), stage1.size());
// setup mbr
mbr.unique_mbr_disk_signature = 0xdeadbeef;
mbr.unknown = 0;
mbr.signature = 0xAA55;
// setup mbr partition records
mbr.partition_records[0].boot_indicator = 0x00;
mbr.partition_records[0].starting_chs[0] = 0x00;
mbr.partition_records[0].starting_chs[1] = 0x02;
mbr.partition_records[0].starting_chs[2] = 0x00;
mbr.partition_records[0].os_type = 0xEE;
mbr.partition_records[0].ending_chs[0] = 0xFF;
mbr.partition_records[0].ending_chs[1] = 0xFF;
mbr.partition_records[0].ending_chs[2] = 0xFF;
mbr.partition_records[0].starting_lba = 1;
mbr.partition_records[0].size_in_lba = 0xFFFFFFFF;
memset(&mbr.partition_records[1], 0x00, sizeof(MBRPartitionRecord));
memset(&mbr.partition_records[2], 0x00, sizeof(MBRPartitionRecord));
memset(&mbr.partition_records[3], 0x00, sizeof(MBRPartitionRecord));
return true;
}
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid)
{
if (data.size() < 16)
{
std::cerr << m_path << ": contains invalid .data section, too small for patches" << std::endl;
return false;
}
// find GUID patch offsets
std::size_t disk_guid_offset(-1);
std::size_t part_guid_offset(-1);
for (std::size_t i = 0; i < data.size() - 16; i++)
{
if (memcmp(data.data() + i, "root disk guid ", 16) == 0)
{
if (disk_guid_offset != std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .data section, multiple patchable disk guids" << std::endl;
return false;
}
disk_guid_offset = i;
}
if (memcmp(data.data() + i, "root part guid ", 16) == 0)
{
if (part_guid_offset != std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .data section, multiple patchable partition guids" << std::endl;
return false;
}
part_guid_offset = i;
}
}
if (disk_guid_offset == std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .data section, no patchable disk guid" << std::endl;
return false;
}
if (part_guid_offset == std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .data section, no patchable partition guid" << std::endl;
return false;
}
auto partition = find_partition_with_type(bios_boot_guid);
if (!partition.has_value())
{
std::cerr << m_path << ": could not find partition with type " << bios_boot_guid << std::endl;
return false;
}
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
std::size_t data_offset = stage2.size();
if (std::size_t rem = data_offset % 512)
data_offset += 512 - rem;
if (data_offset + data.size() > partition_size)
{
std::cerr << m_path << ": can't fit " << stage2.size() + data.size() << " bytes of data to partition of size " << partition_size << std::endl;
return false;
}
uint8_t* partition_start = m_mmap + partition->starting_lba * SECTOR_SIZE;
memcpy(partition_start, stage2.data(), stage2.size());
memcpy(partition_start + data_offset, data.data(), data.size());
// patch GUIDs
*reinterpret_cast<GUID*>(partition_start + data_offset + disk_guid_offset) = gpt_header().disk_guid;
*reinterpret_cast<GUID*>(partition_start + data_offset + part_guid_offset) = root_partition_guid;
return true;
}
bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid)
{
if (!find_partition_with_guid(root_partition_guid).has_value())
{
std::cerr << m_path << ": no partition with GUID " << root_partition_guid << std::endl;
return false;
}
if (!install_stage1(stage1))
return false;
if (!install_stage2(stage2, data, root_partition_guid))
return false;
return true;
}
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_guid(const GUID& guid) const
{
const auto& gpt_header = this->gpt_header();
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
{
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
if (partition_entry.partition_guid != guid)
continue;
return partition_entry;
}
return {};
}
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_type(const GUID& type_guid) const
{
const auto& gpt_header = this->gpt_header();
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
{
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
if (partition_entry.type_guid != type_guid)
continue;
return partition_entry;
}
return {};
}
bool GPTFile::validate_gpt_header() const
{
if (SECTOR_SIZE + m_stat.st_size < sizeof(GPTHeader))
{
std::cerr << m_path << " is too small to have GPT header" << std::endl;
return false;
}
auto gpt_header = this->gpt_header();
if (std::memcmp(gpt_header.signature, "EFI PART", 8) != 0)
{
std::cerr << m_path << " doesn't contain GPT partition header signature" << std::endl;
return false;
}
const uint32_t header_crc32 = gpt_header.header_crc32;
gpt_header.header_crc32 = 0;
if (header_crc32 != crc32_checksum(reinterpret_cast<uint8_t*>(&gpt_header), gpt_header.header_size))
{
std::cerr << m_path << " has non-matching header crc32" << std::endl;
return false;
}
const std::size_t partition_array_size = gpt_header.number_of_partition_entries * gpt_header.size_of_partition_entry;
if (gpt_header.partition_entry_array_crc32 != crc32_checksum(m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE, partition_array_size))
{
std::cerr << m_path << " has non-matching partition entry crc32" << std::endl;
return false;
}
return true;
}

View File

@@ -1,91 +0,0 @@
#pragma once
#include "GUID.h"
#include <cstdint>
#include <optional>
#include <span>
#include <string_view>
#include <string>
#include <sys/stat.h>
struct MBRPartitionRecord
{
uint8_t boot_indicator;
uint8_t starting_chs[3];
uint8_t os_type;
uint8_t ending_chs[3];
uint32_t starting_lba;
uint32_t size_in_lba;
} __attribute__((packed));
struct MBR
{
uint8_t boot_code[440];
uint32_t unique_mbr_disk_signature;
uint16_t unknown;
MBRPartitionRecord partition_records[4];
uint16_t signature;
} __attribute__((packed));
static_assert(sizeof(MBR) == 512);
struct GPTPartitionEntry
{
GUID type_guid;
GUID partition_guid;
uint64_t starting_lba;
uint64_t ending_lba;
uint64_t attributes;
uint16_t name[36];
};
static_assert(sizeof(GPTPartitionEntry) == 128);
struct GPTHeader
{
char signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t header_crc32;
uint32_t reserved;
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
GUID disk_guid;
uint64_t partition_entry_lba;
uint32_t number_of_partition_entries;
uint32_t size_of_partition_entry;
uint32_t partition_entry_array_crc32;
} __attribute__((packed));
static_assert(sizeof(GPTHeader) == 92);
class GPTFile
{
public:
GPTFile(std::string_view path);
~GPTFile();
bool install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid);
const GPTHeader& gpt_header() const;
bool success() const { return m_success; }
std::string_view path() const { return m_path; }
private:
MBR& mbr();
bool validate_gpt_header() const;
std::optional<GPTPartitionEntry> find_partition_with_guid(const GUID& guid) const;
std::optional<GPTPartitionEntry> find_partition_with_type(const GUID& type_guid) const;
bool install_stage1(std::span<const uint8_t> stage1);
bool install_stage2(std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid);
private:
const std::string m_path;
bool m_success { false };
int m_fd { -1 };
struct stat m_stat { };
uint8_t* m_mmap { nullptr };
};

View File

@@ -1,74 +0,0 @@
#include "GUID.h"
#include <iomanip>
#include <cstring>
std::optional<uint64_t> parse_hex(std::string_view hex_string)
{
uint64_t result = 0;
for (char c : hex_string)
{
if (!isxdigit(c))
return {};
uint8_t nibble = 0;
if ('0' <= c && c <= '9')
nibble = c - '0';
else if ('a' <= c && c <= 'f')
nibble = c - 'a' + 10;
else
nibble = c - 'A' + 10;
result = (result << 4) | nibble;
}
return result;
}
std::optional<GUID> GUID::from_string(std::string_view guid_string)
{
if (guid_string.size() != 36)
return {};
if (guid_string[8] != '-' || guid_string[13] != '-' || guid_string[18] != '-' || guid_string[23] != '-')
return {};
auto comp1 = parse_hex(guid_string.substr(0, 8));
auto comp2 = parse_hex(guid_string.substr(9, 4));
auto comp3 = parse_hex(guid_string.substr(14, 4));
auto comp4 = parse_hex(guid_string.substr(19, 4));
auto comp5 = parse_hex(guid_string.substr(24, 12));
if (!comp1.has_value() || !comp2.has_value() || !comp3.has_value() || !comp4.has_value() || !comp5.has_value())
return {};
GUID result;
result.component1 = *comp1;
result.component2 = *comp2;
result.component3 = *comp3;
for (int i = 0; i < 2; i++)
result.component45[i + 0] = *comp4 >> ((2-1) * 8 - i * 8);
for (int i = 0; i < 6; i++)
result.component45[i + 2] = *comp5 >> ((6-1) * 8 - i * 8);
return result;
}
bool GUID::operator==(const GUID& other) const
{
return std::memcmp(this, &other, sizeof(GUID)) == 0;
}
std::ostream& operator<<(std::ostream& out, const GUID& guid)
{
auto flags = out.flags();
out << std::hex << std::setfill('0');
out << std::setw(8) << guid.component1 << '-';
out << std::setw(4) << guid.component2 << '-';
out << std::setw(4) << guid.component3 << '-';
out << std::setw(2);
for (int i = 0; i < 2; i++) out << +guid.component45[i];
out << '-';
for (int i = 2; i < 8; i++) out << +guid.component45[i];
out.flags(flags);
return out;
}

View File

@@ -1,37 +0,0 @@
#pragma once
#include <cstdint>
#include <optional>
#include <ostream>
#include <string_view>
struct GUID
{
static std::optional<GUID> from_string(std::string_view);
uint32_t component1;
uint16_t component2;
uint16_t component3;
// last 2 components are combined so no packed needed
uint8_t component45[8];
bool operator==(const GUID& other) const;
};
std::ostream& operator<<(std::ostream& out, const GUID& guid);
// unused 00000000-0000-0000-0000-000000000000
static constexpr GUID unused_guid = {
0x00000000,
0x0000,
0x0000,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
};
// bios boot 21686148-6449-6E6F-744E-656564454649
static constexpr GUID bios_boot_guid = {
0x21686148,
0x6449,
0x6E6F,
{ 0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 }
};

View File

@@ -1,80 +0,0 @@
#include "crc32.h"
static constexpr uint32_t crc32_table[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
};
uint32_t crc32_checksum(const uint8_t* data, std::size_t count)
{
uint32_t crc32 = 0xFFFFFFFF;
for (size_t i = 0; i < count; i++)
{
uint8_t index = (crc32 ^ data[i]) & 0xFF;
crc32 = (crc32 >> 8) ^ crc32_table[index];
}
return crc32 ^ 0xFFFFFFFF;
}

View File

@@ -1,6 +0,0 @@
#pragma once
#include <cstddef>
#include <cstdint>
uint32_t crc32_checksum(const uint8_t* data, std::size_t count);

View File

@@ -1,45 +0,0 @@
#include "ELF.h"
#include "GPT.h"
#include <iostream>
int main(int argc, char** argv)
{
using namespace std::string_view_literals;
if (argc != 4)
{
std::fprintf(stderr, "usage: %s BOOTLOADER DISK_IMAGE ROOT_PARTITION_GUID\n", argv[0]);
return 1;
}
auto root_partition_guid = GUID::from_string(argv[3]);
if (!root_partition_guid.has_value())
{
std::cerr << "invalid guid '" << argv[3] << '\'' << std::endl;
return 1;
}
ELFFile bootloader(argv[1]);
if (!bootloader.success())
return 1;
auto stage1 = bootloader.find_section(".stage1"sv);
auto stage2 = bootloader.find_section(".stage2"sv);
auto data = bootloader.find_section(".data"sv);
if (!stage1.has_value() || !stage2.has_value() || !data.has_value())
{
std::cerr << bootloader.path() << " doesn't contain .stage1, .stage2 and .data sections" << std::endl;
return 1;
}
GPTFile disk_image(argv[2]);
if (!disk_image.success())
return 1;
if (!disk_image.install_bootloader(*stage1, *stage2, *data, *root_partition_guid))
return 1;
std::cout << "bootloader installed" << std::endl;
return 0;
}

1
bos
View File

@@ -1 +0,0 @@
script/build.sh

10
check-fs.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -e
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
sudo partprobe $LOOP_DEV
sudo fsck.ext2 -fn ${LOOP_DEV}p2 || true
sudo losetup -d $LOOP_DEV

41
image-full.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
set -e
DISK_SIZE=$[50 * 1024 * 1024]
MOUNT_DIR=/mnt
truncate -s 0 $DISK_IMAGE_PATH
truncate -s $DISK_SIZE $DISK_IMAGE_PATH
sed -e 's/\s*\([-\+[:alnum:]]*\).*/\1/' << EOF | fdisk $DISK_IMAGE_PATH > /dev/null
g # gpt
n # new partition
1 # partition number 1
# default (from the beginning of the disk)
+1MiB # bios boot partiton size
n # new partition
2 # partition number 2
# default (right after bios boot partition)
# default (to the end of disk)
t # set type
1 # ... of partition 1
4 # bios boot partition
t # set type
2 # ... of partition 2
20 # Linux filesystem
w # write changes
EOF
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
sudo partprobe $LOOP_DEV
PARTITION1=${LOOP_DEV}p1
PARTITION2=${LOOP_DEV}p2
sudo mkfs.ext2 -d $SYSROOT -b 1024 -q $PARTITION2
sudo mount $PARTITION2 $MOUNT_DIR
sudo grub-install --no-floppy --target=i386-pc --modules="normal ext2 multiboot" --boot-directory=${MOUNT_DIR}/boot $LOOP_DEV
sudo umount $MOUNT_DIR
sudo losetup -d $LOOP_DEV

22
image.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
set -e
if [ ! -f $DISK_IMAGE_PATH ]; then
$(dirname "$0")/image-full.sh
exit 0
fi
MOUNT_DIR=/mnt
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
sudo partprobe $LOOP_DEV
ROOT_PARTITON=${LOOP_DEV}p2
sudo mount $ROOT_PARTITON $MOUNT_DIR
sudo rsync -a ${SYSROOT}/* ${MOUNT_DIR}/
sudo umount $MOUNT_DIR
sudo losetup -d $LOOP_DEV

View File

@@ -1,130 +1,77 @@
cmake_minimum_required(VERSION 3.26)
project(kernel CXX C ASM)
if("${BANAN_ARCH}" STREQUAL "x86_64")
set(ELF_FORMAT elf64-x86-64)
elseif("${BANAN_ARCH}" STREQUAL "i386")
set(ELF_FORMAT elf32-i386)
endif()
set(KERNEL_SOURCES set(KERNEL_SOURCES
font/prefs.psf.o font/prefs.psf.o
kernel/ACPI/ACPI.cpp kernel/ACPI.cpp
kernel/ACPI/AML/Namespace.cpp
kernel/ACPI/AML/Node.cpp
kernel/ACPI/AML/OpRegion.cpp
kernel/ACPI/BatterySystem.cpp
kernel/ACPI/EmbeddedController.cpp
kernel/APIC.cpp kernel/APIC.cpp
kernel/Audio/AC97/Controller.cpp
kernel/Audio/Controller.cpp
kernel/Audio/HDAudio/AudioFunctionGroup.cpp
kernel/Audio/HDAudio/Controller.cpp
kernel/BootInfo.cpp
kernel/CPUID.cpp kernel/CPUID.cpp
kernel/Credentials.cpp
kernel/Debug.cpp kernel/Debug.cpp
kernel/Device/DebugDevice.cpp
kernel/Device/Device.cpp kernel/Device/Device.cpp
kernel/Device/FramebufferDevice.cpp
kernel/Device/NullDevice.cpp kernel/Device/NullDevice.cpp
kernel/Device/RandomDevice.cpp
kernel/Device/ZeroDevice.cpp kernel/Device/ZeroDevice.cpp
kernel/ELF.cpp
kernel/Epoll.cpp
kernel/Errors.cpp kernel/Errors.cpp
kernel/Font.cpp
kernel/FS/DevFS/FileSystem.cpp kernel/FS/DevFS/FileSystem.cpp
kernel/FS/EventFD.cpp
kernel/FS/Ext2/FileSystem.cpp kernel/FS/Ext2/FileSystem.cpp
kernel/FS/Ext2/Inode.cpp kernel/FS/Ext2/Inode.cpp
kernel/FS/FAT/FileSystem.cpp
kernel/FS/FAT/Inode.cpp
kernel/FS/FileSystem.cpp
kernel/FS/Inode.cpp kernel/FS/Inode.cpp
kernel/FS/Pipe.cpp kernel/FS/Pipe.cpp
kernel/FS/ProcFS/FileSystem.cpp kernel/FS/ProcFS/FileSystem.cpp
kernel/FS/ProcFS/Inode.cpp kernel/FS/ProcFS/Inode.cpp
kernel/FS/TmpFS/FileSystem.cpp kernel/FS/RamFS/FileSystem.cpp
kernel/FS/TmpFS/Inode.cpp kernel/FS/RamFS/Inode.cpp
kernel/FS/USTARModule.cpp
kernel/FS/VirtualFileSystem.cpp kernel/FS/VirtualFileSystem.cpp
kernel/GDT.cpp kernel/Input/PS2Controller.cpp
kernel/IDT.cpp kernel/Input/PS2Keyboard.cpp
kernel/Input/InputDevice.cpp kernel/Input/PS2Keymap.cpp
kernel/Input/PS2/Controller.cpp
kernel/Input/PS2/Device.cpp
kernel/Input/PS2/Keyboard.cpp
kernel/Input/PS2/Keymap.cpp
kernel/Input/PS2/Mouse.cpp
kernel/Interruptable.cpp
kernel/InterruptController.cpp kernel/InterruptController.cpp
kernel/kernel.cpp kernel/kernel.cpp
kernel/Lock/SpinLock.cpp
kernel/Memory/ByteRingBuffer.cpp
kernel/Memory/DMARegion.cpp
kernel/Memory/FileBackedRegion.cpp kernel/Memory/FileBackedRegion.cpp
kernel/Memory/GeneralAllocator.cpp
kernel/Memory/Heap.cpp kernel/Memory/Heap.cpp
kernel/Memory/kmalloc.cpp kernel/Memory/kmalloc.cpp
kernel/Memory/MemoryBackedRegion.cpp kernel/Memory/MemoryBackedRegion.cpp
kernel/Memory/MemoryRegion.cpp kernel/Memory/MemoryRegion.cpp
kernel/Memory/PhysicalRange.cpp kernel/Memory/PhysicalRange.cpp
kernel/Memory/SharedMemoryObject.cpp
kernel/Memory/VirtualRange.cpp kernel/Memory/VirtualRange.cpp
kernel/Networking/ARPTable.cpp kernel/Networking/E1000.cpp
kernel/Networking/E1000/E1000.cpp
kernel/Networking/E1000/E1000E.cpp
kernel/Networking/IPv4Layer.cpp
kernel/Networking/Loopback.cpp
kernel/Networking/NetworkInterface.cpp
kernel/Networking/NetworkLayer.cpp
kernel/Networking/NetworkManager.cpp
kernel/Networking/NetworkSocket.cpp
kernel/Networking/RTL8169/RTL8169.cpp
kernel/Networking/TCPSocket.cpp
kernel/Networking/UDPSocket.cpp
kernel/Networking/UNIX/Socket.cpp
kernel/OpenFileDescriptorSet.cpp kernel/OpenFileDescriptorSet.cpp
kernel/Panic.cpp kernel/Panic.cpp
kernel/PCI.cpp kernel/PCI.cpp
kernel/PIC.cpp kernel/PIC.cpp
kernel/Process.cpp kernel/Process.cpp
kernel/Processor.cpp
kernel/Random.cpp
kernel/Scheduler.cpp kernel/Scheduler.cpp
kernel/Semaphore.cpp
kernel/SpinLock.cpp
kernel/SSP.cpp kernel/SSP.cpp
kernel/Storage/ATA/AHCI/Controller.cpp kernel/Storage/ATABus.cpp
kernel/Storage/ATA/AHCI/Device.cpp kernel/Storage/ATAController.cpp
kernel/Storage/ATA/ATABus.cpp kernel/Storage/ATADevice.cpp
kernel/Storage/ATA/ATAController.cpp
kernel/Storage/ATA/ATADevice.cpp
kernel/Storage/DiskCache.cpp kernel/Storage/DiskCache.cpp
kernel/Storage/NVMe/Controller.cpp
kernel/Storage/NVMe/Namespace.cpp
kernel/Storage/NVMe/Queue.cpp
kernel/Storage/Partition.cpp
kernel/Storage/SCSI.cpp
kernel/Storage/StorageDevice.cpp kernel/Storage/StorageDevice.cpp
kernel/Syscall.cpp kernel/Syscall.cpp
kernel/Terminal/FramebufferTerminal.cpp kernel/Syscall.S
kernel/Terminal/PseudoTerminal.cpp
kernel/Terminal/Serial.cpp kernel/Terminal/Serial.cpp
kernel/Terminal/TerminalDriver.cpp
kernel/Terminal/TextModeTerminal.cpp
kernel/Terminal/TTY.cpp kernel/Terminal/TTY.cpp
kernel/Terminal/VesaTerminalDriver.cpp
kernel/Terminal/VirtualTTY.cpp kernel/Terminal/VirtualTTY.cpp
kernel/Thread.cpp kernel/Thread.cpp
kernel/ThreadBlocker.cpp
kernel/Timer/HPET.cpp kernel/Timer/HPET.cpp
kernel/Timer/PIT.cpp kernel/Timer/PIT.cpp
kernel/Timer/RTC.cpp kernel/Timer/RTC.cpp
kernel/Timer/Timer.cpp kernel/Timer/Timer.cpp
kernel/USB/Controller.cpp
kernel/USB/Device.cpp
kernel/USB/HID/HIDDriver.cpp
kernel/USB/HID/Joystick.cpp
kernel/USB/HID/Keyboard.cpp
kernel/USB/HID/Mouse.cpp
kernel/USB/Hub/HubDriver.cpp
kernel/USB/MassStorage/MassStorageDriver.cpp
kernel/USB/MassStorage/SCSIDevice.cpp
kernel/USB/USBManager.cpp
kernel/USB/XHCI/Controller.cpp
kernel/USB/XHCI/Device.cpp
icxxabi.cpp icxxabi.cpp
) )
set(ENABLE_KERNEL_UBSAN False) #set(ENABLE_KERNEL_UBSAN True)
if(ENABLE_KERNEL_UBSAN) if(ENABLE_KERNEL_UBSAN)
set(KERNEL_SOURCES ${KERNEL_SOURCES} ubsan.cpp) set(KERNEL_SOURCES ${KERNEL_SOURCES} ubsan.cpp)
@@ -134,153 +81,127 @@ if("${BANAN_ARCH}" STREQUAL "x86_64")
set(KERNEL_SOURCES set(KERNEL_SOURCES
${KERNEL_SOURCES} ${KERNEL_SOURCES}
arch/x86_64/boot.S arch/x86_64/boot.S
arch/x86_64/GDT.cpp
arch/x86_64/IDT.cpp
arch/x86_64/interrupts.S arch/x86_64/interrupts.S
arch/x86_64/PageTable.cpp arch/x86_64/PageTable.cpp
arch/x86_64/Signal.S arch/x86_64/Signal.S
arch/x86_64/Syscall.S
arch/x86_64/Thread.S arch/x86_64/Thread.S
arch/x86_64/User.S
arch/x86_64/Yield.S
) )
elseif("${BANAN_ARCH}" STREQUAL "i686") elseif("${BANAN_ARCH}" STREQUAL "i386")
set(KERNEL_SOURCES set(KERNEL_SOURCES
${KERNEL_SOURCES} ${KERNEL_SOURCES}
arch/i686/boot.S arch/i386/boot.S
arch/i686/interrupts.S arch/i386/GDT.cpp
arch/i686/PageTable.cpp arch/i386/IDT.cpp
arch/i686/Signal.S arch/i386/MMU.cpp
arch/i686/Syscall.S arch/i386/SpinLock.S
arch/i686/Thread.S arch/i386/Thread.S
arch/i686/User.S
arch/i686/Yield.S
) )
else() else()
message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}") message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}")
endif() endif()
file(GLOB_RECURSE LAI_SOURCES
lai/*.c
)
set(LAI_SOURCES
${LAI_SOURCES}
kernel/lai_host.cpp
)
set(BAN_SOURCES set(BAN_SOURCES
../BAN/BAN/Assert.cpp
../BAN/BAN/New.cpp ../BAN/BAN/New.cpp
../BAN/BAN/String.cpp
../BAN/BAN/StringView.cpp ../BAN/BAN/StringView.cpp
../BAN/BAN/Time.cpp ../BAN/BAN/Time.cpp
) )
set(KLIBC_SOURCES set(LIBC_SOURCES
klibc/ctype.cpp ../libc/ctype.cpp
klibc/string.cpp ../libc/string.cpp
# Ehhh don't do this but for now libc uses the same stuff kernel can use
# This won't work after libc starts using sse implemetations tho
../userspace/libraries/LibC/arch/${BANAN_ARCH}/string.S
) )
set(LIBDEFLATE_SOURCE set(LIBELF_SOURCES
../userspace/libraries/LibDEFLATE/Compressor.cpp ../LibELF/LibELF/LoadableELF.cpp
../userspace/libraries/LibDEFLATE/Decompressor.cpp
../userspace/libraries/LibDEFLATE/HuffmanTree.cpp
)
set(LIBFONT_SOURCES
../userspace/libraries/LibFont/Font.cpp
../userspace/libraries/LibFont/PSF.cpp
)
set(LIBINPUT_SOURCE
../userspace/libraries/LibInput/KeyboardLayout.cpp
../userspace/libraries/LibInput/KeyEvent.cpp
)
set(LIBQR_SOURCE
../userspace/libraries/LibQR/QRCode.cpp
) )
set(KERNEL_SOURCES set(KERNEL_SOURCES
${KERNEL_SOURCES} ${KERNEL_SOURCES}
${LAI_SOURCES}
${BAN_SOURCES} ${BAN_SOURCES}
${KLIBC_SOURCES} ${LIBC_SOURCES}
${LIBDEFLATE_SOURCE} ${LIBELF_SOURCES}
${LIBFONT_SOURCES}
${LIBINPUT_SOURCE}
${LIBQR_SOURCE}
) )
add_executable(kernel ${KERNEL_SOURCES}) add_executable(kernel ${KERNEL_SOURCES})
add_dependencies(kernel headers)
target_compile_definitions(kernel PRIVATE __is_kernel) target_compile_definitions(kernel PUBLIC __is_kernel)
target_compile_definitions(kernel PRIVATE __arch=${BANAN_ARCH}) target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
target_compile_definitions(kernel PRIVATE LIBDEFLATE_AVOID_STACK=1)
target_compile_options(kernel PRIVATE target_compile_options(kernel PUBLIC -O2 -g)
-O2 -g target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-literal-suffix -fno-rtti -fno-exceptions>)
-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=. target_compile_options(kernel PUBLIC -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
-fstack-protector target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -Wall -Werror=return-type -Wstack-usage=1024 -fno-omit-frame-pointer -mgeneral-regs-only)
-ffreestanding
-fno-omit-frame-pointer
-fstrict-volatile-bitfields
-mgeneral-regs-only
-Wall -Wextra -Werror -Wstack-usage=1024
)
# C++ specific
target_compile_options(kernel PRIVATE
-Wno-literal-suffix
-Wno-invalid-offsetof
-fno-rtti
-fno-exceptions
)
if(ENABLE_KERNEL_UBSAN) if(ENABLE_KERNEL_UBSAN)
target_compile_options(kernel PRIVATE -fsanitize=undefined) target_compile_options(kernel PUBLIC -fsanitize=undefined)
endif() endif()
if("${BANAN_ARCH}" STREQUAL "x86_64") if("${BANAN_ARCH}" STREQUAL "x86_64")
target_compile_options(kernel PRIVATE -mcmodel=kernel -mno-red-zone) target_compile_options(kernel PUBLIC -mcmodel=kernel -mno-red-zone -mno-mmx)
target_link_options(kernel PRIVATE LINKER:-z,max-page-size=4096) target_link_options(kernel PUBLIC LINKER:-z,max-page-size=4096)
target_link_options(kernel PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/x86_64/linker.ld) target_link_options(kernel PUBLIC LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/x86_64/linker.ld)
elseif("${BANAN_ARCH}" STREQUAL "i686") elseif("${BANAN_ARCH}" STREQUAL "i386")
target_link_options(kernel PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/i686/linker.ld) target_link_options(kernel PUBLIC LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/i386/linker.ld)
endif() endif()
target_link_options(kernel PRIVATE -ffreestanding -nostdlib -orphan-handling=error) target_link_options(kernel PUBLIC -ffreestanding -nostdlib)
get_target_property(KERNEL_COMPILE_OPTIONS kernel COMPILE_OPTIONS) add_custom_target(crt0
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE) COMMAND ${CMAKE_CXX_COMPILER} -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crt0.S -o ${CMAKE_CURRENT_BINARY_DIR}/crt0.o
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND OUTPUT_STRIP_TRAILING_WHITESPACE) DEPENDS headers
)
add_custom_command(
TARGET crt0
POST_BUILD
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/crt0.o ${BANAN_LIB}/
)
add_custom_target(kernel-headers
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/lai/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
USES_TERMINAL
)
add_custom_target(kernel-install
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/kernel ${BANAN_BOOT}/banan-os.kernel
DEPENDS kernel
USES_TERMINAL
)
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND OUTPUT_STRIP_TRAILING_WHITESPACE)
add_custom_command( add_custom_command(
TARGET kernel PRE_LINK TARGET kernel PRE_LINK
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crti.S ${COMPILE_OPTIONS} COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crti.S ${COMPILE_OPTIONS}
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crtn.S ${COMPILE_OPTIONS} COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crtn.S ${COMPILE_OPTIONS}
COMMAND ${CMAKE_COMMAND} -E copy ${CRTBEGIN} . COMMAND cp ${CRTBEGIN} .
COMMAND ${CMAKE_COMMAND} -E copy ${CRTEND} . COMMAND cp ${CRTEND} .
) )
#add_custom_command( add_custom_command(
# TARGET kernel POST_BUILD TARGET kernel POST_BUILD
# COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
#) )
banan_include_headers(kernel ban)
banan_include_headers(kernel libc)
banan_include_headers(kernel libdeflate)
banan_include_headers(kernel libelf)
banan_include_headers(kernel libfont)
banan_include_headers(kernel libinput)
banan_include_headers(kernel libqr)
banan_install_headers(kernel)
set_target_properties(kernel PROPERTIES OUTPUT_NAME banan-os.kernel)
install(TARGETS kernel DESTINATION ${BANAN_BOOT} OPTIONAL)
if("${BANAN_ARCH}" STREQUAL "x86_64")
set(ELF_FORMAT elf64-x86-64)
elseif("${BANAN_ARCH}" STREQUAL "i686")
set(ELF_FORMAT elf32-i386)
endif()
add_custom_command( add_custom_command(
OUTPUT font/prefs.psf.o OUTPUT font/prefs.psf.o
COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/font && objcopy -O ${ELF_FORMAT} -B i386 -I binary font/prefs.psf ${CMAKE_CURRENT_BINARY_DIR}/font/prefs.psf.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && objcopy -O ${ELF_FORMAT} -B i386 -I binary font/prefs.psf ${CMAKE_CURRENT_BINARY_DIR}/font/prefs.psf.o
) )
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_COMPILER} <CMAKE_CXX_LINK_FLAGS> <FLAGS> <LINK_FLAGS> -o <TARGET> ${CMAKE_CURRENT_BINARY_DIR}/crti.o ${CMAKE_CURRENT_BINARY_DIR}/crtbegin.o <OBJECTS> ${CMAKE_CURRENT_BINARY_DIR}/crtend.o ${CMAKE_CURRENT_BINARY_DIR}/crtn.o -lgcc ") set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_COMPILER} <CMAKE_CXX_LINK_FLAGS> <FLAGS> <LINK_FLAGS> -o <TARGET> ${CMAKE_CURRENT_BINARY_DIR}/crti.o ${CMAKE_CURRENT_BINARY_DIR}/crtbegin.o <OBJECTS> ${CMAKE_CURRENT_BINARY_DIR}/crtend.o ${CMAKE_CURRENT_BINARY_DIR}/crtn.o -lgcc ")

147
kernel/arch/i386/GDT.cpp Normal file
View File

@@ -0,0 +1,147 @@
#include <BAN/Assert.h>
#include <kernel/GDT.h>
#include <string.h>
extern "C" uintptr_t g_boot_stack_top[0];
namespace Kernel::GDT
{
struct TaskStateSegment
{
uint16_t link;
uint16_t reserved1;
uint32_t esp0;
uint16_t ss0;
uint16_t reserved2;
uint32_t esp1;
uint16_t ss1;
uint16_t reserved3;
uint32_t esp2;
uint16_t ss2;
uint16_t reserved4;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint16_t es;
uint16_t reserved5;
uint16_t cs;
uint16_t reserved6;
uint16_t ss;
uint16_t reserved7;
uint16_t ds;
uint16_t reserved8;
uint16_t fs;
uint16_t reserved9;
uint16_t gs;
uint16_t reserved10;
uint16_t ldtr;
uint16_t reserved11;
uint16_t reserved12;
uint16_t iopb;
uint32_t ssp;
} __attribute__((packed));
union SegmentDescriptor
{
struct
{
uint16_t limit1;
uint16_t base1;
uint8_t base2;
uint8_t access;
uint8_t limit2 : 4;
uint8_t flags : 4;
uint8_t base3;
} __attribute__((packed));
struct
{
uint32_t low;
uint32_t high;
} __attribute__((packed));
} __attribute__((packed));
struct GDTR
{
uint16_t size;
uint32_t address;
} __attribute__((packed));
static TaskStateSegment* s_tss = nullptr;
static SegmentDescriptor* s_gdt = nullptr;
static GDTR s_gdtr;
static void write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
{
SegmentDescriptor& desc = *(SegmentDescriptor*)((uintptr_t)s_gdt + offset);
desc.base1 = base;
desc.base2 = base >> 16;
desc.base3 = base >> 24;
desc.limit1 = limit;
desc.limit2 = limit >> 16;
desc.access = access;
desc.flags = flags;
}
static void write_tss(uint8_t offset)
{
s_tss = new TaskStateSegment();
ASSERT(s_tss);
memset(s_tss, 0x00, sizeof(TaskStateSegment));
s_tss->ss0 = 0x10;
s_tss->esp0 = (uintptr_t)g_boot_stack_top;
write_entry(offset, (uint32_t)s_tss, sizeof(TaskStateSegment), 0x89, 0x0);
}
void set_tss_stack(uintptr_t esp)
{
s_tss->esp0 = esp;
}
static void flush_gdt()
{
asm volatile("lgdt %0" :: "m"(s_gdtr));
}
extern "C" void flush_tss(uint16_t offset)
{
asm volatile("ltr %0" :: "m"(offset));
}
void initialize()
{
constexpr uint32_t descriptor_count = 6;
s_gdt = new SegmentDescriptor[descriptor_count];
ASSERT(s_gdt);
s_gdtr.address = (uint64_t)s_gdt;
s_gdtr.size = descriptor_count * sizeof(SegmentDescriptor) - 1;
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xC); // kernel code
write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, 0xC); // kernel data
write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, 0xC); // user code
write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, 0xC); // user data
write_tss(0x28);
flush_gdt();
flush_tss(0x28);
}
}

270
kernel/arch/i386/IDT.cpp Normal file
View File

@@ -0,0 +1,270 @@
#include <BAN/Errors.h>
#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Panic.h>
#include <kernel/Scheduler.h>
#define INTERRUPT_HANDLER____(i, msg) \
static void interrupt ## i () \
{ \
uint32_t eax, ebx, ecx, edx; \
uint32_t esp, ebp; \
uint32_t cr0, cr2, cr3, cr4; \
asm volatile("":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx)); \
asm volatile("movl %%esp, %%eax":"=a"(esp)); \
asm volatile("movl %%ebp, %%eax":"=a"(ebp)); \
asm volatile("movl %%cr0, %%eax":"=a"(cr0)); \
asm volatile("movl %%cr2, %%eax":"=a"(cr2)); \
asm volatile("movl %%cr3, %%eax":"=a"(cr3)); \
asm volatile("movl %%cr4, %%eax":"=a"(cr4)); \
Kernel::panic(msg "\r\nRegister dump\r\n" \
"eax=0x{8H}, ebx=0x{8H}, ecx=0x{8H}, edx=0x{8H}\r\n" \
"esp=0x{8H}, ebp=0x{8H}\r\n" \
"CR0=0x{8H}, CR2=0x{8H}, CR3=0x{8H}, CR4=0x{8H}\r\n", \
eax, ebx, ecx, edx, esp, ebp, cr0, cr2, cr3, cr4); \
}
#define INTERRUPT_HANDLER_ERR(i, msg) \
static void interrupt ## i () \
{ \
uint32_t eax, ebx, ecx, edx; \
uint32_t esp, ebp; \
uint32_t cr0, cr2, cr3, cr4; \
uint32_t error_code; \
asm volatile("":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx)); \
asm volatile("movl %%esp, %%eax":"=a"(esp)); \
asm volatile("movl %%ebp, %%eax":"=a"(ebp)); \
asm volatile("movl %%cr0, %%eax":"=a"(cr0)); \
asm volatile("movl %%cr2, %%eax":"=a"(cr2)); \
asm volatile("movl %%cr3, %%eax":"=a"(cr3)); \
asm volatile("movl %%cr4, %%eax":"=a"(cr4)); \
asm volatile("popl %%eax":"=a"(error_code)); \
Kernel::panic(msg " (error code: 0x{8H})\r\n" \
"Register dump\r\n" \
"eax=0x{8H}, ebx=0x{8H}, ecx=0x{8H}, edx=0x{8H}\r\n" \
"esp=0x{8H}, ebp=0x{8H}\r\n" \
"CR0=0x{8H}, CR2=0x{8H}, CR3=0x{8H}, CR4=0x{8H}\r\n", \
eax, ebx, ecx, edx, esp, ebp, cr0, cr2, cr3, cr4, error_code); \
}
#define REGISTER_HANDLER(i) register_interrupt_handler(i, interrupt ## i)
namespace IDT
{
struct GateDescriptor
{
uint16_t offset1;
uint16_t selector;
uint8_t reserved : 5;
uint8_t zero1 : 3;
uint8_t type : 4;
uint8_t zero2 : 1;
uint8_t DPL : 2;
uint8_t present : 1;
uint16_t offset2;
} __attribute__((packed));
struct IDTR
{
uint16_t size;
void* offset;
} __attribute((packed));
static IDTR s_idtr;
static GateDescriptor* s_idt = nullptr;
static void(*s_irq_handlers[16])() { nullptr };
INTERRUPT_HANDLER____(0x00, "Division Error")
INTERRUPT_HANDLER____(0x01, "Debug")
INTERRUPT_HANDLER____(0x02, "Non-maskable Interrupt")
INTERRUPT_HANDLER____(0x03, "Breakpoint")
INTERRUPT_HANDLER____(0x04, "Overflow")
INTERRUPT_HANDLER____(0x05, "Bound Range Exception")
INTERRUPT_HANDLER____(0x06, "Invalid Opcode")
INTERRUPT_HANDLER____(0x07, "Device Not Available")
INTERRUPT_HANDLER_ERR(0x08, "Double Fault")
INTERRUPT_HANDLER____(0x09, "Coprocessor Segment Overrun")
INTERRUPT_HANDLER_ERR(0x0A, "Invalid TSS")
INTERRUPT_HANDLER_ERR(0x0B, "Segment Not Present")
INTERRUPT_HANDLER_ERR(0x0C, "Stack-Segment Fault")
INTERRUPT_HANDLER_ERR(0x0D, "General Protection Fault")
INTERRUPT_HANDLER_ERR(0x0E, "Page Fault")
INTERRUPT_HANDLER____(0x0F, "Unknown Exception 0x0F")
INTERRUPT_HANDLER____(0x10, "x87 Floating-Point Exception")
INTERRUPT_HANDLER_ERR(0x11, "Alignment Check")
INTERRUPT_HANDLER____(0x12, "Machine Check")
INTERRUPT_HANDLER____(0x13, "SIMD Floating-Point Exception")
INTERRUPT_HANDLER____(0x14, "Virtualization Exception")
INTERRUPT_HANDLER_ERR(0x15, "Control Protection Exception")
INTERRUPT_HANDLER____(0x16, "Unknown Exception 0x16")
INTERRUPT_HANDLER____(0x17, "Unknown Exception 0x17")
INTERRUPT_HANDLER____(0x18, "Unknown Exception 0x18")
INTERRUPT_HANDLER____(0x19, "Unknown Exception 0x19")
INTERRUPT_HANDLER____(0x1A, "Unknown Exception 0x1A")
INTERRUPT_HANDLER____(0x1B, "Unknown Exception 0x1B")
INTERRUPT_HANDLER____(0x1C, "Hypervisor Injection Exception")
INTERRUPT_HANDLER_ERR(0x1D, "VMM Communication Exception")
INTERRUPT_HANDLER_ERR(0x1E, "Security Exception")
INTERRUPT_HANDLER____(0x1F, "Unkown Exception 0x1F")
extern "C" void handle_irq()
{
uint8_t irq;
for (uint32_t i = 0; i < 16; i++)
{
if (InterruptController::get().is_in_service(i))
{
irq = i;
goto found;
}
}
dprintln("Spurious irq");
return;
found:
if (s_irq_handlers[irq])
s_irq_handlers[irq]();
else
dprintln("no handler for irq 0x{2H}\n", irq);
// NOTE: Scheduler sends PIT eoi's
if (irq != PIT_IRQ)
InterruptController::get().eoi(irq);
Kernel::Scheduler::get().reschedule_if_idling();
}
extern "C" void handle_irq_common();
asm(
".globl handle_irq_common;"
"handle_irq_common:"
"pusha;"
"pushw %ds;"
"pushw %es;"
"pushw %ss;"
"pushw %ss;"
"popw %ds;"
"popw %es;"
"call handle_irq;"
"popw %es;"
"popw %ds;"
"popa;"
"iret;"
);
extern "C" void syscall_asm();
asm(
".global syscall_asm;"
"syscall_asm:"
"pusha;"
"pushw %ds;"
"pushw %es;"
"pushw %ss;"
"pushw %ss;"
"popw %ds;"
"popw %es;"
"pushl %edx;"
"pushl %ecx;"
"pushl %ebx;"
"pushl %eax;"
"call cpp_syscall_handler;"
"addl $16, %esp;"
"popw %es;"
"popw %ds;"
// NOTE: following instructions are same as in 'popa', except we skip eax
// since it holds the return value of the syscall.
"popl %edi;"
"popl %esi;"
"popl %ebp;"
"addl $4, %esp;"
"popl %ebx;"
"popl %edx;"
"popl %ecx;"
"addl $4, %esp;"
"iret;"
);
static void flush_idt()
{
asm volatile("lidt %0"::"m"(s_idtr));
}
static void register_interrupt_handler(uint8_t index, void(*f)())
{
GateDescriptor& descriptor = s_idt[index];
descriptor.offset1 = (uint32_t)f & 0xFFFF;
descriptor.selector = 0x08;
descriptor.type = 0xE;
descriptor.DPL = 0;
descriptor.present = 1;
descriptor.offset2 = (uint32_t)f >> 16;
}
void register_irq_handler(uint8_t irq, void(*f)())
{
s_irq_handlers[irq] = f;
register_interrupt_handler(IRQ_VECTOR_BASE + irq, handle_irq_common);
flush_idt();
}
void register_syscall_handler(uint8_t offset, void(*handler)())
{
register_interrupt_handler(offset, handler);
s_idt[offset].DPL = 3;
}
void initialize()
{
constexpr size_t idt_size = 0x100 * sizeof(GateDescriptor);
s_idt = (GateDescriptor*)kmalloc(idt_size);
ASSERT(s_idt);
memset(s_idt, 0x00, idt_size);
s_idtr.offset = s_idt;
s_idtr.size = idt_size - 1;
REGISTER_HANDLER(0x00);
REGISTER_HANDLER(0x01);
REGISTER_HANDLER(0x02);
REGISTER_HANDLER(0x03);
REGISTER_HANDLER(0x04);
REGISTER_HANDLER(0x05);
REGISTER_HANDLER(0x06);
REGISTER_HANDLER(0x07);
REGISTER_HANDLER(0x08);
REGISTER_HANDLER(0x09);
REGISTER_HANDLER(0x0A);
REGISTER_HANDLER(0x0B);
REGISTER_HANDLER(0x0C);
REGISTER_HANDLER(0x0D);
REGISTER_HANDLER(0x0E);
REGISTER_HANDLER(0x0F);
REGISTER_HANDLER(0x10);
REGISTER_HANDLER(0x11);
REGISTER_HANDLER(0x12);
REGISTER_HANDLER(0x13);
REGISTER_HANDLER(0x14);
REGISTER_HANDLER(0x15);
REGISTER_HANDLER(0x16);
REGISTER_HANDLER(0x17);
REGISTER_HANDLER(0x18);
REGISTER_HANDLER(0x19);
REGISTER_HANDLER(0x1A);
REGISTER_HANDLER(0x1B);
REGISTER_HANDLER(0x1C);
REGISTER_HANDLER(0x1D);
REGISTER_HANDLER(0x1E);
REGISTER_HANDLER(0x1F);
register_syscall_handler(0x80, syscall_asm);
flush_idt();
}
}

227
kernel/arch/i386/MMU.cpp Normal file
View File

@@ -0,0 +1,227 @@
#include <BAN/Errors.h>
#include <kernel/Debug.h>
#include <kernel/Memory/MMU.h>
#include <kernel/Memory/kmalloc.h>
#include <string.h>
#define MMU_DEBUG_PRINT 0
// bits 31-12 set
#define PAGE_MASK 0xfffff000
#define FLAGS_MASK 0x00000fff
namespace Kernel
{
static MMU* s_instance = nullptr;
void MMU::initialize()
{
ASSERT(s_instance == nullptr);
s_instance = new MMU();
ASSERT(s_instance);
s_instance->initialize_kernel();
s_instance->load();
}
MMU& MMU::get()
{
ASSERT(s_instance);
return *s_instance;
}
static uint64_t* allocate_page_aligned_page()
{
uint64_t* page = (uint64_t*)kmalloc(PAGE_SIZE, PAGE_SIZE);
ASSERT(page);
ASSERT(((uintptr_t)page % PAGE_SIZE) == 0);
memset(page, 0, PAGE_SIZE);
return page;
}
void MMU::initialize_kernel()
{
m_highest_paging_struct = (uint64_t*)kmalloc(sizeof(uint64_t) * 4, 32);
ASSERT(m_highest_paging_struct);
ASSERT(((uintptr_t)m_highest_paging_struct % 32) == 0);
// allocate all page directories
for (int i = 0; i < 4; i++)
{
uint64_t* page_directory = allocate_page_aligned_page();
m_highest_paging_struct[i] = (uint64_t)page_directory | Flags::Present;
}
// FIXME: We should just identity map until g_kernel_end
// create and identity map first 6 MiB
uint64_t* page_directory1 = (uint64_t*)(m_highest_paging_struct[0] & PAGE_MASK);
for (uint64_t i = 0; i < 3; i++)
{
uint64_t* page_table = allocate_page_aligned_page();
for (uint64_t j = 0; j < 512; j++)
page_table[j] = (i << 21) | (j << 12) | Flags::ReadWrite | Flags::Present;
page_directory1[i] = (uint64_t)page_table | Flags::ReadWrite | Flags::Present;
}
// dont map first page (0 -> 4 KiB) so that nullptr dereference
// causes page fault :)
uint64_t* page_table1 = (uint64_t*)(page_directory1[0] & PAGE_MASK);
page_table1[0] = 0;
}
MMU::MMU()
{
if (s_instance == nullptr)
return;
// Here we copy the s_instances paging structs since they are
// global for every process
uint64_t* global_pdpt = s_instance->m_highest_paging_struct;
uint64_t* pdpt = (uint64_t*)kmalloc(sizeof(uint64_t) * 4, 32);
ASSERT(pdpt);
for (uint32_t pdpte = 0; pdpte < 4; pdpte++)
{
if (!(global_pdpt[pdpte] & Flags::Present))
continue;
uint64_t* global_pd = (uint64_t*)(global_pdpt[pdpte] & PAGE_MASK);
uint64_t* pd = allocate_page_aligned_page();
pdpt[pdpte] = (uint64_t)pd | (global_pdpt[pdpte] & ~PAGE_MASK);
for (uint32_t pde = 0; pde < 512; pde++)
{
if (!(global_pd[pde] & Flags::Present))
continue;
uint64_t* global_pt = (uint64_t*)(global_pd[pde] & PAGE_MASK);
uint64_t* pt = allocate_page_aligned_page();
pd[pde] = (uint64_t)pt | (global_pd[pde] & ~PAGE_MASK);
memcpy(pt, global_pt, PAGE_SIZE);
}
}
m_highest_paging_struct = pdpt;
}
MMU::~MMU()
{
uint64_t* pdpt = m_highest_paging_struct;
for (uint32_t pdpte = 0; pdpte < 512; pdpte++)
{
if (!(pdpt[pdpte] & Flags::Present))
continue;
uint64_t* pd = (uint64_t*)(pdpt[pdpte] & PAGE_MASK);
for (uint32_t pde = 0; pde < 512; pde++)
{
if (!(pd[pde] & Flags::Present))
continue;
kfree((void*)(pd[pde] & PAGE_MASK));
}
kfree(pd);
}
kfree(pdpt);
}
void MMU::load()
{
asm volatile("movl %0, %%cr3" :: "r"(m_highest_paging_struct));
}
void MMU::map_page_at(paddr_t paddr, vaddr_t vaddr, uint8_t flags)
{
#if MMU_DEBUG_PRINT
dprintln("AllocatePage(0x{8H})", address);
#endif
ASSERT(flags & Flags::Present);
ASSERT(!(paddr & ~PAGE_MASK));
ASSERT(!(vaddr & ~PAGE_MASK));
uint32_t pdpte = (vaddr & 0xC0000000) >> 30;
uint32_t pde = (vaddr & 0x3FE00000) >> 21;
uint32_t pte = (vaddr & 0x001FF000) >> 12;
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
if (!(page_directory[pde] & Flags::Present))
{
uint64_t* page_table = allocate_page_aligned_page();
page_directory[pde] = (uint64_t)page_table;
}
page_directory[pde] |= flags;
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
page_table[pte] = paddr | flags;
}
void MMU::identity_map_page(paddr_t address, uint8_t flags)
{
address &= PAGE_MASK;
map_page_at(address, address, flags);
}
void MMU::identity_map_range(paddr_t address, ptrdiff_t size, uint8_t flags)
{
paddr_t s_page = address & PAGE_MASK;
paddr_t e_page = (address + size - 1) & PAGE_MASK;
for (paddr_t page = s_page; page <= e_page; page += PAGE_SIZE)
identity_map_page(page, flags);
}
void MMU::unmap_page(vaddr_t address)
{
#if MMU_DEBUG_PRINT
dprintln("UnAllocatePage(0x{8H})", address & PAGE_MASK);
#endif
uint32_t pdpte = (address & 0xC0000000) >> 30;
uint32_t pde = (address & 0x3FE00000) >> 21;
uint32_t pte = (address & 0x001FF000) >> 12;
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
if (!(page_directory[pde] & Flags::Present))
return;
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
if (!(page_table[pte] & Flags::Present))
return;
page_table[pte] = 0;
// TODO: Unallocate the page table if this was the only allocated page
}
void MMU::unmap_range(vaddr_t address, ptrdiff_t size)
{
uintptr_t s_page = address & PAGE_MASK;
uintptr_t e_page = (address + size - 1) & PAGE_MASK;
for (uintptr_t page = s_page; page <= e_page; page += PAGE_SIZE)
unmap_page(page);
}
uint8_t MMU::get_page_flags(vaddr_t address) const
{
uint32_t pdpte = (address & 0xC0000000) >> 30;
uint32_t pde = (address & 0x3FE00000) >> 21;
uint32_t pte = (address & 0x001FF000) >> 12;
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
if (!(page_directory[pde] & Flags::Present))
return 0;
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
if (!(page_table[pte] & Flags::Present))
return 0;
return page_table[pte] & FLAGS_MASK;
}
}

View File

@@ -0,0 +1,19 @@
.global spinlock_lock_asm
spinlock_lock_asm:
movl 4(%esp), %eax
lock; btsl $0, (%eax)
jnc .done
.retry:
pause
testl $1, (%eax)
jne .retry
lock; btsl $0, (%eax)
jc .retry
.done:
ret
.global spinlock_unlock_asm
spinlock_unlock_asm:
movl 4(%esp), %eax
movl $0, (%eax)
ret

47
kernel/arch/i386/Thread.S Normal file
View File

@@ -0,0 +1,47 @@
# uint32_t read_rip()
.global read_rip
read_rip:
popl %eax
jmp *%eax
exit_thread_trampoline:
addl $4, %esp
pushl (%esp)
ret
# void start_thread(uint32_t esp, uint32_t eip)
.global start_thread
start_thread:
movl 8(%esp), %ecx
movl 4(%esp), %esp
movl $0, %ebp
pushl $exit_thread_trampoline
sti
jmp *%ecx
# void continue_thread(uint32_t rsp, uint32_t rip)
.global continue_thread
continue_thread:
movl 8(%esp), %ecx
movl 4(%esp), %esp
movl $0, %eax
jmp *%ecx
# void thread_jump_userspace(uint32_t rsp, uint32_t rip)
.global thread_jump_userspace
thread_jump_userspace:
movl $0x23, %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movl 8(%esp), %ecx
movl 4(%esp), %esp
pushl $0x23
pushl %esp
pushfl
pushl $0x1B
pushl %ecx
iret

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