Compare commits

..

885 Commits
main ... main

Author SHA1 Message Date
Sinipelto 46a31b8efa Merge pull request 'update main 21.11.23' (#2) from Bananymous/banan-os:main into main
Reviewed-on: Sinipelto/banan-os#2
2023-11-21 14:58:13 +02:00
Bananymous 85a5e81224 BuildSystem: Check value of BANAN_UEFI_BOOT with `if ((...)); then` 2023-11-21 14:58:13 +02:00
Bananymous 7d4cdcd1fd BuildSystem: Add missing bootloader install script 2023-11-21 14:58:13 +02:00
Sinipelto d72db1f81c BuildSystem: image sh
mount in build dir

Signed-off-by: Sinipelto <sinipelto@noreply.bananymous.com>
2023-11-20 14:12:35 +02:00
Sinipelto 6cfa56dcec BuildSystem: image create sh
use banan build dir

Signed-off-by: Sinipelto <sinipelto@noreply.bananymous.com>
2023-11-20 14:11:29 +02:00
Sinipelto f97922a2b5 Update script/image.sh
mount dir default value

Signed-off-by: Sinipelto <sinipelto@noreply.bananymous.com>
2023-11-20 13:28:53 +02:00
Sinipelto 566724d986 Update script/image-create.sh
custom mount dir

Signed-off-by: Sinipelto <sinipelto@noreply.bananymous.com>
2023-11-20 13:28:10 +02:00
Sinipelto a7b1810aa2 Update script/image.sh
handle custom mount dir

Signed-off-by: Sinipelto <sinipelto@noreply.bananymous.com>
2023-11-20 13:24:15 +02:00
Sinipelto a6a5c00763 Merge pull request 'update main' (#1) from Bananymous/banan-os:main into main
Reviewed-on: Sinipelto/banan-os#1
2023-11-20 13:20:51 +02:00
Bananymous f2397b775c BuildSystem: Remove old bootloader target
And creating image now builds the bootloader
2023-11-20 00:56:06 +02:00
Bananymous 8b81406b81 Toolchain: Build full toolchain with one call to toolchain/build.sh 2023-11-20 00:56:06 +02:00
Bananymous e2515c1109 Buildsystem: default bootloader is not my custom one
You can set BANAN_BOOTLOADER=GRUB to use grub instead. Image creation
does not convert disk image now automatically between bootloaders and
calling ./bos image-full is now required.
2023-11-18 17:18:03 +02:00
Bananymous 5293ae070d Kernel: ProcFS inodes reflect processes ruid/rgid
setgid/setuid did not change the permissions of procfs inodes. This
made Shell launched by init not appear in meminfo.
2023-11-18 14:26:44 +02:00
Bananymous 6e2443ca72 Bootloader do some directory restructuring 2023-11-18 13:59:45 +02:00
Bananymous a312d75bb2 Bootloader: Implement VESA video mode query and pass it to kernel
Kernel now gets framebuffer from bootloader. Framebuffer dimensions
and bpp are hardcoded in bootloader, but will probably be read from
config file at some point.
2023-11-17 22:45:35 +02:00
Bananymous a554bd0fd8 Bootloader: Fix kernel memset to zero 2023-11-17 21:05:02 +02:00
Bananymous f0d2a211ea Bootloader add temporary initial command line
This will probably be read from some config file at some point
2023-11-17 20:38:38 +02:00
Bananymous 065eec430e Kernel/Bootloader: banan-os can now be booted with my bootloader :D 2023-11-17 20:33:02 +02:00
Bananymous 5f4d81a502 Bootloader: Clear screen, better memcpy
Clear screen before jumping to kernel. Memcpy now uses ebx as offset
register, so only one register has to updated every loop
2023-11-17 20:31:42 +02:00
Bananymous 41065d2f9a Kernel: Don't calculate divisor in a for loop in ext2 inodes 2023-11-17 19:02:01 +02:00
Bananymous 3daf3d53a3 Kernel: Serial now uses random size for some serial ports
If the serial port doesn't repond with a size, just use a random
one. There is no reason to ditch the whole output if you cannot
determine its size.
2023-11-17 18:56:02 +02:00
Bananymous ec56e9c6f1 Kernel: Don't use multiboot2 explicitly. Parse it to common structure
This allows support of multiple different bootloaders
2023-11-17 18:54:59 +02:00
Bananymous 07ae1bbf34 Bootloader: Load kernel to memory and jump to it! 2023-11-17 16:36:29 +02:00
Bananymous 03b80ed113 Bootloader enter unreal mode at the start of stage2 2023-11-17 14:22:21 +02:00
Bananymous 407a7b80c5 Bootloader: Fix getting command line 2023-11-17 13:17:44 +02:00
Bananymous 8b4f169d0f Bootloader: implement reading from inode 2023-11-17 13:17:44 +02:00
Bananymous 6f9b3ab5de Bootloader: add support for indirect inode blocks 2023-11-16 13:34:21 +02:00
Bananymous aa7a8124ce Bootloader: Add helpers for printing n bit hexadecimal numbers 2023-11-16 13:30:01 +02:00
Bananymous a9412aa741 Bootloader: Implement basic ext2 filesystem
This can search for files in an ext2 filesystem. Only 12 blocks
are currently supported.

Now only ELF loading is missing for loading the actual kernel!
2023-11-15 16:58:26 +02:00
Bananymous 8aab3a62cc Bootloader: Build with cmake instead of custom script 2023-11-14 03:44:47 +02:00
Bananymous b0b39c56ba Bootloader: Split bootloader into multiple files
This cleans up the code since bootloader is starting to near 1k lines
2023-11-14 03:27:52 +02:00
Bananymous 055b1a2a1a Bootloader move bootloader code from arch directory
The os itself only supports x86 so this won't matter. x86_64 and i386
use the same bootloader assembly.
2023-11-13 21:42:58 +02:00
Bananymous d99ef11e48 Bootloader: installer now uses banan os elf headers intead of Linux's 2023-11-13 21:40:15 +02:00
Bananymous 732eb9da41 fixup 2023-11-13 21:39:48 +02:00
Bananymous 8faad47843 LibELF: Remove 2 32 bit types that don't exist 2023-11-13 21:39:39 +02:00
Bananymous aa4f3046ff Bootloader: Find root partition from GPT header 2023-11-13 18:55:48 +02:00
Bananymous b4775fbe75 Bootloader: installer now patches GPT GUID's 2023-11-13 18:53:55 +02:00
Bananymous 8a5753b0fe Bootloader: Add API to create GUID from string 2023-11-13 18:52:41 +02:00
Bananymous 1a75262b04 BuildSystem: add bootloader target
Use this target to run banan-os with custom bootloader
2023-11-12 01:50:30 +02:00
Bananymous 39801e51da BuildSystem: add proper clean target 2023-11-12 01:14:42 +02:00
Bananymous 6e3f176457 ls: print link targets when listing files 2023-11-11 23:17:18 +02:00
Bananymous 447da99f0b Kernel/LibC: Implement readlink and readlinkat 2023-11-11 23:16:52 +02:00
Bananymous a3a287f5ca Bootloader: Continue work on bootloader
Bootloader can now get the memory map and read cmdline from user.

Now 'just' video mode query, ext2 and ELF parsing are needed :D
2023-11-11 22:49:00 +02:00
Bananymous c47f6a78bc Bootloader: Start work on bootloader
I wrote a fast first stage bootloader and a installer to put it into
a disk image.
2023-11-09 22:42:47 +02:00
Bananymous 430a006acf Toolchain: Fix typo when setting make flags
I defaultet MAKEFLAGS to -j which will launch processes in parallel
without any limit.
2023-11-09 21:57:45 +02:00
Bananymous 845ed66e5e Toolchain: add em=gnu to gas. This allows using / in expressions 2023-11-09 21:43:13 +02:00
Bananymous 2191ca46bb Kernel: Make TmpFS enforce max page count. 2023-11-07 16:13:21 +02:00
Bananymous cec04a2858 Kernel: Remove now obsolete RamFS
Everything is using now the better TmpFS which uses physical pages
for page allocation instead of the static kmalloc memory.
2023-11-07 16:07:11 +02:00
Bananymous b87351f6d5 Kernel: Make DevFS use the new and better TmpFS instead of RamFS 2023-11-07 16:05:05 +02:00
Bananymous 464737fbe9 Kernel: Add method to TmpFS for looping over all (cached) inodes 2023-11-07 16:04:34 +02:00
Bananymous 8b4f661acb Kernel: Lock TmpFS in all its methods 2023-11-07 16:03:52 +02:00
Bananymous 27963febc0 Kernel: Implement symlinks to TmpFS 2023-11-07 15:59:50 +02:00
Bananymous 6d4b684219 Kernel: Make PS/2 keyboard wait until interrupts are enabled 2023-11-07 15:58:50 +02:00
Bananymous 670c787af3 BuildSystem: Fix temporary sysroot creation in toolchain compilation 2023-11-07 14:16:49 +02:00
Bananymous a0fbf18d3b meminfo: better format for files without permissions 2023-11-07 02:41:01 +02:00
Bananymous 1acc0abf2e Kernel: Make unlinking from /proc always fail with EPERM 2023-11-07 02:40:27 +02:00
Bananymous c20f773c5d Kernel: /tmp is now TmpFS instead of RamFS 2023-11-07 02:36:22 +02:00
Bananymous a46b2f43d9 Kernel: Make ProcFS use the new TmpFS internally 2023-11-07 02:35:44 +02:00
Bananymous a20f8607de Kernel: Implement TmpFS Inode unlinking and deletion 2023-11-06 21:49:12 +02:00
Bananymous af330f7b8e Kernel: TmpFS directory inodes now iterate over only valid entries 2023-11-06 21:41:51 +02:00
Bananymous e33b3bcdff Kernel: Fix TmpFS directory entry enumeration early return 2023-11-06 21:06:10 +02:00
Bananymous 181d139c7d Kernel: Fix ext2 directory listing for big directories 2023-11-06 21:05:58 +02:00
Bananymous 639fd8804c Kernel: Implement TmpFS directory listing 2023-11-06 21:05:58 +02:00
Bananymous cbb2c37e00 Kernel: Implement TmpFS inode chmod 2023-11-06 20:11:34 +02:00
Bananymous ab4f033385 Kernel: Cleanup TmpFS code and block access doesn't require allocs
TmpFS blocks are now accessed with a simple wrapper
2023-11-06 20:07:09 +02:00
Bananymous 1ed08f62d3 Kernel: TmpInode blocks are on demand allocated 2023-11-06 10:44:37 +02:00
Bananymous 8164c15b6c Kernel: Implement read/write/truncate for TmpFileInode 2023-11-05 02:28:43 +02:00
Bananymous f9bf47ab30 Kernel: Start work on proper TmpFS in Heap instead of kmalloc memory 2023-11-04 18:18:45 +02:00
Bananymous e5ffadb109 Kernel: Add better APIs for fast page 2023-11-04 18:13:16 +02:00
Bananymous 061d10e635 BAN: Update bytespan -> span API 2023-11-04 18:12:46 +02:00
Bananymous 6d899aa6ce BuildSystem: using sysroot doesn't need root privileges anymore!
Sysroot is now created with fakeroot. This allows root access to be
only needed for disk image creation, since it uses loopback devices.
2023-11-04 17:50:43 +02:00
Bananymous 120f7329b1 BAN: Update ASSERT api
its now much harder to mix < with <= and > with >=
2023-11-02 00:01:12 +02:00
Bananymous 4f25c20c97 Kernel: Canonicalize vaddr before using it 2023-10-30 19:20:17 +02:00
Bananymous 5e396851f4 Kernel: Remove unused externs in kernel.cpp 2023-10-30 19:09:31 +02:00
Bananymous a44482639d Kernel: Temporarily force FileBackedRegion mappings writable
Now that write-protect bit is enabled this is neccessary.
2023-10-30 19:08:33 +02:00
Bananymous 3bac19e518 Kernel: Add fast page to page table
Add "fast page" to KERNEL_OFFSET. This is always present in page
tables and only requires changing the page table entry to map. This
requires no interrupts since it should only be for very operations
like memcpy.

I used to map all temporary mappings to vaddr 0, but this is much
better. C++ standard always says that nullptr access is undefined
and this gets rid of it.

Fixed some bugs I found along the way
2023-10-30 19:02:09 +02:00
Bananymous 4dbe15aa0e Kernel: Remove GeneralAllocator since it was not used 2023-10-30 18:13:17 +02:00
Bananymous 1c5985148c Kernel: Allow offsetof with packed fields
This is not standard C++ but should be fine with my toolchain.
2023-10-30 17:51:18 +02:00
Bananymous ce2461d0e8 Kernel: panic takes arguments as rvalue references 2023-10-30 16:22:02 +02:00
Bananymous 4e785a133c Kernel: Fix ext2 small link deallocation
Also fix deallocation bug
2023-10-30 16:22:02 +02:00
Bananymous 26c7aee327 Kernel: only map kernel from g_kernel_start onwards 2023-10-30 16:22:02 +02:00
Bananymous 0405461742 BAN: Implement better ASSERT macros
Implement macros for all basic binary ops. These macros print failed
values when the fail.
2023-10-30 16:22:02 +02:00
Bananymous 8a10853ba7 Kernel: Enable Write Protect. This seems to be good for security 2023-10-30 12:23:22 +02:00
Bananymous 5d34cebeca Kernel: Fix stack OOB detection
I now check both interrupt and normal stack to detect OOB. Processes
are killed if they encouner stack over/under flow.
2023-10-30 12:17:08 +02:00
Bananymous 3d899d2e44 Kernel/LibELF: Map pages always as RW so kernel can write to them
Kernel doesn't seem to require W permissions to a page without UEFI
so this had not been found earlier.
2023-10-30 12:14:12 +02:00
Bananymous f72fdeeb59 BAN: String now uses union for its sso storage
This allows String to shrink by 8 bytes since Variant's 8 index is
no longer stored in here.

This required me to make Strings max size one bit less, but that
should still be fine. There should never be strings with size of
over half of the computer's address space.
2023-10-30 11:13:16 +02:00
Bananymous 382f9d9bb3 Userspace: Add quick test for global ctors and dtors 2023-10-30 11:11:10 +02:00
Bananymous bc1d1bf919 LibC: implement and call __cxa_finalize() on exit()
This allows global destructors to be actually called
2023-10-30 11:10:08 +02:00
Bananymous f05b9a6877 Kernel/LibC: Add crt* files to LibC and remove crt0 from kernel
There was no reason for libc get crt0 from kernel.
2023-10-30 11:06:13 +02:00
Bananymous ea5ed3001e Toolchain: Clone GCC and Binutils from git
This feels much cleaner than just downloading tar balls from
pregiven urls. Also patching works much better like this!

I added --disable-initfini-array since global constructors were
not called.
2023-10-30 11:02:57 +02:00
Bananymous f312c3a4d7 Kernel: Fix ACPI DSDT address
Read x_dsdt address only if fadt's length contains it. Bochs seems
to have version 1 fadt without the x_* fields.
2023-10-29 21:35:11 +02:00
Bananymous 1af3ca19ab BAN: Rewrite String with small string optimizations
String now holds a 15 byte sso buffer. I'm not sure what the size
should actually be but 15 will work for now. Maybe the sso buffer
should be contained in an union with one bit flag in size instead of
variant that uses extra 8 bytes for type index.

This patch buffs sizeof(String) from 24 bytes to 32 bytes on 64 bit.
I assume this is much better version than the old which had to make
allocation even for empty strings :D.
2023-10-29 03:11:13 +03:00
Bananymous 09fcc613c7 BAN: Add variant to ForwardList
I should be using the forward list more
2023-10-29 02:28:55 +03:00
Bananymous 3940f53231 BuildSystem: Add bos short hand for building with zsh completions :) 2023-10-28 22:23:29 +03:00
Bananymous 0757834176 Kernel: Rewrite a lot of ext2 code
This commit consists of multiple big changes

1. blocks for inodes are now allocated on demand
  - reading from non allocated block will just return zeroes
  - writing to non allocated block allocates it

2. code doesn't really use raw pointers anymore
  - all casts to uint32_t or structures are now replaced with
    spans. either as<T> or as_span<T> which both are bounds
	checked

3. code doesn't depend on random macros for accessing indirect blocks
  - i added some recursive functions which take care of this :)
