diff --git a/.idea/dictionaries/elsecaller.xml b/.idea/dictionaries/elsecaller.xml index e0b7213..e10f3e6 100644 --- a/.idea/dictionaries/elsecaller.xml +++ b/.idea/dictionaries/elsecaller.xml @@ -1,8 +1,10 @@ + bootimage deallocator fifos + syscalls unyield virt diff --git a/.idea/vcs.xml b/.idea/vcs.xml index cb89bdf..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 44d85bb..9a65593 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,199 +4,197 @@ name = "bit_field" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bootloader" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad686b9b47363de7d36c05fb6885f17d08d0f2d15b1bc782a101fe3c94a2c7c" [[package]] name = "cpuio" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d531514efb06912141fa65967447de805691b685a7565c87d1765afe34a98aa7" [[package]] name = "derivative" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "spin", ] [[package]] name = "my-tidy-os" version = "0.1.0" dependencies = [ - "bootloader 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pc-keyboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pic8259_simple 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "postcard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uart_16550 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "x86_64 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader", + "lazy_static", + "num_enum", + "pc-keyboard", + "pic8259_simple", + "postcard", + "spin", + "uart_16550", + "volatile", + "x86_64", ] [[package]] name = "num_enum" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" dependencies = [ - "derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derivative", + "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pc-keyboard" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac" [[package]] name = "pic8259_simple" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2a5497fb8e59bf8015f67b7dff238d75ef445e03f23edac24ac3a8f09be952" dependencies = [ - "cpuio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cpuio", ] [[package]] name = "postcard" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e3f5c2e9a91383c6594ec68aa2dfdfe19a3c86f34b088ba7203f2483d2682f" dependencies = [ - "postcard-cobs 0.1.5-pre (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "postcard-cobs", + "serde", ] [[package]] name = "postcard-cobs" version = "0.1.5-pre" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" dependencies = [ - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "serde" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" dependencies = [ - "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "1.0.33" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "uart_16550" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58fc40dc1712664fc9b0a7bd8ca2f21ab49960924fb245a80a05e1e92f3dfe9" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "x86_64 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "x86_64", ] [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "volatile" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e76fae08f03f96e166d2dfda232190638c10e0383841252416f9cfe2ae60e6" [[package]] name = "x86_64" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ca13fe418403c0df903ee7eb3bf1a51a742bb339b42a329642f386224b8e64" dependencies = [ - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field", + "bitflags", ] - -[metadata] -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum bootloader 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bb7239572eacd7c74ac0d6c75eb87a615058d6497a667728694e0d32b7bd5a20" -"checksum cpuio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d531514efb06912141fa65967447de805691b685a7565c87d1765afe34a98aa7" -"checksum derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum num_enum 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1e2d7c9c4282839fc56f549bc006a54e6f28a851a7de7adcf3f50575751760" -"checksum num_enum_derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4a9f19dafa80d8af21ede328f2c4ed836604a2eb1c309d688f89a7cc40568923" -"checksum pc-keyboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c48392db76c4e9a69e0b3be356c5f97ebb7b14413c5e4fd0af4755dbf86e2fce" -"checksum pic8259_simple 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af2a5497fb8e59bf8015f67b7dff238d75ef445e03f23edac24ac3a8f09be952" -"checksum postcard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2ba083a6a640a9f09d294c0f504acdd7a2b000c179e80c73d4b7a036bfd8164" -"checksum postcard-cobs 0.1.5-pre (registry+https://github.com/rust-lang/crates.io-index)" = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" -"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" -"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -"checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" -"checksum serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" -"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -"checksum syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" -"checksum uart_16550 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e58fc40dc1712664fc9b0a7bd8ca2f21ab49960924fb245a80a05e1e92f3dfe9" -"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -"checksum volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" -"checksum x86_64 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9144cdef072afe8afcbc511be8f421de5b3eb9f4b8ff7abff369bee38f91e7e3" diff --git a/Cargo.toml b/Cargo.toml index 64de65a..4a14feb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,4 +43,4 @@ spin = "*" # Standard rust serialization library, #serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } -postcard = { version = "*", default-features = false, features = ["alloc"]} \ No newline at end of file +postcard = { version = "*", default-features = false, features = ["alloc"]} diff --git a/README.md b/README.md index 26504c0..65e4257 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,193 @@ ## Design notes -~~~~ -If a sporadic process yields, but there are no other processes available. We just return control directly back to that process. \ No newline at end of file + +If a sporadic process yields, but there are no other processes available. We just return control directly back to that process. + +--- + +### Quick descriptions of the dependencies + +- `bootloader`: Connects to the rust boot image, this is just to get the type for the struct passed by the bootloader. +- `volatile`: Thin wrapper around the volatile instructions to avoid compiler optimisations. +- `x86_64`: A lot of structures for working with x86 to prevent the need for using random magic number. Also a bunch of paging logic. +- `uart_16550`: Structures for the uart_16550 serial output, used to help with debugging. +- `pic8259_simple`: Structures for the CPU's programmable interface controller. +- `pc-keyboard`: Structures and translation for the keyboard events. +- `lazy_static`: General Rust helper library to have safe, lazily runtime initialized, static variables. +- `num_enum`: General Rust helper library, allows conversion from numbers to enums. +- `spin`: Spinlock mutex. Interrupt semaphores doesn't work for a lot of synchronization stuff. + +### Build/Run Instructions: +#### Requirements: +- Rust Nightly (Easiest way is to get [rustup](https://rustup.rs/), then run `rustup install nightly`) +- Cargo-xbuild (`cargo install cargo-xbuild` After installing rust) +- Bootimage runner (`cargo install bootimage`) +- Qemu +- Maybe more? Look at the error messages I suppose + +#### Steps: +- Run `cargo xrun --release`. +- Unpause the qemu instance + +## OS Design + +> Note: The top of main.rs contains +>``` +>#![no_main] +>#![no_std] +>``` +>This indicates that no Rust standard library is imported, and no C main function is defined a for the linker. +> +>This is then followed by a ton of nightly unstable features. See [TODO] for what each is required for. + +### Boot process + +The bootloader calls the `_start` function in `main.rs` after the entire kernel is loaded into memory. +The bootloader does physical to virtual mapping before `_start` is called. +Some parts of the memory like `0xb8000` have identity mapping, while kernel code and stack are mapped arbitrarily. +The bootloader also comes with some options to configure how to map the rest of the memory, I +configured it to also map the entire physical memory into virtual memory at a very high offset. The exact offset and other memory information +is passed in via the `&'static bootloader::BootInfo` argument in `_start`. This means that if the offset is `0x100000000`, +then both `0xb8000` and `0x1000b8000` are mapped to the exact same position in memory. +This does break some of the rust memory safety guarantees if not careful. + +This full physical memory mapping is used mainly to get access to page table. +The page table physical address is stored in the `Cr3` Register, which means to view and modify the page table, we make +use of the full physical mapping done by the bootloader. + +Then the frame allocator and the global heap allocator is then initialized, `HEAP_SIZE` const contains the size of allowed heap memory of the entire OS +for the allocator. This must be lower than the available memory on the system, as the frames for the heap is allocated at OS boot up. +Defining and initializing a global allocator allows us to bring back part of the standard library as the `alloc` library. + +We then setup and load the GDT (Global Descriptor Table), TSS (Task State Segment), and IDT (Interrupt Descriptor Table). +Here we define where our stack is going to be for interrupts, what hardware and software interrupts to catch...etc. +Of specific note the timer interrupt is defined here, and it is what will govern context switches. + +Next we write to the x86_64 model specific registers `LStar, SFMask, KernelGsBase, Star, Efer`, so that we can handle syscalls. +(Explained later in the syscall section) + +Finally, if we are running this as a test, it will run the kernel tests (doesn't really work at the moment). +otherwise, we run `os_start()`, which will schedule the startup processes and return. After that, we enable interrupts, +then run the idle process which is just a infinite loop with `hlt` to save CPU processing power. +We wait here until the timer interrupts begins the scheduling of the processes. + +### Memory Management +Currently there are 2 allocators for the OS. One is the `BootInfoFrameAllocator`, which allocates fixed size physical frames +of 4KiB, for use by pages to map memory to. Then there is the `FixedSizeBlockAllocator`, which, counter to what the name suggests, +is actually for allocating dynamically sized pieces of memory for the kernel heap. This allocator is also currently allocating heap memory +for each program as well, as everything is compiled together. Once the programs are actually separate pieces of code, they will probably +have their own allocators. + +`BootInfoFrameAllocator`: Uses a free list for a fast linear allocation time proportional to the number of frames to be allocated. +If there are no more frames in the free list, it look for usable regions in the memory map passed by the boot loader. + +`FixedSizeBlockAllocator`: Block based allocator that will allocate continuous block/chunks of virtual memory in powers of 2. +This will split up bigger blocks of memory into smaller pieces to more efficiently fill the requirements. Currently does not join smaller blocks +back together into bigger blocks, so eventually you will run out of big chunks of continuous memory. + +Further improvements can be made here to join together smaller blocks to make larger blocks when needed. +Apparently this is called a "Buddy Allocator". +Another thing is that the current implementation the largest continuous allocation is the size of the largest block. A +solution to this would be to use paging for bigger allocations. + +Stack memory is allocated for each process with a different address. Currently every process share the same memory, +with just different offsets for where their stack begins from, so any process can access the memory of any other process. +Improvements would be to have the same virtual stack location for every process, but swap out pages so it points to different physical memory. + +### Interrupts + +The `create_idt()` function creates a Interrupt Descriptor Table. We use the structure provided by `x86_64` crate, +to prevent us messing up the bit shifts to create the IDT entry, and also to verify the signature of functions passed as handlers. + +Here we register a bunch of CPU exception handlers like divide by zero, page fault...etc. +Of special note, we also handle double faults, which is called when a fault happens inside a exception handler for the first fault, +or if the first fault/exception does not have a handler. The double fault handler also needs a custom stack pointer, because if +something a page fault happens with a invalid stack position, it will cause a second fault in the page fault handler. + +We also register some hardware/PIC registers, specifically the keyboard and the timer. + +The syscall handler behaviour is registered in the `LStar, SFMask, KernelGsBase, Star, Efer, EferFlags` model_specific specific registers. + +Most of the interrupt handlers use the `x86-interrupt` calling convention, where the compiler will save all the registers +used in the function body, and restore them at the end, along with the necessary interrupt flags. +This doesn't work for handlers that need to do process switches, which is explained later. + +### Process Management + +Process management data is stored in the one static `PROCESS_MANAGER` variable, which is controlled by a spinlock Mutex, +to make sure of synchronisation. + +#### Process Creation: + +1. Get free PID from incrementing pool +2. Allocate some stack space +3. Swap to the new process stack. +4. Push the terminate function address, so when returning from the application will call the terminate process syscall +5. Modify the stack to fake that an interrupt has happened. +6. Push all the registers, currently just pushing 0 to everything +7. Return the modified stack pointer to put in the PCB. +8. Store the PCB in PROCESS_MANAGER, and the pid in the relevant scheduling structures. + +### Task Switching + +The overall idea for task switching is instead of saving all the registers in the PCB, we push them onto the stack, and +just save the stack pointer. When switching, all we need to do is change the stack pointer, and pop all the stack registers. +When creating a new process, we need to fake the stack so when we switch the stack pointer to a newly created process, +the same logic as restoring a interrupted process can be applied. + +### Syscalls + +Every syscall command on the system is stored in the enum in `interrupts::SyscallCommand`. Currently there are only 3 commands. +- Terminate (self) +- Yield (self) +- TerminateEverythingElse (only implemented because we don't have a good way to list all processes, or terminate other processes) + +The syscall handler is marked as a `#[naked]` function, meaning that there is no function prologue and epilogue is generated. +This allows us to manage exactly what registers to push, what order to push them, and everything else. + +The same logic for task switching is used in the syscall as well. + +### Semaphores +The semaphores mostly follow the `kernel.h` definitions, with the exception of processes being able to call the semaphore +even if they technically don't own it. This way allows us to easily signal that a process has finished work. + +I make the assumption that no *Device* scheduled process is allowed to use a semaphore that can block, since if this is the case, +it will no longer have a "very short execution time". + +When a process calls `os_wait()`, and the semaphore counter smaller or equal to 0, it will be put into a wait queue, +and call `os_yield()`. However, it turns out because of the behaviour of os_yield(), only periodic processes need to be +on the queue. When a semaphore is freed, we only need to check if the current periodic timeslot process is in the wait queue. +If not, we can continue execution of the process that called `os_signal()`, as yielding won't allow any process +with higher priority to continue. + +To clarify, think of the following scenarios: + +| Process calling `os_wait()` | Process calling `os_signal()` | Result | +|-----------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Periodic | Periodic | No need to yield, one periodic process can never manually yield to another periodic process, since they always occupy different time slots. | +| Sporadic | Sporadic | No need to yield, every sporadic process has the same priority. Unless it manually yields, there is no need to force it to yield to another sporadic process. | +| Sporadic | Periodic | No need to yield, periodic processes has higher priority than sporadic. | +| Periodic | Sporadic | Only need to yield if the current timeslot contains a periodic process that is blocked on this specific semaphore. | + +And *Device* processes never has semaphores as explained above. + +### FIFO IPC +The First-In-First-Out Inter-Process-Communication is just a btree with dequeues to be filled with data, and some synchronization code. + +## Further work +- Make heap allocator per process +- Add actual swapping in and out of pages for each process. +- Unify stack and heap virtual address, since pages are the ones being swapped (or maybe not to prevent memory attacks). +- Allow bigger continuous allocation sizes. +- Dynamically allocate heap as is needed. +- Floating point doesn't work yet. +It shouldn't be too hard to make work, since we just need to push +the floating point registers to the stack when task switching. +--- + +## Tests +All test applications are located in the `tests` folder/module. +`tests/applications.rs` contains the actual applications to run, and +tests runner is in `tests/app_test_runner.rs`. Because of the nature +of scheduling, it is really hard to actually put asserts in to test. +So currently the best you can do is eyeball the output and see if everything looks right. +The apps also will test memory, IPC, semaphores, \ No newline at end of file diff --git a/src/interrupts/mod.rs b/src/interrupts/mod.rs index a508f32..b1d26e0 100644 --- a/src/interrupts/mod.rs +++ b/src/interrupts/mod.rs @@ -54,7 +54,6 @@ pub unsafe extern fn syscall_handler() -> ! { : "intel", "volatile"); interrupt_push!(); - // TODO: Make everything use the same syntax llvm_asm!(" mov rdi, rsp // Store process rsp as first argument @@ -66,6 +65,7 @@ pub unsafe extern fn syscall_handler() -> ! { : : "intel", "volatile"); + // TODO: Make everything use the same syntax, basically I haven't found a example for how to do rust function calls with the intel syntax lol llvm_asm!( " mov %rax, %rsi //; Pass rax (first argument of syscall) as second argument call ${0:c} diff --git a/src/kernel.rs b/src/kernel.rs index 8f60d0a..125efb9 100644 --- a/src/kernel.rs +++ b/src/kernel.rs @@ -30,11 +30,6 @@ pub fn os_init() { SFMask::write(RFlags::INTERRUPT_FLAG | RFlags::TRAP_FLAG); KernelGsBase::write(VirtAddr::new(crate::gdt::TSS.deref() as *const _ as u64)); - // FIXME: IDK WHATS GOING ON!!! - // I don't understand why GsBase needs to be set, but otherwise `swapgs` doesn't work properly - // Or maybe it does work properly. - // GsBase::write(VirtAddr::new(crate::gdt::TSS.deref() as *const _ as u64)); - unsafe { Star::write_raw(0, crate::gdt::GDT.1.code_selector.0); Efer::write(Efer::read() | EferFlags::SYSTEM_CALL_EXTENSIONS); diff --git a/src/main.rs b/src/main.rs index ff79e3c..4056d03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,8 +59,8 @@ pub extern "C" fn _start(boot_info: &'static bootloader::BootInfo) -> ! { crate::test_main(); kernel::os_start(); - x86_64::instructions::interrupts::enable(); println!("Didn't quite crash"); + x86_64::instructions::interrupts::enable(); processes::idle_process(); } @@ -93,6 +93,6 @@ fn panic(info: &PanicInfo) -> ! { #[alloc_error_handler] fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! { - panic!("allocation error: {:?}", layout) + panic!("allocation error: {:?}, probably not enough bytes", layout) } diff --git a/src/memory/allocator.rs b/src/memory/allocator.rs index 4f2d835..5406286 100644 --- a/src/memory/allocator.rs +++ b/src/memory/allocator.rs @@ -8,7 +8,7 @@ use x86_64::VirtAddr; // These are virtual addresses pub const HEAP_START: usize = 0x_2222_2222_0000; -pub const HEAP_SIZE: usize = 64 * 1024; // 64 KiB +pub const HEAP_SIZE: usize = 640 * 1024; // 64 KiB pub fn init_heap(mapper: &mut impl Mapper, frame_allocator: &mut impl FrameAllocator) -> Result<(), MapToError> { diff --git a/src/memory/allocator/fixed_pow2_block.rs b/src/memory/allocator/fixed_pow2_block.rs index bb4473d..dd5ad55 100644 --- a/src/memory/allocator/fixed_pow2_block.rs +++ b/src/memory/allocator/fixed_pow2_block.rs @@ -44,9 +44,9 @@ impl FixedSizeBlockAllocator { current_increment_idx -= 1; current_increment_idx > 0 } {} - println!("{:?}", self.list_heads); } + /// Creates and returns some block_idx sized blocks by splitting larger blocks fn split_larger_block(&mut self, block_idx: usize) -> Option<&'static mut ListNode> { let bigger_idx = self.pop_first_larger_block_available(block_idx)?; for idx in (block_idx..bigger_idx).rev() { @@ -84,6 +84,7 @@ impl FixedSizeBlockAllocator { fn list_index(layout: &Layout) -> Option { let required_block_size = layout.size().max(layout.align()); + // println!("Req: {}", required_block_size); // TODO: Please optimise, though not a big deal BLOCK_SIZES.iter().position(|&s| s >= required_block_size) } @@ -105,8 +106,8 @@ unsafe impl GlobalAlloc for Locked { } } } - // TODO: Panic - None => panic!("Allocations bigger than 2^16 not supported yet") + // TODO: Don't panic + None => panic!("Allocations bigger than 2^16 not supported yet, please just make multiple (non continuous) allocations") } } @@ -126,7 +127,7 @@ unsafe impl GlobalAlloc for Locked { allocator.list_heads[index] = Some(&mut *new_node_ptr); } None => { - panic!("Wtf, you can't even allocate this") + unreachable!("Wtf, you can't even allocate this") } } } diff --git a/src/processes/process.rs b/src/processes/process.rs index 5994af3..afb26bf 100644 --- a/src/processes/process.rs +++ b/src/processes/process.rs @@ -52,7 +52,7 @@ impl Process { } }); - println!("Fake stack point: {} {} {:x}", name, pid, fake_int_sp); + // println!("Fake stack point: {} {} {:x}", name, pid, fake_int_sp); Process { pid, level, diff --git a/src/setup_process_stack.s b/src/setup_process_stack.s index d313fc7..9e1a7b0 100644 --- a/src/setup_process_stack.s +++ b/src/setup_process_stack.s @@ -38,8 +38,6 @@ asm_fake_register: push rax //; push rax //; - // NO, just use naked function like a normal person - //sub rsp, // Change based on build mode in build.rs (This is so stupid, we need a better way of doing this) mov rax, rsp // Save new stack pointer in rax, the C return register mov rsp, r8 // Get back our original stack pointer diff --git a/src/tests/app_test_runner.rs b/src/tests/app_test_runner.rs index 67c3275..e10854b 100644 --- a/src/tests/app_test_runner.rs +++ b/src/tests/app_test_runner.rs @@ -33,6 +33,12 @@ pub extern "C" fn run_tests() { os_create(9993, SchedulingLevel::Sporadic, 4,test_app_spor).unwrap(); os_create(10, SchedulingLevel::Device, 10, test_app_device).unwrap(); os_create(15, SchedulingLevel::Device, 15, test_app_device).unwrap(); + // os_wait(TEST_SEMAPHORE_ID); + wait_and_reset_semaphore(TEST_SEMAPHORE_ID, 4); + os_create(fifo_key as i32, SchedulingLevel::Periodic, 1, test_app_printer).unwrap(); + os_create(1000, SchedulingLevel::Sporadic, 199, crate::tests::applications::big_memory).unwrap(); + os_create(10000, SchedulingLevel::Sporadic, 299, crate::tests::applications::big_memory).unwrap(); + os_create(100000, SchedulingLevel::Sporadic, 399, crate::tests::applications::big_memory).unwrap(); os_wait(TEST_SEMAPHORE_ID); println!("All tests complete, time to kill everything"); syscall1(SyscallCommand::TerminateEverythingElse); diff --git a/src/tests/applications.rs b/src/tests/applications.rs index 97bb3b0..20807da 100644 --- a/src/tests/applications.rs +++ b/src/tests/applications.rs @@ -1,5 +1,5 @@ use crate::kernel::*; -use alloc::prelude::v1::*; +use alloc::prelude::v1::{Vec, ToOwned, String, ToString}; use crate::processes::SchedulingLevel; use super::app_test_runner::TEST_SEMAPHORE_ID; use crate::println; @@ -15,12 +15,31 @@ pub extern "C" fn test_app() { } // println!("Enter the gates: {} {}", os_getparam(), a); - os_write(arg as u32, format!("Enter the gates: {}", a).as_bytes()).unwrap(); + os_write(arg as u32, format!("Periodic Test: {}", a).as_bytes()).unwrap(); stuff.resize(20000, 0); os_write(arg as u32, "Ends".as_bytes()).unwrap(); os_signal(TEST_SEMAPHORE_ID); } +pub extern "C" fn test_app_printer() { + use alloc::format; + + let mut a: i64 = 0; + let arg = os_getparam(); + let mut stuff: Vec = Vec::new(); + for i in 0..5000000 { + a = a.wrapping_add(i); + } + + stuff.resize(20000, 0); + let read_size = os_read(arg as u32, &mut stuff).unwrap(); + stuff.truncate(read_size); + let read_data = String::from_utf8_lossy(&stuff); + println!("Test App String Read: {}", read_data); + // println!("Enter the gates: {} {}", os_getparam(), a);/ + os_signal(TEST_SEMAPHORE_ID); +} + pub extern "C" fn test_app_signals() { println!("Before Init"); os_init_sem(123, 1).unwrap(); @@ -83,12 +102,12 @@ pub extern "C" fn read_test_app() { pub extern "C" fn test_app_spor() { let mut a: i64 = 0; let param = os_getparam(); - for i in 0..10000000 { + for i in 0..10_000_000 { // if i % 100000 == 0 { // println!("{}", param); // } - if i % 1000000 == 0 { - println!("SPORE!!! {}", param); + if i % 1_000_000 == 0 { + println!("Hey, progress {} {}", param, i / 1_000_000); os_yield(); } a = a.wrapping_add(i); @@ -112,6 +131,18 @@ pub extern "C" fn test_app_device() { } } +pub extern "C" fn big_memory() { + let param = os_getparam(); + let mut array_of_arrays = alloc::vec::Vec::with_capacity(16); + println!("about to allocate: {} + some more bytes", param); + for x in (0..param).step_by(4096 * 4) { + let mut array = alloc::vec::Vec::with_capacity((4096 * 4).min(param - x) as usize); + array.resize((4096 * 4).min(param) as usize, 255u8); + array_of_arrays.push(array); + } + os_signal(TEST_SEMAPHORE_ID); +} + fn do_work() { let mut a = volatile::Volatile::new(0i32); for i in 0..5000000 { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index bcc5873..e184576 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,5 +1,5 @@ // #![cfg(test)] -mod applications; +pub mod applications; pub mod app_test_runner; use crate::{println, print, eprintln, eprint, serial_println, serial_print};