2023-10-28 22:13:28 +03:00
Bananymous 3bffbe330d BAN: Update ByteSpan API
Add ByteSpan::as_span<T> and const versions of as() and as_span()
require T to be const.
2023-10-28 22:10:47 +03:00
Bananymous 8d583c8b67 Kernel: Fix ext2 inode block allocation with triply indirect blocks 2023-10-28 16:53:00 +03:00
Bananymous 99bde9aa49 Kernel: Fix ext2 inode deletion cleanup
I now cleanup all blocks (including direct) in i_block array
2023-10-28 16:52:56 +03:00
Bananymous 98d702ac60 All: Remove read only from ext2 filesystem :) 2023-10-26 13:26:10 +03:00
Bananymous 1ec341e2dd rm: add option to remove recursively 2023-10-26 02:32:49 +03:00
Bananymous d09310f388 Kernel: Fix ext2 inode deletion
fsck now reports clean filesystem even after deleting files
2023-10-26 02:05:05 +03:00
Bananymous 126edea119 Userspace: implement basic rm command 2023-10-25 21:45:27 +03:00
Bananymous 74bfb930f2 Kernel/LibC: Add syscall and wrapper for unlink 2023-10-25 21:45:04 +03:00
Bananymous 091c5b6a66 BAN: Implement Ext2 file unlinking
Ext2 inodes can now be unlinked from directories and after last
inode closes (destructor gets called) we check if link count is 0
and cleanup the inode from filesystem
2023-10-25 21:43:36 +03:00
Bananymous fda4a4ad24 BAN: ByteSpan can be sliced without specified size
This will give span with all remaining size after offset
2023-10-25 21:41:11 +03:00
Bananymous 8bb47aee02 Kernel/LibC/Userspace: Implement mkdir and creat
Touch now uses creat insteadd of open with O_CREAT flag
2023-10-25 21:07:26 +03:00
Bananymous 1f794e4ac0 Kernel: Implement directory creation for RamFS 2023-10-25 19:41:34 +03:00
Bananymous e85f9ac6a1 Kernel: Implement Ext2 directory creation 2023-10-25 19:37:04 +03:00
Bananymous 6ee5576dcc Kernel: Add Inode API for creating directories 2023-10-25 19:36:04 +03:00
Bananymous b890e2fc14 Kernel: Ext2FS now uses Ext2Inodes as cached values 2023-10-25 19:34:00 +03:00
Bananymous 4f4b8ada8c Kernel: Fix read offset of RamFileInode 2023-10-25 02:53:20 +03:00
Bananymous 9e4adc1264 cp: abort copy if write fails 2023-10-25 02:43:02 +03:00
Bananymous 7a54a088b4 Userspace: Add basic chmod command 2023-10-25 02:37:19 +03:00
Bananymous 15bb1804ef Kernel/LibC: implement chmod syscall + libc wrapper 2023-10-25 02:35:37 +03:00
Bananymous e8890062d6 Userspace: Implement basic cp
This does not support any meaningful command line arguments but
is a good start.
2023-10-25 00:07:25 +03:00
Bananymous 1e2c2fb973 Shell: Set get old termios earlier
I sourced the config file before getting old termios. Sourcing
updated the termios so old_termios was always in non canonical, non
echoing mode.
2023-10-24 19:10:53 +03:00
Bananymous 988a4e1cd8 BAN: Fix bug of size of splice after slice()
I have no idea what was I doing before :D
2023-10-24 17:23:45 +03:00
Bananymous adbbdf73c4 meminfo: fix g++ warning for oob write
g++ doesn't realize that read can only return -1
2023-10-24 16:50:21 +03:00
Bananymous e8d20bc653 BuildSystem: Fix bugs in new build system
I had not tested the new build system with clean toolchain build
but it seems to work now.
2023-10-24 16:48:46 +03:00
Bananymous 00ee86920a Kernel: Add timeout to ACHI commands
ACHI commands can now fail from timeouts.
2023-10-24 11:56:25 +03:00
Bananymous 51ad27ea3c BuildSystem: Match README.md with the new buildsystem 2023-10-24 11:56:00 +03:00
Bananymous df69612bb1 BuildSystem: Rewrite whole build system structure
Now you have to use script/build.sh for building and running banan-os
2023-10-24 11:56:00 +03:00
Bananymous 5bfeb9f3ca Kernel: Rewrite all read/write functions to use BAN::ByteSpan
This allows us to not work with raw pointers and use sized containers
for reading and writing.
2023-10-24 11:56:00 +03:00
Bananymous db5c24b2a5 BAN: Implement ByteSpan
This is a span over exisiting containers/data types. I'm not too
happy with the constructors and assignment operators, but they will
work for now
2023-10-20 04:59:29 +03:00
Bananymous 781c950af6 BAN: add helper to cast Span<T> to Span<const T> 2023-10-20 04:59:08 +03:00
Bananymous e2e5c31d54 Kernel: Map multiboot2 memory in PageTable initialization
It cannot be assumed that multiboot data lies between kernel_end
and 2 GiB mark, so I properly allocate virtual address space for it.
2023-10-17 01:15:08 +03:00
Bananymous be3efb0b92 Kernel: Start using multiboot2 instead of multiboot
This allows better compatibility with (U)EFI and gives RSDP location
instead of me having to scan ram to find it.
2023-10-17 01:06:24 +03:00
Bananymous 792bb2df1c Kernel: TTY doesn't panic if it doesn't find input device 2023-10-16 16:58:39 +03:00
Bananymous e01928d186 Kernel: Fix device identification with all bits as ones
If device identification sends all ones, don't initialize the device.
2023-10-16 16:57:07 +03:00
Bananymous 48980b56ab Kernel: ATABuses are but to compatibility mode if possible
I don't support native mode ata bus (irq sharing) so ata buses are
but to compatibility mode if possible.
2023-10-16 16:56:12 +03:00
Bananymous b767317a7a Kernel: Fix ATADevice naming
ATADevice now stores its name instead of using static buffer. Old
static buffer was changing on every name query. I just hadn't noticed
since virtual machine disks were always sda.
2023-10-16 16:52:15 +03:00
Bananymous 6f8fce94a0 Kernel: Fix PCI bugs
IO BarRegion used vaddr instead of the correct paddr. Add API for
memory region iobase query.
2023-10-16 16:50:49 +03:00
Bananymous 31aa157201 Kernel: Don't require framebuffer
Initializes virtual tty only if framebuffer is initialized
2023-10-16 01:44:54 +03:00
Bananymous 5977341610 Kernel: PCI checks if ethernet device is E1000 before initialization
I used to treat all ethernet deivices as E1000 but now it is actually
verified before initialization
2023-10-16 01:44:54 +03:00
Bananymous 76f17bd569 Kernel: PCIDevice stores its vendor id and device id 2023-10-16 01:44:54 +03:00
Bananymous 6b1b3d333c BuildSystem: add cmake variable UEFI_BOOT
If this variable is defined in cmake, image will be build with esp
and booted with uefi.
2023-10-16 01:44:54 +03:00
Bananymous cb65be3e33 Toolchain: Build grub with efi capabilities 2023-10-16 01:37:12 +03:00
Bananymous dafc016293 Kernel: Clear TTY when setting as current
Actually this should replace from old buffer, but this works
for now.
2023-10-13 17:20:26 +03:00
Bananymous c7b6fc950a Kernel: Don't crash if header type != 0 in bar region creation
Also remove spammy debug printing
2023-10-13 16:32:32 +03:00
Bananymous 45a6783c3d Kernel: Cleanup GDT code 2023-10-13 16:18:22 +03:00
Bananymous 6b180da4e8 Kernel: Separate scheduler execution and stack loading
Not sure if this is actually needed, but this allows actual
executing function to be in clean environment
2023-10-13 16:17:27 +03:00
Bananymous cf4f5f64a5 Kernel: add NEVER_INLINE and make Interruptable not constructable 2023-10-13 16:17:27 +03:00
Bananymous 5630f64175 Kernel: Add 16 more irq handlers
IDT will now panic if trying to assing handler for non supported
irq.
2023-10-13 16:17:27 +03:00
Bananymous 1d61bccfc3 Kernel: Debug temporary debug print just to beginning when full 2023-10-13 12:43:52 +03:00
Bananymous f842a9255f Kernel: Allow getting ACPI headers with same signature 2023-10-13 11:24:21 +03:00
Bananymous 72f3c378dd Kernel: Fix PhysicalRange mapping size 2023-10-13 03:45:01 +03:00
Bananymous 39be6ab099 Kernel: Add temporary terminal output before controlling terminal
Starting work on getting this boot on real hardware.
2023-10-13 03:31:36 +03:00
Bananymous 773dcdd3a2 Kernel: Check whether ELF address space can be loaded
Before reserving address space in SYS_EXEC verify that ELF address
space is actually loadable. For example when trying to execute the
kernel binary in userspace, binarys address space would overlap with
current kernel address space. Now kernel won't crash anymore and
will just send SIGKILL to the process calling exec*().
2023-10-12 22:59:36 +03:00
Bananymous f0820e6f24 Shell: Fix parsing $ with unknown character 2023-10-12 22:24:27 +03:00
Bananymous a2b5e71654 Kernel: Implement AHCI driver
SATA drives can now be used with banan-os. This allows much faster
disk access (writing 10 MiB from 30s to 1.5s). This can definitely
be optimized but the main slow down is probably the whole disk
structure in the os.

AHCI drive is now the default when running qemu.
2023-10-12 21:53:48 +03:00
Bananymous d3e5c8e0aa Kernel: Generalize ATA device and cleanup code 2023-10-12 21:35:25 +03:00
Bananymous f4b901a646 Kernel: Add Timer::ns_since_boot() 2023-10-12 21:16:39 +03:00
Bananymous 790064d248 Kernel: Add vaddr/paddr conversion functions to DMARegion 2023-10-12 15:20:05 +03:00
Bananymous ab8b77406d Kernel: PCI can now get interrupts for devices 2023-10-11 22:18:58 +03:00
Bananymous 1b9e14a53b Kernel: PCI cleanup PCI::Device API 2023-10-11 21:52:08 +03:00
Bananymous d2cfc843e4 BAN: Optional can now be constructed from another Optional
Also fix bug in release_value() where we did not call the
destructor.
2023-10-11 20:10:06 +03:00
Bananymous 521513bed2 Kernel: make DMARegion::create static and fix mapping 2023-10-08 18:18:36 +03:00
Bananymous 400db176d1 Kernel: fix some math in physical ranges
I allocated 1 bitmap page per 8 data pages. Bitmap page can actually
store 8*PAGE_SIZE data pages.

Also properly set last bits in bitmap. I did not care about endianness
but now we set the bits on unsigned long longs instead of bytes.
2023-10-08 13:25:34 +03:00
Bananymous 211cad03ff Kernel: Implement bare boness DMA Region
This does nothing but allocate contiguous physical and virtual memory
and map it as CacheDisable. Also memory is automatically freed RAII style.
2023-10-08 02:57:22 +03:00
Bananymous 8a9816d6e0 Kernel: Add API for getting contiguous physcial pages
This will be used to create DMA regions.
2023-10-08 02:57:22 +03:00
Bananymous 03d2bf4002 Kernel: Rework physical memory allocation
PhysicalRange is now much simpler bitmap. This makes expanding
PhysicalRange API much easier.
2023-10-08 02:50:23 +03:00
Bananymous f071240b33 Kernel: Fix PCI BarRegion offsets
Calculations accidentally assumed bar registers are 8 byte instead
of 4.
2023-10-08 02:50:23 +03:00
Bananymous 27364f64a6 Kernel: Rework whole ATA driver structure
Make ATA driver more compatible when we are adding SATA support
2023-10-08 02:50:23 +03:00
Bananymous bcf62c5f2e Kernel: Rework interrupt mechanism
All interruptrable classes now inherit from Interruptable which
has methdo handle_irq which is called on a interrupt.
2023-10-05 18:53:45 +03:00
Bananymous 4d6322ff9c BuildSystem: Don't strip kernel 2023-10-05 18:52:44 +03:00
Bananymous 2eef581737 BuildSystem: Try to set compiler only if it exists 2023-10-05 18:52:05 +03:00
Bananymous 7ce8e2d57b cat: Use write() instead of puts to print file contents
This allows printing files that contain null bytes behave more like
you would expect
2023-10-04 22:16:19 +03:00
Bananymous e780eaa45f meminfo: Print allocated physical memory percentage 2023-10-03 10:39:27 +03:00
Bananymous 44cb0af64f Shell: source $HOME/.shellrc if found on Shell startup 2023-10-03 10:39:27 +03:00
Bananymous 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
914 changed files with 21643 additions and 69293 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__
]

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

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

@ -0,0 +1,269 @@
#include <BAN/String.h>
#include <BAN/New.h>
namespace BAN
{
String::String()
{
}
String::String(const String& other)
{
*this = other;
}
String::String(String&& other)
{
*this = move(other);
}
String::String(StringView other)
{
*this = other;
}
String::~String()
{
clear();
}
String& 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& 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& 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> String::push_back(char c)
{
TRY(ensure_capacity(m_size + 1));
data()[m_size] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::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> String::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> String::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 String::pop_back()
{
ASSERT(m_size > 0);
m_size--;
data()[m_size] = '\0';
}
void String::remove(size_type index)
{
ASSERT(index < m_size);
memcpy(data() + index, data() + index + 1, m_size - index);
m_size--;
data()[m_size] = '\0';
}
void String::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';
}
bool String::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 String::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> String::resize(size_type new_size, char init_c)
{
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> String::reserve(size_type new_size)
{
TRY(ensure_capacity(new_size));
return {};
}
ErrorOr<void> String::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 {};
}
String::size_type String::capacity() const
{
if (has_sso())
return sso_capacity;
return m_storage.general_storage.capacity;
}
char* String::data()
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
const char* String::data() const
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
ErrorOr<void> String::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);
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 String::has_sso() const
{
return m_has_sso;
}
}

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,15 +1,24 @@
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 ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
)
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 ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/libban.a ${BANAN_LIB}/
banan_install_headers(ban) DEPENDS ban
install(TARGETS ban OPTIONAL) BYPRODUCTS ${BANAN_LIB}/libban.a
)

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,7 +19,7 @@ namespace BAN
using const_iterator = ConstIteratorSimple<T, Array>; using const_iterator = ConstIteratorSimple<T, Array>;
public: public:
Array() = default; Array();
Array(const T&); Array(const T&);
iterator begin() { return iterator(m_data); } iterator begin() { return iterator(m_data); }
@ -43,9 +44,16 @@ namespace BAN
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>
Array<T, S>::Array()
{
for (size_type i = 0; i < S; i++)
m_data[i] = T();
}
template<typename T, size_t S> template<typename T, size_t S>
Array<T, S>::Array(const T& value) Array<T, S>::Array(const T& value)
{ {

View File

@ -1,14 +1,33 @@
#pragma once #pragma once
#define __ban_assert_stringify_helper(s) #s #include <BAN/Traits.h>
#define __ban_assert_stringify(s) __ban_assert_stringify_helper(s)
#define ASSERT(cond) \ #if defined(__is_kernel)
(__builtin_expect(!(cond), 0) \ #include <kernel/Panic.h>
? __ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT(" #cond ") failed") \
: (void)0)
#define ASSERT_NOT_REACHED() \ #define ASSERT(cond) \
__ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT_NOT_REACHED() reached") do { \
if (!(cond)) \
Kernel::panic("ASSERT(" #cond ") failed"); \
} while (false)
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg); #define __ASSERT_BIN_OP(lhs, rhs, name, op) \
do { \
auto&& _lhs = lhs; \
auto&& _rhs = rhs; \
if (!(_lhs op _rhs)) \
Kernel::panic(name "(" #lhs ", " #rhs ") ({} " #op " {}) failed", _lhs, _rhs); \
} while (false)
#define ASSERT_LT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LT", <)
#define ASSERT_LTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LTE", <=)
#define ASSERT_GT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GT", >)
#define ASSERT_GTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GTE", >=)
#define ASSERT_EQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_EQ", ==)
#define ASSERT_NEQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_NEQ", !=)
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
#else
#include <assert.h>
#define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
#define ASSERT_NOT_REACHED() do { assert(false && "ASSERT_NOT_REACHED() failed"); __builtin_unreachable(); } while (false)
#endif

View File

@ -1,69 +0,0 @@
#pragma once
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, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
requires requires { __atomic_always_lock_free(sizeof(T), 0); }
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_n(&m_value, mem_order); }
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { __atomic_store_n(&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_n(&m_value, &expected, desired, false, mem_order, mem_order); }
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_exchange_n(&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_nfetch_and(&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

@ -2,8 +2,6 @@
#include <BAN/Span.h> #include <BAN/Span.h>
#include <stdint.h>
namespace BAN namespace BAN
{ {
@ -21,56 +19,60 @@ namespace BAN
, m_size(size) , m_size(size)
{ } { }
template<bool SRC_CONST> ByteSpanGeneral(ByteSpanGeneral& other)
ByteSpanGeneral(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
: m_data(other.data()) : m_data(other.data())
, m_size(other.size()) , m_size(other.size())
{ } { }
template<bool SRC_CONST> template<bool C2>
ByteSpanGeneral(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST) ByteSpanGeneral(const ByteSpanGeneral<C2>& other) requires(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_data(other.data())
, m_size(other.size()) , m_size(other.size())
{ } { }
template<typename T> ByteSpanGeneral(Span<uint8_t> other)
ByteSpanGeneral(Span<T>&& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
: m_data(other.data()) : m_data(other.data())
, m_size(other.size()) , m_size(other.size())
{ { }
other.clear(); ByteSpanGeneral(const Span<const uint8_t>& other) requires(CONST)
} : m_data(other.data())
, m_size(other.size())
{ }
template<bool SRC_CONST> ByteSpanGeneral& operator=(ByteSpanGeneral other)
ByteSpanGeneral& operator=(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
{ {
m_data = other.data(); m_data = other.data();
m_size = other.size(); m_size = other.size();
return *this; return *this;
} }
template<bool SRC_CONST> template<bool C2>
ByteSpanGeneral& operator=(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST) ByteSpanGeneral& operator=(const ByteSpanGeneral<C2>& other) requires(CONST)
{
m_data = other.data();
m_size = other.size();
return *this;
}
ByteSpanGeneral& operator=(Span<uint8_t> other)
{
m_data = other.data();
m_size = other.size();
return *this;
}
ByteSpanGeneral& operator=(const Span<const uint8_t>& other) requires(CONST)
{ {
m_data = other.data(); m_data = other.data();
m_size = other.size(); m_size = other.size();
other.clear();
return *this; return *this;
} }
template<typename S> template<typename S>
static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v<S>) requires(CONST || !is_const_v<S>)
static ByteSpanGeneral from(S& value)
{ {
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S)); return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
} }
template<typename S> template<typename S>
S& as() const requires(!CONST || is_const_v<S>) requires(!CONST && !is_const_v<S>)
S& as()
{ {
ASSERT(m_data); ASSERT(m_data);
ASSERT(m_size >= sizeof(S)); ASSERT(m_size >= sizeof(S));
@ -78,13 +80,30 @@ namespace BAN
} }
template<typename S> template<typename S>
Span<S> as_span() const requires(!CONST || is_const_v<S>) requires(is_const_v<S>)
S& as() const
{
ASSERT(m_data);
ASSERT(m_size >= sizeof(S));
return *reinterpret_cast<S*>(m_data);
}
template<typename S>
requires(!CONST && !is_const_v<S>)
Span<S> as_span()
{ {
ASSERT(m_data); ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S)); 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 template<typename S>
const Span<S> as_span() const
{
ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
}
ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1))
{ {
ASSERT(m_data); ASSERT(m_data);
ASSERT(m_size >= offset); ASSERT(m_size >= offset);
@ -94,28 +113,25 @@ namespace BAN
return ByteSpanGeneral(m_data + offset, length); return ByteSpanGeneral(m_data + offset, length);
} }
value_type& operator[](size_type offset) const value_type& operator[](size_type offset)
{
ASSERT(offset < m_size);
return m_data[offset];
}
const value_type& operator[](size_type offset) const
{ {
ASSERT(offset < m_size); ASSERT(offset < m_size);
return m_data[offset]; return m_data[offset];
} }
value_type* data() const { return m_data; } value_type* data() { return m_data; }
const value_type* data() const { return m_data; }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; } size_type size() const { return m_size; }
void clear()
{
m_data = nullptr;
m_size = 0;
}
private: private:
value_type* m_data { nullptr }; value_type* m_data { nullptr };
size_type m_size { 0 }; size_type m_size { 0 };
friend class ByteSpanGeneral<!CONST>;
}; };
using ByteSpan = ByteSpanGeneral<false>; using ByteSpan = ByteSpanGeneral<false>;

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,16 +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();
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(); }
@ -71,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)...);
@ -101,20 +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> 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,55 +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(c, stddbg); }
#define dprintln(...) \
do { \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar,"\r\n"); \
fflush(stddbg); \
} while (false)
#define dwarnln(...) \
do { \
BAN::Formatter::print(__debug_putchar, "\e[33m"); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
fflush(stddbg); \
} while(false)
#define derrorln(...) \
do { \
BAN::Formatter::print(__debug_putchar, "\e[31m"); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
fflush(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>
@ -13,9 +13,9 @@
#define MUST(expr) ({ auto&& e = expr; 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(expr) *({ auto&& e = expr; 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(expr) ({ auto&& e = expr; if (e.is_error()) { derrorln("MUST(" #expr "): {}", e.error()); __builtin_trap(); } e.release_value(); }) #define MUST(expr) ({ auto&& e = expr; assert(!e.is_error()); e.release_value(); })
#define MUST_REF(expr) *({ auto&& e = expr; if (e.is_error()) { derrorln("MUST(" #expr "): {}", 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(expr) ({ auto&& e = expr; 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(); })
@ -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,13 +157,12 @@ 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,7 +197,7 @@ 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)
{ {
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;
@ -225,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");
@ -241,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

@ -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++)
} {
if (it->key == key)
template<typename Key, typename T, typename HASH> {
void HashMap<Key, T, HASH>::remove(iterator it) bucket.remove(it);
{ m_size--;
it.outer_current()->remove(it.inner_current()); return;
m_size--; }
}
} }
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,75 +0,0 @@
#pragma once
#include <BAN/Endianness.h>
#include <BAN/Formatter.h>
#include <BAN/Hash.h>
namespace BAN
{
struct IPv4Address
{
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)
{ {
@ -59,211 +52,41 @@ 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<BAN::integral T>
static constexpr bool will_multiplication_overflow(T a, T b)
{
if (a == 0 || b == 0)
return false;
if ((a > 0) == (b > 0))
return a > BAN::numeric_limits<T>::max() / b;
else
return a < BAN::numeric_limits<T>::min() / b;
}
template<BAN::integral T>
static constexpr bool will_addition_overflow(T a, T b)
{
if (a > 0 && b > 0)
return a > BAN::numeric_limits<T>::max() - b;
if (a < 0 && b < 0)
return a < BAN::numeric_limits<T>::min() - b;
return false;
}
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;
} }
template<floating_point T> template<floating_point T>
inline constexpr T floor(T x) inline constexpr T log2(T value)
{ {
if constexpr(is_same_v<T, float>) T result;
return __builtin_floorf(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_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 log10(T value)
{ {
if constexpr(is_same_v<T, float>) constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
return __builtin_ceilf(x); T result;
if constexpr(is_same_v<T, double>) asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"(INV_LOG_2_10) : "st(1)");
return __builtin_ceil(x); return result;
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 log(T value, T base)
{ {
if (x == (T)0.0) return log2(value) / log2(base);
return x;
if (x > (T)0.0)
return floor<T>(x + (T)0.5);
return ceil<T>(x - (T)0.5);
} }
template<floating_point T> template<floating_point T>
inline constexpr T trunc(T x) inline constexpr T pow(T base, T exp)
{ {
if constexpr(is_same_v<T, float>) T result;
return __builtin_truncf(x); asm volatile(
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>
inline constexpr T rint(T x)
{
asm("frndint" : "+t"(x));
return x;
}
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"
);
return a;
}
template<floating_point T>
static T modf(T x, T* iptr)
{
const T frac = BAN::Math::fmod<T>(x, 1);
*iptr = x - frac;
return frac;
}
template<floating_point T>
inline constexpr T frexp(T num, int* exp)
{
if (num == 0.0)
{
*exp = 0;
return 0.0;
}
T _exp;
asm("fxtract" : "+t"(num), "=u"(_exp));
*exp = (int)_exp + 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;"
"fld %%st(1);"
"fprem;"
"f2xm1;"
"faddp;"
"fscale;"
"fstp %%st(1);"
: "+t"(x)
);
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)
{
asm(
"fyl2x;" "fyl2x;"
"fld1;" "fld1;"
"fld %%st(1);" "fld %%st(1);"
@ -271,173 +94,12 @@ namespace BAN::Math
"f2xm1;" "f2xm1;"
"faddp;" "faddp;"
"fscale;" "fscale;"
: "+t"(x), "+u"(y) "fxch %%st(1);"
"fstp %%st;"
: "=t"(result)
: "0"(base), "u"(exp)
); );
return result;
return 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)
{
asm("fsqrt" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr T cbrt(T value)
{
if (value == 0.0)
return 0.0;
return pow<T>(value, 1.0 / 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, 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);
} }
} }

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

@ -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,48 +12,48 @@ namespace BAN
class Optional class Optional
{ {
public: public:
constexpr Optional(); Optional();
constexpr Optional(Optional&&); Optional(Optional&&);
constexpr Optional(const Optional&); Optional(const Optional&);
constexpr Optional(const T&); Optional(const T&);
constexpr Optional(T&&); Optional(T&&);
template<typename... Args>
Optional(Args&&...);
~Optional(); ~Optional();
constexpr Optional& operator=(Optional&&); Optional& operator=(Optional&&);
constexpr Optional& operator=(const Optional&); Optional& operator=(const 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(); T& value();
constexpr const T& value() const; const T& value() const;
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(Optional<T>&& other)
: m_has_value(other.has_value()) : m_has_value(other.has_value())
{ {
if (other.has_value()) if (other.has_value())
@ -62,7 +61,7 @@ namespace BAN
} }
template<typename T> template<typename T>
constexpr Optional<T>::Optional(const Optional<T>& other) Optional<T>::Optional(const Optional<T>& other)
: m_has_value(other.has_value()) : m_has_value(other.has_value())
{ {
if (other.has_value()) if (other.has_value())
@ -70,19 +69,27 @@ namespace BAN
} }
template<typename T> template<typename T>
constexpr Optional<T>::Optional(const T& value) 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(move(value));
} }
template<typename T>
template<typename... Args>
Optional<T>::Optional(Args&&... args)
: m_has_value(true)
{
new (m_storage) T(forward<Args>(args)...);
}
template<typename T> template<typename T>
Optional<T>::~Optional() Optional<T>::~Optional()
{ {
@ -90,7 +97,7 @@ namespace BAN
} }
template<typename T> template<typename T>
constexpr Optional<T>& Optional<T>::operator=(Optional&& other) Optional<T>& Optional<T>::operator=(Optional&& other)
{ {
clear(); clear();
m_has_value = other.has_value(); m_has_value = other.has_value();
@ -100,18 +107,18 @@ namespace BAN
} }
template<typename T> template<typename T>
constexpr Optional<T>& Optional<T>::operator=(const Optional& other) Optional<T>& Optional<T>::operator=(const Optional& other)
{ {
clear(); clear();
m_has_value = other.has_value(); m_has_value = other.has_value();
if (other.has_value()) if (other.has_value)
new (m_storage) T(other.value()); new (m_storage) T(other.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;
@ -120,41 +127,41 @@ namespace BAN
} }
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()); T released_value = move(value());
@ -164,37 +171,21 @@ namespace BAN
} }
template<typename T> template<typename T>
constexpr T& Optional<T>::value() T& Optional<T>::value()
{ {
ASSERT(has_value()); ASSERT(has_value());
return *reinterpret_cast<T*>(&m_storage); return (T&)m_storage;
} }
template<typename T> template<typename T>
constexpr const T& Optional<T>::value() const const T& Optional<T>::value() const
{ {
ASSERT(has_value()); ASSERT(has_value());
return *reinterpret_cast<const T*>(&m_storage); return (const 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

@ -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,240 +0,0 @@
#pragma once
#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<typename It::value_type>>
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<typename It::value_type>>
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<typename It::value_type>>
void insertion_sort(It begin, It end, Comp comp = {})
{
if (distance(begin, end) <= 1)
return;
for (It it1 = next(begin, 1); it1 != end; ++it1)
{
typename It::value_type x = move(*it1);
It it2 = it1;
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
*it2 = move(*prev(it2, 1));
*it2 = move(x);
}
}
namespace detail
{
template<typename It, typename Comp>
void push_heap(It begin, size_t hole_index, size_t top_index, typename It::value_type value, Comp comp)
{
size_t parent = (hole_index - 1) / 2;
while (hole_index > top_index && comp(*next(begin, parent), value))
{
*next(begin, hole_index) = move(*next(begin, parent));
hole_index = parent;
parent = (hole_index - 1) / 2;
}
*next(begin, hole_index) = move(value);
}
template<typename It, typename Comp>
void adjust_heap(It begin, size_t hole_index, size_t len, typename It::value_type value, Comp comp)
{
const size_t top_index = hole_index;
size_t child = hole_index;
while (child < (len - 1) / 2)
{
child = 2 * (child + 1);
if (comp(*next(begin, child), *next(begin, child - 1)))
child--;
*next(begin, hole_index) = move(*next(begin, child));
hole_index = child;
}
if (len % 2 == 0 && child == (len - 2) / 2)
{
child = 2 * (child + 1);
*next(begin, hole_index) = move(*next(begin, child - 1));
hole_index = child - 1;
}
push_heap(begin, hole_index, top_index, move(value), comp);
}
}
template<typename It, typename Comp = less<typename It::value_type>>
void make_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
size_t parent = (len - 2) / 2;
while (true)
{
detail::adjust_heap(begin, parent, len, move(*next(begin, parent)), comp);
if (parent == 0)
break;
parent--;
}
}
template<typename It, typename Comp = less<typename It::value_type>>
void sort_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
size_t last = len;
while (last > 1)
{
last--;
typename It::value_type x = move(*next(begin, last));
*next(begin, last) = move(*begin);
detail::adjust_heap(begin, 0, last, move(x), comp);
}
}
template<typename It, typename Comp = less<typename It::value_type>>
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<typename It::value_type>>
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<typename It::value_type> && (radix > 0 && (radix & (radix - 1)) == 0)
BAN::ErrorOr<void> radix_sort(It begin, It end)
{
using value_type = typename It::value_type;
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<typename It::value_type>>
void sort(It begin, It end, Comp comp = {})
{
return intro_sort(begin, end, comp);
}
}

View File

@ -14,91 +14,121 @@ 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;
ASSERT(index < m_size);
return m_data[index];
}
value_type* data() const T* data();
{ const T* data() const;
ASSERT(m_data);
return m_data;
}
bool empty() const { return m_size == 0; } bool empty() const;
size_type size() const { return m_size; } size_type size() const;
void clear() void clear();
{
m_data = nullptr;
m_size = 0;
}
Span slice(size_type start, size_type length = ~size_type(0)) const Span slice(size_type, size_type = ~size_type(0));
{
ASSERT(m_data);
ASSERT(start <= m_size);
if (length == ~size_type(0))
length = m_size - start;
ASSERT(m_size - start >= length);
return Span(m_data + start, length);
}
Span<const value_type> as_const() const { return *this; } Span<const T> as_const() const { return Span<const T>(m_data, m_size); }
private: private:
value_type* m_data = nullptr; T* m_data = nullptr;
size_type m_size = 0; size_type m_size = 0;
friend class Span<const value_type>;
}; };
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);
return m_data[index];
}
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;
}
template<typename T>
const T* Span<T>::data() const
{
return m_data;
}
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_size = 0;
}
template<typename T>
Span<T> Span<T>::slice(size_type start, size_type length)
{
ASSERT(m_data);
ASSERT(start <= m_size);
if (length == ~size_type(0))
length = m_size - start;
ASSERT(m_size - start >= length);
return Span(m_data + start, length);
}
} }

View File

@ -2,10 +2,9 @@
#include <BAN/Errors.h> #include <BAN/Errors.h>
#include <BAN/Formatter.h> #include <BAN/Formatter.h>
#include <BAN/ForwardList.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
{ {
@ -19,130 +18,28 @@ namespace BAN
static constexpr size_type sso_capacity = 15; 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);
{
size_type length = 0;
BAN::Formatter::print([&](char) { length++; }, format, BAN::forward<Args>(args)...);
String result; String& operator=(const String&);
TRY(result.reserve(length)); String& operator=(String&&);
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward<Args>(args)...); String& operator=(StringView);
return result; ErrorOr<void> push_back(char);
} ErrorOr<void> insert(char, size_type);
ErrorOr<void> insert(StringView, size_type);
ErrorOr<void> append(StringView);
String& operator=(const String& other) void pop_back();
{ void remove(size_type);
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size() + 1);
m_size = other.size();
return *this;
}
String& operator=(String&& other) void clear();
{
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()); } const_iterator begin() const { return const_iterator(data()); }
iterator begin() { return iterator(data()); } iterator begin() { return iterator(data()); }
@ -158,151 +55,26 @@ namespace BAN
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; } 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]; } char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
bool operator==(const String& str) const bool operator==(StringView) const;
{ bool operator==(const char*) 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 ErrorOr<void> resize(size_type, char = '\0');
{ ErrorOr<void> reserve(size_type);
if (size() != str.size()) ErrorOr<void> shrink_to_fit();
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()); } StringView sv() const { return StringView(data(), size()); }
bool empty() const { return m_size == 0; } bool empty() const { return m_size == 0; }
size_type size() const { return m_size; } size_type size() const { return m_size; }
size_type capacity() const;
size_type capacity() const char* data();
{ const char* data() 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: private:
ErrorOr<void> ensure_capacity(size_type new_size) ErrorOr<void> ensure_capacity(size_type);
{
if (m_size >= new_size)
return {};
if (has_sso() && fits_in_sso(new_size))
return {};
char* new_data = (char*)allocator(new_size + 1); bool has_sso() const;
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); } bool fits_in_sso() const { return fits_in_sso(m_size); }
static bool fits_in_sso(size_type size) { return size < sso_capacity; } static bool fits_in_sso(size_type size) { return size < sso_capacity; }
@ -327,6 +99,14 @@ namespace BAN
size_type m_has_sso : 1 { true }; size_type m_has_sso : 1 { true };
}; };
template<typename... Args>
String String::formatted(const char* format, const Args&... args)
{
String result;
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, args...);
return result;
}
template<> template<>
struct hash<String> struct hash<String>
{ {

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
{ {
@ -17,229 +14,43 @@ namespace BAN
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))
len = strlen(string);
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
{ {

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,33 +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_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>;
@ -87,57 +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 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

@ -21,21 +21,20 @@ namespace BAN::UTF8
return 0; 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;

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
@ -216,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>)
{ {
@ -286,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&);
@ -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)

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() 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
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();
@ -101,8 +82,8 @@ namespace BAN
RefPtr<T> lock() 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

@ -4,54 +4,41 @@ if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "banan-os")
message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os") message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os")
endif () endif ()
add_compile_options(-mno-sse -mno-sse2)
add_compile_definitions(__enable_sse=0)
project(banan-os CXX C ASM) project(banan-os CXX C ASM)
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
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_BOOT ${BANAN_SYSROOT}/boot)
set(BANAN_SHARE ${BANAN_SYSROOT}/usr/share)
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
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)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
# include headers of ${library} to ${target}
function(banan_include_headers target library)
target_include_directories(${target} PRIVATE $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
endfunction()
# include headers and link ${library} to ${target}
function(banan_link_library target library)
target_link_libraries(${target} PRIVATE ${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(bootloader)
add_subdirectory(BAN) add_subdirectory(BAN)
add_subdirectory(libc)
add_subdirectory(LibELF)
add_subdirectory(userspace) add_subdirectory(userspace)
add_custom_target(sysroot
COMMAND ${CMAKE_COMMAND} -E make_directory ${BANAN_SYSROOT}
COMMAND cd ${BANAN_SYSROOT} && tar xf ${BANAN_BASE_SYSROOT}
)
add_custom_target(headers
DEPENDS kernel-headers
DEPENDS ban-headers
DEPENDS libc-headers
DEPENDS libelf-headers
)
add_custom_target(install-sysroot
COMMAND cd ${BANAN_SYSROOT} && tar cf ${BANAN_SYSROOT_TAR} *
DEPENDS kernel-install
DEPENDS ban-install
DEPENDS libc-install
DEPENDS userspace-install
DEPENDS libelf-install
)

View File

12
LibELF/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.26)
project(LibELF CXX)
add_custom_target(libelf-headers
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
)
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,331 @@
#include <BAN/ScopeGuard.h>
#include <kernel/CriticalScope.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()
{
if (!m_loaded)
return;
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, BAN::ByteSpan::from(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, BAN::ByteSpan::from(m_program_headers[i])));
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;
}
bool LoadableELF::is_address_space_free() const
{
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);
if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE))
return false;
break;
}
default:
ASSERT_NOT_REACHED();
}
}
return true;
}
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();
}
}
m_loaded = true;
}
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);
// Temporarily map page as RW so kernel can write to it
m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
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, { (uint8_t*)vaddr + vaddr_offset, bytes }));
}
// Map page with the correct flags
m_page_table.map_page_at(paddr, vaddr, flags);
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);
{
CriticalScope _;
PageTable::map_fast_page(paddr);
memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
PageTable::unmap_fast_page();
}
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,54 @@
#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;
bool is_address_space_free() 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;
bool m_loaded { false };
};
}

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,17 +155,7 @@ namespace LibELF
Elf64Xword p_align; Elf64Xword p_align;
}; };
struct Elf64Dynamic #if ARCH(i386)
{
Elf64Sxword d_tag;
union
{
Elf64Xword d_val;
Elf64Addr d_ptr;
} d_un;
};
#if ARCH(i686)
using ElfNativeAddr = Elf32Addr; using ElfNativeAddr = Elf32Addr;
using ElfNativeOff = Elf32Off; using ElfNativeOff = Elf32Off;
using ElfNativeHalf = Elf32Half; using ElfNativeHalf = Elf32Half;
@ -187,7 +167,6 @@ namespace LibELF
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 +181,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,
};
}

117
README.md
View File

@ -1,73 +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 ext2 filesystem, basic ramfs, IDE disk drivers in ATA PIO mode, ATA AHCI drivers, 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
- [ ] 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] Keyboard
- [x] Mouse
- [x] Mass storage
- [ ] 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)
@ -77,59 +12,37 @@ 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)
```# apt install build-essential git ninja-build texinfo bison flex libgmp-dev libmpfr-dev libmpc-dev parted qemu-system-x86 cpu-checker```
#### pacman
```# pacman -S --needed base-devel git wget cmake ninja parted qemu-system-x86```
### Compilation
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 ./script/build.sh toolchain
``` ```
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 one of the following commands. You will need root access for disk image creation/modification.
```sh ```sh
./bos qemu ./script/build.sh qemu
./bos qemu-nographic ./script/build.sh qemu-nographic
./bos qemu-debug ./script/build.sh qemu-debug
./bos bochs ./script/build.sh 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 ./script/build.sh kernel
./bos image ./script/build.sh image
``` ```
To build for other architectures set environment variable BANAN\_ARCH=*arch* (e.g. BANAN\_ARCH=i686).
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*).
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. 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 ./script/build.sh image-full
``` ```
If you feel like ```./script/build.sh``` is too verbose, there exists a symlink _bos_ in this projects root directory. All build commands can be used with ```./bos args...``` instead.
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_. 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_.
## 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

@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.26)
project(bootloader ASM) project(bootloader ASM)
set(BOOTLOADER_SOURCES set(BOOTLOADER_SOURCES
a20_line.S
boot.S boot.S
command_line.S command_line.S
disk.S disk.S
@ -15,6 +14,5 @@ set(BOOTLOADER_SOURCES
) )
add_executable(bootloader ${BOOTLOADER_SOURCES}) 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 LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
target_link_options(bootloader PRIVATE -nostdlib) 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,5 +1,3 @@
.include "common.S"
.code16 .code16
######################################### #########################################
@ -15,10 +13,12 @@
.global stage1_main .global stage1_main
stage1_main: stage1_main:
# setup segments and stack # setup segments
xorw %ax, %ax movw $0, %ax
movw %ax, %ds movw %ax, %ds
movw %ax, %es movw %ax, %es
# setup stack
movw %ax, %ss movw %ax, %ss
movl $0x7C00, %esp movl $0x7C00, %esp
@ -53,19 +53,15 @@ stage2_main:
movw $hello_msg, %si movw $hello_msg, %si
call puts; call print_newline call puts; call print_newline
lgdt gdtr
call enter_unreal_mode call enter_unreal_mode
movw $unreal_enter_msg, %si movw $unreal_enter_msg, %si
call puts; call print_newline call puts; call print_newline
call enable_a20
call get_memory_map call get_memory_map
call print_newline
call read_user_command_line call read_user_command_line
call vesa_find_video_mode
call print_newline call print_newline
movw $start_kernel_load_msg, %si movw $start_kernel_load_msg, %si
@ -88,36 +84,36 @@ stage2_main:
call elf_read_kernel_to_memory call elf_read_kernel_to_memory
call vesa_set_video_mode call vesa_set_target_mode
cli cli
# kernel entry point
movl %eax, %ecx
# setup kernel parameters
movl $0xD3C60CFF, %eax
movl $banan_boot_info, %ebx
# setup protected mode # setup protected mode
movl %cr0, %edx movl %cr0, %ebx
orb $1, %dl orb $1, %bl
movl %edx, %cr0 movl %ebx, %cr0
# jump to kernel in protected mode
ljmpl $0x18, $protected_mode
# jump to protected mode
ljmpl $GDT_CODE32, $protected_mode
.code32 .code32
protected_mode: protected_mode:
# setup protected mode segments movw $0x10, %bx
movw $GDT_DATA32, %dx movw %bx, %ds
movw %dx, %ds movw %bx, %es
movw %dx, %es movw %bx, %fs
movw %dx, %fs movw %bx, %gs
movw %dx, %gs movw %bx, %ss
movw %dx, %ss
movl %eax, %ecx
movl $0xD3C60CFF, %eax
movl $banan_boot_info, %ebx
xorl %edx, %edx
xorl %esi, %esi
xorl %edi, %edi
# jump to kernel entry
jmp *%ecx jmp *%ecx
@ -126,18 +122,20 @@ enter_unreal_mode:
cli cli
pushw %ds pushw %ds
lgdt gdtr
movl %cr0, %eax movl %cr0, %eax
orb $1, %al orb $1, %al
movl %eax, %cr0 movl %eax, %cr0
ljmpl $GDT_CODE16, $.enter_unreal_mode_pmode ljmpl $0x8, $.enter_unreal_mode_pmode
.enter_unreal_mode_pmode: .enter_unreal_mode_pmode:
movw $GDT_DATA32, %bx movw $0x10, %bx
movw %bx, %ds movw %bx, %ds
andb $0xFE, %al andb 0xFE, %al
movl %eax, %cr0 movl %eax, %cr0
ljmpl $0x00, $.enter_unreal_mode_unreal ljmpl $0x0, $.enter_unreal_mode_unreal
.enter_unreal_mode_unreal: .enter_unreal_mode_unreal:
popw %ds popw %ds
@ -145,8 +143,6 @@ enter_unreal_mode:
ret ret
.section .data
hello_msg: hello_msg:
.asciz "This is banan-os bootloader" .asciz "This is banan-os bootloader"
@ -158,12 +154,12 @@ start_kernel_load_msg:
gdt: gdt:
.quad 0x0000000000000000 .quad 0x0000000000000000
.quad 0x008F9A000000FFFF # 16-bit code .quad 0x00009A000000FFFF
.quad 0x00CF92000000FFFF # 32-bit data .quad 0x00CF92000000FFFF
.quad 0x00CF9A000000FFFF # 32-bit code .quad 0x00CF9A000000FFFF
gdtr: gdtr:
.short . - gdt - 1 .short . - gdt - 1
.long gdt .quad gdt
banan_boot_info: banan_boot_info:
boot_command_line: boot_command_line:
@ -172,5 +168,3 @@ banan_boot_info:
.long framebuffer .long framebuffer
boot_memory_map: boot_memory_map:
.long memory_map .long memory_map
boot_kernel_paddr:
.long 0

View File

@ -26,8 +26,6 @@ read_user_command_line:
cmpb $'\b', %al cmpb $'\b', %al
je .read_user_command_line_backspace 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 # Not sure if some BIOSes return '\n' as enter, but check it just in case
cmpb $'\r', %al cmpb $'\r', %al
@ -74,8 +72,6 @@ read_user_command_line:
ret ret
.section .data
command_line_enter_msg: command_line_enter_msg:
.asciz "cmdline: " .asciz "cmdline: "
@ -84,4 +80,4 @@ command_line:
# 100 character command line # 100 character command line
command_line_buffer: command_line_buffer:
.ascii "root=/dev/sda2" .ascii "root=/dev/sda2"
.skip 100 - (. - command_line_buffer) .skip 100 - 28

View File

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

View File

@ -41,7 +41,7 @@ read_from_disk:
call drive_has_int13_ext call drive_has_int13_ext
# prepare disk read packet # prepare disk read packet
movw $disk_address_packet, %si mov $disk_address_packet, %si
movb $0x10, 0x00(%si) # packet size movb $0x10, 0x00(%si) # packet size
movb $0x00, 0x01(%si) # always 0 movb $0x00, 0x01(%si) # always 0
movw %cx, 0x02(%si) # lba count movw %cx, 0x02(%si) # lba count
@ -389,8 +389,10 @@ find_root_partition:
# increment 8 byte entry array lba # increment 8 byte entry array lba
incl 0(%esp) incl 0(%esp)
adcl $0, 4(%esp) jnc .find_root_partition_no_overflow
incl 4(%esp)
.find_root_partition_no_overflow:
# loop to read next section if entries remaining # loop to read next section if entries remaining
cmpl $0, 12(%esp) cmpl $0, 12(%esp)
jnz .find_root_partition_read_entry_section jnz .find_root_partition_read_entry_section
@ -414,10 +416,12 @@ find_root_partition:
# ebx:eax -= first lba - 1 # ebx:eax -= first lba - 1
subl (root_partition_entry + 36), %ebx subl (root_partition_entry + 36), %ebx
movl (root_partition_entry + 32), %ecx movl (root_partition_entry + 32), %ecx;
decl %ecx decl %ecx
subl %ecx, %eax subl %ecx, %eax
sbbl $0, %ebx jnc .find_root_partition_count_sub_no_carry
decl %ebx
.find_root_partition_count_sub_no_carry:
# ecx: min(partition count, 0xFFFFFFFF) # ecx: min(partition count, 0xFFFFFFFF)
movl $0xFFFFFFFF, %edx movl $0xFFFFFFFF, %edx
@ -470,7 +474,6 @@ print_root_partition_info:
popw %ax popw %ax
ret ret
.section .data
# These will be patched during bootloader installation # These will be patched during bootloader installation
root_disk_guid: root_disk_guid:

View File

@ -5,26 +5,15 @@
.set e_machine, 18 .set e_machine, 18
.set e_version, 20 .set e_version, 20
.set e_entry, 24 .set e_entry, 24
.set e_phoff, 32
.set e32_phoff, 28 .set e_shoff, 40
.set e32_shoff, 32 .set e_flags, 48
.set e32_flags, 36 .set e_ehsize, 52
.set e32_ehsize, 40 .set e_phentsize, 54
.set e32_phentsize, 42 .set e_phnum, 56
.set e32_phnum, 44 .set e_shentsize, 58
.set e32_shentsize, 46 .set e_shnum, 60
.set e32_shnum, 48 .set e_shstrndx, 62
.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 # e_ident offsets
.set EI_CLASS, 4 .set EI_CLASS, 4
@ -33,7 +22,6 @@
# e_ident constants # e_ident constants
.set ELFMAGIC, 0x464C457F .set ELFMAGIC, 0x464C457F
.set ELFCLASS32, 1
.set ELFCLASS64, 2 .set ELFCLASS64, 2
.set ELFDATA2LSB, 1 .set ELFDATA2LSB, 1
.set EV_CURRENT, 1 .set EV_CURRENT, 1
@ -43,30 +31,18 @@
# program header field offsets # program header field offsets
.set p_type, 0 .set p_type, 0
.set p_flags, 4
.set p32_offset, 4 .set p_offset, 8
.set p32_vaddr, 8 .set p_vaddr, 16
.set p32_paddr, 12 .set p_paddr, 24
.set p32_filesz, 16 .set p_filesz, 32
.set p32_memsz, 20 .set p_memsz, 40
.set p32_flags, 24 .set p_align, 48
.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 # p_type constants
.set PT_NULL, 0 .set PT_NULL, 0
.set PT_LOAD, 1 .set PT_LOAD, 1
# mask for entry point and segment loading
.set LOAD_MASK, 0x07FFFFFF
.code16 .code16
.section .stage2 .section .stage2
@ -76,12 +52,8 @@ elf_validate_file_header:
cmpl $ELFMAGIC, (elf_file_header) cmpl $ELFMAGIC, (elf_file_header)
jne .elf_validate_file_header_invalid_magic 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) cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
je .elf_validate_file_header_class_valid jne .elf_validate_file_header_only_64bit_supported
jmp .elf_validate_file_header_invalid_class
.elf_validate_file_header_class_valid:
cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA) cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA)
jne .elf_validate_file_header_only_little_endian_supported jne .elf_validate_file_header_only_little_endian_supported
@ -100,8 +72,8 @@ elf_validate_file_header:
.elf_validate_file_header_invalid_magic: .elf_validate_file_header_invalid_magic:
movw $elf_validate_file_header_invalid_magic_msg, %si movw $elf_validate_file_header_invalid_magic_msg, %si
jmp print_and_halt jmp print_and_halt
.elf_validate_file_header_invalid_class: .elf_validate_file_header_only_64bit_supported:
movw $elf_validate_file_header_invalid_class_msg, %si movw $elf_validate_file_header_only_64bit_supported_msg, %si
jmp print_and_halt jmp print_and_halt
.elf_validate_file_header_only_little_endian_supported: .elf_validate_file_header_only_little_endian_supported:
movw $elf_validate_file_header_only_little_endian_supported_msg, %si movw $elf_validate_file_header_only_little_endian_supported_msg, %si
@ -113,59 +85,6 @@ elf_validate_file_header:
movw $elf_validate_file_header_not_executable_msg, %si movw $elf_validate_file_header_not_executable_msg, %si
jmp print_and_halt 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 # read callback format
# eax: first byte # eax: first byte
@ -185,72 +104,42 @@ elf_read_kernel_to_memory:
movl %esp, %ebp movl %esp, %ebp
subl $2, %esp subl $2, %esp
# read start of file header # read file header
movl $0, %eax movl $0, %eax
movl $24, %ecx movl $64, %ecx
movl $elf_file_header, %edi movl $elf_file_header, %edi
call *%esi call *%esi
call elf_validate_file_header call elf_validate_file_header
# determine file header size cmpl $0, (elf_file_header + e_phoff + 4)
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 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 # current program header
movw $0, -2(%ebp) movw $0, -2(%ebp)
.elf_read_kernel_to_memory_loop_program_headers: .elf_read_kernel_to_memory_loop_program_headers:
movw -2(%ebp), %cx movw -2(%ebp), %cx
cmpw (elf_file_header_phnum), %cx cmpw (elf_file_header + e_phnum), %cx
jae .elf_read_kernel_to_memory_done jae .elf_read_kernel_to_memory_done
# eax := program_header_index * e_phentsize + e_phoff # eax := program_header_index * e_phentsize + e_phoff
xorl %eax, %eax xorl %eax, %eax
movw %cx, %ax movw %cx, %ax
xorl %ebx, %ebx xorl %ebx, %ebx
movw (elf_file_header_phentsize), %bx movw (elf_file_header + e_phentsize), %bx
mull %ebx mull %ebx
addl (elf_file_header_phoff), %eax addl (elf_file_header + e_phoff), %eax
jc .elf_read_kernel_to_memory_unsupported_offset jc .elf_read_kernel_to_memory_unsupported_offset
# determine program header size # setup program header size and address
movl $32, %ecx movl $56, %ecx
movl $56, %edx
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
cmovel %edx, %ecx
# read program header
movl $elf_program_header, %edi movl $elf_program_header, %edi
# read the program header
call *%esi call *%esi
# test if program header is NULL header # test if program header is empty
cmpl $PT_NULL, (elf_program_header + p_type) cmpl $PT_NULL, (elf_program_header + p_type)
je .elf_read_kernel_to_memory_null_program_header je .elf_read_kernel_to_memory_null_program_header
@ -258,12 +147,33 @@ elf_read_kernel_to_memory:
cmpl $PT_LOAD, (elf_program_header + p_type) cmpl $PT_LOAD, (elf_program_header + p_type)
jne .elf_read_kernel_to_memory_not_loadable_header jne .elf_read_kernel_to_memory_not_loadable_header
# read program header to memory # memset p_filesz -> p_memsz to 0
movl $elf_read_program_header32_to_memory, %eax movl (elf_program_header + p_filesz), %ebx
movl $elf_read_program_header64_to_memory, %ebx
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS) movl (elf_program_header + p_vaddr), %edi
cmovel %ebx, %eax andl $0x7FFFFFFF, %edi
call *%eax addl %ebx, %edi
movl (elf_program_header + p_memsz), %ecx
subl %ebx, %ecx
jz .elf_read_kernel_to_memory_memset_done
.elf_read_kernel_to_memory_memset:
movb $0, (%edi)
incl %edi
decl %ecx
jnz .elf_read_kernel_to_memory_memset
.elf_read_kernel_to_memory_memset_done:
# read file specified in program header to memory
movl (elf_program_header + p_offset), %eax
movl (elf_program_header + p_vaddr), %edi
andl $0x7FFFFFFF, %edi
movl (elf_program_header + p_filesz), %ecx
#call print_hex32; call print_newline
call *%esi
.elf_read_kernel_to_memory_null_program_header: .elf_read_kernel_to_memory_null_program_header:
incw -2(%ebp) incw -2(%ebp)
@ -275,7 +185,7 @@ elf_read_kernel_to_memory:
# set kernel entry address # set kernel entry address
movl (elf_file_header + e_entry), %eax movl (elf_file_header + e_entry), %eax
andl $LOAD_MASK, %eax andl $0x7FFFFF, %eax
ret ret
@ -286,12 +196,11 @@ elf_read_kernel_to_memory:
movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si
jmp print_and_halt jmp print_and_halt
.section .data
elf_validate_file_header_invalid_magic_msg: elf_validate_file_header_invalid_magic_msg:
.asciz "ELF: file has invalid ELF magic" .asciz "ELF: file has invalid ELF magic"
elf_validate_file_header_invalid_class_msg: elf_validate_file_header_only_64bit_supported_msg:
.asciz "ELF: file has invalid ELF class" .asciz "ELF: file is not targettint 64 bit"
elf_validate_file_header_only_little_endian_supported_msg: elf_validate_file_header_only_little_endian_supported_msg:
.asciz "ELF: file is not in little endian format" .asciz "ELF: file is not in little endian format"
elf_validate_file_header_not_current_version_msg: elf_validate_file_header_not_current_version_msg:
@ -309,12 +218,5 @@ elf_read_kernel_to_memory_not_loadable_header_msg:
elf_file_header: elf_file_header:
.skip 64 .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: elf_program_header:
.skip 56 .skip 56

View File

@ -2,7 +2,9 @@
.set SECTOR_SHIFT, 9 .set SECTOR_SHIFT, 9
.set SECTOR_SIZE, 1 << SECTOR_SHIFT .set SECTOR_SIZE, 1 << SECTOR_SHIFT
.set EXT2_MAX_BLOCK_SIZE, 4096 # FIXME: don't assume 1024 byte blocks
.set EXT2_BLOCK_SHIFT, 10
.set EXT2_BLOCK_SIZE, 1 << EXT2_BLOCK_SHIFT
.set EXT2_SUPERBLOCK_SIZE, 264 .set EXT2_SUPERBLOCK_SIZE, 264
.set EXT2_BGD_SHIFT, 5 .set EXT2_BGD_SHIFT, 5
.set EXT2_BGD_SIZE, 1 << EXT2_BGD_SHIFT .set EXT2_BGD_SIZE, 1 << EXT2_BGD_SHIFT
@ -16,7 +18,6 @@
.set EXT2_S_IFREG, 0x8000 .set EXT2_S_IFREG, 0x8000
# superblock offsets # superblock offsets
.set s_first_data_block, 20
.set s_log_block_size, 24 .set s_log_block_size, 24
.set s_inodes_per_group, 40 .set s_inodes_per_group, 40
.set s_magic, 56 .set s_magic, 56
@ -65,7 +66,9 @@ has_ext2_filesystem:
# from byte offset 1024 # from byte offset 1024
addl $(1024 / SECTOR_SIZE), %eax addl $(1024 / SECTOR_SIZE), %eax
adcw $0, %bx jnc .has_ext2_filesystem_no_overflow
incw %bx
.has_ext2_filesystem_no_overflow:
# into sector buffer # into sector buffer
movw $ext2_block_buffer, %di movw $ext2_block_buffer, %di
@ -87,16 +90,11 @@ has_ext2_filesystem:
movl (ext2_superblock_buffer + s_log_block_size), %ecx movl (ext2_superblock_buffer + s_log_block_size), %ecx
testl $0xFFFFFF00, %ecx testl $0xFFFFFF00, %ecx
jnz .has_ext2_filesystem_unsupported_block_size jnz .has_ext2_filesystem_unsupported_block_size
# verify 1024 << s_log_block_size <= EXT2_MAX_BLOCK_SIZE # verify 1024 << s_log_block_size == EXT2_BLOCK_SIZE
movl $1024, %eax movl $1024, %eax
shll %cl, %eax shll %cl, %eax
cmpl $EXT2_MAX_BLOCK_SIZE, %eax cmpl $EXT2_BLOCK_SIZE, %eax
ja .has_ext2_filesystem_unsupported_block_size jne .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 # fill inode size
movl $128, %eax movl $128, %eax
@ -132,23 +130,38 @@ has_ext2_filesystem:
# reads block in to ext2_block_buffer # reads block in to ext2_block_buffer
# eax: block number # eax: block number
ext2_read_block: ext2_read_block:
pushal pushl %eax
pushl %ebx
pushw %cx
pushl %edx
pushw %di
# ecx := sectors_per_block := block_size / sector_size # NOTE: this assumes 1024 block size
movl (ext2_block_size), %ecx # eax := (block * block_size) / sector_size := (eax << EXT2_BLOCK_SHIFT) >> SECTOR_SHIFT
shrl $SECTOR_SHIFT, %ecx xorl %edx, %edx
shll $EXT2_BLOCK_SHIFT, %eax
shrl $SECTOR_SHIFT, %eax
# ebx:eax := block * sectors_per_block + (ext2_partition_first_sector) # ebx:eax := eax + (ext2_partition_first_sector)
xorl %ebx, %ebx movl (ext2_partition_first_sector + 4), %ebx
mull %ecx
addl (ext2_partition_first_sector + 0), %eax addl (ext2_partition_first_sector + 0), %eax
adcl (ext2_partition_first_sector + 4), %ebx jnc .ext2_read_block_no_carry
incl %ebx
.ext2_read_block_no_carry:
# sectors per block
movw $(EXT2_BLOCK_SIZE / SECTOR_SIZE), %cx
movw $ext2_block_buffer, %di movw $ext2_block_buffer, %di
movb (ext2_drive_number), %dl movb (ext2_drive_number), %dl
call read_from_disk call read_from_disk
popal popw %di
popl %edx
popw %cx
popl %ebx
popl %eax
ret ret
@ -157,23 +170,15 @@ ext2_read_block:
ext2_read_block_group_descriptor: ext2_read_block_group_descriptor:
pushal pushal
# ebx := bgd_block_byte_offset := (s_first_data_block + 1) * block_size # eax := bgd_byte_offset := 2048 + EXT2_BGD_SIZE * eax := (eax << EXT2_BGD_SHIFT) + 2048
# := (s_first_data_block + 1) << ext2_block_shift shll $EXT2_BGD_SHIFT, %eax
movl (ext2_superblock_buffer + s_first_data_block), %ebx addl $2048, %eax
incl %ebx
movb (ext2_block_shift), %cl
shll %cl, %ebx
# eax := bgd_byte_offset := bgd_block_byte_offset + EXT2_BGD_SIZE * block_group; # eax: bgd_block := bgd_byte_offset / EXT2_BLOCK_SIZE
# := bgd_block_byte_offset + (block_group << EXT2_BGD_SHIFT) # ebx: bgd_offset := bgd_byte_offset % EXT2_BLOCK_SIZE
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 xorl %edx, %edx
divl (ext2_block_size) movl $EXT2_BLOCK_SIZE, %ebx
divl %ebx
movl %edx, %ebx movl %edx, %ebx
call ext2_read_block call ext2_read_block
@ -199,19 +204,23 @@ ext2_read_inode:
# ebx := inode_index = (ino - 1) % s_inodes_per_group # ebx := inode_index = (ino - 1) % s_inodes_per_group
xorl %edx, %edx xorl %edx, %edx
decl %eax decl %eax
divl (ext2_superblock_buffer + s_inodes_per_group) movl (ext2_superblock_buffer + s_inodes_per_group), %ebx
divl %ebx
movl %edx, %ebx movl %edx, %ebx
call ext2_read_block_group_descriptor call ext2_read_block_group_descriptor
# eax := inode_table_block := (inode_index * inode_size) / block_size # eax := inode_table_block := (inode_index * inode_size) / EXT2_BLOCK_SIZE
# ebx := inode_table_offset := (inode_index * inode_size) % block_size # ebx := inode_table_offset := (inode_index * inode_size) % EXT2_BLOCK_SIZE
xorl %edx, %edx
movl %ebx, %eax movl %ebx, %eax
mull (ext2_inode_size) movl (ext2_inode_size), %ebx
divl (ext2_block_size) mull %ebx
movl $EXT2_BLOCK_SIZE, %ebx
divl %ebx
movl %edx, %ebx movl %edx, %ebx
# eax := filesystem_block := eax + bg_inode_table # eax := file system block := eax + bg_inode_table
addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax
movb (ext2_drive_number), %dl movb (ext2_drive_number), %dl
@ -227,10 +236,6 @@ ext2_read_inode:
movl (ext2_inode_size), %ecx movl (ext2_inode_size), %ecx
rep movsb rep movsb
# reset indirect cache to zero
movl $0, (ext2_inode_indirect_number)
.ext2_read_inode_done:
popal popal
ret ret
@ -244,19 +249,15 @@ ext2_data_block_index:
pushl %ecx pushl %ecx
pushl %edx pushl %edx
pushl %esi pushl %esi
pushl %edi
# ebx := max_data_blocks := (file_size + block_size - 1) / block_size # calculate max data blocks
# := (i_size + ext2_block_size - 1) >> ext2_block_shift movl (ext2_inode_buffer + i_size), %ecx
# cl := ext2_block_shift addl (ext2_inode_size), %ecx
movl (ext2_inode_buffer + i_size), %ebx decl %ecx
addl (ext2_block_size), %ebx shll $EXT2_BLOCK_SHIFT, %ecx
decl %ebx
movb (ext2_block_shift), %cl
shrl %cl, %ebx
# verify data block is within bounds # verify data block is within bounds
cmpl %ebx, %eax cmpl %ecx, %eax
jae .ext2_data_block_index_out_of_bounds jae .ext2_data_block_index_out_of_bounds
# check if this is direct block access # check if this is direct block access
@ -264,26 +265,18 @@ ext2_data_block_index:
jb .ext2_data_block_index_direct jb .ext2_data_block_index_direct
subl $12, %eax 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 # check if this is singly indirect block access
cmpl %ebx, %eax cmpl $(EXT2_BLOCK_SIZE / 4), %eax
jb .ext2_data_block_index_singly_indirect jb .ext2_data_block_index_singly_indirect
subl %ebx, %eax subl $(EXT2_BLOCK_SIZE / 4), %eax
shll %cl, %ebx
# check if this is doubly indirect block access # check if this is doubly indirect block access
cmpl %ebx, %eax cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
jb .ext2_data_block_index_doubly_indirect jb .ext2_data_block_index_doubly_indirect
subl %ebx, %eax subl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
shll %cl, %ebx
# check if this is triply indirect block access # check if this is triply indirect block access
cmpl %ebx, %eax cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
jb .ext2_data_block_index_triply_indirect jb .ext2_data_block_index_triply_indirect
# otherwise this is invalid access # otherwise this is invalid access
@ -316,28 +309,6 @@ ext2_data_block_index:
# ebx := index # ebx := index
# cx := depth # cx := depth
.ext2_data_block_index_indirect: .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 call ext2_read_block
# store depth and index # store depth and index
@ -348,21 +319,20 @@ ext2_data_block_index:
jbe .ext2_data_block_index_no_shift jbe .ext2_data_block_index_no_shift
# cl := shift # cl := shift
movb (ext2_block_shift), %al movb $(EXT2_BLOCK_SHIFT - 2), %al
subb $2, %al
decb %cl decb %cl
mulb %cl mulb %cl
movb %al, %cl movb %al, %cl
# ebx := ebx >> shift # ebx := ebx >> cl
shrl %cl, %ebx shrl %cl, %ebx
.ext2_data_block_index_no_shift: .ext2_data_block_index_no_shift:
# edx := index of next block (ebx & (block_size / 4 - 1)) # edx := index of next block
movl (ext2_block_size), %edx movl %ebx, %eax
shrl $2, %edx xorl %edx, %edx
decl %edx movl $(EXT2_BLOCK_SIZE / 4), %ebx
andl %ebx, %edx divl %ebx
# eax := next block # eax := next block
movl $ext2_block_buffer, %esi movl $ext2_block_buffer, %esi
@ -372,13 +342,7 @@ ext2_data_block_index:
popl %ebx popl %ebx
popw %cx popw %cx
loop .ext2_data_block_index_indirect_loop loop .ext2_data_block_index_indirect
# 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 jmp .ext2_data_block_index_done
@ -394,16 +358,7 @@ ext2_data_block_index:
movl $0, %eax movl $0, %eax
jmp .ext2_data_block_index_done 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: .ext2_data_block_index_done:
popl %edi
popl %esi popl %esi
popl %edx popl %edx
popl %ecx popl %ecx
@ -419,7 +374,6 @@ ext2_data_block_index:
.global ext2_inode_read_bytes .global ext2_inode_read_bytes
ext2_inode_read_bytes: ext2_inode_read_bytes:
pushal pushal
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
subl $8, %esp subl $8, %esp
@ -428,11 +382,11 @@ ext2_inode_read_bytes:
movl %eax, 0(%esp) movl %eax, 0(%esp)
movl %ecx, 4(%esp) movl %ecx, 4(%esp)
# eax := first_byte / block_size # check if eax % EXT2_BLOCK_SIZE != 0,
# edx := first_byte % block_size # then we need to read a partial block starting from an offset
# when edx == 0, no partial read needed
xorl %edx, %edx xorl %edx, %edx
divl (ext2_block_size) movl $EXT2_BLOCK_SIZE, %ebx
divl %ebx
testl %edx, %edx testl %edx, %edx
jz .ext2_inode_read_bytes_no_partial_start jz .ext2_inode_read_bytes_no_partial_start
@ -441,7 +395,7 @@ ext2_inode_read_bytes:
call ext2_read_block call ext2_read_block
# ecx := byte count (min(block_size - edx, remaining_bytes)) # ecx := byte count (min(block_size - edx, remaining_bytes))
movl (ext2_block_size), %ecx movl $EXT2_BLOCK_SIZE, %ecx
subl %edx, %ecx subl %edx, %ecx
cmpl %ecx, 4(%esp) cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx cmovbl 4(%esp), %ecx
@ -454,7 +408,15 @@ ext2_inode_read_bytes:
movl $ext2_block_buffer, %esi movl $ext2_block_buffer, %esi
addl %edx, %esi addl %edx, %esi
call memcpy32 # very dumb memcpy with 32 bit addresses
movl $0, %ebx
.ext2_inode_read_bytes_memcpy_partial:
movb (%esi, %ebx), %al
movb %al, (%edi, %ebx)
incl %ebx
decl %ecx
jnz .ext2_inode_read_bytes_memcpy_partial
addl %ebx, %edi
# check if all sectors are read # check if all sectors are read
cmpl $0, 4(%esp) cmpl $0, 4(%esp)
@ -463,15 +425,14 @@ ext2_inode_read_bytes:
.ext2_inode_read_bytes_no_partial_start: .ext2_inode_read_bytes_no_partial_start:
# eax := data block index (byte_start / block_size) # eax := data block index (byte_start / block_size)
movl 0(%esp), %eax movl 0(%esp), %eax
movb (ext2_block_shift), %cl shrl $(EXT2_BLOCK_SHIFT), %eax
shrl %cl, %eax
# get data block index and read block # get data block index and read block
call ext2_data_block_index call ext2_data_block_index
call ext2_read_block call ext2_read_block
# calculate bytes to copy (min(block_size, remaining_bytes)) # calculate bytes to copy (min(block_size, remaining_bytes))
movl (ext2_block_size), %ecx movl $EXT2_BLOCK_SIZE, %ecx
cmpl %ecx, 4(%esp) cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx cmovbl 4(%esp), %ecx
@ -479,8 +440,16 @@ ext2_inode_read_bytes:
addl %ecx, 0(%esp) addl %ecx, 0(%esp)
subl %ecx, 4(%esp) subl %ecx, 4(%esp)
# very dumb memcpy with 32 bit addresses
movl $ext2_block_buffer, %esi movl $ext2_block_buffer, %esi
call memcpy32 movl $0, %ebx
.ext2_inode_read_bytes_memcpy:
movb (%esi, %ebx), %al
movb %al, (%edi, %ebx)
incl %ebx
decl %ecx
jnz .ext2_inode_read_bytes_memcpy
addl %ebx, %edi
# read next block if more sectors remaining # read next block if more sectors remaining
cmpl $0, 4(%esp) cmpl $0, 4(%esp)
@ -519,12 +488,11 @@ ext2_directory_find_inode:
cmpw $0xFF, %cx cmpw $0xFF, %cx
ja .ext2_directory_find_inode_not_found ja .ext2_directory_find_inode_not_found
# ebx := max data blocks: ceil(i_size / block_size) # ebx := max data blocks: ceil(i_size / EXT2_BLOCK_SIZE)
movl (ext2_inode_buffer + i_size), %ebx movl (ext2_inode_buffer + i_size), %ebx
addl (ext2_block_size), %ebx addl $EXT2_BLOCK_SHIFT, %ebx
decl %ebx decl %ebx
movb (ext2_block_shift), %cl shrl $EXT2_BLOCK_SHIFT, %ebx
shrl %cl, %ebx
jz .ext2_directory_find_inode_not_found jz .ext2_directory_find_inode_not_found
# 4(%esp) := current block # 4(%esp) := current block
@ -571,9 +539,7 @@ ext2_directory_find_inode:
# go to next entry if this block contains one # go to next entry if this block contains one
addw 4(%si), %si addw 4(%si), %si
movw $ext2_block_buffer, %di cmpw $(ext2_block_buffer + EXT2_BLOCK_SIZE), %si
addw (ext2_block_size), %di
cmpw %di, %si
jb .ext2_directory_find_inode_loop_entries jb .ext2_directory_find_inode_loop_entries
.ext2_directory_find_inode_next_block: .ext2_directory_find_inode_next_block:
@ -582,7 +548,7 @@ ext2_directory_find_inode:
jb .ext2_directory_find_inode_block_read_loop jb .ext2_directory_find_inode_block_read_loop
.ext2_directory_find_inode_not_found: .ext2_directory_find_inode_not_found:
xorb %al, %al movb $0, %al
jmp .ext2_directory_find_inode_done jmp .ext2_directory_find_inode_done
.ext2_directory_find_inode_found: .ext2_directory_find_inode_found:
@ -676,7 +642,6 @@ ext2_find_kernel:
movw $ext2_kernel_not_reg_msg, %si movw $ext2_kernel_not_reg_msg, %si
jmp print_and_halt jmp print_and_halt
.section .data
kernel_path: kernel_path:
.short kernel_path1 .short kernel_path1
@ -689,12 +654,13 @@ kernel_path2:
.short 15 .short 15
.asciz "banan-os.kernel" .asciz "banan-os.kernel"
root_partition_does_not_fit_ext2_filesystem_msg: root_partition_does_not_fit_ext2_filesystem_msg:
.asciz "Root partition is too small to contain ext2 filesystem" .asciz "Root partition is too small to contain ext2 filesystem"
root_partition_has_invalid_ext2_magic_msg: root_partition_has_invalid_ext2_magic_msg:
.asciz "Root partition doesn't contain ext2 magic number" .asciz "Root partition doesn't contain ext2 magic number"
root_partition_has_unsupported_ext2_block_size_msg: 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)" .asciz "Root partition has unsupported ext2 block size (only 1024 supported)"
ext2_part_not_dir_msg: ext2_part_not_dir_msg:
.asciz "inode in root path is not directory" .asciz "inode in root path is not directory"
@ -715,14 +681,8 @@ ext2_looking_for_msg:
.section .bss .section .bss
.align SECTOR_SIZE
ext2_block_buffer: ext2_block_buffer:
.skip EXT2_MAX_BLOCK_SIZE .skip EXT2_BLOCK_SIZE
ext2_inode_indirect_buffer:
.skip EXT2_MAX_BLOCK_SIZE
ext2_inode_indirect_number:
.skip 4
ext2_partition_first_sector: ext2_partition_first_sector:
.skip 8 .skip 8
@ -734,10 +694,6 @@ ext2_drive_number:
# NOTE: fits in 2 bytes # NOTE: fits in 2 bytes
ext2_inode_size: ext2_inode_size:
.skip 4 .skip 4
ext2_block_size:
.skip 4
ext2_block_shift:
.skip 4
ext2_superblock_buffer: ext2_superblock_buffer:
.skip EXT2_SUPERBLOCK_SIZE .skip EXT2_SUPERBLOCK_SIZE

View File

@ -1,67 +1,19 @@
.set TARGET_WIDTH, 800
.set TARGET_HEIGHT, 600
.set TARGET_BPP, 32
.code16 .code16
.section .stage2 .section .stage2
# kernel framebuffer information format # Find suitable video mode
# .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: # return:
# ax: target width # ax: video mode number if found, 0 otherwise
# bx: target height .global vesa_find_video_mode
# 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: vesa_find_video_mode:
pushal pushw %ax
pushw %cx
pushl %ebp pushw %di
movl %esp, %ebp pushl %esi
subl $6, %esp
# clear target mode and frame buffer # clear target mode and frame buffer
movw $0, (vesa_target_mode) movw $0, (vesa_target_mode)
@ -71,19 +23,10 @@ vesa_find_video_mode:
movl $0, (framebuffer + 12) movl $0, (framebuffer + 12)
movw $0, (framebuffer + 16) 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 # get vesa information
movw $0x4F00, %ax movw $0x4F00, %ax
movw $vesa_info_buffer, %di movw $vesa_info_buffer, %di
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp int $0x10
cmpb $0x4F, %al; jne .vesa_unsupported cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error cmpb $0x00, %ah; jne .vesa_error
@ -95,7 +38,8 @@ vesa_find_video_mode:
cmpw $0x0200, (vesa_info_buffer + 0x04) cmpw $0x0200, (vesa_info_buffer + 0x04)
jb .vesa_unsupported_version jb .vesa_unsupported_version
movl (vesa_info_buffer + 0x0E), %esi movl $(vesa_info_buffer + 0x0E), %esi
movl (%esi), %esi
.vesa_find_video_mode_loop_modes: .vesa_find_video_mode_loop_modes:
cmpw $0xFFFF, (%esi) cmpw $0xFFFF, (%esi)
je .vesa_find_video_mode_loop_modes_done je .vesa_find_video_mode_loop_modes_done
@ -104,7 +48,7 @@ vesa_find_video_mode:
movw $0x4F01, %ax movw $0x4F01, %ax
movw (%esi), %cx movw (%esi), %cx
movw $vesa_mode_info_buffer, %di movw $vesa_mode_info_buffer, %di
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp int $0x10
cmpb $0x4F, %al; jne .vesa_unsupported cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error cmpb $0x00, %ah; jne .vesa_error
@ -113,24 +57,21 @@ vesa_find_video_mode:
jz .vesa_find_video_mode_next_mode jz .vesa_find_video_mode_next_mode
# compare mode's dimensions # compare mode's dimensions
movw -2(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x12) cmpw $TARGET_WIDTH, (vesa_mode_info_buffer + 0x12)
jne .vesa_find_video_mode_next_mode jne .vesa_find_video_mode_next_mode
movw -4(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x14) cmpw $TARGET_HEIGHT, (vesa_mode_info_buffer + 0x14)
jne .vesa_find_video_mode_next_mode jne .vesa_find_video_mode_next_mode
movb -6(%ebp), %al; cmpb %al, (vesa_mode_info_buffer + 0x19) cmpb $TARGET_BPP, (vesa_mode_info_buffer + 0x19)
jne .vesa_find_video_mode_next_mode jne .vesa_find_video_mode_next_mode
# set address, pitch, type
movl (vesa_mode_info_buffer + 0x28), %esi movl (vesa_mode_info_buffer + 0x28), %esi
movl %esi, (framebuffer + 0) movl %esi, (framebuffer + 0)
movw (vesa_mode_info_buffer + 0x10), %ax movw (vesa_mode_info_buffer + 0x10), %ax
movw %ax, (framebuffer + 4) movw %ax, (framebuffer + 4)
movb $1, (framebuffer + 17) movl $TARGET_WIDTH, (framebuffer + 8)
movl $TARGET_HEIGHT, (framebuffer + 12)
# set width, height, bpp movb $TARGET_BPP, (framebuffer + 16)
movw -2(%ebp), %ax; movw %ax, (framebuffer + 8) movb $1, (framebuffer + 17)
movw -4(%ebp), %ax; movw %ax, (framebuffer + 12)
movw -6(%ebp), %ax; movb %al, (framebuffer + 16)
movw %cx, (vesa_target_mode) movw %cx, (vesa_target_mode)
jmp .vesa_find_video_mode_loop_modes_done jmp .vesa_find_video_mode_loop_modes_done
@ -140,8 +81,10 @@ vesa_find_video_mode:
jmp .vesa_find_video_mode_loop_modes jmp .vesa_find_video_mode_loop_modes
.vesa_find_video_mode_loop_modes_done: .vesa_find_video_mode_loop_modes_done:
leavel popl %esi
popal popw %di
popw %cx
popw %ax
ret ret
.vesa_unsupported: .vesa_unsupported:
@ -155,37 +98,33 @@ vesa_find_video_mode:
jmp print_and_halt jmp print_and_halt
# scan for video mode in kernel memory and set the correct one. # set mode found from vesa_find_video_mode. if no mode
# when video mode is not found or does not exists, # was found, set it to 80x25 text mode to clear the screen.
# set it to 80x25 text mode to clear the screen. .global vesa_set_target_mode
.global vesa_set_video_mode vesa_set_target_mode:
vesa_set_video_mode:
pushw %ax pushw %ax
pushw %bx pushw %bx
call vesa_find_video_mode
movw (vesa_target_mode), %bx movw (vesa_target_mode), %bx
testw %bx, %bx testw %bx, %bx
jz .vesa_set_target_mode_generic jz .vesa_set_target_mode_generic
movw $0x4F02, %ax movw $0x4F02, %ax
orw $0x4000, %bx orw $0x4000, %bx
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp int $0x10
jmp .set_video_done jmp .set_video_done
.vesa_set_target_mode_generic: .vesa_set_target_mode_generic:
movb $0x03, %al movb $0x03, %al
movb $0x00, %ah movb $0x00, %ah
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp int $0x10
.set_video_done: .set_video_done:
popw %bx popw %bx
popw %ax popw %ax
ret ret
.section .data
vesa_error_msg: vesa_error_msg:
.asciz "VESA error" .asciz "VESA error"
@ -208,7 +147,6 @@ vesa_target_mode:
.skip 2 .skip 2
.global framebuffer .global framebuffer
.align 8
framebuffer: framebuffer:
.skip 4 # address .skip 4 # address
.skip 4 # pitch .skip 4 # pitch

View File

@ -8,8 +8,6 @@ SECTIONS
. = ALIGN(512); . = ALIGN(512);
stage2_start = .; stage2_start = .;
.stage2 : { *(.stage2) } .stage2 : { *(.stage2) }
. = ALIGN(512);
.data : { *(.data) }
stage2_end = .; stage2_end = .;
. = ALIGN(512); . = ALIGN(512);

View File

@ -114,7 +114,6 @@ print_memory_map:
ret ret
.section .data
memory_map_msg: memory_map_msg:
.asciz "memmap:" .asciz "memmap:"

View File

@ -1,5 +1,3 @@
.include "common.S"
.set SCREEN_WIDTH, 80 .set SCREEN_WIDTH, 80
.set SCREEN_HEIGHT, 25 .set SCREEN_HEIGHT, 25
@ -275,127 +273,6 @@ isprint:
movb $0, %al movb $0, %al
ret 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
movl %ecx, %edx
andl $3, %ecx
rep stosb %es:(%edi)
movl %edx, %ecx
shrl $2, %ecx
movb %al, %ah
movw %ax, %dx
shll $16, %eax
movw %dx, %ax
rep stosl %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
movl %ecx, %edx
andl $3, %ecx
rep movsb %ds:(%esi), %es:(%edi)
movl %edx, %ecx
shrl $2, %ecx
rep movsl %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 .section .bss
# enough for base 2 printing # enough for base 2 printing

View File

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

View File

@ -81,7 +81,7 @@ bool ELFFile::validate_elf_header() const
#if ARCH(x86_64) #if ARCH(x86_64)
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64) if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
#elif ARCH(i686) #elif ARCH(i386)
if (elf_header.e_ident[EI_CLASS] != ELFCLASS32) if (elf_header.e_ident[EI_CLASS] != ELFCLASS32)
#endif #endif
{ {

View File

@ -99,33 +99,33 @@ bool GPTFile::install_stage1(std::span<const uint8_t> stage1)
return true; return true;
} }
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid) bool GPTFile::install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid)
{ {
if (data.size() < 16) if (stage2.size() < 16)
{ {
std::cerr << m_path << ": contains invalid .data section, too small for patches" << std::endl; std::cerr << m_path << ": contains invalid .stage2 section, too small for patches" << std::endl;
return false; return false;
} }
// find GUID patch offsets // find GUID patch offsets
std::size_t disk_guid_offset(-1); std::size_t disk_guid_offset(-1);
std::size_t part_guid_offset(-1); std::size_t part_guid_offset(-1);
for (std::size_t i = 0; i < data.size() - 16; i++) for (std::size_t i = 0; i < stage2.size() - 16; i++)
{ {
if (memcmp(data.data() + i, "root disk guid ", 16) == 0) if (memcmp(stage2.data() + i, "root disk guid ", 16) == 0)
{ {
if (disk_guid_offset != std::size_t(-1)) if (disk_guid_offset != std::size_t(-1))
{ {
std::cerr << m_path << ": contains invalid .data section, multiple patchable disk guids" << std::endl; std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable disk guids" << std::endl;
return false; return false;
} }
disk_guid_offset = i; disk_guid_offset = i;
} }
if (memcmp(data.data() + i, "root part guid ", 16) == 0) if (memcmp(stage2.data() + i, "root part guid ", 16) == 0)
{ {
if (part_guid_offset != std::size_t(-1)) if (part_guid_offset != std::size_t(-1))
{ {
std::cerr << m_path << ": contains invalid .data section, multiple patchable partition guids" << std::endl; std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable partition guids" << std::endl;
return false; return false;
} }
part_guid_offset = i; part_guid_offset = i;
@ -133,15 +133,16 @@ bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const ui
} }
if (disk_guid_offset == std::size_t(-1)) if (disk_guid_offset == std::size_t(-1))
{ {
std::cerr << m_path << ": contains invalid .data section, no patchable disk guid" << std::endl; std::cerr << m_path << ": contains invalid .stage2 section, no patchable disk guid" << std::endl;
return false; return false;
} }
if (part_guid_offset == std::size_t(-1)) if (part_guid_offset == std::size_t(-1))
{ {
std::cerr << m_path << ": contains invalid .data section, no patchable partition guid" << std::endl; std::cerr << m_path << ": contains invalid .stage2 section, no patchable partition guid" << std::endl;
return false; return false;
} }
auto partition = find_partition_with_type(bios_boot_guid); auto partition = find_partition_with_type(bios_boot_guid);
if (!partition.has_value()) if (!partition.has_value())
{ {
@ -151,28 +152,23 @@ bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const ui
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE; const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
std::size_t data_offset = stage2.size(); if (stage2.size() > partition_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; std::cerr << m_path << ": can't fit " << stage2.size() << " bytes of data to partition of size " << partition_size << std::endl;
return false; return false;
} }
uint8_t* partition_start = m_mmap + partition->starting_lba * SECTOR_SIZE; uint8_t* partition_start = m_mmap + partition->starting_lba * SECTOR_SIZE;
memcpy(partition_start, stage2.data(), stage2.size()); memcpy(partition_start, stage2.data(), stage2.size());
memcpy(partition_start + data_offset, data.data(), data.size());
// patch GUIDs // patch GUIDs
*reinterpret_cast<GUID*>(partition_start + data_offset + disk_guid_offset) = gpt_header().disk_guid; *reinterpret_cast<GUID*>(partition_start + disk_guid_offset) = gpt_header().disk_guid;
*reinterpret_cast<GUID*>(partition_start + data_offset + part_guid_offset) = root_partition_guid; *reinterpret_cast<GUID*>(partition_start + part_guid_offset) = root_partition_guid;
return true; 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) bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid)
{ {
if (!find_partition_with_guid(root_partition_guid).has_value()) if (!find_partition_with_guid(root_partition_guid).has_value())
{ {
@ -181,7 +177,7 @@ bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<cons
} }
if (!install_stage1(stage1)) if (!install_stage1(stage1))
return false; return false;
if (!install_stage2(stage2, data, root_partition_guid)) if (!install_stage2(stage2, root_partition_guid))
return false; return false;
return true; return true;
} }

View File

@ -65,7 +65,7 @@ public:
GPTFile(std::string_view path); GPTFile(std::string_view path);
~GPTFile(); ~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); bool install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid);
const GPTHeader& gpt_header() const; const GPTHeader& gpt_header() const;
@ -80,7 +80,7 @@ private:
std::optional<GPTPartitionEntry> find_partition_with_type(const GUID& type_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_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); bool install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid);
private: private:
const std::string m_path; const std::string m_path;

3
bootloader/installer/build.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
g++ -O2 -std=c++20 main.cpp crc32.cpp ELF.cpp GPT.cpp GUID.cpp -o install-bootloader

View File

@ -26,10 +26,9 @@ int main(int argc, char** argv)
auto stage1 = bootloader.find_section(".stage1"sv); auto stage1 = bootloader.find_section(".stage1"sv);
auto stage2 = bootloader.find_section(".stage2"sv); auto stage2 = bootloader.find_section(".stage2"sv);
auto data = bootloader.find_section(".data"sv); if (!stage1.has_value() || !stage2.has_value())
if (!stage1.has_value() || !stage2.has_value() || !data.has_value())
{ {
std::cerr << bootloader.path() << " doesn't contain .stage1, .stage2 and .data sections" << std::endl; std::cerr << bootloader.path() << " doesn't contain .stage1 and .stage2 sections" << std::endl;
return 1; return 1;
} }
@ -37,7 +36,7 @@ int main(int argc, char** argv)
if (!disk_image.success()) if (!disk_image.success())
return 1; return 1;
if (!disk_image.install_bootloader(*stage1, *stage2, *data, *root_partition_guid)) if (!disk_image.install_bootloader(*stage1, *stage2, *root_partition_guid))
return 1; return 1;
std::cout << "bootloader installed" << std::endl; std::cout << "bootloader installed" << std::endl;

View File

@ -1,35 +1,28 @@
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.cpp
kernel/ACPI/AML/Field.cpp
kernel/ACPI/AML/Integer.cpp
kernel/ACPI/AML/NamedObject.cpp
kernel/ACPI/AML/Namespace.cpp
kernel/ACPI/AML/Node.cpp
kernel/ACPI/AML/Package.cpp
kernel/ACPI/AML/Register.cpp
kernel/ACPI/AML/Scope.cpp
kernel/ACPI/AML/String.cpp
kernel/APIC.cpp kernel/APIC.cpp
kernel/BootInfo.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/Errors.cpp kernel/Errors.cpp
kernel/Font.cpp
kernel/FS/DevFS/FileSystem.cpp kernel/FS/DevFS/FileSystem.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
@ -37,15 +30,9 @@ set(KERNEL_SOURCES
kernel/FS/TmpFS/FileSystem.cpp kernel/FS/TmpFS/FileSystem.cpp
kernel/FS/TmpFS/Inode.cpp kernel/FS/TmpFS/Inode.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/Memory/DMARegion.cpp kernel/Memory/DMARegion.cpp
@ -55,29 +42,16 @@ set(KERNEL_SOURCES
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/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/ThreadBlocker.cpp kernel/Semaphore.cpp
kernel/SpinLock.cpp
kernel/SSP.cpp kernel/SSP.cpp
kernel/Storage/ATA/AHCI/Controller.cpp kernel/Storage/ATA/AHCI/Controller.cpp
kernel/Storage/ATA/AHCI/Device.cpp kernel/Storage/ATA/AHCI/Device.cpp
@ -85,36 +59,22 @@ set(KERNEL_SOURCES
kernel/Storage/ATA/ATAController.cpp kernel/Storage/ATA/ATAController.cpp
kernel/Storage/ATA/ATADevice.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/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/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/Device.cpp
kernel/USB/HID/HIDDriver.cpp
kernel/USB/HID/Keyboard.cpp
kernel/USB/HID/Mouse.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)
@ -124,97 +84,100 @@ 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
) )
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
) )
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
) )
set(LIBFONT_SOURCES set(LIBELF_SOURCES
../userspace/libraries/LibFont/Font.cpp ../LibELF/LibELF/LoadableELF.cpp
../userspace/libraries/LibFont/PSF.cpp
)
set(LIBINPUT_SOURCE
../userspace/libraries/LibInput/KeyboardLayout.cpp
../userspace/libraries/LibInput/KeyEvent.cpp
) )
set(KERNEL_SOURCES set(KERNEL_SOURCES
${KERNEL_SOURCES} ${KERNEL_SOURCES}
${LAI_SOURCES}
${BAN_SOURCES} ${BAN_SOURCES}
${KLIBC_SOURCES} ${LIBC_SOURCES}
${LIBFONT_SOURCES} ${LIBELF_SOURCES}
${LIBINPUT_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_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 # This might not work with other toolchains
target_compile_options(kernel PRIVATE target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
-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(kernel-headers
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE) COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND OUTPUT_STRIP_TRAILING_WHITESPACE) COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/lai/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
)
add_custom_target(kernel-install
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/kernel ${BANAN_BOOT}/banan-os.kernel
DEPENDS kernel
)
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
@ -229,22 +192,6 @@ add_custom_command(
# 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 libfont)
banan_include_headers(kernel libelf)
banan_include_headers(kernel libinput)
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} && 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

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

182
kernel/arch/i386/boot.S Normal file
View File

@ -0,0 +1,182 @@
# Declare constants for the multiboot header
.set ALIGN, 1<<0 # align loaded modules on page boundaries
.set MEMINFO, 1<<1 # provide memory map
.set VIDEOINFO, 1<<2 # provide video info
.set MB_FLAGS, ALIGN | MEMINFO | VIDEOINFO # this is the Multiboot 'flag' field
.set MB_MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
.set MB_CHECKSUM, -(MB_MAGIC + MB_FLAGS) #checksum of above, to prove we are multiboot
# Multiboot header
.section .multiboot, "aw"
.align 4
.long MB_MAGIC
.long MB_FLAGS
.long MB_CHECKSUM
.skip 20
.long 0
.long 800
.long 600
.long 32
.section .bss, "aw", @nobits
# Create stack
.global g_boot_stack_bottom
g_boot_stack_bottom:
.skip 16384
.global g_boot_stack_top
g_boot_stack_top:
# 0 MiB -> 1 MiB: bootloader stuff
# 1 MiB -> : kernel
.align 32
boot_page_directory_pointer_table:
.skip 4 * 8
.align 4096
boot_page_directory1:
.skip 512 * 8
.global g_kernel_cmdline
g_kernel_cmdline:
.skip 4096
.global g_multiboot_info
g_multiboot_info:
.skip 4
.global g_multiboot_magic
g_multiboot_magic:
.skip 4
.section .text
boot_gdt:
.quad 0x0000000000000000 # null
.quad 0x00CF9A000000FFFF # kernel code
.quad 0x00CF92000000FFFF # kernel data
boot_gdtr:
.short . - boot_gdt - 1
.long boot_gdt
has_cpuid:
pushfl
pushfl
xorl $0x00200000, (%esp)
popfl
pushfl
popl %eax
xorl (%esp), %eax
popfl
testl $0x00200000, %eax
ret
has_pae:
movl $0, %eax
cpuid
testl $(1 << 6), %edx
ret
has_sse:
movl $1, %eax
cpuid
testl $(1 << 25), %edx
ret
check_requirements:
call has_cpuid
jz .exit
call has_pae
jz .exit
call has_sse
jz .exit
ret
.exit:
jmp system_halt
copy_kernel_commandline:
pushl %esi
pushl %edi
movl g_multiboot_info, %esi
addl $16, %esi
movl (%esi), %esi
movl $1024, %ecx
movl $g_kernel_cmdline, %edi
rep movsl
popl %edi
popl %esi
ret
enable_sse:
movl %cr0, %eax
andw $0xFFFB, %ax
orw $0x0002, %ax
movl %eax, %cr0
movl %cr4, %eax
orw $0x0600, %ax
movl %eax, %cr4
ret
initialize_paging:
# identity map first 6 MiB
movl $(0x00000000 + 0x83), boot_page_directory1 + 0
movl $(0x00200000 + 0x83), boot_page_directory1 + 8
movl $(0x00400000 + 0x83), boot_page_directory1 + 16
movl $(boot_page_directory1 + 0x01), boot_page_directory_pointer_table
# enable PAE
movl %cr4, %ecx
orl $0x20, %ecx
movl %ecx, %cr4
# set address of paging structures
movl $boot_page_directory_pointer_table, %ecx
movl %ecx, %cr3
# enable paging
movl %cr0, %ecx
orl $0x80000000, %ecx
movl %ecx, %cr0
ret
initialize_gdt:
lgdt boot_gdtr
# flush gdt
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
jmp $0x08, $flush
flush:
ret
.global _start
.type _start, @function
_start:
# Initialize stack and multiboot info
movl $g_boot_stack_top, %esp
movl %eax, g_multiboot_magic
movl %ebx, g_multiboot_info
call copy_kernel_commandline
call check_requirements
call enable_sse
call initialize_paging
call initialize_gdt
call _init
# call to the kernel itself (clear ebp for stacktrace)
xorl %ebp, %ebp
call kernel_main
call _fini
system_halt:
xchgw %bx, %bx
cli
1: hlt
jmp 1b

23
kernel/arch/i386/crt0.S Normal file
View File

@ -0,0 +1,23 @@
.section .text
.global _start
_start:
# Set up end of the stack frame linked list.
movl $0, %ebp
pushl %ebp # rip=0
pushl %ebp # rbp=0
movl %esp, %ebp
# Prepare signals, memory allocation, stdio and such.
#call initialize_standard_library
# Run the global constructors.
call _init
# Run main
call main
# Terminate the process with the exit code.
movl %eax, %edi
call exit
.size _start, . - _start

View File

@ -1,9 +1,9 @@
/* i686 crti.s */ /* x86 crti.s */
.section .init .section .init
.global _init .global _init
.type _init, @function .type _init, @function
_init: _init:
pushl %ebp push %ebp
movl %esp, %ebp movl %esp, %ebp
/* gcc will nicely put the contents of crtbegin.o's .init section here. */ /* gcc will nicely put the contents of crtbegin.o's .init section here. */
@ -11,6 +11,6 @@ _init:
.global _fini .global _fini
.type _fini, @function .type _fini, @function
_fini: _fini:
pushl %ebp push %ebp
movl %esp, %ebp movl %esp, %ebp
/* gcc will nicely put the contents of crtbegin.o's .fini section here. */ /* gcc will nicely put the contents of crtbegin.o's .fini section here. */

View File

@ -1,4 +1,4 @@
/* i686 crtn.s */ /* x86 crtn.s */
.section .init .section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */ /* gcc will nicely put the contents of crtend.o's .init section here. */
popl %ebp popl %ebp

View File

@ -0,0 +1,28 @@
ENTRY (_start)
SECTIONS
{
. = 0x00100000;
g_kernel_start = .;
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata.*)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
g_kernel_end = .;
}

View File

@ -1,682 +0,0 @@
#include <kernel/BootInfo.h>
#include <kernel/CPUID.h>
#include <kernel/Lock/SpinLock.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Memory/PageTable.h>
extern uint8_t g_kernel_start[];
extern uint8_t g_kernel_end[];
extern uint8_t g_kernel_execute_start[];
extern uint8_t g_kernel_execute_end[];
extern uint8_t g_kernel_writable_start[];
extern uint8_t g_kernel_writable_end[];
extern uint8_t g_userspace_start[];
extern uint8_t g_userspace_end[];
namespace Kernel
{
SpinLock PageTable::s_fast_page_lock;
static PageTable* s_kernel = nullptr;
static bool s_has_nxe = false;
static bool s_has_pge = false;
static bool s_has_pat = false;
static paddr_t s_global_pdpte = 0;
static inline PageTable::flags_t parse_flags(uint64_t entry)
{
using Flags = PageTable::Flags;
PageTable::flags_t result = 0;
if (s_has_nxe && !(entry & (1ull << 63)))
result |= Flags::Execute;
if (entry & Flags::Reserved)
result |= Flags::Reserved;
if (entry & Flags::UserSupervisor)
result |= Flags::UserSupervisor;
if (entry & Flags::ReadWrite)
result |= Flags::ReadWrite;
if (entry & Flags::Present)
result |= Flags::Present;
return result;
}
void PageTable::initialize_pre_heap()
{
if (CPUID::has_nxe())
s_has_nxe = true;
if (CPUID::has_pge())
s_has_pge = true;
if (CPUID::has_pat())
s_has_pat = true;
ASSERT(s_kernel == nullptr);
s_kernel = new PageTable();
ASSERT(s_kernel);
s_kernel->initialize_kernel();
s_kernel->initial_load();
}
void PageTable::initialize_post_heap()
{
// NOTE: this is no-op as our 32 bit target does not use hhdm
}
void PageTable::initial_load()
{
if (s_has_nxe)
{
asm volatile(
"movl $0xC0000080, %%ecx;"
"rdmsr;"
"orl $0x800, %%eax;"
"wrmsr"
::: "eax", "ecx", "edx", "memory"
);
}
if (s_has_pge)
{
asm volatile(
"movl %%cr4, %%eax;"
"orl $0x80, %%eax;"
"movl %%eax, %%cr4;"
::: "eax"
);
}
if (s_has_pat)
{
asm volatile(
"movl $0x277, %%ecx;"
"rdmsr;"
"movw $0x0401, %%dx;"
"wrmsr;"
::: "eax", "ecx", "edx", "memory"
);
}
// enable write protect
asm volatile(
"movl %%cr0, %%eax;"
"orl $0x10000, %%eax;"
"movl %%eax, %%cr0;"
::: "rax"
);
load();
}
PageTable& PageTable::kernel()
{
ASSERT(s_kernel);
return *s_kernel;
}
bool PageTable::is_valid_pointer(uintptr_t)
{
return true;
}
static uint64_t* allocate_zeroed_page_aligned_page()
{
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
ASSERT(page);
memset(page, 0, PAGE_SIZE);
return (uint64_t*)page;
}
template<typename T>
static paddr_t V2P(const T vaddr)
{
return (vaddr_t)vaddr - KERNEL_OFFSET + g_boot_info.kernel_paddr;
}
template<typename T>
static vaddr_t P2V(const T paddr)
{
return (paddr_t)paddr - g_boot_info.kernel_paddr + KERNEL_OFFSET;
}
void PageTable::initialize_kernel()
{
ASSERT(s_global_pdpte == 0);
s_global_pdpte = V2P(allocate_zeroed_page_aligned_page());
map_kernel_memory();
prepare_fast_page();
// Map (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
ASSERT((vaddr_t)g_kernel_start % PAGE_SIZE == 0);
map_range_at(
V2P(g_kernel_start),
(vaddr_t)g_kernel_start,
g_kernel_end - g_kernel_start,
Flags::Present
);
// Map executable kernel memory as executable
map_range_at(
V2P(g_kernel_execute_start),
(vaddr_t)g_kernel_execute_start,
g_kernel_execute_end - g_kernel_execute_start,
Flags::Execute | Flags::Present
);
// Map writable kernel memory as writable
map_range_at(
V2P(g_kernel_writable_start),
(vaddr_t)g_kernel_writable_start,
g_kernel_writable_end - g_kernel_writable_start,
Flags::ReadWrite | Flags::Present
);
// Map userspace memory
map_range_at(
V2P(g_userspace_start),
(vaddr_t)g_userspace_start,
g_userspace_end - g_userspace_start,
Flags::Execute | Flags::UserSupervisor | Flags::Present
);
}
void PageTable::prepare_fast_page()
{
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
ASSERT(pdpt[pdpte] & Flags::Present);
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte]) & PAGE_ADDR_MASK);
ASSERT(!(pd[pde] & Flags::Present));
pd[pde] = V2P(allocate_zeroed_page_aligned_page()) | Flags::ReadWrite | Flags::Present;
}
void PageTable::map_fast_page(paddr_t paddr)
{
ASSERT(s_kernel);
ASSERT(paddr);
ASSERT(s_fast_page_lock.current_processor_has_lock());
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(s_kernel->m_highest_paging_struct));
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
ASSERT(!(pt[pte] & Flags::Present));
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
invalidate(fast_page(), false);
}
void PageTable::unmap_fast_page()
{
ASSERT(s_kernel);
ASSERT(s_fast_page_lock.current_processor_has_lock());
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(s_kernel->m_highest_paging_struct));
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
ASSERT(pt[pte] & Flags::Present);
pt[pte] = 0;
invalidate(fast_page(), false);
}
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
{
SpinLockGuard _(s_kernel->m_lock);
PageTable* page_table = new PageTable;
if (page_table == nullptr)
return BAN::Error::from_errno(ENOMEM);
page_table->map_kernel_memory();
return page_table;
}
void PageTable::map_kernel_memory()
{
ASSERT(s_kernel);
ASSERT(s_global_pdpte);
ASSERT(m_highest_paging_struct == 0);
m_highest_paging_struct = V2P(kmalloc(32, 32, true));
ASSERT(m_highest_paging_struct);
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
pdpt[0] = 0;
pdpt[1] = 0;
pdpt[2] = 0;
pdpt[3] = s_global_pdpte | Flags::Present;
static_assert(KERNEL_OFFSET == 0xC0000000);
}
PageTable::~PageTable()
{
if (m_highest_paging_struct == 0)
return;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
for (uint32_t pdpte = 0; pdpte < 3; pdpte++)
{
if (!(pdpt[pdpte] & Flags::Present))
continue;
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
for (uint32_t pde = 0; pde < 512; pde++)
{
if (!(pd[pde] & Flags::Present))
continue;
kfree(reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK)));
}
kfree(pd);
}
kfree(pdpt);
}
void PageTable::load()
{
SpinLockGuard _(m_lock);
ASSERT(m_highest_paging_struct < 0x100000000);
const uint32_t pdpt_lo = m_highest_paging_struct;
asm volatile("movl %0, %%cr3" :: "r"(pdpt_lo));
Processor::set_current_page_table(this);
}
void PageTable::invalidate(vaddr_t vaddr, bool send_smp_message)
{
ASSERT(vaddr % PAGE_SIZE == 0);
asm volatile("invlpg (%0)" :: "r"(vaddr) : "memory");
if (send_smp_message)
{
Processor::broadcast_smp_message({
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = 1
}
});
}
}
void PageTable::unmap_page(vaddr_t vaddr, bool send_smp_message)
{
ASSERT(vaddr);
ASSERT(vaddr % PAGE_SIZE == 0);
ASSERT(vaddr != fast_page());
if (vaddr >= KERNEL_OFFSET)
ASSERT(vaddr >= (vaddr_t)g_kernel_start);
if ((vaddr >= KERNEL_OFFSET) != (this == s_kernel))
Kernel::panic("unmapping {8H}, kernel: {}", vaddr, this == s_kernel);
const uint64_t pdpte = (vaddr >> 30) & 0x1FF;
const uint64_t pde = (vaddr >> 21) & 0x1FF;
const uint64_t pte = (vaddr >> 12) & 0x1FF;
SpinLockGuard _(m_lock);
if (is_page_free(vaddr))
Kernel::panic("trying to unmap unmapped page 0x{H}", vaddr);
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
pt[pte] = 0;
invalidate(vaddr, send_smp_message);
}
void PageTable::unmap_range(vaddr_t vaddr, size_t size)
{
ASSERT(vaddr % PAGE_SIZE == 0);
size_t page_count = range_page_count(vaddr, size);
SpinLockGuard _(m_lock);
for (vaddr_t page = 0; page < page_count; page++)
unmap_page(vaddr + page * PAGE_SIZE, false);
Processor::broadcast_smp_message({
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
}
});
}
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type, bool send_smp_message)
{
ASSERT(vaddr);
ASSERT(vaddr != fast_page());
if ((vaddr >= KERNEL_OFFSET) != (this == s_kernel))
Kernel::panic("mapping {8H} to {8H}, kernel: {}", paddr, vaddr, this == s_kernel);
ASSERT(paddr % PAGE_SIZE == 0);
ASSERT(vaddr % PAGE_SIZE == 0);
ASSERT(flags & Flags::Used);
const uint64_t pdpte = (vaddr >> 30) & 0x1FF;
const uint64_t pde = (vaddr >> 21) & 0x1FF;
const uint64_t pte = (vaddr >> 12) & 0x1FF;
uint64_t extra_flags = 0;
if (s_has_pge && vaddr >= KERNEL_OFFSET) // Map kernel memory as global
extra_flags |= 1ull << 8;
if (s_has_nxe && !(flags & Flags::Execute))
extra_flags |= 1ull << 63;
if (flags & Flags::Reserved)
extra_flags |= Flags::Reserved;
if (memory_type == MemoryType::Uncached)
extra_flags |= (1ull << 4);
if (s_has_pat && memory_type == MemoryType::WriteCombining)
extra_flags |= (1ull << 7);
if (s_has_pat && memory_type == MemoryType::WriteThrough)
extra_flags |= (1ull << 7) | (1ull << 3);
// NOTE: we add present here, since it has to be available in higher level structures
flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present;
SpinLockGuard _(m_lock);
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
if (!(pdpt[pdpte] & Flags::Present))
pdpt[pdpte] = V2P(allocate_zeroed_page_aligned_page()) | Flags::Present;
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
if ((pd[pde] & uwr_flags) != uwr_flags)
{
if (!(pd[pde] & Flags::Present))
pd[pde] = V2P(allocate_zeroed_page_aligned_page());
pd[pde] |= uwr_flags;
}
if (!(flags & Flags::Present))
uwr_flags &= ~Flags::Present;
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
pt[pte] = paddr | uwr_flags | extra_flags;
invalidate(vaddr, send_smp_message);
}
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
{
ASSERT(vaddr);
ASSERT(paddr % PAGE_SIZE == 0);
ASSERT(vaddr % PAGE_SIZE == 0);
size_t page_count = range_page_count(vaddr, size);
SpinLockGuard _(m_lock);
for (size_t page = 0; page < page_count; page++)
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags, memory_type, false);
Processor::broadcast_smp_message({
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
}
});
}
uint64_t PageTable::get_page_data(vaddr_t vaddr) const
{
ASSERT(vaddr % PAGE_SIZE == 0);
const uint64_t pdpte = (vaddr >> 30) & 0x1FF;
const uint64_t pde = (vaddr >> 21) & 0x1FF;
const uint64_t pte = (vaddr >> 12) & 0x1FF;
SpinLockGuard _(m_lock);
uint64_t* pdpt = (uint64_t*)P2V(m_highest_paging_struct);
if (!(pdpt[pdpte] & Flags::Present))
return 0;
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
if (!(pd[pde] & Flags::Present))
return 0;
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
if (!(pt[pte] & Flags::Used))
return 0;
return pt[pte];
}
PageTable::flags_t PageTable::get_page_flags(vaddr_t vaddr) const
{
return parse_flags(get_page_data(vaddr));
}
paddr_t PageTable::physical_address_of(vaddr_t vaddr) const
{
uint64_t page_data = get_page_data(vaddr);
return (page_data & PAGE_ADDR_MASK) & ~(1ull << 63);
}
bool PageTable::is_page_free(vaddr_t vaddr) const
{
ASSERT(vaddr % PAGE_SIZE == 0);
return !(get_page_flags(vaddr) & Flags::Used);
}
bool PageTable::is_range_free(vaddr_t vaddr, size_t size) const
{
vaddr_t s_page = vaddr / PAGE_SIZE;
vaddr_t e_page = BAN::Math::div_round_up<vaddr_t>(vaddr + size, PAGE_SIZE);
SpinLockGuard _(m_lock);
for (vaddr_t page = s_page; page < e_page; page++)
if (!is_page_free(page * PAGE_SIZE))
return false;
return true;
}
bool PageTable::reserve_page(vaddr_t vaddr, bool only_free)
{
SpinLockGuard _(m_lock);
ASSERT(vaddr % PAGE_SIZE == 0);
if (only_free && !is_page_free(vaddr))
return false;
map_page_at(0, vaddr, Flags::Reserved);
return true;
}
bool PageTable::reserve_range(vaddr_t vaddr, size_t bytes, bool only_free)
{
if (size_t rem = bytes % PAGE_SIZE)
bytes += PAGE_SIZE - rem;
ASSERT(vaddr % PAGE_SIZE == 0);
SpinLockGuard _(m_lock);
if (only_free && !is_range_free(vaddr, bytes))
return false;
for (size_t offset = 0; offset < bytes; offset += PAGE_SIZE)
reserve_page(vaddr + offset);
return true;
}
vaddr_t PageTable::reserve_free_page(vaddr_t first_address, vaddr_t last_address)
{
if (first_address >= KERNEL_OFFSET && first_address < (vaddr_t)g_kernel_end)
first_address = (vaddr_t)g_kernel_end;
if (size_t rem = first_address % PAGE_SIZE)
first_address += PAGE_SIZE - rem;
if (size_t rem = last_address % PAGE_SIZE)
last_address -= rem;
const uint32_t s_pdpte = (first_address >> 30) & 0x1FF;
const uint32_t s_pde = (first_address >> 21) & 0x1FF;
const uint32_t s_pte = (first_address >> 12) & 0x1FF;
const uint32_t e_pdpte = (last_address >> 30) & 0x1FF;
const uint32_t e_pde = (last_address >> 21) & 0x1FF;
const uint32_t e_pte = (last_address >> 12) & 0x1FF;
SpinLockGuard _(m_lock);
// Try to find free page that can be mapped without
// allocations (page table with unused entries)
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
for (uint32_t pdpte = s_pdpte; pdpte < 4; pdpte++)
{
if (pdpte > e_pdpte)
break;
if (!(pdpt[pdpte] & Flags::Present))
continue;
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
for (uint32_t pde = s_pde; pde < 512; pde++)
{
if (pdpte == e_pdpte && pde > e_pde)
break;
if (!(pd[pde] & Flags::Present))
continue;
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
for (uint32_t pte = s_pte; pte < 512; pte++)
{
if (pdpte == e_pdpte && pde == e_pde && pte >= e_pte)
break;
if (!(pt[pte] & Flags::Used))
{
vaddr_t vaddr = 0;
vaddr |= (vaddr_t)pdpte << 30;
vaddr |= (vaddr_t)pde << 21;
vaddr |= (vaddr_t)pte << 12;
ASSERT(reserve_page(vaddr));
return vaddr;
}
}
}
}
// Find any free page
for (vaddr_t vaddr = first_address; vaddr < last_address; vaddr += PAGE_SIZE)
{
if (is_page_free(vaddr))
{
ASSERT(reserve_page(vaddr));
return vaddr;
}
}
ASSERT_NOT_REACHED();
}
vaddr_t PageTable::reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address)
{
if (first_address >= KERNEL_OFFSET && first_address < (vaddr_t)g_kernel_start)
first_address = (vaddr_t)g_kernel_start;
if (size_t rem = first_address % PAGE_SIZE)
first_address += PAGE_SIZE - rem;
if (size_t rem = last_address % PAGE_SIZE)
last_address -= rem;
SpinLockGuard _(m_lock);
for (vaddr_t vaddr = first_address; vaddr < last_address;)
{
bool valid { true };
for (size_t page = 0; page < page_count; page++)
{
if (!is_page_free(vaddr + page * PAGE_SIZE))
{
vaddr += (page + 1) * PAGE_SIZE;
valid = false;
break;
}
}
if (valid)
{
ASSERT(reserve_range(vaddr, page_count * PAGE_SIZE));
return vaddr;
}
}
ASSERT_NOT_REACHED();
}
static void dump_range(vaddr_t start, vaddr_t end, PageTable::flags_t flags)
{
if (start == 0)
return;
dprintln("{}-{}: {}{}{}{}",
(void*)(start), (void*)(end - 1),
flags & PageTable::Flags::Execute ? 'x' : '-',
flags & PageTable::Flags::UserSupervisor ? 'u' : '-',
flags & PageTable::Flags::ReadWrite ? 'w' : '-',
flags & PageTable::Flags::Present ? 'r' : '-'
);
}
void PageTable::debug_dump()
{
SpinLockGuard _(m_lock);
flags_t flags = 0;
vaddr_t start = 0;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
for (uint32_t pdpte = 0; pdpte < 4; pdpte++)
{
if (!(pdpt[pdpte] & Flags::Present))
{
dump_range(start, (pdpte << 30), flags);
start = 0;
continue;
}
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
for (uint64_t pde = 0; pde < 512; pde++)
{
if (!(pd[pde] & Flags::Present))
{
dump_range(start, (pdpte << 30) | (pde << 21), flags);
start = 0;
continue;
}
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
for (uint64_t pte = 0; pte < 512; pte++)
{
if (parse_flags(pt[pte]) != flags)
{
dump_range(start, (pdpte << 30) | (pde << 21) | (pte << 12), flags);
start = 0;
}
if (!(pt[pte] & Flags::Used))
continue;
if (start == 0)
{
flags = parse_flags(pt[pte]);
start = (pdpte << 30) | (pde << 21) | (pte << 12);
}
}
}
}
}
}

View File

@ -1,35 +0,0 @@
.section .userspace, "ax"
// stack contains
// return address
// signal number
// signal handler
.global signal_trampoline
signal_trampoline:
pushl %ebp
movl %esp, %ebp
pusha
movl 40(%esp), %edi
movl 36(%esp), %eax
// align stack to 16 bytes
movl %esp, %ebx
andl $0x0F, %ebx
subl %ebx, %esp
subl $12, %esp
pushl %edi
call *%eax
addl $16, %esp
// restore stack
addl %ebx, %esp
popa
leave
addl $8, %esp
ret

View File

@ -1,92 +0,0 @@
// arguments in EAX, EBX, ECX, EDX, ESI, EDI
.global asm_syscall_handler
asm_syscall_handler:
# save segment registers
pushw %ds
pushw %es
pushw %fs
pushw %gs
# save general purpose registers
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
# align stack
movl %esp, %ebp
subl $15, %esp
andl $0xFFFFFFF0, %esp
# push arguments
subl $4, %esp
pushl %ebp
addl $32, (%esp)
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
# load kernel segments
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw $0x28, %ax
movw %ax, %gs
call cpp_syscall_handler
movl %ebp, %esp
# restore general purpose registers
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
# restore segment registers
popw %gs
popw %fs
popw %es
popw %ds
iret
.global sys_fork_trampoline
sys_fork_trampoline:
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
call read_ip
testl %eax, %eax
jz .reload_stack
movl %esp, %ebx
subl $8, %esp
pushl %eax
pushl %ebx
call sys_fork
addl $16, %esp
.done:
popl %edi
popl %esi
popl %ebx
popl %ebp
ret
.reload_stack:
call get_thread_start_sp
movl %eax, %esp
xorl %eax, %eax
jmp .done

View File

@ -1,69 +0,0 @@
# uint32_t read_ip()
.global read_ip
read_ip:
popl %eax
jmp *%eax
# void start_kernel_thread()
.global start_kernel_thread
start_kernel_thread:
call get_thread_start_sp
movl %eax, %esp
# STACK LAYOUT
# on_exit arg
# on_exit func
# entry arg
# entry func
movl 4(%esp), %edi
movl 0(%esp), %esi
subl $12, %esp
pushl %edi
sti
call *%esi
addl $16, %esp
movl 12(%esp), %edi
movl 8(%esp), %esi
subl $12, %esp
pushl %edi
call *%esi
addl $16, %esp
.global start_userspace_thread
start_userspace_thread:
call get_thread_start_sp
movl %eax, %esp
# STACK LAYOUT
# entry
# argc
# argv
# envp
# userspace stack
call get_userspace_thread_stack_top
movw $(0x20 | 3), %bx
movw %bx, %ds
movw %bx, %es
movw %bx, %fs
movw %bx, %gs
xorw %bx, %bx
popl %edi
popl %esi
popl %edx
popl %ecx
popl %ebx
pushl $(0x20 | 3)
pushl %eax
pushl $0x202
pushl $(0x18 | 3)
pushl %ebx
iret

View File

@ -1,316 +0,0 @@
.set PG_PRESENT, 1<<0
.set PG_READ_WRITE, 1<<1
.set PG_PAGE_SIZE, 1<<7
.set FB_WIDTH, 800
.set FB_HEIGHT, 600
.set FB_BPP, 32
#define KERNEL_OFFSET 0xC0000000
#define V2P(vaddr) ((vaddr) - KERNEL_OFFSET)
.code32
# multiboot2 header
.section .multiboot, "aw"
.align 8
multiboot2_start:
.long 0xE85250D6
.long 0
.long multiboot2_end - multiboot2_start
.long -(0xE85250D6 + (multiboot2_end - multiboot2_start))
# framebuffer tag
.align 8
.short 5
.short 0
.long 20
.long FB_WIDTH
.long FB_HEIGHT
.long FB_BPP
# legacy start
.align 8
.short 3
.short 0
.long 12
.long V2P(_start)
.align 8
.short 0
.short 0
.long 8
multiboot2_end:
.section .bananboot, "aw"
.align 8
bananboot_start:
.long 0xBABAB007
.long -(0xBABAB007 + FB_WIDTH + FB_HEIGHT + FB_BPP)
.long FB_WIDTH
.long FB_HEIGHT
.long FB_BPP
bananboot_end:
.section .bss, "aw", @nobits
.align 4096
boot_stack_bottom:
.skip 4096 * 4
boot_stack_top:
.global g_kernel_cmdline
g_kernel_cmdline:
.skip 4096
bootloader_magic:
.skip 8
bootloader_info:
.skip 8
.section .data
# Map first GiB to 0x00000000 and 0xC0000000
.align 32
boot_pdpt:
.long V2P(boot_pd) + (PG_PRESENT)
.long 0
.quad 0
.quad 0
.long V2P(boot_pd) + (PG_PRESENT)
.long 0
.align 4096
boot_pd:
.set i, 0
.rept 512
.long V2P(boot_pts) + i + (PG_READ_WRITE | PG_PRESENT)
.long 0
.set i, i + 0x1000
.endr
boot_pts:
.set i, 0
.rept 512
.rept 512
.long i + (PG_READ_WRITE | PG_PRESENT)
.long 0
.set i, i + 0x1000
.endr
.endr
boot_gdt:
.quad 0x0000000000000000 # null descriptor
.quad 0x00CF9A000000FFFF # kernel code
.quad 0x00CF92000000FFFF # kernel data
boot_gdtr:
.short . - boot_gdt - 1
.long V2P(boot_gdt)
.global g_ap_startup_done
g_ap_startup_done:
.byte 0
.global g_ap_running_count
g_ap_running_count:
.byte 0
.global g_ap_stack_loaded
g_ap_stack_loaded:
.byte 0
.section .text
has_cpuid:
pushfl
pushfl
xorl $0x00200000, (%esp)
popfl
pushfl
popl %eax
xorl (%esp), %eax
popfl
testl $0x00200000, %eax
ret
has_pae:
movl $0, %eax
cpuid
testl $(1 << 6), %edx
ret
has_sse:
movl $1, %eax
cpuid
testl $(1 << 25), %edx
ret
check_requirements:
call has_cpuid
jz .exit
call has_pae
jz .exit
call has_sse
jz .exit
ret
.exit:
jmp system_halt
enable_sse:
movl %cr0, %eax
andw $0xFFFB, %ax
orw $0x0002, %ax
movl %eax, %cr0
movl %cr4, %eax
orw $0x0600, %ax
movl %eax, %cr4
ret
initialize_paging:
# enable PAE
movl %cr4, %ecx
orl $(1 << 5), %ecx
movl %ecx, %cr4
# load page tables
movl $V2P(boot_pdpt), %ecx
movl %ecx, %cr3
# enable paging
movl %cr0, %ecx
orl $(1 << 31), %ecx
movl %ecx, %cr0
ret
.global _start
.type _start, @function
_start:
cli; cld
# save bootloader magic and info
movl %eax, V2P(bootloader_magic)
movl %ebx, V2P(bootloader_info)
# load boot stack
movl $V2P(boot_stack_top), %esp
# load boot GDT
lgdt V2P(boot_gdtr)
ljmpl $0x08, $V2P(gdt_flush)
gdt_flush:
# set correct segment registers
movw $0x10, %ax
movw %ax, %ds
movw %ax, %ss
movw %ax, %es
# do processor initialization
call check_requirements
call enable_sse
call initialize_paging
# load higher half stack pointer
movl $boot_stack_top, %esp
# jump to higher half
leal higher_half, %ecx
jmp *%ecx
higher_half:
# call global constuctors
call _init
movl $g_init_array_start, %ebx
jmp 2f
1: call *(%ebx)
addl $4, %ebx
2: cmpl $g_init_array_end, %ebx
jne 1b
# call to the kernel itself (clear ebp for stacktrace)
xorl %ebp, %ebp
subl $8, %esp
pushl bootloader_info
pushl bootloader_magic
call kernel_main
addl $16, %esp
# call global destructors
call _fini
system_halt:
xchgw %bx, %bx
cli
1: hlt
jmp 1b
#define AP_V2P(vaddr) ((vaddr) - ap_trampoline + 0xF000)
.section .ap_init, "ax"
.code16
.global ap_trampoline
ap_trampoline:
jmp 1f
.align 8
ap_stack_ptr:
.skip 4
ap_stack_loaded:
.skip 1
1: cli; cld
ljmpl $0x00, $AP_V2P(ap_cs_clear)
ap_cs_clear:
# load ap gdt and enter protected mode
lgdt AP_V2P(ap_gdtr)
movl %cr0, %eax
orb $1, %al
movl %eax, %cr0
ljmpl $0x08, $AP_V2P(ap_protected_mode)
.code32
ap_protected_mode:
movw $0x10, %ax
movw %ax, %ds
movw %ax, %ss
movw %ax, %es
movl AP_V2P(ap_stack_ptr), %esp
movb $1, AP_V2P(ap_stack_loaded)
leal V2P(enable_sse), %ecx; call *%ecx
leal V2P(initialize_paging), %ecx; call *%ecx
# load boot gdt and enter long mode
lgdt V2P(boot_gdtr)
ljmpl $0x08, $AP_V2P(ap_flush_gdt)
ap_flush_gdt:
# move stack pointer to higher half
movl %esp, %esp
addl $KERNEL_OFFSET, %esp
# jump to higher half
leal ap_higher_half, %ecx
jmp *%ecx
ap_higher_half:
# clear rbp for stacktrace
xorl %ebp, %ebp
1: pause
cmpb $0, g_ap_startup_done
jz 1b
lock incb g_ap_running_count
call ap_main
jmp system_halt
ap_gdt:
.quad 0x0000000000000000 # null descriptor
.quad 0x00CF9A000000FFFF # 32 bit code
.quad 0x00CF92000000FFFF # 32 bit data
ap_gdtr:
.short . - ap_gdt - 1
.long ap_gdt

View File

@ -1,396 +0,0 @@
.macro push_userspace
pushw %gs
pushw %fs
pushw %es
pushw %ds
pushal
.endm
.macro load_kernel_segments
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw $0x28, %ax
movw %ax, %gs
.endm
.macro pop_userspace
popal
popw %ds
popw %es
popw %fs
popw %gs
.endm
isr_stub:
push_userspace
load_kernel_segments
movl %cr0, %eax; pushl %eax
movl %cr2, %eax; pushl %eax
movl %cr3, %eax; pushl %eax
movl %cr4, %eax; pushl %eax
movl %esp, %eax // register ptr
leal 64(%esp), %ebx // interrupt stack ptr
movl 60(%esp), %ecx // error code
movl 56(%esp), %edx // isr number
movl %esp, %ebp
subl $15, %esp
andl $0xFFFFFFF0, %esp
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
call cpp_isr_handler
movl %ebp, %esp
addl $16, %esp
pop_userspace
addl $8, %esp
iret
irq_stub:
push_userspace
load_kernel_segments
movl 40(%esp), %eax # interrupt number
movl %esp, %ebp
subl $15, %esp
andl $0xFFFFFFF0, %esp
subl $12, %esp
pushl %eax
call cpp_irq_handler
movl %ebp, %esp
pop_userspace
addl $8, %esp
iret
.global asm_yield_handler
asm_yield_handler:
# This can only be called from kernel, so no segment saving is needed
pushal
movl %esp, %eax # interrupt registers ptr
leal 32(%esp), %ebx # interrupt stack ptr
movl %esp, %ebp
subl $15, %esp
andl $0xFFFFFFF0, %esp
subl $8, %esp
pushl %eax
pushl %ebx
call cpp_yield_handler
movl %ebp, %esp
popal
iret
.global asm_ipi_handler
asm_ipi_handler:
push_userspace
load_kernel_segments
movl %esp, %ebp
subl $15, %esp
andl $0xFFFFFFF0, %esp
call cpp_ipi_handler
movl %ebp, %esp
pop_userspace
iret
.global asm_timer_handler
asm_timer_handler:
push_userspace
load_kernel_segments
movl %esp, %ebp
subl $15, %esp
andl $0xFFFFFFF0, %esp
call cpp_timer_handler
movl %ebp, %esp
pop_userspace
iret
.macro isr n
.global isr\n
isr\n:
pushl $0
pushl $\n
jmp isr_stub
.endm
.macro isr_err n
.global isr\n
isr\n:
pushl $\n
jmp isr_stub
.endm
.macro irq n
.global irq\n
irq\n:
pushl $0
pushl $\n
jmp irq_stub
.endm
isr 0
isr 1
isr 2
isr 3
isr 4
isr 5
isr 6
isr 7
isr_err 8
isr 9
isr_err 10
isr_err 11
isr_err 12
isr_err 13
isr_err 14
isr 15
isr 16
isr_err 17
isr 18
isr 19
isr 20
isr 21
isr 22
isr 23
isr 24
isr 25
isr 26
isr 27
isr 28
isr 29
isr 30
isr 31
irq 0
irq 1
irq 2
irq 3
irq 4
irq 5
irq 6
irq 7
irq 8
irq 9
irq 10
irq 11
irq 12
irq 13
irq 14
irq 15
irq 16
irq 17
irq 18
irq 19
irq 20
irq 21
irq 22
irq 23
irq 24
irq 25
irq 26
irq 27
irq 28
irq 29
irq 30
irq 31
irq 32
irq 33
irq 34
irq 35
irq 36
irq 37
irq 38
irq 39
irq 40
irq 41
irq 42
irq 43
irq 44
irq 45
irq 46
irq 47
irq 48
irq 49
irq 50
irq 51
irq 52
irq 53
irq 54
irq 55
irq 56
irq 57
irq 58
irq 59
irq 60
irq 61
irq 62
irq 63
irq 64
irq 65
irq 66
irq 67
irq 68
irq 69
irq 70
irq 71
irq 72
irq 73
irq 74
irq 75
irq 76
irq 77
irq 78
irq 79
irq 80
irq 81
irq 82
irq 83
irq 84
irq 85
irq 86
irq 87
irq 88
irq 89
irq 90
irq 91
irq 92
irq 93
irq 94
irq 95
irq 96
irq 97
irq 98
irq 99
irq 100
irq 101
irq 102
irq 103
irq 104
irq 105
irq 106
irq 107
irq 108
irq 109
irq 110
irq 111
irq 112
irq 113
irq 114
irq 115
irq 116
irq 117
irq 118
irq 119
irq 120
irq 121
irq 122
irq 123
irq 124
irq 125
irq 126
irq 127
irq 128
irq 129
irq 130
irq 131
irq 132
irq 133
irq 134
irq 135
irq 136
irq 137
irq 138
irq 139
irq 140
irq 141
irq 142
irq 143
irq 144
irq 145
irq 146
irq 147
irq 148
irq 149
irq 150
irq 151
irq 152
irq 153
irq 154
irq 155
irq 156
irq 157
irq 158
irq 159
irq 160
irq 161
irq 162
irq 163
irq 164
irq 165
irq 166
irq 167
irq 168
irq 169
irq 170
irq 171
irq 172
irq 173
irq 174
irq 175
irq 176
irq 177
irq 178
irq 179
irq 180
irq 181
irq 182
irq 183
irq 184
irq 185
irq 186
irq 187
irq 188
irq 189
irq 190
irq 191
irq 192
irq 193
irq 194
irq 195
irq 196
irq 197
irq 198
irq 199
irq 200
irq 201
irq 202
irq 203
irq 204
irq 205
irq 206
irq 207

View File

@ -1,51 +0,0 @@
ENTRY (_start)
KERNEL_OFFSET = 0xC0000000;
SECTIONS
{
. = 0x00100000 + KERNEL_OFFSET;
g_kernel_start = .;
.text ALIGN(4K) : AT(ADDR(.text) - KERNEL_OFFSET)
{
g_kernel_execute_start = .;
*(.multiboot)
*(.bananboot)
*(.text.*)
}
.userspace ALIGN(4K) : AT(ADDR(.userspace) - KERNEL_OFFSET)
{
g_userspace_start = .;
*(.userspace)
g_userspace_end = .;
g_kernel_execute_end = .;
}
.ap_init ALIGN(4K) : AT(ADDR(.ap_init) - KERNEL_OFFSET)
{
g_ap_init_addr = .;
*(.ap_init)
}
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET)
{
*(.rodata.*)
}
.init_array ALIGN(4K) : AT(ADDR(.init_array) - KERNEL_OFFSET)
{
g_init_array_start = .;
*(.init_array)
g_init_array_end = .;
}
.data ALIGN(4K) : AT(ADDR(.data) - KERNEL_OFFSET)
{
g_kernel_writable_start = .;
*(.data)
}
.bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_OFFSET)
{
*(COMMON)
*(.bss)
g_kernel_writable_end = .;
}
g_kernel_end = .;
}

125
kernel/arch/x86_64/GDT.cpp Normal file
View File

@ -0,0 +1,125 @@
#include <BAN/Array.h>
#include <kernel/GDT.h>
#include <string.h>
extern "C" uintptr_t g_boot_stack_top[0];
namespace Kernel::GDT
{
struct TaskStateSegment
{
uint32_t reserved1;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t reserved2;
uint64_t ist1;
uint64_t ist2;
uint64_t ist3;
uint64_t ist4;
uint64_t ist5;
uint64_t ist6;
uint64_t ist7;
uint64_t reserved3;
uint16_t reserved4;
uint16_t iopb;
} __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;
uint64_t address;
} __attribute__((packed));
static constexpr uint16_t s_tss_offset = 0x28;
static TaskStateSegment s_tss;
static BAN::Array<SegmentDescriptor, 7> s_gdt; // null, kernel code, kernel data, user code, user data, tss low, tss high
static GDTR s_gdtr;
static void write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
{
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
SegmentDescriptor& desc = s_gdt[offset / sizeof(SegmentDescriptor)];
desc.base1 = (base >> 0) & 0xFFFF;
desc.base2 = (base >> 16) & 0xFF;
desc.base3 = (base >> 24) & 0xFF;
desc.limit1 = (limit >> 0) & 0xFFFF;
desc.limit2 = (limit >> 16) & 0x0F;
desc.access = access & 0xFF;
desc.flags = flags & 0x0F;
}
static void write_tss()
{
memset(&s_tss, 0x00, sizeof(TaskStateSegment));
s_tss.iopb = sizeof(TaskStateSegment);
uint64_t base = (uint64_t)&s_tss;
write_entry(s_tss_offset, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
SegmentDescriptor& desc = s_gdt[s_tss_offset / sizeof(SegmentDescriptor) + 1];
desc.low = base >> 32;
desc.high = 0;
}
void set_tss_stack(uintptr_t rsp)
{
s_tss.rsp0 = rsp;
}
static void flush_gdt()
{
asm volatile("lgdt %0" :: "m"(s_gdtr));
}
static void flush_tss()
{
asm volatile("ltr %0" :: "m"(s_tss_offset));
}
void initialize()
{
s_gdtr.address = (uint64_t)&s_gdt;
s_gdtr.size = s_gdt.size() * sizeof(SegmentDescriptor) - 1;
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code
write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, 0xC); // kernel data
write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, 0xA); // user code
write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, 0xC); // user data
write_tss();
flush_gdt();
flush_tss();
}
}

413
kernel/arch/x86_64/IDT.cpp Normal file
View File

@ -0,0 +1,413 @@
#include <BAN/Array.h>
#include <BAN/Errors.h>
#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
#include <kernel/InterruptStack.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Panic.h>
#include <kernel/Process.h>
#include <kernel/Scheduler.h>
#include <kernel/Timer/PIT.h>
#define ISR_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
#define IRQ_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
namespace Kernel::IDT
{
struct Registers
{
uint64_t rsp;
uint64_t rip;
uint64_t rflags;
uint64_t cr4;
uint64_t cr3;
uint64_t cr2;
uint64_t cr0;
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rsi;
uint64_t rdi;
uint64_t rbp;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
};
struct GateDescriptor
{
uint16_t offset1;
uint16_t selector;
uint8_t IST;
uint8_t flags;
uint16_t offset2;
uint32_t offset3;
uint32_t reserved;
} __attribute__((packed));
struct IDTR
{
uint16_t size;
uint64_t offset;
} __attribute__((packed));
static IDTR s_idtr;
static GateDescriptor* s_idt = nullptr;
#define X(num) 1 +
static BAN::Array<Interruptable*, IRQ_LIST_X 0> s_interruptables;
#undef X
enum ISR
{
DivisionError,
Debug,
NonMaskableInterrupt,
Breakpoint,
Overflow,
BoundRangeException,
InvalidOpcode,
DeviceNotAvailable,
DoubleFault,
CoprocessorSegmentOverrun,
InvalidTSS,
SegmentNotPresent,
StackSegmentFault,
GeneralProtectionFault,
PageFault,
UnknownException0x0F,
x87FloatingPointException,
AlignmentCheck,
MachineCheck,
SIMDFloatingPointException,
VirtualizationException,
ControlProtectionException,
UnknownException0x16,
UnknownException0x17,
UnknownException0x18,
UnknownException0x19,
UnknownException0x1A,
UnknownException0x1B,
HypervisorInjectionException,
VMMCommunicationException,
SecurityException,
UnkownException0x1F,
};
struct PageFaultError
{
union
{
uint32_t raw;
struct
{
uint32_t present : 1;
uint32_t write : 1;
uint32_t userspace : 1;
uint32_t reserved_write : 1;
uint32_t instruction : 1;
uint32_t protection_key : 1;
uint32_t shadow_stack : 1;
uint32_t reserved1 : 8;
uint32_t sgx_violation : 1;
uint32_t reserved2 : 16;
};
};
};
static_assert(sizeof(PageFaultError) == 4);
static const char* isr_exceptions[] =
{
"Division Error",
"Debug",
"Non-maskable Interrupt",
"Breakpoint",
"Overflow",
"Bound Range Exception",
"Invalid Opcode",
"Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack-Segment Fault",
"General Protection Fault",
"Page Fault",
"Unknown Exception 0x0F",
"x87 Floating-Point Exception",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Unknown Exception 0x16",
"Unknown Exception 0x17",
"Unknown Exception 0x18",
"Unknown Exception 0x19",
"Unknown Exception 0x1A",
"Unknown Exception 0x1B",
"Hypervisor Injection Exception",
"VMM Communication Exception",
"Security Exception",
"Unkown Exception 0x1F",
};
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, InterruptStack& interrupt_stack, const Registers* regs)
{
#if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace)
Thread::current().save_sse();
#endif
pid_t tid = Scheduler::current_tid();
pid_t pid = tid ? Process::current().pid() : 0;
if (tid)
{
Thread::current().set_return_rsp(interrupt_stack.rsp);
Thread::current().set_return_rip(interrupt_stack.rip);
if (isr == ISR::PageFault)
{
// Check if stack is OOB
auto& stack = Thread::current().stack();
auto& istack = Thread::current().interrupt_stack();
if (stack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= stack.vaddr() + stack.size())
; // using normal stack
else if (istack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= istack.vaddr() + istack.size())
; // using interrupt stack
else
{
derrorln("Stack pointer out of bounds!");
derrorln("rsp {H}, stack {H}->{H}, istack {H}->{H}",
interrupt_stack.rsp,
stack.vaddr(), stack.vaddr() + stack.size(),
istack.vaddr(), istack.vaddr() + istack.size()
);
Thread::current().handle_signal(SIGKILL);
goto done;
}
// Try demand paging on non present pages
PageFaultError page_fault_error;
page_fault_error.raw = error;
if (!page_fault_error.present)
{
asm volatile("sti");
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2);
asm volatile("cli");
if (!result.is_error() && result.value())
goto done;
if (result.is_error())
{
dwarnln("Demand paging: {}", result.error());
Thread::current().handle_signal(SIGKILL);
goto done;
}
}
}
}
if (PageTable::current().get_page_flags(interrupt_stack.rip & PAGE_ADDR_MASK) & PageTable::Flags::Present)
{
auto* machine_code = (const uint8_t*)interrupt_stack.rip;
dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}",
machine_code[0],
machine_code[1],
machine_code[2],
machine_code[3],
machine_code[4],
machine_code[5],
machine_code[6],
machine_code[7]
);
}
dwarnln(
"{} (error code: 0x{16H}), pid {}, tid {}\r\n"
"Register dump\r\n"
"rax=0x{16H}, rbx=0x{16H}, rcx=0x{16H}, rdx=0x{16H}\r\n"
"rsp=0x{16H}, rbp=0x{16H}, rdi=0x{16H}, rsi=0x{16H}\r\n"
"rip=0x{16H}, rflags=0x{16H}\r\n"
"cr0=0x{16H}, cr2=0x{16H}, cr3=0x{16H}, cr4=0x{16H}",
isr_exceptions[isr], error, pid, tid,
regs->rax, regs->rbx, regs->rcx, regs->rdx,
regs->rsp, regs->rbp, regs->rdi, regs->rsi,
regs->rip, regs->rflags,
regs->cr0, regs->cr2, regs->cr3, regs->cr4
);
if (isr == ISR::PageFault)
PageTable::current().debug_dump();
Debug::dump_stack_trace();
if (tid && Thread::current().is_userspace())
{
// TODO: Confirm and fix the exception to signal mappings
int signal = 0;
switch (isr)
{
case ISR::DeviceNotAvailable:
case ISR::DivisionError:
case ISR::SIMDFloatingPointException:
case ISR::x87FloatingPointException:
signal = SIGFPE;
break;
case ISR::AlignmentCheck:
signal = SIGBUS;
break;
case ISR::InvalidOpcode:
signal = SIGILL;
break;
case ISR::PageFault:
signal = SIGSEGV;
break;
default:
dwarnln("Unhandled exception");
signal = SIGABRT;
break;
}
Thread::current().handle_signal(signal);
}
else
{
panic("Unhandled exception");
}
ASSERT(Thread::current().state() != Thread::State::Terminated);
done:
#if __enable_sse
if (from_userspace)
{
ASSERT(Thread::current().state() == Thread::State::Executing);
Thread::current().load_sse();
}
#endif
return;
}
extern "C" void cpp_irq_handler(uint64_t irq, InterruptStack& interrupt_stack)
{
#if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace)
Thread::current().save_sse();
#endif
if (Scheduler::current_tid())
{
Thread::current().set_return_rsp(interrupt_stack.rsp);
Thread::current().set_return_rip(interrupt_stack.rip);
}
if (!InterruptController::get().is_in_service(irq))
dprintln("spurious irq 0x{2H}", irq);
else
{
InterruptController::get().eoi(irq);
if (s_interruptables[irq])
s_interruptables[irq]->handle_irq();
else
dprintln("no handler for irq 0x{2H}\n", irq);
}
Scheduler::get().reschedule_if_idling();
ASSERT(Thread::current().state() != Thread::State::Terminated);
#if __enable_sse
if (from_userspace)
{
ASSERT(Thread::current().state() == Thread::State::Executing);
Thread::current().load_sse();
}
#endif
}
static void flush_idt()
{
asm volatile("lidt %0"::"m"(s_idtr));
}
static void register_interrupt_handler(uint8_t index, void(*handler)())
{
GateDescriptor& descriptor = s_idt[index];
descriptor.offset1 = (uint16_t)((uint64_t)handler >> 0);
descriptor.offset2 = (uint16_t)((uint64_t)handler >> 16);
descriptor.offset3 = (uint32_t)((uint64_t)handler >> 32);
descriptor.selector = 0x08;
descriptor.IST = 0;
descriptor.flags = 0x8E;
}
static void register_syscall_handler(uint8_t index, void(*handler)())
{
register_interrupt_handler(index, handler);
s_idt[index].flags = 0xEE;
}
void register_irq_handler(uint8_t irq, Interruptable* interruptable)
{
if (irq > s_interruptables.size())
Kernel::panic("Trying to assign handler for irq {} while only {} are supported", irq, s_interruptables.size());
s_interruptables[irq] = interruptable;
}
#define X(num) extern "C" void isr ## num();
ISR_LIST_X
#undef X
#define X(num) extern "C" void irq ## num();
IRQ_LIST_X
#undef X
extern "C" void syscall_asm();
void initialize()
{
s_idt = (GateDescriptor*)kmalloc(0x100 * sizeof(GateDescriptor));
ASSERT(s_idt);
memset(s_idt, 0x00, 0x100 * sizeof(GateDescriptor));
s_idtr.offset = (uint64_t)s_idt;
s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1;
#define X(num) register_interrupt_handler(num, isr ## num);
ISR_LIST_X
#undef X
#define X(num) register_interrupt_handler(IRQ_VECTOR_BASE + num, irq ## num);
IRQ_LIST_X
#undef X
register_syscall_handler(0x80, syscall_asm);
flush_idt();
}
[[noreturn]] void force_triple_fault()
{
// load 0 sized IDT and trigger an interrupt to force triple fault
asm volatile("cli");
s_idtr.size = 0;
flush_idt();
asm volatile("int $0x00");
ASSERT_NOT_REACHED();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
.section .userspace, "ax" .section .userspace, "aw"
#include <sys/syscall.h>
// stack contains // stack contains
// return address // return address
@ -23,18 +25,16 @@ signal_trampoline:
pushq %r14 pushq %r14
pushq %r15 pushq %r15
// This is 16 byte aligned
movq 128(%rsp), %rdi movq 128(%rsp), %rdi
movq 120(%rsp), %rax movq 120(%rsp), %rax
// align stack to 16 bytes
movq %rsp, %rbx
andq $0x0F, %rbx
subq %rbx, %rsp
call *%rax call *%rax
// restore stack movq $SYS_SIGNAL_DONE, %rax
addq %rbx, %rsp movq 128(%rsp), %rbx
int $0x80
popq %r15 popq %r15
popq %r14 popq %r14
popq %r13 popq %r13

View File

@ -1,77 +0,0 @@
// arguments in RAX, RBX, RCX, RDX, RSI, RDI
// System V ABI: RDI, RSI, RDX, RCX, R8, R9
.global asm_syscall_handler
asm_syscall_handler:
pushq %rbx
pushq %rcx
pushq %rdx
pushq %rdi
pushq %rsi
pushq %rbp
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %rsi, %r8
movq %rdi, %r9
movq %rax, %rdi
movq %rbx, %rsi
xchgq %rcx, %rdx
leaq 112(%rsp), %rbx
pushq %rbx
call cpp_syscall_handler
addq $8, %rsp
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rbp
popq %rsi
popq %rdi
popq %rdx
popq %rcx
popq %rbx
iretq
.global sys_fork_trampoline
sys_fork_trampoline:
pushq %rbx
pushq %rbp
pushq %r12
pushq %r13
pushq %r14
pushq %r15
call read_ip
testq %rax, %rax
je .reload_stack
movq %rax, %rsi
movq %rsp, %rdi
call sys_fork
.done:
popq %r15
popq %r14
popq %r13
popq %r12
popq %rbp
popq %rbx
ret
.reload_stack:
call get_thread_start_sp
movq %rax, %rsp
xorq %rax, %rax
jmp .done

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