├── .github └── workflows │ └── check.yml ├── COPYING ├── README.md ├── doc ├── .gitignore ├── book.toml ├── license-header.txt └── src │ ├── SUMMARY.md │ ├── booting.md │ ├── build.md │ ├── debug.md │ ├── device.md │ ├── external_doc.md │ ├── file │ ├── fs.md │ ├── procfs.md │ ├── sysfs.md │ └── tmpfs.md │ ├── installer.md │ ├── intro.md │ ├── memory │ ├── alloc.md │ ├── mem_map.md │ ├── mem_space.md │ └── tracing.md │ ├── module.md │ ├── process.md │ ├── tty.md │ └── userspace │ ├── elf.md │ ├── exec.md │ └── script.md ├── inttest ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.sh └── src │ ├── boot.rs │ ├── filesystem.rs │ ├── main.rs │ ├── mount.rs │ ├── procfs.rs │ ├── signal.rs │ └── util.rs ├── kernel ├── .cargo │ └── config.toml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── arch │ ├── x86 │ │ ├── linker.ld │ │ ├── src │ │ │ ├── context.s │ │ │ ├── memcpy.s │ │ │ ├── memmove.s │ │ │ ├── memset.s │ │ │ └── raw_copy.s │ │ └── x86.json │ └── x86_64 │ │ ├── linker.ld │ │ ├── src │ │ ├── context.s │ │ ├── memcpy.s │ │ ├── memmove.s │ │ ├── memset.s │ │ └── raw_copy.s │ │ └── x86_64.json ├── build │ ├── compile.rs │ ├── config.rs │ ├── main.rs │ ├── target.rs │ └── util.rs ├── ci │ └── test.sh ├── default.build-config.toml ├── grub.cfg ├── scripts │ ├── gdb.sh │ ├── link_partitions.sh │ └── qemu.sh ├── src │ ├── acpi │ │ ├── aml │ │ │ ├── mod.rs │ │ │ ├── named_obj.rs │ │ │ ├── namespace_modifier.rs │ │ │ ├── term_obj.rs │ │ │ ├── type1_opcode.rs │ │ │ └── type2_opcode.rs │ │ ├── dsdt.rs │ │ ├── fadt.rs │ │ ├── madt.rs │ │ ├── mod.rs │ │ └── rsdt.rs │ ├── arch │ │ ├── mod.rs │ │ └── x86 │ │ │ ├── gdt.rs │ │ │ ├── idt.rs │ │ │ ├── io.rs │ │ │ ├── mod.rs │ │ │ ├── paging.rs │ │ │ ├── pic.rs │ │ │ └── tss.rs │ ├── bin.rs │ ├── boot.rs │ ├── cmdline.rs │ ├── crypto │ │ ├── chacha20.rs │ │ ├── checksum.rs │ │ ├── mod.rs │ │ └── rand.rs │ ├── debug.rs │ ├── device │ │ ├── bar.rs │ │ ├── bus │ │ │ ├── mod.rs │ │ │ └── pci.rs │ │ ├── default.rs │ │ ├── id.rs │ │ ├── keyboard.rs │ │ ├── manager.rs │ │ ├── mod.rs │ │ ├── serial.rs │ │ ├── storage │ │ │ ├── ide.rs │ │ │ ├── mod.rs │ │ │ ├── partition │ │ │ │ ├── gpt.rs │ │ │ │ ├── mbr.rs │ │ │ │ └── mod.rs │ │ │ └── pata.rs │ │ └── tty.rs │ ├── elf │ │ ├── kernel.rs │ │ ├── mod.rs │ │ └── parser.rs │ ├── event.rs │ ├── file │ │ ├── fd.rs │ │ ├── fs │ │ │ ├── ext2 │ │ │ │ ├── bgd.rs │ │ │ │ ├── dirent.rs │ │ │ │ ├── inode.rs │ │ │ │ └── mod.rs │ │ │ ├── initramfs.rs │ │ │ ├── kernfs.rs │ │ │ ├── mod.rs │ │ │ ├── proc │ │ │ │ ├── mem_info.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── proc_dir │ │ │ │ │ ├── cmdline.rs │ │ │ │ │ ├── cwd.rs │ │ │ │ │ ├── environ.rs │ │ │ │ │ ├── exe.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── mounts.rs │ │ │ │ │ ├── stat.rs │ │ │ │ │ └── status.rs │ │ │ │ ├── self_link.rs │ │ │ │ ├── sys_dir │ │ │ │ │ └── mod.rs │ │ │ │ ├── uptime.rs │ │ │ │ └── version.rs │ │ │ └── tmp │ │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── perm.rs │ │ ├── pipe.rs │ │ ├── socket.rs │ │ ├── util.rs │ │ ├── vfs │ │ │ ├── mod.rs │ │ │ ├── mountpoint.rs │ │ │ └── node.rs │ │ └── wait_queue.rs │ ├── kernel.rs │ ├── libc │ │ ├── memcmp.c │ │ └── strlen.c │ ├── logger.rs │ ├── memory │ │ ├── alloc.rs │ │ ├── buddy.rs │ │ ├── cache.rs │ │ ├── malloc │ │ │ ├── block.rs │ │ │ ├── chunk.rs │ │ │ └── mod.rs │ │ ├── memmap.rs │ │ ├── mmio.rs │ │ ├── mod.rs │ │ ├── oom.rs │ │ ├── ring_buffer.rs │ │ ├── stats.rs │ │ ├── trace.rs │ │ ├── user.rs │ │ └── vmem.rs │ ├── module │ │ ├── mod.rs │ │ ├── relocation.rs │ │ └── version.rs │ ├── multiboot.rs │ ├── net │ │ ├── buff.rs │ │ ├── icmp.rs │ │ ├── ip.rs │ │ ├── lo.rs │ │ ├── mod.rs │ │ ├── osi.rs │ │ ├── sockaddr.rs │ │ └── tcp.rs │ ├── panic.rs │ ├── power.rs │ ├── print.rs │ ├── process │ │ ├── exec │ │ │ ├── elf.rs │ │ │ ├── mod.rs │ │ │ └── vdso.rs │ │ ├── mem_space │ │ │ ├── gap.rs │ │ │ ├── mapping.rs │ │ │ ├── mod.rs │ │ │ └── transaction.rs │ │ ├── mod.rs │ │ ├── pid.rs │ │ ├── rusage.rs │ │ ├── scheduler │ │ │ ├── mod.rs │ │ │ └── switch.rs │ │ ├── signal │ │ │ ├── mod.rs │ │ │ └── ucontext.rs │ │ └── user_desc.rs │ ├── selftest.rs │ ├── sync │ │ ├── atomic.rs │ │ ├── mod.rs │ │ ├── mutex.rs │ │ ├── once.rs │ │ ├── rcu.rs │ │ ├── rwlock.rs │ │ └── spinlock.rs │ ├── syscall │ │ ├── dirent.rs │ │ ├── execve.rs │ │ ├── fcntl.rs │ │ ├── fd.rs │ │ ├── fs.rs │ │ ├── getrandom.rs │ │ ├── host.rs │ │ ├── ioctl.rs │ │ ├── mem.rs │ │ ├── mod.rs │ │ ├── module.rs │ │ ├── mount.rs │ │ ├── pipe.rs │ │ ├── process.rs │ │ ├── select.rs │ │ ├── signal.rs │ │ ├── socket.rs │ │ ├── stat.rs │ │ ├── sync.rs │ │ ├── time.rs │ │ ├── user.rs │ │ ├── util │ │ │ ├── at.rs │ │ │ └── mod.rs │ │ └── wait.rs │ ├── time │ │ ├── clock.rs │ │ ├── hw │ │ │ ├── mod.rs │ │ │ ├── pit.rs │ │ │ └── rtc.rs │ │ ├── mod.rs │ │ ├── timer.rs │ │ └── unit.rs │ └── tty │ │ ├── ansi.rs │ │ ├── mod.rs │ │ ├── termios.rs │ │ └── vga.rs └── vdso │ ├── linker.ld │ ├── x86.s │ └── x86_64.s ├── macros ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── rustfmt.toml └── src │ ├── aml.rs │ ├── lib.rs │ └── util.rs ├── mod ├── build └── template │ ├── Cargo.toml │ ├── rust-toolchain.toml │ └── src │ └── mod.rs ├── rust-toolchain.toml ├── rustfmt.toml └── utils ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src ├── boxed.rs ├── bytes.rs ├── collections ├── bitfield.rs ├── btreemap.rs ├── hashmap │ ├── hash.rs │ ├── mod.rs │ └── raw.rs ├── hashset.rs ├── id_allocator.rs ├── list.rs ├── mod.rs ├── path.rs ├── string.rs └── vec.rs ├── cpio.rs ├── errno.rs ├── lib.rs ├── limits.rs ├── math.rs ├── ptr ├── arc.rs ├── cow.rs └── mod.rs └── unsafe_mut.rs /doc/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | mermaid.min.js 3 | mermaid-init.js 4 | -------------------------------------------------------------------------------- /doc/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["llenotre"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Maestro Kernel" 7 | 8 | [preprocessor.mermaid] 9 | command = "mdbook-mermaid" 10 | 11 | [output.html] 12 | additional-js = ["mermaid.min.js", "mermaid-init.js"] 13 | mathjax-support = true 14 | -------------------------------------------------------------------------------- /doc/license-header.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | -------------------------------------------------------------------------------- /doc/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./intro.md) 4 | - [External Documentation](./external_doc.md) 5 | 6 | # Getting started 7 | 8 | - [Installer](./installer.md) 9 | - [Build a system from scratch](./build.md) 10 | - [Debug](./debug.md) 11 | 12 | # Booting 13 | 14 | - [Booting](./booting.md) 15 | 16 | # Display 17 | 18 | - [TTY](./tty.md) 19 | 20 | # Memory 21 | 22 | - [Allocators](./memory/alloc.md) 23 | - [Memory map](./memory/mem_map.md) 24 | - [Memory space](./memory/mem_space.md) 25 | - [Tracing](./memory/tracing.md) 26 | 27 | # Process 28 | 29 | - [Process](./process.md) 30 | 31 | # Device 32 | 33 | - [Devices](./device.md) 34 | 35 | # File 36 | 37 | - [Filesystem](./file/fs.md) 38 | - [tmpfs](./file/tmpfs.md) 39 | - [procfs](./file/procfs.md) 40 | - [sysfs](./file/sysfs.md) 41 | 42 | # Userspace 43 | 44 | - [Userspace](./userspace/exec.md) 45 | - [ELF](./userspace/elf.md) 46 | - [Script](./userspace/script.md) 47 | 48 | # Modules 49 | 50 | - [Module](./module.md) 51 | -------------------------------------------------------------------------------- /doc/src/booting.md: -------------------------------------------------------------------------------- 1 | # Booting 2 | 3 | The kernel booting sequence is supervised by the **Multiboot2** standard. 4 | 5 | ### Command line arguments 6 | 7 | Multiboot allows passing command line arguments to the kernel at boot. The following arguments are supported: 8 | 9 | - `-root <major> <minor>` (required): Tells the major/minor version numbers of the VFS's root device 10 | - `-init <path>`: Tells the path of the binary to be run as the first process instead of the default path 11 | - `-silent`: Tells the kernel not to show logs on screen while booting 12 | 13 | ## Memory remapping 14 | 15 | The kernel is divided into two parts: 16 | - Booting stub, located at `0x100000` on virtual memory 17 | - Main kernel code, located at different positions depending on the architecture in virtual memory: 18 | - **x86**: `0xc0200000` 19 | - **x86_64**: `0xffff800000200000` 20 | 21 | Because GRUB loads the whole kernel at `0x100000`, it is required to remap the memory to use the main code of the kernel. This is done through paging. 22 | 23 | TODO: Add schematics of memory mapping 24 | 25 | The mapping of memory at `0x100000` is removed later because it is not required anymore. 26 | 27 | ## Init process 28 | 29 | The init process is the first program to be run by the kernel, which is in charge of initializing the system. 30 | 31 | The program must be located at `/sbin/init`, or another path if specified as a command line argument. 32 | 33 | The init process has PID `1` and is running as the superuser (uid: `0`, gid: `0`). If this process is killed, the kernel panics. 34 | -------------------------------------------------------------------------------- /doc/src/debug.md: -------------------------------------------------------------------------------- 1 | # Debug 2 | 3 | This section describes debugging features integrated to the kernel. 4 | 5 | 6 | 7 | ## Selftesting 8 | 9 | Unit tests and integration tests are present in the kernel. 10 | 11 | To run them, use the command: 12 | 13 | ```sh 14 | cargo test 15 | ``` 16 | 17 | 18 | 19 | ## GDB 20 | 21 | GDB can be attached to the kernel in order to debug it. To do so, run the script located at `scripts/gdb.sh`. 22 | 23 | The script runs the kernel with QEMU, using the disk present in the file `qemu_disk` and automatically attaches GDB to it. To begin execution, just type the `continue` command on GDB. 24 | 25 | 26 | 27 | ## Logging 28 | 29 | The kernel can transmit logs to another machine (the host machine if running in a virtual machine) using the serial port. 30 | 31 | On QEMU, logs can be saved to the `serial.log` file by setting the `QEMUFLAGS` environment variable: 32 | 33 | ``` 34 | QEMUFLAGS="-serial file:serial.log" cargo run 35 | ``` 36 | -------------------------------------------------------------------------------- /doc/src/device.md: -------------------------------------------------------------------------------- 1 | # Devices 2 | 3 | Devices are represented by two types of files: Block Devices and Char Devices. 4 | 5 | Each device file is also associated with a major and minor number, allowing to identify it. 6 | 7 | Those files are usually present in the `/dev` directory. 8 | 9 | Device type abbreviations: 10 | - C = Char Device 11 | - B = Block Device 12 | 13 | The following sections describe devices that may be present on the system. This list may be extended by kernel modules and as such, doesn't include every possible devices. 14 | 15 | 16 | 17 | ## Default devices list 18 | 19 | The following devices are present on the system by default. 20 | 21 | | Path | Type | Major | Minor | Description | 22 | |----------------|------|-------|-------|-----------------------------------------------------------------------------------------------------------------------------------------| 23 | | `/dev/null` | C | `1` | `3` | This device does nothing. Reading from it returns EOF and writing to it discards the data | 24 | | `/dev/zero` | C | `1` | `5` | Reading returns an infinite amount of zeros bytes and writing to it discards the data | 25 | | `/dev/random` | C | `1` | `8` | Reading returns random bytes and writing to it feeds the kernel's entropy pool. If not enough entropy is available, reading is blocking | 26 | | `/dev/urandom` | C | `1` | `9` | Reading returns random bytes and writing to it feeds the kernel's entropy pool. Contrary to `/dev/random`, reading is never blocking | 27 | | `/dev/kmsg` | C | `1` | `11` | Reading returns kernel logs and writing appends kernel logs | 28 | | `/dev/tty` | C | `5` | `0` | Device representing the TTY of the current process | 29 | 30 | 31 | 32 | ## Dynamic devices 33 | 34 | This section describes devices that may or may not be present depending on the system's peripherals. 35 | 36 | | Path | Type | Major | Minor | Description | 37 | |-------------|------|-------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 38 | | `/dev/sdX` | B | `8` | `n * 16` | A SCSI drive. `X` has to be replaced by a single letter. Each disk has its own unique letter. `n` is the number associated with the letter (`a` -> `0`, `b` -> `1`, etc...) | 39 | | `/dev/sdXN` | B | `8` | `n * 16 + N + 1` | A partition on a SCSI drive. This device works the same as the previous, except `N` is the partition number | 40 | -------------------------------------------------------------------------------- /doc/src/external_doc.md: -------------------------------------------------------------------------------- 1 | # External Documentation 2 | 3 | This page lists the external documentation that is used for the development of the kernel. 4 | 5 | # Hardware technical references 6 | 7 | - [Intel® 64 and IA-32 Architectures Software Developer Manuals](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html) 8 | - [UEFI specification documents](https://uefi.org/uefi) 9 | - [PCI Local Bus Specification](https://www.ics.uci.edu/~harris/ics216/pci/PCI_22.pdf) 10 | 11 | # Software interfaces references 12 | 13 | - [Multiboot2 Specification](https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html) 14 | - [POSIX Technical Standard Base Specifications](https://pubs.opengroup.org/onlinepubs/9799919799/) 15 | 16 | ## ELF 17 | 18 | - [Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification](https://refspecs.linuxfoundation.org/elf/elf.pdf) 19 | - [ELF-64 Object File Format](https://uclibc.org/docs/elf-64-gen.pdf) 20 | 21 | ## System V 22 | 23 | - [System V Application Binary Interface - Intel386 Architecture Processor Supplement](https://www.uclibc.org/docs/psABI-i386.pdf) 24 | - [System V Application Binary Interface AMD64 Architecture Processor Supplement](https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf) 25 | 26 | # Miscellaneous 27 | 28 | - [OSDev](https://wiki.osdev.org/) 29 | -------------------------------------------------------------------------------- /doc/src/file/fs.md: -------------------------------------------------------------------------------- 1 | # Filesystem 2 | 3 | A filesystem is a representation of a files hierarchy on a storage device. 4 | 5 | The following filesystems are natively supported: 6 | - **ext2**: a common filesystem in UNIX environments. Now obsolete (to be replaced by **ext4**) 7 | 8 | ## kernfs 9 | 10 | A **kernfs** is a special kind of filesystem that do not store any information on any storage device. Its purpose is to provide a file interface to easily transmit information to the userspace. 11 | 12 | Native kernfs kinds include: 13 | - [tmpfs](tmpfs.md): storage for temporary files on RAM 14 | - [procfs](procfs.md): provides information about processes 15 | - [sysfs](sysfs.md): provides information about the system 16 | 17 | ## Virtual FileSystem 18 | 19 | The **VFS** is a filesystem that has no representation on any storage device. Rather, it is built from other filesystems that are assembled together to form the system's files hierarchy. 20 | 21 | **Mounting** a filesystem is the action of adding a filesystem to the VFS so that it becomes accessible to users. 22 | 23 | The directory on which a filesystem is mounted is called a **mountpoint**. 24 | -------------------------------------------------------------------------------- /doc/src/file/procfs.md: -------------------------------------------------------------------------------- 1 | # procfs 2 | 3 | The `procfs` is a filesystem providing information for each running processes. Its structure is based on the one from Linux. 4 | 5 | Each process has its own directory at the root of the filesystem. The name of the directory is the PID of the process in decimal. 6 | 7 | A process's directory contains files with information about the process. 8 | 9 | TODO: list files 10 | -------------------------------------------------------------------------------- /doc/src/file/sysfs.md: -------------------------------------------------------------------------------- 1 | # sysfs 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /doc/src/file/tmpfs.md: -------------------------------------------------------------------------------- 1 | # tmpfs 2 | 3 | The **tmpfs** provides temporary storage on RAM for any file. It is usually mounted at the path `/tmp`. 4 | 5 | Since files are stored in RAM, they are all removed when the system is shutdown on reboot. 6 | 7 | The main goal is to provide fast access to files that do not require persistence. 8 | -------------------------------------------------------------------------------- /doc/src/installer.md: -------------------------------------------------------------------------------- 1 | # Installer 2 | 3 | Since the kernel cannot run by itself, the [installer](https://github.com/maestro-os/maestro-install) can be used to build it. 4 | 5 | The utility on the installer's repository produces an ISO file which can be run on a virtual or physical machine to install the operating system on it. 6 | 7 | For more information, check the documentation of the installer on its repository. 8 | -------------------------------------------------------------------------------- /doc/src/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## Overview 4 | 5 | Maestro is a Unix-like kernel written in Rust. It follows the **POSIX** specifications. 6 | 7 | This documentation is a global overview on the way the Maestro kernel and its interfaces work. It is not intended to be an exhaustive documentation. 8 | 9 | ## License 10 | 11 | The kernel and this documentation are distributed under the [AGPL-3.0 license](https://www.gnu.org/licenses/agpl-3.0.en.html). 12 | -------------------------------------------------------------------------------- /doc/src/memory/alloc.md: -------------------------------------------------------------------------------- 1 | # Allocators 2 | 3 | This page describes memory allocators that are implemented inside the kernel. 4 | 5 | ## Buddy allocator 6 | 7 | The buddy allocator is the primary allocator which provides memory pages to all other allocators. 8 | 9 | This allocator takes most of the memory on the system and works by recursively dividing them in two until a block of the required size is available. 10 | 11 | Freeing memory works the other way around, by merging adjacent free blocks. 12 | 13 | More details are available on [Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation). 14 | 15 | Since this allocator provides at least one page of memory per allocation, smaller objects need another allocator to subdivide pages into usable chunks. This is the role of **malloc**. 16 | 17 | ## malloc 18 | 19 | The kernel has its own version of the `malloc` function to allow memory allocations internal to the kernel. 20 | 21 | The implementation is a located in `kernel::memory::malloc`. 22 | 23 | Functions: 24 | - `alloc`: Allocates the given amount of bytes and returns a pointer to the chunk 25 | - `realloc`: Changes the size of an allocation 26 | - `free`: Frees a previously allocated chunk 27 | 28 | The allocator works using memory pages provided by the buddy allocator. 29 | 30 | Allocated chunks are guaranteed to: 31 | - Be accessible from kernelspace 32 | - Not overlap with other chunks 33 | - Be aligned in memory 34 | 35 | ### Safe interface 36 | 37 | It is recommended to use the safe interface through the `Alloc` structure instead of the low-level functions described above. 38 | 39 | ## vmem 40 | 41 | Virtual memory allows the kernel to provide each process with its own memory space, independent of other processes. 42 | 43 | Refer to the documentation of the target CPU architecture for details on the way virtual memory works. 44 | -------------------------------------------------------------------------------- /doc/src/memory/mem_map.md: -------------------------------------------------------------------------------- 1 | # Memory map 2 | 3 | The memory space of each process is divided into several chunks. Those chunks are described for each architecture in the following sections. 4 | 5 | ## x86 (32-bit) 6 | 7 | | Begin | End | Description | 8 | |--------------|--------------|----------------------------------------| 9 | | `0x00000000` | `0x00001000` | Not mapped, for the `NULL` pointer | 10 | | `0x00001000` | `0xc0000000` | Userspace (program image, stack, heap) | 11 | | `0xc0000000` | end | Kernel space | 12 | 13 | ## x86_64 14 | 15 | | Begin | End | Description | 16 | |----------------------|----------------------|----------------------------------------| 17 | | `0x0000000000000000` | `0x0000000000001000` | Not mapped, for the `NULL` pointer | 18 | | `0x0000000000001000` | `0x0000800000000000` | Userspace (program image, stack, heap) | 19 | | `0x0000800000000000` | `0xffff800000000000` | Canonical hole, cannot be used | 20 | | `0xffff800000000000` | end | Kernel space | 21 | -------------------------------------------------------------------------------- /doc/src/memory/mem_space.md: -------------------------------------------------------------------------------- 1 | # Memory space 2 | 3 | A memory space is a virtual memory context on which reside one or more process. It allows isolation of processes from each others. 4 | 5 | Some processes may share the same memory space, for example using the `clone` system call. 6 | 7 | A memory space contains the following components: 8 | - Memory mapping: a region of virtual memory in use 9 | - Memory gap: a region of virtual memory which is free, ready for allocations 10 | 11 | A process can interact with its memory space using system calls such as `mmap`, `munmap`, `mlock`, `munlock` and `mprotect`. 12 | 13 | ## Lazy allocations 14 | 15 | A memory mapping is supposed to point to a physical memory in order to work properly. However, allocating physical memory directly when the memory mapping is created or cloned takes significant resources that might not be used. 16 | 17 | For example, when using the `fork` system call, the whole memory space has to be duplicated, often to be quickly followed by a call to `execve`, which removes the whole memory space. 18 | 19 | To prevent this problem, physical memory is allocated lazily. 20 | 21 | To do so, the kernel maps the virtual memory in read-only. Then, when an attempt to modify the memory (write) occurs, the CPU triggers a page fault, which can then be handled by the kernel to make the actual allocation 22 | 23 | The following cases can occur: 24 | - simple allocation (example: `mmap`): The virtual memory is mapped to a default page which contains only zeros. When the kernel receives a page fault for this mapping, it allocates a new physical page and maps it at the appropriate location 25 | - duplication (example: `fork`): The virtual memory of the new memory space is mapped to the same physical memory as the original. Then writing is disabled on both. When a page fault is received, the kernel performs the same operation as the previous point, except the data present on the page is also copied. 26 | 27 | Once the allocation has been made, the kernel enables writing permission on the mapping, then resume the execution. This procedure is totally transparent from the process's point of view. 28 | -------------------------------------------------------------------------------- /doc/src/memory/tracing.md: -------------------------------------------------------------------------------- 1 | # Tracing 2 | 3 | The `memtrace` feature allows to trace usage of memory allocators. This is a debug feature, so it is not meant to be used in production. 4 | 5 | To use it, compile the kernel with the `memtrace` feature: 6 | 7 | ```shell 8 | cargo build --features memtrace 9 | ``` 10 | 11 | When run, the kernel will then write tracing data to the **COM2** serial port. 12 | 13 | This data can then be fed to [kern-profile](https://github.com/maestro-os/kern-profile) to generate a FlameGraph. 14 | 15 | ## Data format 16 | 17 | The output data is a series of samples. A sample represents an operation on the allocator. 18 | 19 | The following operations exist: 20 | 21 | | ID | Name | Description | 22 | |----|---------|------------------------------------------------| 23 | | 0 | alloc | Allocate memory | 24 | | 1 | realloc | Resize a previously allocated region of memory | 25 | | 2 | free | Frees a previously allocated region of memory | 26 | 27 | Each sample is written one after the other and has the following layout in memory: 28 | 29 | | Offset | Size | Name | Description | 30 | |-------------|--------------|--------|------------------------------------------------------| 31 | | `0` | `1` | nlen | The length of the name of the allocator | 32 | | `1` | `nlen` | name | The name of the allocator | 33 | | `nlen + 1` | `1` | op | The ID of the operation (see table above) | 34 | | `nlen + 2` | `8` | ptr | The address of the region affected by the operation | 35 | | `nlen + 10` | `8` | size | The new size of the region affected by the operation | 36 | | `nlen + 18` | `1` | nframe | The number of frames in the callstack | 37 | | `nlen + 19` | `nframe * 8` | frames | The pointers of the frames in the callstack | 38 | -------------------------------------------------------------------------------- /doc/src/module.md: -------------------------------------------------------------------------------- 1 | # Module 2 | 3 | A kernel module allows to add a feature to the kernel at runtime. 4 | 5 | This chapter describes how to write a kernel module. 6 | 7 | ## Template 8 | 9 | A basic kernel module contains the following files: 10 | 11 | ``` 12 | |- Cargo.toml 13 | |- Cargo.lock 14 | |- rust-toolchain.toml 15 | |- src/ 16 | |- mod.rs 17 | ``` 18 | 19 | These files are located in the `mod/template/` directory of the kernel's sources. 20 | 21 | `Cargo.toml`: 22 | 23 | ```toml 24 | {{#include ../../mod/template/Cargo.toml}} 25 | ``` 26 | 27 | `mod.rs`: 28 | 29 | ```rust 30 | {{#include ../../mod/template/src/mod.rs}} 31 | ``` 32 | 33 | The `kernel` crate gives access to the kernel's functions. 34 | 35 | The `kernel::module` macro allows to define the kernel module with its dependencies. 36 | 37 | > **NOTE**: if the `kernel::module` declaration is not present, the module will not work 38 | 39 | The following properties have to be taken into account when writing a module: 40 | - `init` is called once each times the module is loaded. The execution must be not block since it would freeze the system 41 | - `fini` can be called at all times and must free every resource allocated by the module 42 | 43 | On success, `init` returns `true`. On failure, it returns `false`. 44 | 45 | ## Versioning 46 | 47 | Kernel module versioning is a small subset of the [SemVer](https://semver.org/) specification. 48 | 49 | Versions MUST have the following format: `X.Y.Z` where: 50 | - `X` is a positive number (including zero) representing the *major version* 51 | - `Y` is a positive number (including zero) representing the *minor version* 52 | - `Z` is a positive number (including zero) representing the *patch version* 53 | 54 | The same rules as the SemVer specification apply for those numbers. 55 | 56 | ### Backus-Naur Form 57 | 58 | ``` 59 | <version> ::= <major> "." <minor> "." <patch> 60 | ``` 61 | 62 | ## Interface references 63 | 64 | The references to the kernel's internals and module interfaces can be found [here](references/kernel/index.html). 65 | 66 | ## Building 67 | 68 | The procedure to build a kernel module is the following: 69 | - Build the kernel in debug or release mode (`--release`), depending on which profile you want 70 | - `cd` into the root of the module's source directory 71 | - Set environment variables: 72 | - `PROFILE`: profile to build for (either `debug` or `release`). Default value: `debug` 73 | - `ARCH`: architecture to build for (example: `x86`). Default value: `x86` 74 | - Build the module 75 | 76 | Example: 77 | ```sh 78 | PROFILE="debug" ARCH="x86" ../maestro/mod/build 79 | ``` 80 | 81 | Then, the built module can be found at `target/<arch>/<profile>/lib<name>.so` 82 | 83 | > **NOTE**: It is important that the specified profile and architecture match the compilation of the kernel, otherwise compilation will not work 84 | -------------------------------------------------------------------------------- /doc/src/process.md: -------------------------------------------------------------------------------- 1 | # Process 2 | 3 | A process is a program being executed by the kernel. Each process has a unique PID which is allocated at creation. 4 | 5 | ## State 6 | 7 | A process can have the following states: 8 | 9 | | Name | Associated character | Description | 10 | |------------|----------------------|------------------------------------------------------------------------------------------| 11 | | `Running` | R | The process is currently running or is ready to be resumed by the scheduler | 12 | | `Sleeping` | S | The process is waiting on a resource to become available (usualy I/O or another process) | 13 | | `Stopped` | T | The process is paused | 14 | | `Zombie` | Z | The process has been terminated and cannot resume, ever | 15 | 16 | The `Running` state is the only state in which a process can be executed. 17 | 18 | The following state transitions are valid: 19 | 20 | ```mermaid 21 | stateDiagram-v2 22 | [*] --> Running 23 | 24 | Sleeping --> Running 25 | Stopped --> Running 26 | 27 | Running --> Sleeping 28 | 29 | Running --> Stopped 30 | Sleeping --> Stopped 31 | 32 | Stopped --> Zombie 33 | Sleeping --> Zombie 34 | Running --> Zombie 35 | 36 | Zombie --> [*] 37 | ``` 38 | 39 | ## Scheduler 40 | 41 | The scheduler is a component that decide which process is running, and when. 42 | 43 | The system triggers interruptions periodically in order to interrupt the current process, then the scheduler determines the next process to be run, and switches context to that process. 44 | 45 | The frequency of interruption is determined by the number of processes in running state. 46 | 47 | To determine the next process to be run, the scheduler uses different information such as state and priority of the process. 48 | -------------------------------------------------------------------------------- /doc/src/tty.md: -------------------------------------------------------------------------------- 1 | # TTY 2 | 3 | The TTY (TeleTypeWriter) is the main interface with the kernel. 4 | 5 | The TTY (partially) supports: 6 | - POSIX terminal interface ([Wikipedia](https://en.wikipedia.org/wiki/POSIX_terminal_interface)) 7 | - ANSI escape codes ([Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code)) 8 | 9 | If running with the VGA text mode, the TTY can only display the following characters: 10 | 11 |  12 | -------------------------------------------------------------------------------- /doc/src/userspace/elf.md: -------------------------------------------------------------------------------- 1 | # ELF 2 | 3 | ELF (Executable and Linkable Format) is an executable format supported by the kernel, which can be used to represent programs. 4 | 5 | The specification of this format can be found on the page [External Documentation](../external_doc.md). 6 | -------------------------------------------------------------------------------- /doc/src/userspace/exec.md: -------------------------------------------------------------------------------- 1 | # Userspace 2 | 3 | The userspace is the place where user programs run. It is meant to have an interface close to Linux. 4 | 5 | A program running in userspace is initialized using the **System V ABI** specification (see [external documentation](../external_doc.md)). 6 | 7 | ## System call 8 | 9 | System calls are the main way for programs to communicate with the kernel. 10 | 11 | The system call table can be found at the root of the module `kernel::syscall`. 12 | 13 | ### ABI by architecture 14 | 15 | **Note**: the **end** bound of errno ranges are exclusive 16 | 17 | #### x86 (32 bits) 18 | 19 | | | | 20 | |--------------|------------------------------------------| 21 | | Instruction | `int 0x80` | 22 | | Syscall ID | `eax` | 23 | | Arguments | `ebx`, `ecx`, `edx`, `esi`, `edi`, `ebp` | 24 | | Return value | `eax` | 25 | | Errno range | `-4095..0` | 26 | 27 | #### x86_64 28 | 29 | | | | 30 | |--------------|----------------------------------------| 31 | | Instruction | `int 0x80`, `syscall` | 32 | | Syscall ID | `rax` | 33 | | Arguments | `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9` | 34 | | Return value | `rax` | 35 | | Errno range | `-4095..0` | 36 | 37 | ## Compatibility mode 38 | 39 | The kernel supports running 32-bit programs on 64-bit kernels. The ABI is the same as kernels compiled for 32-bit. 40 | -------------------------------------------------------------------------------- /doc/src/userspace/script.md: -------------------------------------------------------------------------------- 1 | # Script 2 | 3 | A script can be run directly by the kernel by specifying its interpreter use a **shebang**. 4 | 5 | The syntax of a shebang is the following: 6 | 7 | ``` 8 | #!interpreter-path [optional-arg] 9 | ``` 10 | 11 | The shebang is placed at the top of the script file. 12 | 13 | Description: 14 | - `interpreter-path` is the path to the interpreter program. The interpreter can itself be a script, up to 4 recursions 15 | - `optional-arg` is an optional argument to be appended to the interpreter 16 | -------------------------------------------------------------------------------- /inttest/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | disk -------------------------------------------------------------------------------- /inttest/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "inttest" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "libc", 10 | "memmap2", 11 | ] 12 | 13 | [[package]] 14 | name = "libc" 15 | version = "0.2.172" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 18 | 19 | [[package]] 20 | name = "memmap2" 21 | version = "0.9.5" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" 24 | dependencies = [ 25 | "libc", 26 | ] 27 | -------------------------------------------------------------------------------- /inttest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inttest" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [[bin]] 7 | name = "inttest" 8 | path = "src/main.rs" 9 | 10 | [[bin]] 11 | name = "init" 12 | path = "src/boot.rs" 13 | 14 | [dependencies] 15 | libc = "0.2.172" 16 | memmap2 = "0.9.5" 17 | -------------------------------------------------------------------------------- /inttest/README.md: -------------------------------------------------------------------------------- 1 | # Integration tests 2 | 3 | Test suite for the kernel, made to be run by continuous integration. 4 | 5 | The test suite reports its results through the serial port. 6 | 7 | 8 | 9 | ## Build an image 10 | 11 | To use the test suite, one must first build a disk image. 12 | 13 | > **Note**: Building the image requires the command `debugfs` 14 | 15 | This can be done with the `build.sh` script: 16 | ```sh 17 | ./build.sh 18 | ``` 19 | 20 | This script produces the `disk` file, which can then be used by QEMU with the following option: 21 | ``` 22 | -drive file=disk,format=raw 23 | ``` 24 | -------------------------------------------------------------------------------- /inttest/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | if [ -z "$TARGET" ]; then 6 | export TARGET=x86_64-unknown-linux-musl 7 | fi 8 | 9 | # Build 10 | cargo build -Zbuild-std --target "$TARGET" 11 | 12 | # Create disk and filesystem 13 | dd if=/dev/zero of=disk bs=1M count=1024 14 | mkfs.ext2 disk 15 | 16 | # Fill filesystem 17 | debugfs -wf - disk <<EOF 18 | mkdir /dev 19 | mkdir /sbin 20 | write target/$TARGET/debug/init /sbin/init 21 | write target/$TARGET/debug/inttest /inttest 22 | EOF 23 | -------------------------------------------------------------------------------- /inttest/src/boot.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Boot stub for integration tests. 20 | //! 21 | //! This file exists to run the tests as a second process in order to retrieve the exit code, then 22 | //! shutdown the machine. 23 | 24 | use std::{os::unix::process::ExitStatusExt, process::Command}; 25 | 26 | pub fn main() { 27 | let status = Command::new("/inttest").status().unwrap(); 28 | if let Some(sig) = status.signal() { 29 | eprintln!("[KILLED] {sig}"); 30 | } 31 | let cmd = if status.success() { -1 } else { -2 }; 32 | unsafe { 33 | // Sync to disk 34 | libc::sync(); 35 | // Shutdown 36 | libc::syscall(libc::SYS_reboot, 0xde145e83u32, 0x40367d6eu32, cmd, 0); 37 | } 38 | panic!("Shutdown failed!"); 39 | } 40 | -------------------------------------------------------------------------------- /inttest/src/mount.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Filesystem mounting tests. 20 | 21 | use crate::{log, util, util::TestResult}; 22 | use std::{ffi::CString, fs, ptr::null}; 23 | 24 | pub fn mount(src: &str, target: &str, fstype: &str) -> TestResult { 25 | log!("Create directory"); 26 | fs::create_dir_all(target)?; 27 | log!("Mount"); 28 | let src = CString::new(src)?; 29 | let target = CString::new(target)?; 30 | let fstype = CString::new(fstype)?; 31 | util::mount( 32 | src.as_c_str(), 33 | target.as_c_str(), 34 | fstype.as_c_str(), 35 | 0, 36 | null(), 37 | )?; 38 | Ok(()) 39 | } 40 | 41 | pub fn umount(target: &str) -> TestResult { 42 | let target = CString::new(target)?; 43 | util::umount(target.as_c_str())?; 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /inttest/src/procfs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! procfs filesystem testing. 20 | 21 | use crate::{ 22 | test_assert_eq, 23 | util::{TestError, TestResult}, 24 | }; 25 | use std::{collections::HashMap, env, env::current_dir, fs, os::unix::ffi::OsStrExt}; 26 | 27 | pub fn cwd() -> TestResult { 28 | let cwd = fs::read_link("/proc/self/cwd")?; 29 | test_assert_eq!(cwd, current_dir()?); 30 | Ok(()) 31 | } 32 | 33 | pub fn exe() -> TestResult { 34 | let exe = fs::read_link("/proc/self/exe")?; 35 | test_assert_eq!(exe.as_os_str().as_bytes(), b"/inttest"); 36 | Ok(()) 37 | } 38 | 39 | pub fn cmdline() -> TestResult { 40 | let args0 = fs::read("/proc/self/cmdline")?; 41 | let args1 = env::args_os(); 42 | for (a0, a1) in args0.split(|b| *b == b'\0').zip(args1) { 43 | test_assert_eq!(a0, a1.as_bytes()); 44 | } 45 | Ok(()) 46 | } 47 | 48 | pub fn environ() -> TestResult { 49 | let environ = fs::read("/proc/self/environ")?; 50 | let args0 = environ 51 | .split(|b| *b == b'\0') 52 | .filter(|var| !var.is_empty()) 53 | .map(|var| { 54 | let off = var 55 | .iter() 56 | .enumerate() 57 | .find(|(_, b)| **b == b'=') 58 | .map(|(i, _)| i) 59 | .ok_or_else(|| TestError("missing `=` for environment variable".to_owned()))?; 60 | let (name, value) = var.split_at(off); 61 | Ok((name, &value[1..])) 62 | }) 63 | .collect::<Result<HashMap<_, _>, TestError>>()?; 64 | let args1: Vec<_> = env::vars_os().collect(); 65 | let args1 = args1 66 | .iter() 67 | .map(|(name, val)| (name.as_bytes(), val.as_bytes())) 68 | .collect::<HashMap<_, _>>(); 69 | test_assert_eq!(args0, args1); 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /inttest/src/signal.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Signals testing. 20 | 21 | use crate::{ 22 | log, 23 | util::{TestResult, kill, signal}, 24 | }; 25 | use libc::{SIG_DFL, SIGINT, getpid}; 26 | use std::{ 27 | ffi::c_int, 28 | sync::atomic::{ 29 | AtomicBool, 30 | Ordering::{Acquire, Release}, 31 | }, 32 | }; 33 | 34 | static HIT: AtomicBool = AtomicBool::new(false); 35 | 36 | extern "C" fn signal_handler(_: c_int) { 37 | HIT.store(true, Release); 38 | } 39 | 40 | pub fn handler() -> TestResult { 41 | log!("Register signal handler"); 42 | signal(SIGINT, signal_handler as usize)?; 43 | 44 | log!("Kill self"); 45 | assert!(!HIT.load(Acquire)); 46 | unsafe { 47 | kill(getpid(), SIGINT)?; 48 | } 49 | assert!(HIT.load(Acquire)); 50 | 51 | log!("Cleanup"); 52 | HIT.store(false, Release); 53 | signal(SIGINT, SIG_DFL)?; 54 | 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /kernel/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [unstable] 2 | build-std = ["core"] 3 | 4 | [target.x86] 5 | runner = "scripts/qemu.sh" 6 | 7 | [target.x86_64] 8 | runner = "scripts/qemu.sh" 9 | 10 | [build] 11 | # Set default target 12 | target = "arch/x86_64/x86_64.json" 13 | rustflags = [ 14 | "-Zexport-executable-symbols" 15 | ] 16 | -------------------------------------------------------------------------------- /kernel/.gitignore: -------------------------------------------------------------------------------- 1 | # Configuration file 2 | build-config.toml 3 | 4 | # Compiled files 5 | *.o 6 | *.a 7 | *.so 8 | iso/ 9 | *.iso 10 | 11 | # Random garbage 12 | *.gch 13 | *.swp 14 | 15 | # Rust compilation files 16 | target/ 17 | 18 | # Log files 19 | *.log 20 | 21 | # Disk files 22 | qemu_disk 23 | mnt/ 24 | -------------------------------------------------------------------------------- /kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["profile-rustflags"] 2 | 3 | [package] 4 | name = "maestro" 5 | version = "0.1.0" 6 | authors = ["llenotre <llenotre@student.42.fr>"] 7 | edition = "2024" 8 | build = "build/main.rs" 9 | 10 | [lib] 11 | name = "kernel" 12 | path = "src/kernel.rs" 13 | 14 | # Export the kernel as executable 15 | [[bin]] 16 | name = "maestro" 17 | path = "src/bin.rs" 18 | 19 | [dependencies] 20 | macros = { path = "../macros" } 21 | utils = { path = "../utils" } 22 | 23 | [build-dependencies] 24 | cc = { version = "1.2.6", features = ["parallel"] } 25 | serde = { version = "1.0.217", features = ["derive"] } 26 | serde_json = "1.0.134" 27 | toml = "0.8.19" 28 | 29 | [features] 30 | default = [] 31 | memtrace = [] 32 | strace = ["macros/strace"] 33 | 34 | [lints.rust] 35 | unexpected_cfgs = { level = "warn", check-cfg = [ 36 | "cfg(config_debug_storage_test)", 37 | "cfg(config_debug_qemu)", 38 | "cfg(config_debug_malloc_magic)", 39 | "cfg(config_debug_malloc_check)" 40 | ] } 41 | 42 | [profile.release] 43 | panic = "abort" 44 | 45 | [profile.dev] 46 | rustflags = [ 47 | "-Cforce-frame-pointers=yes" 48 | ] 49 | -------------------------------------------------------------------------------- /kernel/README.md: -------------------------------------------------------------------------------- 1 | # Kernel 2 | 3 | ## Build 4 | 5 | The configuration file located at `build-config.toml` allows to specify which features have to be enabled in the kernel. 6 | 7 | A default configuration is available in the file `default.build-config.toml`. 8 | If `build-config.toml` does not exist, the default configuration is used instead. 9 | 10 | The kernel can be built using the following commands: 11 | 12 | ```sh 13 | cargo build # Debug mode 14 | cargo build --release # Release mode 15 | ``` 16 | 17 | The default architecture is `x86_64`. To specify another architecture, add the following parameter to the build command: `--target arch/<arch>/<arch>.json`, where `<arch>` is the selected architecture. 18 | 19 | The list of available architectures can be retrieved by typing the command: 20 | 21 | ```sh 22 | ls -1 arch/ 23 | ``` 24 | 25 | 26 | 27 | ## Run 28 | 29 | ### With QEMU 30 | 31 | QEMU is the preferred virtual machine to test the kernel. 32 | 33 | To install QEMU, type the following command: 34 | 35 | Ubuntu/Debian: 36 | 37 | ```sh 38 | apt install qemu 39 | ``` 40 | 41 | Arch Linux: 42 | 43 | ```sh 44 | pacman -S qemu 45 | ``` 46 | 47 | A fully built operating system is required to run the system. This system must be present on a raw disk in the file `qemu_disk` at the root of the repository. The option `-drive file=qemu_disk,format=raw` is used on QEMU to reference the disk. 48 | 49 | The kernel can be run using: 50 | 51 | ```sh 52 | cargo run # Debug mode 53 | cargo run --release # Release mode 54 | ``` 55 | 56 | 57 | #### Run unit tests 58 | 59 | The following command runs unit tests in QEMU: 60 | 61 | ```sh 62 | cargo test --lib 63 | ``` 64 | 65 | 66 | 67 | ## Documentation 68 | 69 | The documentation of the kernel's interface for modules can be built using: 70 | 71 | ```sh 72 | cargo doc 73 | ``` 74 | 75 | Then, it can be accessed at `target/<arch>/doc/kernel/index.html`, where `<arch>` is the architecture the kernel has been compiled for. 76 | -------------------------------------------------------------------------------- /kernel/arch/x86/linker.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | /* 20 | * The linker script for the x86 architecture. 21 | * 22 | * The kernel image is split into two parts: 23 | * - The boot part, in lower memory (sections with the `.boot` prefix) 24 | * - The kernel part, in higher memory, starting at 0xc0000000 25 | * 26 | * In the first boot stage of the kernel, the memory must be remapped so that 27 | * the kernel image is relocated to higher memory. 28 | * After running the kernel code, the booting code isn't useful anymore. 29 | * 30 | * Sections need to be aligned on the page boundary to be protected against 31 | * writing (for those where it applies). 32 | * 33 | * BSS sections are located right after read-only sections to limit damages if 34 | * the stack(s) they contain overflows. 35 | */ 36 | 37 | ENTRY(multiboot_entry) 38 | 39 | SECTIONS 40 | { 41 | /* Boot stub sections */ 42 | . = 0x100000; 43 | 44 | .boot.text : ALIGN(4K) 45 | { 46 | *(.boot.text) 47 | } 48 | 49 | .boot.data : ALIGN(4K) 50 | { 51 | *(.boot.data) 52 | } 53 | 54 | .boot.stack : ALIGN(4K) 55 | { 56 | *(.boot.stack) 57 | } 58 | 59 | /* Kernel sections */ 60 | . = 0xc0200000; 61 | 62 | .text : AT (ADDR (.text) - 0xc0000000) ALIGN(4K) 63 | { 64 | *(.text*) 65 | } 66 | 67 | .rodata : AT (ADDR (.rodata) - 0xc0000000) ALIGN(4K) 68 | { 69 | *(.rodata*) 70 | } 71 | 72 | /* Accessible to the userspace (readonly) */ 73 | .user : AT (ADDR (.user) - 0xc0000000) ALIGN(4K) 74 | { 75 | *(.user*) 76 | } 77 | 78 | .bss : AT (ADDR (.bss) - 0xc0000000) ALIGN(4K) 79 | { 80 | *(COMMON) 81 | *(.bss*) 82 | } 83 | 84 | .data : AT (ADDR (.data) - 0xc0000000) ALIGN(4K) 85 | { 86 | *(.data*) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /kernel/arch/x86/src/memcpy.s: -------------------------------------------------------------------------------- 1 | # Code taken from musl. License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 2 | 3 | .intel_syntax noprefix 4 | 5 | .section .text 6 | 7 | .global memcpy 8 | .global __memcpy_fwd 9 | .hidden __memcpy_fwd 10 | .type memcpy, @function 11 | 12 | memcpy: 13 | __memcpy_fwd: 14 | push esi 15 | push edi 16 | mov edi, [esp + 12] 17 | mov esi, [esp + 16] 18 | mov ecx, [esp + 20] 19 | mov eax, edi 20 | cmp ecx, 4 21 | jc 1f 22 | test edi, 3 23 | jz 1f 24 | 2: 25 | movsb 26 | dec ecx 27 | test edi, 3 28 | jnz 2b 29 | 1: 30 | mov edx, ecx 31 | shr ecx, 2 32 | rep movsd 33 | and edx, 3 34 | jz 1f 35 | 2: 36 | movsb 37 | dec edx 38 | jnz 2b 39 | 1: 40 | pop edi 41 | pop esi 42 | ret 43 | -------------------------------------------------------------------------------- /kernel/arch/x86/src/memmove.s: -------------------------------------------------------------------------------- 1 | # Code taken from musl. License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 2 | 3 | .intel_syntax noprefix 4 | 5 | .section .text 6 | 7 | .global memmove 8 | .type memmove, @function 9 | 10 | memmove: 11 | mov eax, [esp + 4] 12 | sub eax, [esp + 8] 13 | cmp eax, [esp + 12] 14 | .hidden __memcpy_fwd 15 | jae __memcpy_fwd 16 | push esi 17 | push edi 18 | mov edi, [esp + 12] 19 | mov esi, [esp + 16] 20 | mov ecx, [esp + 20] 21 | lea edi, [edi + ecx - 1] 22 | lea esi, [esi + ecx - 1] 23 | std 24 | rep movsb 25 | cld 26 | lea eax, [edi + 1] 27 | pop edi 28 | pop esi 29 | ret 30 | -------------------------------------------------------------------------------- /kernel/arch/x86/src/memset.s: -------------------------------------------------------------------------------- 1 | # Code taken from musl. License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 2 | 3 | .intel_syntax noprefix 4 | 5 | .section .text 6 | 7 | .global memset 8 | .type memset, @function 9 | 10 | memset: 11 | mov ecx, [esp + 12] 12 | cmp ecx, 62 13 | ja 2f 14 | 15 | mov dl, [esp + 8] 16 | mov eax, [esp + 4] 17 | test ecx, ecx 18 | jz 1f 19 | 20 | mov dh, dl 21 | 22 | mov [eax], dl 23 | mov [eax + ecx - 1], dl 24 | cmp ecx, 2 25 | jbe 1f 26 | 27 | mov [eax + 1], dx 28 | mov [eax + ecx + -1-2], %dx 29 | cmp ecx, 6 30 | jbe 1f 31 | 32 | shl edx, 16 33 | mov dl, [esp + 8] 34 | mov dh, [esp + 8] 35 | 36 | mov [eax + 1+2], %edx 37 | mov [eax + ecx + -1-2-4], edx 38 | cmp ecx, 14 39 | jbe 1f 40 | 41 | mov [eax + 1+2+4], edx 42 | mov [eax + 1+2+4+4], edx 43 | mov [eax + ecx + -1-2-4-8], edx 44 | mov [eax + ecx + -1-2-4-4], edx 45 | cmp ecx, 30 46 | jbe 1f 47 | 48 | mov [eax + 1+2+4+8], edx 49 | mov [eax + 1+2+4+8+4], edx 50 | mov [eax + 1+2+4+8+8], edx 51 | mov [eax + 1+2+4+8+12], edx 52 | mov [eax + ecx + -1-2-4-8-16], edx 53 | mov [eax + ecx + -1-2-4-8-12], edx 54 | mov [eax + ecx + -1-2-4-8-8], edx 55 | mov [eax + ecx + -1-2-4-8-4], edx 56 | 57 | 1: 58 | ret 59 | 60 | 2: 61 | movzb eax, [esp + 8] 62 | mov [esp + 12], edi 63 | imul eax, 0x1010101 64 | mov edi, [esp + 4] 65 | test edi, 15 66 | mov [edi + ecx - 4], eax 67 | jnz 2f 68 | 69 | 1: 70 | shr ecx, 2 71 | rep 72 | stosd 73 | mov eax, [esp + 4] 74 | mov edi, [esp + 12] 75 | ret 76 | 77 | 2: 78 | xor edx, edx 79 | sub edx, edi 80 | and edx, 15 81 | mov [edi], eax 82 | mov [edi + 4], eax 83 | mov [edi + 8], eax 84 | mov [edi + 12], eax 85 | sub ecx, edx 86 | add edi, edx 87 | jmp 1b 88 | -------------------------------------------------------------------------------- /kernel/arch/x86/src/raw_copy.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | // Copy from/to userspace 20 | 21 | .intel_syntax noprefix 22 | 23 | .section .text 24 | 25 | .global raw_copy 26 | .global copy_fault 27 | 28 | // TODO can be optimized 29 | raw_copy: 30 | push esi 31 | push edi 32 | 33 | mov edi, 12[esp] 34 | mov esi, 16[esp] 35 | mov ecx, 20[esp] 36 | 37 | rep movsb 38 | 39 | pop edi 40 | pop esi 41 | mov eax, 1 42 | ret 43 | 44 | copy_fault: 45 | pop edi 46 | pop esi 47 | xor eax, eax 48 | ret 49 | -------------------------------------------------------------------------------- /kernel/arch/x86/x86.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "x86", 3 | "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", 4 | "disable-redzone": true, 5 | "dynamic-linking": true, 6 | "executables": true, 7 | "features": "-mmx,-sse,-sse2,+soft-float", 8 | "linker": "i686-elf-ld", 9 | "linker-flavor": "ld", 10 | "llvm-target": "i686-unknown-none", 11 | "os": "none", 12 | "panic-strategy": "abort", 13 | "target-c-int-width": "32", 14 | "target-endian": "little", 15 | "target-pointer-width": "32", 16 | "rustc-abi": "x86-softfloat" 17 | } 18 | -------------------------------------------------------------------------------- /kernel/arch/x86_64/linker.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | /* 20 | * The linker script for the x86_64 architecture. 21 | * 22 | * For more information about the organization of this particular file, check 23 | * the documentation in the linker script for the x86 architecture. 24 | */ 25 | 26 | ENTRY(multiboot_entry) 27 | 28 | SECTIONS 29 | { 30 | . = 0x100000; 31 | 32 | .boot.text : ALIGN(4K) 33 | { 34 | *(.boot.text) 35 | } 36 | 37 | .boot.data : ALIGN(4K) 38 | { 39 | *(.boot.data) 40 | } 41 | 42 | .boot.stack : ALIGN(4K) 43 | { 44 | *(.boot.stack) 45 | } 46 | 47 | . = 0xffff800000200000; 48 | 49 | .text : AT (ADDR (.text) - 0xffff800000000000) ALIGN(4K) 50 | { 51 | *(.text*) 52 | } 53 | 54 | .rodata : AT (ADDR (.rodata) - 0xffff800000000000) ALIGN(4K) 55 | { 56 | *(.rodata*) 57 | } 58 | 59 | .user : AT (ADDR (.user) - 0xffff800000000000) ALIGN(4K) 60 | { 61 | *(.user*) 62 | } 63 | 64 | .bss : AT (ADDR (.bss) - 0xffff800000000000) ALIGN(4K) 65 | { 66 | *(COMMON) 67 | *(.bss*) 68 | } 69 | 70 | .data : AT (ADDR (.data) - 0xffff800000000000) ALIGN(4K) 71 | { 72 | *(.data*) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /kernel/arch/x86_64/src/memcpy.s: -------------------------------------------------------------------------------- 1 | # Code taken from musl. License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 2 | 3 | .intel_syntax noprefix 4 | 5 | .section .text 6 | 7 | .global memcpy 8 | .global __memcpy_fwd 9 | .hidden __memcpy_fwd 10 | .type memcpy, @function 11 | 12 | memcpy: 13 | __memcpy_fwd: 14 | mov rax, rdi 15 | cmp rdx, 8 16 | jc 1f 17 | test edi, 7 18 | jz 1f 19 | 2: 20 | movsb 21 | dec rdx 22 | test edi, 7 23 | jnz 2b 24 | 1: 25 | mov rcx, rdx 26 | shr rcx, 3 27 | rep movsq 28 | and edx, 7 29 | jz 1f 30 | 2: 31 | movsb 32 | dec edx 33 | jnz 2b 34 | 1: 35 | ret 36 | -------------------------------------------------------------------------------- /kernel/arch/x86_64/src/memmove.s: -------------------------------------------------------------------------------- 1 | # Code taken from musl. License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 2 | 3 | .intel_syntax noprefix 4 | 5 | .section .text 6 | 7 | .global memmove 8 | .type memmove, @function 9 | 10 | memmove: 11 | mov rax, rdi 12 | sub rax, rsi 13 | cmp rax, rdx 14 | .hidden __memcpy_fwd 15 | jae __memcpy_fwd 16 | mov rcx, rdx 17 | lea rdi, [rdi + rdx - 1] 18 | lea rsi, [rsi + rdx - 1] 19 | std 20 | rep movsb 21 | cld 22 | lea rax, [rdi + 1] 23 | ret 24 | -------------------------------------------------------------------------------- /kernel/arch/x86_64/src/memset.s: -------------------------------------------------------------------------------- 1 | # Code taken from musl. License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 2 | 3 | .intel_syntax noprefix 4 | 5 | .global memset 6 | .type memset, @function 7 | 8 | memset: 9 | movzb rax, sil 10 | mov r8, 0x101010101010101 11 | imul rax, r8 12 | 13 | cmp rdx, 126 14 | ja 2f 15 | 16 | test edx, edx 17 | jz 1f 18 | 19 | mov [rdi], sil 20 | mov [rdi + rdx -1], sil 21 | cmp edx, 2 22 | jbe 1f 23 | 24 | mov [rdi + 1], ax 25 | mov [rdi + rdx -1-2], ax 26 | cmp edx, 6 27 | jbe 1f 28 | 29 | mov [rdi + 1+2], eax 30 | mov [rdi + rdx -1-2-4], eax 31 | cmp edx, 14 32 | jbe 1f 33 | 34 | mov [rdi + 1+2+4], rax 35 | mov [rdi + rdx -1-2-4-8], rax 36 | cmp edx, 30 37 | jbe 1f 38 | 39 | mov [rdi + 1+2+4+8], rax 40 | mov [rdi + 1+2+4+8+8], rax 41 | mov [rdi + rdx -1-2-4-8-16], rax 42 | mov [rdi + rdx -1-2-4-8-8], rax 43 | cmp edx, 62 44 | jbe 1f 45 | 46 | mov [rdi + 1+2+4+8+16], rax 47 | mov [rdi + 1+2+4+8+16+8], rax 48 | mov [rdi + 1+2+4+8+16+16], rax 49 | mov [rdi + 1+2+4+8+16+24], rax 50 | mov [rdi + rdx -1-2-4-8-16-32], rax 51 | mov [rdi + rdx -1-2-4-8-16-24], rax 52 | mov [rdi + rdx -1-2-4-8-16-16], rax 53 | mov [rdi + rdx -1-2-4-8-16-8], rax 54 | 55 | 1: 56 | mov rax, rdi 57 | ret 58 | 59 | 2: 60 | test edi, 15 61 | mov r8, rdi 62 | mov [rdi + rdx - 8], rax 63 | mov rcx, rdx 64 | jnz 2f 65 | 66 | 1: 67 | shr rcx, 3 68 | rep 69 | stosq 70 | mov rax, r8 71 | ret 72 | 73 | 2: 74 | xor edx, edx 75 | sub edx, edi 76 | and edx, 15 77 | mov [rdi], rax 78 | mov [rdi + 8], rax 79 | sub rcx, rdx 80 | add rdi, rdx 81 | jmp 1b 82 | -------------------------------------------------------------------------------- /kernel/arch/x86_64/src/raw_copy.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | // Copy from/to userspace 20 | 21 | .intel_syntax noprefix 22 | 23 | .section .text 24 | 25 | .global raw_copy 26 | .global copy_fault 27 | 28 | // TODO can be optimized 29 | raw_copy: 30 | mov rcx, rdx 31 | rep movsb 32 | mov rax, 1 33 | ret 34 | 35 | copy_fault: 36 | xor rax, rax 37 | ret 38 | -------------------------------------------------------------------------------- /kernel/arch/x86_64/x86_64.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "x86_64", 3 | "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", 4 | "disable-redzone": true, 5 | "dynamic-linking": true, 6 | "executables": true, 7 | "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float", 8 | "linker": "x86_64-elf-ld", 9 | "linker-flavor": "ld", 10 | "llvm-target": "x86_64-unknown-none", 11 | "os": "none", 12 | "panic-strategy": "abort", 13 | "target-c-int-width": "32", 14 | "target-endian": "little", 15 | "target-pointer-width": "64", 16 | "rustc-abi": "x86-softfloat" 17 | } 18 | -------------------------------------------------------------------------------- /kernel/build/config.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This file implements the configuration file for compilation. 20 | 21 | use serde::Deserialize; 22 | use std::{fs, io}; 23 | 24 | /// The debug section of the configuration file. 25 | #[derive(Deserialize)] 26 | struct ConfigDebug { 27 | /// If enabled, the kernel tests storage. 28 | /// 29 | /// **Warning**: this option is destructive for any data present on disks connected to the 30 | /// host. 31 | storage_test: bool, 32 | 33 | /// If enabled, the kernel is compiled for QEMU. This feature is not *required* for QEMU but 34 | /// it can provide additional features. 35 | qemu: bool, 36 | 37 | /// If enabled, the kernel places a magic number in malloc chunks to allow checking integrity. 38 | malloc_magic: bool, 39 | /// If enabled, the kernel checks integrity of memory allocations. 40 | /// 41 | /// **Warning**: this options slows down the system significantly. 42 | malloc_check: bool, 43 | } 44 | 45 | /// The compilation configuration. 46 | #[derive(Deserialize)] 47 | pub struct Config { 48 | /// Debug section. 49 | debug: ConfigDebug, 50 | } 51 | 52 | impl Config { 53 | /// Reads the configuration file. 54 | pub fn read() -> io::Result<Self> { 55 | const FILE_DEFAULT: &str = "default.build-config.toml"; 56 | const FILE: &str = "build-config.toml"; 57 | 58 | println!("cargo:rerun-if-changed={FILE_DEFAULT}"); 59 | println!("cargo:rerun-if-changed={FILE}"); 60 | 61 | let config_str = match fs::read_to_string(FILE) { 62 | Ok(s) => s, 63 | // Fallback to default configuration file 64 | Err(e) if e.kind() == io::ErrorKind::NotFound => fs::read_to_string(FILE_DEFAULT)?, 65 | Err(e) => return Err(e), 66 | }; 67 | toml::from_str(&config_str).map_err(|e| io::Error::other(e.to_string())) 68 | } 69 | 70 | /// Sets the crate's cfg flags according to the configuration. 71 | pub fn set_cfg(&self, debug: bool) { 72 | if debug { 73 | if self.debug.storage_test { 74 | println!("cargo:rustc-cfg=config_debug_storage_test"); 75 | } 76 | if self.debug.qemu { 77 | println!("cargo:rustc-cfg=config_debug_qemu"); 78 | } 79 | if self.debug.malloc_magic { 80 | println!("cargo:rustc-cfg=config_debug_malloc_magic"); 81 | } 82 | if self.debug.malloc_check { 83 | println!("cargo:rustc-cfg=config_debug_malloc_check"); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /kernel/build/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The build script reads the configuration file, compiles required libraries and prepares for the 20 | //! compilation of the kernel. 21 | 22 | pub mod compile; 23 | pub mod config; 24 | pub mod target; 25 | pub mod util; 26 | 27 | use crate::{config::Config, target::Target}; 28 | use std::{env, path::PathBuf, process::exit}; 29 | 30 | /// The environment passed to the build script. 31 | pub struct Env { 32 | /// The path to the root of the workspace. 33 | pub manifest_dir: PathBuf, 34 | /// The name of the profile used to compile the crate. 35 | pub profile: String, 36 | /// The optimization level, between `0` and `3` included. 37 | pub opt_level: u32, 38 | /// The name of the target architecture. 39 | pub arch: String, 40 | /// The path to the target file. 41 | pub target_path: PathBuf, 42 | } 43 | 44 | impl Env { 45 | /// Reads the current environment. 46 | pub fn get() -> Self { 47 | let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 48 | let profile = env::var("PROFILE").unwrap(); 49 | let opt_level = env::var("OPT_LEVEL").unwrap().parse().unwrap(); 50 | // Unwrapping is safe because a default target is specified in `.cargo/config.toml` 51 | let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 52 | let target_path = manifest_dir.join(format!("arch/{arch}/{arch}.json")); 53 | Self { 54 | manifest_dir, 55 | profile, 56 | opt_level, 57 | arch, 58 | target_path, 59 | } 60 | } 61 | 62 | /// Tells whether compiling in debug mode. 63 | pub fn is_debug(&self) -> bool { 64 | self.profile == "debug" 65 | } 66 | } 67 | 68 | fn main() { 69 | // Read config 70 | let env = Env::get(); 71 | let target = Target::from_env(&env).unwrap_or_else(|e| { 72 | eprintln!("Cannot retrieve target: {e}"); 73 | exit(1); 74 | }); 75 | let config = Config::read().unwrap_or_else(|e| { 76 | eprintln!("Failed to read build configuration file: {e}"); 77 | exit(1); 78 | }); 79 | config.set_cfg(env.is_debug()); 80 | // Compile 81 | compile::compile_c(&env, &target).unwrap_or_else(|e| { 82 | eprintln!("Compilation failed: {e}"); 83 | exit(1); 84 | }); 85 | compile::compile_vdso(&env, &target).unwrap_or_else(|e| { 86 | eprintln!("vDSO compilation failed: {e}"); 87 | exit(1); 88 | }); 89 | // Add the linker script 90 | println!( 91 | "cargo:rerun-if-changed={}", 92 | target.get_linker_script_path().display() 93 | ); 94 | println!( 95 | "cargo:rustc-link-arg=-T{}", 96 | target.get_linker_script_path().display() 97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /kernel/build/target.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Compilation target information. 20 | 21 | use crate::Env; 22 | use serde::Deserialize; 23 | use std::{fs, io, path::PathBuf}; 24 | 25 | /// The content of the target JSON file. 26 | /// 27 | /// This structure contains only the fields that are of interest. 28 | #[derive(Deserialize)] 29 | struct TargetFile { 30 | /// The LLVM target triplet. 31 | #[serde(rename = "llvm-target")] 32 | llvm_target: String, 33 | } 34 | 35 | /// A build target. 36 | pub struct Target<'s> { 37 | /// The name of the target. 38 | pub name: &'s str, 39 | /// The target triplet. 40 | pub triplet: String, 41 | } 42 | 43 | impl<'s> Target<'s> { 44 | /// Returns the selected triplet according to the environment. 45 | pub fn from_env(env: &'s Env) -> io::Result<Self> { 46 | // Read and parse target file 47 | let content = fs::read_to_string(&env.target_path)?; 48 | let content: TargetFile = serde_json::from_str(&content).map_err(io::Error::from)?; 49 | Ok(Self { 50 | name: &env.arch, 51 | triplet: content.llvm_target, 52 | }) 53 | } 54 | 55 | /// Returns the path to the linker script of the target. 56 | pub fn get_linker_script_path(&self) -> PathBuf { 57 | PathBuf::from(format!("arch/{}/linker.ld", self.name)) 58 | } 59 | 60 | /// Returns the path to the directory containing target-specific sources. 61 | pub fn src(&self) -> PathBuf { 62 | PathBuf::from(format!("arch/{}/src/", self.name)) 63 | } 64 | 65 | /// Returns the name of the architecture for the compatibility vDSO, if any. 66 | pub fn compat_vdso(&self) -> Option<&str> { 67 | (self.name == "x86_64").then_some("x86") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kernel/build/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Build script utilities 20 | 21 | use std::{ 22 | ffi::OsStr, 23 | fs, io, 24 | path::{Path, PathBuf}, 25 | }; 26 | 27 | fn list_c_files_impl(dir: &Path, paths: &mut Vec<PathBuf>) -> io::Result<()> { 28 | let dir = match fs::read_dir(dir) { 29 | Ok(dir) => dir, 30 | Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()), 31 | Err(e) => return Err(e), 32 | }; 33 | for e in dir { 34 | let e = e?; 35 | let e_path = e.path(); 36 | let e_type = e.file_type()?; 37 | if e_type.is_file() { 38 | let ext = e_path.extension().and_then(OsStr::to_str); 39 | if !matches!(ext, Some("c" | "s")) { 40 | continue; 41 | } 42 | paths.push(e_path); 43 | } else if e_type.is_dir() { 44 | list_c_files_impl(&e_path, paths)?; 45 | } 46 | } 47 | Ok(()) 48 | } 49 | 50 | /// Lists paths to C and assembly files. 51 | pub fn list_c_files(dir: &Path) -> io::Result<Vec<PathBuf>> { 52 | let mut paths = vec![]; 53 | list_c_files_impl(dir, &mut paths)?; 54 | Ok(paths) 55 | } 56 | -------------------------------------------------------------------------------- /kernel/ci/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | export QEMUFLAGS="-nographic -monitor none -serial stdio" 6 | 7 | cp default.build-config.toml build-config.toml 8 | sed -i 's/^qemu = false$/qemu = true/' build-config.toml 9 | 10 | case $1 in 11 | self) 12 | cargo test --lib $CARGOFLAGS 13 | ;; 14 | int) 15 | cargo run $CARGOFLAGS 16 | 17 | # Check filesystem integrity 18 | # FIXME: the clock currently starts at the timestamp zero, which causes fsck to detect errors due to the low value for dtime 19 | #fsck.ext2 -fnv qemu_disk 20 | # Check persistence 21 | echo 'Check `/persistent` exists' 22 | echo 'cat /persistent' | debugfs -f - qemu_disk 2>&1 | grep 'persistence OK' 23 | ;; 24 | *) 25 | >&2 echo "Invalid tests kind" 26 | exit 1 27 | ;; 28 | esac 29 | -------------------------------------------------------------------------------- /kernel/default.build-config.toml: -------------------------------------------------------------------------------- 1 | # This is the default configuration for the kernel compilation. 2 | # To setup a configuration, copy this file under the name `build-config.toml`, then modify it 3 | 4 | 5 | 6 | # These options are only enabled when compiling in debug mode 7 | [debug] 8 | # If enabled, the kernel tests storage. 9 | # 10 | # **Warning**: this option is destructive for any data present on disks connected to the 11 | # host. 12 | storage_test = false 13 | 14 | # If enabled, the kernel is compiled for QEMU. This feature is not *required* for QEMU but 15 | # it can provide additional features. On panic, the kernel will shut down the virtual machine. 16 | qemu = false 17 | 18 | # If enabled, the kernel places a magic number in malloc chunks to allow checking integrity. 19 | malloc_magic = false 20 | # If enabled, the kernel checks integrity of memory allocations. 21 | # 22 | # **Warning**: this options slows down the system significantly. 23 | malloc_check = false 24 | -------------------------------------------------------------------------------- /kernel/grub.cfg: -------------------------------------------------------------------------------- 1 | menuentry "Maestro" { 2 | multiboot2 /boot/maestro -root 8 0 3 | } 4 | 5 | set default=0 6 | set timeout=0 7 | -------------------------------------------------------------------------------- /kernel/scripts/gdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script allows to run gdb to debug the kernel using QEMU. 4 | 5 | # Environment variables: 6 | # - ARCH: specifies the architecture to build for 7 | # - AUX_ELF: specifies the path to an auxiliary ELF file whose symbols will be added to gdb 8 | 9 | if [ -z "$ARCH" ]; then 10 | ARCH="x86_64" 11 | fi 12 | 13 | cargo build $CARGOFLAGS --target arch/$ARCH/$ARCH.json 14 | 15 | export QEMUFLAGS="$QEMUFLAGS -s -S -d int" 16 | setsid cargo run $CARGOFLAGS --target arch/$ARCH/$ARCH.json >qemu.log 2>&1 & 17 | QEMU_PID=$! 18 | 19 | KERN_PATH="target/$ARCH/debug/maestro" 20 | 21 | if ! [ -z "$AUX_ELF" ]; then 22 | gdb $KERN_PATH -ex 'target remote :1234' -ex 'set confirm off' -ex 'add-symbol-file -o $AUX_ELF' -ex 'set confirm on' 23 | else 24 | gdb $KERN_PATH -ex 'target remote :1234' 25 | fi 26 | 27 | kill -- -$QEMU_PID 28 | -------------------------------------------------------------------------------- /kernel/scripts/link_partitions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script links partitions from the partitions table on the disk `qemu_disk` to device files in order to mount them 4 | 5 | export IFS=' 6 | ' 7 | 8 | SECTOR_SIZE=512 9 | 10 | for p in $(fdisk -l qemu_disk | grep '^qemu_disk'); do 11 | NAME=$(echo $p | awk '{print $1}') 12 | START=$(echo $p | awk '{print $2}') 13 | END=$(echo $p | awk '{print $3}') 14 | SIZE=$(($END - $START)) 15 | 16 | DEV=$(losetup -o $(($START * $SECTOR_SIZE)) --sizelimit $(($SIZE * $SECTOR_SIZE)) --sector-size $SECTOR_SIZE --show -f qemu_disk) 17 | echo "$NAME linked as $DEV (offset: $(($START * $SECTOR_SIZE)); size: $(($SIZE * $SECTOR_SIZE)))" 18 | done 19 | -------------------------------------------------------------------------------- /kernel/scripts/qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Runs the kernel in QEMU. This script is meant to be used through `cargo` 4 | 5 | if [ -z "$ARCH" ]; then 6 | ARCH="x86_64" 7 | fi 8 | 9 | case $ARCH in 10 | "x86") 11 | QEMU=qemu-system-i386 12 | ;; 13 | "x86_64") 14 | QEMU=qemu-system-x86_64 15 | ;; 16 | *) 17 | >&2 echo "Invalid architecture '$ARCH'" 18 | exit 1 19 | ;; 20 | esac 21 | 22 | # Build ISO 23 | 24 | mkdir -p iso/boot/grub 25 | cp $1 iso/boot/maestro 26 | cp grub.cfg iso/boot/grub 27 | grub-mkrescue -o kernel.iso iso 28 | 29 | # Run the kernel 30 | 31 | export QEMUDISK=qemu_disk 32 | export QEMUFLAGS="-device isa-debug-exit,iobase=0xf4,iosize=0x04 $QEMUFLAGS" 33 | if [ -f $QEMUDISK ]; then 34 | QEMUFLAGS="-drive file=$QEMUDISK,format=raw $QEMUFLAGS" 35 | fi 36 | 37 | $QEMU -cdrom kernel.iso $QEMUFLAGS 38 | EXIT=$? 39 | 40 | if [ "$EXIT" -ne 33 ]; then 41 | exit 1 42 | fi 43 | -------------------------------------------------------------------------------- /kernel/src/acpi/aml/named_obj.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! TODO doc 20 | 21 | use super::{AMLParseable, Error}; 22 | use macros::Parseable; 23 | 24 | /// TODO doc 25 | #[derive(Parseable)] 26 | pub enum NamedObj { 27 | // TODO 28 | } 29 | -------------------------------------------------------------------------------- /kernel/src/acpi/aml/namespace_modifier.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! TODO doc 20 | 21 | use super::{AMLParseable, Error}; 22 | use macros::Parseable; 23 | 24 | /// TODO doc 25 | #[derive(Parseable)] 26 | pub struct DefAlias { 27 | // TODO 28 | } 29 | 30 | /// TODO doc 31 | #[derive(Parseable)] 32 | pub struct DefName { 33 | // TODO 34 | } 35 | 36 | /// TODO doc 37 | #[derive(Parseable)] 38 | pub struct DefScope { 39 | // TODO 40 | } 41 | 42 | /// TODO doc 43 | #[allow(clippy::enum_variant_names)] 44 | #[derive(Parseable)] 45 | pub enum NameSpaceModifierObj { 46 | DefAlias(DefAlias), 47 | DefName(DefAlias), 48 | DefScope(DefAlias), 49 | } 50 | -------------------------------------------------------------------------------- /kernel/src/acpi/aml/term_obj.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! TODO doc 20 | 21 | use super::{ 22 | AMLParseable, Error, named_obj::NamedObj, namespace_modifier::NameSpaceModifierObj, 23 | type1_opcode::Type1Opcode, type2_opcode::Type2Opcode, 24 | }; 25 | use macros::Parseable; 26 | 27 | /// TODO doc 28 | #[derive(Parseable)] 29 | pub enum Object { 30 | NameSpaceModifierObj(NameSpaceModifierObj), 31 | NamedObj(NamedObj), 32 | } 33 | 34 | /// TODO doc 35 | #[derive(Parseable)] 36 | pub enum TermObject { 37 | Object(Object), 38 | Type1Opcode(Type1Opcode), 39 | Type2Opcode(Type2Opcode), 40 | } 41 | 42 | /// TODO doc 43 | #[derive(Parseable)] 44 | pub struct TermList { 45 | // TODO objects: Vec<TermObject>, 46 | } 47 | -------------------------------------------------------------------------------- /kernel/src/acpi/aml/type1_opcode.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! TODO doc 20 | 21 | use super::{AMLParseable, Error}; 22 | use macros::Parseable; 23 | 24 | /// TODO doc 25 | #[derive(Parseable)] 26 | pub enum Type1Opcode { 27 | // TODO 28 | } 29 | -------------------------------------------------------------------------------- /kernel/src/acpi/aml/type2_opcode.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! TODO doc 20 | 21 | use super::{AMLParseable, Error}; 22 | use macros::Parseable; 23 | 24 | /// TODO doc 25 | #[derive(Parseable)] 26 | pub enum Type2Opcode { 27 | // TODO 28 | } 29 | -------------------------------------------------------------------------------- /kernel/src/acpi/dsdt.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The DSDT (Differentiated System Description Table) provides information about supported power 20 | //! events. 21 | //! 22 | //! This table contains AML code which has to be parsed and executed to retrieve the required 23 | //! information. 24 | 25 | use super::{Table, TableHdr}; 26 | use core::mem::size_of; 27 | 28 | /// The Differentiated System Description Table. 29 | #[repr(C)] 30 | #[derive(Debug)] 31 | pub struct Dsdt { 32 | /// The table's header. 33 | pub header: TableHdr, 34 | /// The definition of the AML code. 35 | definition_block: [u8], 36 | } 37 | 38 | impl Dsdt { 39 | /// Returns a slice to the AML code. 40 | pub fn get_aml(&self) -> &[u8] { 41 | let code_len = self.header.length as usize - size_of::<TableHdr>(); 42 | &self.definition_block[..code_len] 43 | } 44 | } 45 | 46 | impl Table for Dsdt { 47 | const SIGNATURE: &'static [u8; 4] = b"DSDT"; 48 | } 49 | -------------------------------------------------------------------------------- /kernel/src/acpi/madt.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! ACPI's Multiple APIC Description Table (MADT) handling. 20 | 21 | use super::{Table, TableHdr}; 22 | use core::{ffi::c_void, hint::likely}; 23 | 24 | /// The offset of the entries in the MADT. 25 | const ENTRIES_OFF: usize = 0x2c; 26 | 27 | /// Indicates that the system also has a PC-AT-compatible dual-8259 setup (which 28 | /// must be disabled when enabling ACPI APIC). 29 | const PCAT_COMPAT: u32 = 0b1; 30 | 31 | /// The Multiple APIC Description Table. 32 | #[repr(C)] 33 | #[derive(Debug)] 34 | pub struct Madt { 35 | /// The table's header. 36 | pub header: TableHdr, 37 | 38 | /// The physical address at which each process can access its local 39 | /// interrupt controller. 40 | local_apic_addr: u32, 41 | /// APIC flags. 42 | flags: u32, 43 | } 44 | 45 | impl Madt { 46 | /// Returns an iterator over each entry of the MADT. 47 | pub fn entries(&self) -> EntriesIterator { 48 | EntriesIterator { 49 | madt: self, 50 | cursor: 0, 51 | } 52 | } 53 | } 54 | 55 | impl Table for Madt { 56 | const SIGNATURE: &'static [u8; 4] = b"APIC"; 57 | } 58 | 59 | /// Represents an MADT entry header. 60 | #[repr(C)] 61 | #[derive(Debug)] 62 | pub struct EntryHeader { 63 | /// The entry type. 64 | pub entry_type: u8, 65 | /// The entry length. 66 | pub length: u8, 67 | } 68 | 69 | /// Iterator over MADT entries. 70 | pub struct EntriesIterator<'m> { 71 | madt: &'m Madt, 72 | /// Cursor. 73 | cursor: usize, 74 | } 75 | 76 | impl<'m> Iterator for EntriesIterator<'m> { 77 | type Item = &'m EntryHeader; 78 | 79 | fn next(&mut self) -> Option<Self::Item> { 80 | let entries_len = self.madt.header.length as usize - ENTRIES_OFF; 81 | if likely(self.cursor < entries_len) { 82 | let entry = unsafe { 83 | let ptr = (self as *const _ as *const c_void).add(ENTRIES_OFF + self.cursor) 84 | as *const EntryHeader; 85 | &*ptr 86 | }; 87 | self.cursor += entry.length as usize; 88 | Some(entry) 89 | } else { 90 | None 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /kernel/src/acpi/rsdt.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module handles ACPI's Root System Description Table (RSDT). 20 | 21 | use super::{Table, TableHdr}; 22 | use core::{mem::size_of, ptr, ptr::Pointee, slice}; 23 | 24 | /// The Root System Description Table. 25 | #[repr(C)] 26 | #[derive(Debug)] 27 | pub struct Rsdt { 28 | /// The table's header. 29 | pub header: TableHdr, 30 | } 31 | 32 | // TODO XSDT 33 | 34 | impl Rsdt { 35 | /// Iterates over every ACPI tables. 36 | pub fn tables(&self) -> impl Iterator<Item = &TableHdr> { 37 | let entries_len = self.header.length as usize - size_of::<Rsdt>(); 38 | let entries_count = entries_len / size_of::<u32>(); 39 | unsafe { 40 | let entries_start = (self as *const Self).add(1) as *const u32; 41 | slice::from_raw_parts(entries_start, entries_count) 42 | .iter() 43 | .map(|p| &*ptr::with_exposed_provenance(*p as usize)) 44 | } 45 | } 46 | 47 | /// Returns a reference to the ACPI table with type `T`. 48 | /// 49 | /// If the table does not exist, the function returns `None`. 50 | /// 51 | /// If the table is invalid, the function panics. 52 | pub fn get_table<T: Table>(&self) -> Option<&T> { 53 | let hdr = self.tables().find(|hdr| hdr.signature == *T::SIGNATURE)?; 54 | if !hdr.check::<T>() { 55 | panic!("APCI: invalid table for signature {:?}", hdr.signature) 56 | } 57 | Some(unsafe { &*(hdr as *const _ as *const T) }) 58 | } 59 | 60 | /// Returns a reference to the ACPI table with type `T`. 61 | /// 62 | /// The table must be `Unsized`. 63 | /// 64 | /// If the table doesn't exist, the function returns `None`. 65 | pub fn get_table_unsized<T: Table + ?Sized + Pointee<Metadata = usize>>(&self) -> Option<&T> { 66 | let hdr = self.tables().find(|hdr| hdr.signature == *T::SIGNATURE)?; 67 | if !hdr.check::<T>() { 68 | panic!("APCI: invalid table for signature {:?}", hdr.signature) 69 | } 70 | Some(unsafe { 71 | let ptr = ptr::from_raw_parts::<T>(hdr as *const _ as *const (), hdr.length as usize); 72 | &*ptr 73 | }) 74 | } 75 | } 76 | 77 | impl Table for Rsdt { 78 | const SIGNATURE: &'static [u8; 4] = b"RSDT"; 79 | } 80 | -------------------------------------------------------------------------------- /kernel/src/arch/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Architecture-specific **Hardware Abstraction Layers** (HAL). 20 | 21 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 22 | pub mod x86; 23 | 24 | /// The name of the current CPU architecture. 25 | pub const ARCH: &str = { 26 | #[cfg(target_arch = "x86")] 27 | { 28 | "x86" 29 | } 30 | #[cfg(target_arch = "x86_64")] 31 | { 32 | "x86_64" 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /kernel/src/arch/x86/io.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The I/O functions allow to communicate with the other components on the 20 | //! system. 21 | 22 | use core::arch::asm; 23 | 24 | /// Inputs a byte from the specified port. 25 | /// 26 | /// # Safety 27 | /// 28 | /// Reading from an invalid port has an undefined behaviour. 29 | /// 30 | /// This function is not thread safe. 31 | #[inline(always)] 32 | pub unsafe fn inb(port: u16) -> u8 { 33 | let ret: i8; 34 | asm!("in al, dx", out("al") ret, in("dx") port); 35 | 36 | ret as _ 37 | } 38 | 39 | /// Inputs a word from the specified port. 40 | /// 41 | /// # Safety 42 | /// 43 | /// Reading from an invalid port has an undefined behaviour. 44 | /// 45 | /// This function is not thread safe. 46 | #[inline(always)] 47 | pub unsafe fn inw(port: u16) -> u16 { 48 | let ret: i16; 49 | asm!("in ax, dx", out("ax") ret, in("dx") port); 50 | 51 | ret as _ 52 | } 53 | 54 | /// Inputs a long from the specified port. 55 | /// 56 | /// # Safety 57 | /// 58 | /// Reading from an invalid port has an undefined behaviour. 59 | /// 60 | /// This function is not thread safe. 61 | #[inline(always)] 62 | pub unsafe fn inl(port: u16) -> u32 { 63 | let ret: i32; 64 | asm!("in eax, dx", out("eax") ret, in("dx") port); 65 | 66 | ret as _ 67 | } 68 | 69 | /// Outputs a byte to the specified port. 70 | /// 71 | /// # Safety 72 | /// 73 | /// Writing to an invalid port has an undefined behaviour. 74 | /// 75 | /// This function is not thread safe. 76 | #[inline(always)] 77 | pub unsafe fn outb(port: u16, value: u8) { 78 | asm!("out dx, al", in("al") value, in("dx") port); 79 | } 80 | 81 | /// Outputs a word to the specified port. 82 | /// 83 | /// # Safety 84 | /// 85 | /// Writing to an invalid port has an undefined behaviour. 86 | /// 87 | /// This function is not thread safe. 88 | #[inline(always)] 89 | pub unsafe fn outw(port: u16, value: u16) { 90 | asm!("out dx, ax", in("ax") value, in("dx") port); 91 | } 92 | 93 | /// Outputs a long to the specified port. 94 | /// 95 | /// # Safety 96 | /// 97 | /// Writing to an invalid port has an undefined behaviour. 98 | /// 99 | /// This function is not thread safe. 100 | #[inline(always)] 101 | pub unsafe fn outl(port: u16, value: u32) { 102 | asm!("out dx, eax", in("eax") value, in("dx") port); 103 | } 104 | -------------------------------------------------------------------------------- /kernel/src/arch/x86/pic.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The PIC is a component handling external interruptions, which allows to 20 | //! block interruptions until the CPU tells that it's ready to handle another 21 | //! one. 22 | 23 | use crate::arch::x86::io::{inb, outb}; 24 | 25 | /// The master PIC's command port. 26 | const MASTER_COMMAND: u16 = 0x20; 27 | /// The master PIC's data port. 28 | const MASTER_DATA: u16 = 0x21; 29 | /// The slave PIC's command port. 30 | const SLAVE_COMMAND: u16 = 0xa0; 31 | /// The slave PIC's data port. 32 | const SLAVE_DATA: u16 = 0xa1; 33 | 34 | /// Indicates that ICW4 will be present 35 | const ICW1_ICW4: u8 = 0x01; 36 | /// Initialization 37 | const ICW1_INIT: u8 = 0x10; 38 | /// TODO doc 39 | const ICW3_SLAVE_PIC: u8 = 0x04; 40 | /// TODO doc 41 | const ICW3_CASCADE: u8 = 0x02; 42 | /// 8086/88 (MCS-80/85) mode 43 | const ICW4_8086: u8 = 0x01; 44 | 45 | /// The end-of-interrupt command. 46 | const COMMAND_EOI: u8 = 0x20; 47 | 48 | /// Initializes the PIC. 49 | pub fn init(offset1: u8, offset2: u8) { 50 | unsafe { 51 | let mask1 = inb(MASTER_DATA); 52 | let mask2 = inb(SLAVE_DATA); 53 | 54 | outb(MASTER_COMMAND, ICW1_INIT | ICW1_ICW4); 55 | outb(SLAVE_COMMAND, ICW1_INIT | ICW1_ICW4); 56 | 57 | outb(MASTER_DATA, offset1); 58 | outb(SLAVE_DATA, offset2); 59 | 60 | outb(MASTER_DATA, ICW3_SLAVE_PIC); 61 | outb(SLAVE_DATA, ICW3_CASCADE); 62 | 63 | outb(MASTER_DATA, ICW4_8086); 64 | outb(SLAVE_DATA, ICW4_8086); 65 | 66 | outb(MASTER_DATA, mask1); 67 | outb(SLAVE_DATA, mask2); 68 | } 69 | } 70 | 71 | /// Enable interruptions on the given IRQ. 72 | pub fn enable_irq(mut n: u8) { 73 | let port = if n < 8 { 74 | MASTER_DATA 75 | } else { 76 | n -= 8; 77 | SLAVE_DATA 78 | }; 79 | 80 | unsafe { 81 | let value = inb(port) & !(1 << n); 82 | outb(port, value); 83 | } 84 | } 85 | 86 | /// Disable interruptions on the given IRQ. 87 | pub fn disable_irq(mut n: u8) { 88 | let port = if n < 8 { 89 | MASTER_DATA 90 | } else { 91 | n -= 8; 92 | SLAVE_DATA 93 | }; 94 | 95 | unsafe { 96 | let value = inb(port) | (1 << n); 97 | outb(port, value); 98 | } 99 | } 100 | 101 | /// Sends an End-Of-Interrupt message to the PIC for the given interrupt `irq`. 102 | #[unsafe(no_mangle)] 103 | pub extern "C" fn end_of_interrupt(irq: u8) { 104 | unsafe { 105 | if irq >= 0x8 { 106 | outb(SLAVE_COMMAND, COMMAND_EOI); 107 | } 108 | outb(MASTER_COMMAND, COMMAND_EOI); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /kernel/src/bin.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module exists only to import symbols from the kernel that has been compiled as a library. 20 | 21 | #![no_std] 22 | #![no_main] 23 | // Force presence of the test code for both `cargo test` and `cargo clippy --tests` 24 | #![feature(custom_test_frameworks)] 25 | #![test_runner(kernel::selftest::runner)] 26 | 27 | extern crate kernel; 28 | -------------------------------------------------------------------------------- /kernel/src/crypto/chacha20.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the ChaCha20 algorithm. 20 | 21 | use core::ptr; 22 | 23 | /// Performs a left rotation of `b` bits on the value `a`. 24 | macro_rules! rotl { 25 | ($a:expr, $b:expr) => { 26 | ($a << $b) | ($a >> (32 - $b)) 27 | }; 28 | } 29 | 30 | /// Performs a quarter round on the given values. 31 | macro_rules! quarter_round { 32 | ($a:expr, $b:expr, $c:expr, $d:expr) => { 33 | #[allow(clippy::manual_rotate)] 34 | { 35 | $a = $a.wrapping_add($b); 36 | $d ^= $a; 37 | $d = rotl!($d, 16); 38 | 39 | $c = $c.wrapping_add($d); 40 | $b ^= $c; 41 | $b = rotl!($b, 12); 42 | 43 | $a = $a.wrapping_add($b); 44 | $d ^= $a; 45 | $d = rotl!($d, 8); 46 | 47 | $c = $c.wrapping_add($d); 48 | $b ^= $c; 49 | $b = rotl!($b, 7); 50 | } 51 | }; 52 | } 53 | 54 | /// Computes a ChaCha20 block. 55 | pub fn block(inout: &mut [u8; 64]) { 56 | let mut buff: [u32; 16] = [0; 16]; 57 | 58 | unsafe { 59 | ptr::copy_nonoverlapping(inout.as_ptr(), buff.as_mut_ptr() as *mut u8, 64); 60 | } 61 | 62 | for _ in (0..20).step_by(2) { 63 | // Odd round 64 | quarter_round!(buff[0], buff[4], buff[8], buff[12]); 65 | quarter_round!(buff[1], buff[5], buff[9], buff[13]); 66 | quarter_round!(buff[2], buff[6], buff[10], buff[14]); 67 | quarter_round!(buff[3], buff[7], buff[11], buff[15]); 68 | 69 | // Even round 70 | quarter_round!(buff[0], buff[5], buff[10], buff[15]); 71 | quarter_round!(buff[1], buff[6], buff[11], buff[12]); 72 | quarter_round!(buff[2], buff[7], buff[8], buff[13]); 73 | quarter_round!(buff[3], buff[4], buff[9], buff[14]); 74 | } 75 | 76 | unsafe { 77 | ptr::copy_nonoverlapping(buff.as_ptr() as *mut u8, inout.as_mut_ptr(), 64); 78 | } 79 | } 80 | 81 | // TODO unit tests 82 | -------------------------------------------------------------------------------- /kernel/src/crypto/checksum.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements checksum algorithms. A checksum is a value allowing 20 | //! to verify the integrity of a structure. 21 | 22 | /// Computes a checksum on `data` according to RFC1071. 23 | pub fn compute_rfc1071(data: &[u8]) -> u16 { 24 | let mut sum: u32 = 0; 25 | let mut i = 0; 26 | 27 | // Main loop 28 | while i < (data.len() & !1) { 29 | sum += ((data[i + 1] as u32) << 8) | (data[i] as u32); 30 | i += 2; 31 | } 32 | 33 | // Add remaining byte 34 | if i < data.len() { 35 | sum += data[i] as u32; 36 | } 37 | 38 | // Folding 32-bits value into 16-bits 39 | while (sum >> 16) != 0 { 40 | sum = (sum & 0xffff) + (sum >> 16); 41 | } 42 | 43 | (!sum) as u16 44 | } 45 | 46 | /// Computes the lookup table for the given generator polynomial. 47 | /// 48 | /// Arguments: 49 | /// - `table` is filled with the table's values. 50 | /// - `polynom` is the polynom. 51 | pub fn compute_crc32_lookuptable(table: &mut [u32; 256], polynom: u32) { 52 | // Little endian 53 | let mut i = table.len() / 2; 54 | let mut crc = 1; 55 | 56 | while i > 0 { 57 | if crc & 1 != 0 { 58 | crc = (crc >> 1) ^ polynom; 59 | } else { 60 | crc >>= 1; 61 | } 62 | 63 | for j in (0..table.len()).step_by(2 * i) { 64 | table[i ^ j] = crc ^ table[j]; 65 | } 66 | 67 | i >>= 1; 68 | } 69 | } 70 | 71 | /// Computes the CRC32 checksum on the given data `data` with the given table 72 | /// `table` for the wanted generator polynomial. 73 | pub fn compute_crc32(data: &[u8], table: &[u32; 256]) -> u32 { 74 | // Sarwate algorithm 75 | let mut crc = !0u32; 76 | 77 | for b in data { 78 | let i = ((crc as usize) ^ (*b as usize)) & 0xff; 79 | crc = table[i] ^ (crc >> 8); 80 | } 81 | 82 | !crc 83 | } 84 | 85 | #[cfg(test)] 86 | mod test { 87 | use super::*; 88 | 89 | #[test_case] 90 | fn rfc1071_0() { 91 | for i in 0..=u16::MAX { 92 | let data = [(i & 0xff) as _, (i >> 8) as _]; 93 | assert_eq!(compute_rfc1071(&data), !i); 94 | } 95 | } 96 | 97 | // TODO More tests on RFC1071 98 | // TODO Test CRC32 99 | } 100 | -------------------------------------------------------------------------------- /kernel/src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Cryptographic algorithms and tools. 20 | 21 | use utils::errno::AllocResult; 22 | 23 | pub mod chacha20; 24 | pub mod checksum; 25 | pub mod rand; 26 | 27 | /// Initializes cryptographic features. 28 | pub(crate) fn init() -> AllocResult<()> { 29 | rand::init() 30 | } 31 | -------------------------------------------------------------------------------- /kernel/src/debug.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Debugging tools for the kernel. 20 | 21 | use crate::{elf, memory, memory::VirtAddr}; 22 | use core::ptr; 23 | use utils::DisplayableStr; 24 | 25 | /// Fills the slice `stack` with the callstack starting at `frame`. 26 | /// 27 | /// The first element is the last called function and the last element is the first called 28 | /// function. 29 | /// 30 | /// When the stack ends, the function fills the rest of the slice with `None`. 31 | /// 32 | /// # Safety 33 | /// 34 | /// The caller must ensure the `frame` parameter points ta a valid stack frame. 35 | pub unsafe fn get_callstack(mut frame: *const usize, stack: &mut [VirtAddr]) { 36 | stack.fill(VirtAddr::default()); 37 | for f in stack.iter_mut() { 38 | if frame.is_null() { 39 | break; 40 | } 41 | let pc = ptr::read_unaligned(frame.add(1) as _); 42 | if pc < memory::PROCESS_END { 43 | break; 44 | } 45 | *f = pc; 46 | frame = ptr::read_unaligned(frame as *const *const usize); 47 | } 48 | } 49 | 50 | /// Prints a callstack, including symbols' names and addresses. 51 | /// 52 | /// `stack` is the callstack to print. 53 | /// 54 | /// If the callstack is empty, the function just prints `Empty`. 55 | pub fn print_callstack(stack: &[VirtAddr]) { 56 | if !matches!(stack.first(), Some(p) if !p.is_null()) { 57 | crate::println!("Empty"); 58 | return; 59 | } 60 | for (i, pc) in stack.iter().enumerate() { 61 | if pc.is_null() { 62 | break; 63 | } 64 | let name = elf::kernel::get_function_name(*pc).unwrap_or(b"???"); 65 | crate::println!("{i}: {pc:p} -> {}", DisplayableStr(name)); 66 | } 67 | } 68 | 69 | /// Utilities to manipulate QEMU. 70 | #[cfg(config_debug_qemu)] 71 | pub mod qemu { 72 | use crate::{arch::x86::io::outl, power}; 73 | 74 | /// The port used to trigger QEMU emulator exit with the given exit code. 75 | const EXIT_PORT: u16 = 0xf4; 76 | 77 | /// QEMU exit code for success. 78 | pub const SUCCESS: u32 = 0x10; 79 | /// QEMU exit code for failure. 80 | pub const FAILURE: u32 = 0x11; 81 | 82 | /// Exits QEMU with the given status. 83 | pub fn exit(status: u32) { 84 | unsafe { 85 | outl(EXIT_PORT, status); 86 | } 87 | // halt in case exiting did not succeed for some reason 88 | power::halt(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /kernel/src/device/bus/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements internal buses, including PCI and USB. 20 | 21 | pub mod pci; 22 | 23 | use crate::device::manager; 24 | use utils::errno::EResult; 25 | 26 | /// Detects internal buses and registers them. 27 | pub fn detect() -> EResult<()> { 28 | // PCI 29 | let mut pci_manager = pci::PCIManager::new(); 30 | pci_manager.scan()?; 31 | manager::register(pci_manager)?; 32 | 33 | // TODO USB 34 | 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /kernel/src/device/storage/partition/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! A storage device can be divided into several blocks called partitions, 20 | //! allowing for instance to install several systems on the same machine. 21 | 22 | mod gpt; 23 | mod mbr; 24 | 25 | use crate::device::BlkDev; 26 | use gpt::Gpt; 27 | use mbr::MbrTable; 28 | use utils::{boxed::Box, collections::vec::Vec, errno::EResult, ptr::arc::Arc}; 29 | 30 | /// A disk partition bounds. 31 | #[derive(Debug)] 32 | pub struct Partition { 33 | /// The offset to the first sector of the partition. 34 | pub offset: u64, 35 | /// The number of sectors in the partition. 36 | pub size: u64, 37 | } 38 | 39 | /// Trait representing a partition table. 40 | pub trait Table { 41 | /// Reads the partition table from the given storage device `dev`. 42 | /// 43 | /// If the partition table isn't present on the storage interface, the 44 | /// function returns `None`. 45 | fn read(dev: &Arc<BlkDev>) -> EResult<Option<Self>> 46 | where 47 | Self: Sized; 48 | 49 | /// Returns the type of the partition table. 50 | fn get_type(&self) -> &'static str; 51 | 52 | /// Reads the partitions list. 53 | /// 54 | /// `dev` is the storage device on which the partitions are to be read. 55 | fn read_partitions(&self, dev: &Arc<BlkDev>) -> EResult<Vec<Partition>>; 56 | } 57 | 58 | /// Reads the list of partitions from the block device. 59 | /// 60 | /// If no partitions table is present, the function returns `None`. 61 | pub fn read(dev: &Arc<BlkDev>) -> EResult<Option<Box<dyn Table>>> { 62 | // Try GPT 63 | if let Some(table) = Gpt::read(dev)? { 64 | return Ok(Some(Box::new(table)?)); 65 | } 66 | // Try MBR 67 | if let Some(table) = MbrTable::read(dev)? { 68 | return Ok(Some(Box::new(table)?)); 69 | } 70 | Ok(None) 71 | } 72 | -------------------------------------------------------------------------------- /kernel/src/file/fs/ext2/bgd.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! A Block Group Descriptor is a structure stored in the Block Group Descriptor 20 | //! Table which represents a block group, which is a subdivision of the 21 | //! filesystem. 22 | 23 | use super::{Ext2Fs, read_block}; 24 | use crate::memory::cache::RcFrameVal; 25 | use core::{mem::size_of, sync::atomic::AtomicU16}; 26 | use macros::AnyRepr; 27 | use utils::errno::EResult; 28 | 29 | /// Start block of the block group descriptor table 30 | const BGDT_START_BLK: u32 = 1; 31 | 32 | /// A block group descriptor. 33 | #[repr(C)] 34 | #[derive(AnyRepr)] 35 | pub struct BlockGroupDescriptor { 36 | /// The block address of the block usage bitmap. 37 | pub bg_block_bitmap: u32, 38 | /// The block address of the inode usage bitmap. 39 | pub bg_inode_bitmap: u32, 40 | /// Starting block address of inode table. 41 | pub bg_inode_table: u32, 42 | /// Number of unallocated blocks in group. 43 | pub bg_free_blocks_count: AtomicU16, 44 | /// Number of unallocated inodes in group. 45 | pub bg_free_inodes_count: AtomicU16, 46 | /// Number of directories in group. 47 | pub bg_used_dirs_count: AtomicU16, 48 | 49 | pub bg_pad: [u8; 14], 50 | } 51 | 52 | impl BlockGroupDescriptor { 53 | /// Returns the `i`th block group descriptor 54 | pub fn get(i: u32, fs: &Ext2Fs) -> EResult<RcFrameVal<Self>> { 55 | let blk_size = fs.sp.get_block_size() as usize; 56 | let bgd_per_blk = blk_size / size_of::<Self>(); 57 | // Read block 58 | let blk_off = BGDT_START_BLK + (i / bgd_per_blk as u32); 59 | let blk = read_block(fs, blk_off as _)?; 60 | // Get entry 61 | let off = i as usize % bgd_per_blk; 62 | Ok(RcFrameVal::new(blk, off)) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/mem_info.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the `meminfo` file, allows to retrieve information about memory usage of the 20 | //! system. 21 | 22 | use crate::{ 23 | file::{File, fs::FileOps}, 24 | format_content, memory, 25 | memory::user::UserSlice, 26 | }; 27 | use utils::errno::EResult; 28 | 29 | /// The `meminfo` file. 30 | #[derive(Debug, Default)] 31 | pub struct MemInfo; 32 | 33 | impl FileOps for MemInfo { 34 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 35 | let mem_info = memory::stats::MEM_INFO.lock().clone(); 36 | format_content!(off, buf, "{}", mem_info) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/proc_dir/cmdline.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The `cmdline` node allows to retrieve the list of command line arguments of 20 | //! the process. 21 | 22 | use super::read_memory; 23 | use crate::{ 24 | file::{File, fs::FileOps}, 25 | format_content, 26 | memory::user::UserSlice, 27 | process::{Process, pid::Pid}, 28 | }; 29 | use utils::{DisplayableStr, errno, errno::EResult}; 30 | 31 | /// The cmdline node of the proc. 32 | #[derive(Clone, Debug)] 33 | pub struct Cmdline(pub Pid); 34 | 35 | impl FileOps for Cmdline { 36 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 37 | let proc = Process::get_by_pid(self.0).ok_or_else(|| errno!(ENOENT))?; 38 | let Some(mem_space) = proc.mem_space.as_ref() else { 39 | return Ok(0); 40 | }; 41 | let cmdline = read_memory( 42 | mem_space, 43 | mem_space.exe_info.argv_begin, 44 | mem_space.exe_info.argv_end, 45 | )?; 46 | format_content!(off, buf, "{}", DisplayableStr(&cmdline)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/proc_dir/cwd.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the `cwd` node, which is a link to the current 20 | //! working directory of the process. 21 | 22 | use crate::{ 23 | file::{fs::NodeOps, vfs, vfs::node::Node}, 24 | format_content, 25 | memory::user::UserSlice, 26 | process::{Process, pid::Pid}, 27 | }; 28 | use utils::{errno, errno::EResult}; 29 | 30 | /// The `cwd` node. 31 | #[derive(Debug)] 32 | pub struct Cwd(pub Pid); 33 | 34 | impl NodeOps for Cwd { 35 | fn readlink(&self, _node: &Node, buf: UserSlice<u8>) -> EResult<usize> { 36 | let proc = Process::get_by_pid(self.0).ok_or_else(|| errno!(ENOENT))?; 37 | let fs = proc.fs.lock(); 38 | let cwd = vfs::Entry::get_path(&fs.cwd)?; 39 | format_content!(0, buf, "{cwd}") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/proc_dir/environ.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The `environ` node allows to retrieve the environment variables of the process. 20 | 21 | use crate::{ 22 | file::{ 23 | File, 24 | fs::{FileOps, proc::proc_dir::read_memory}, 25 | }, 26 | format_content, 27 | memory::user::UserSlice, 28 | process::{Process, pid::Pid}, 29 | }; 30 | use utils::{DisplayableStr, errno, errno::EResult}; 31 | 32 | /// The `environ` node of the proc. 33 | #[derive(Clone, Debug)] 34 | pub struct Environ(pub Pid); 35 | 36 | impl FileOps for Environ { 37 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 38 | let proc = Process::get_by_pid(self.0).ok_or_else(|| errno!(ENOENT))?; 39 | let Some(mem_space) = proc.mem_space.as_ref() else { 40 | return Ok(0); 41 | }; 42 | let environ = read_memory( 43 | mem_space, 44 | mem_space.exe_info.envp_begin, 45 | mem_space.exe_info.envp_end, 46 | )?; 47 | format_content!(off, buf, "{}", DisplayableStr(&environ)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/proc_dir/exe.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the `exe` node, which is a link to the executable 20 | //! file of the process. 21 | 22 | use crate::{ 23 | file::{fs::NodeOps, vfs, vfs::node::Node}, 24 | format_content, 25 | memory::user::UserSlice, 26 | process::{Process, pid::Pid}, 27 | }; 28 | use utils::{errno, errno::EResult}; 29 | 30 | /// The `exe` node. 31 | #[derive(Debug)] 32 | pub struct Exe(pub Pid); 33 | 34 | impl NodeOps for Exe { 35 | fn readlink(&self, _node: &Node, buf: UserSlice<u8>) -> EResult<usize> { 36 | let proc = Process::get_by_pid(self.0).ok_or_else(|| errno!(ENOENT))?; 37 | let path = proc 38 | .mem_space 39 | .as_ref() 40 | .map(|mem_space| vfs::Entry::get_path(&mem_space.exe_info.exe)) 41 | .transpose()? 42 | .unwrap_or_default(); 43 | format_content!(0, buf, "{path}") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/proc_dir/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the directory of a process in the proc. 20 | 21 | use crate::{ 22 | memory::{VirtAddr, user::UserSlice}, 23 | process::mem_space::MemSpace, 24 | }; 25 | use utils::{collections::vec::Vec, errno::AllocResult, ptr::arc::Arc, vec}; 26 | 27 | pub mod cmdline; 28 | pub mod cwd; 29 | pub mod environ; 30 | pub mod exe; 31 | pub mod mounts; 32 | pub mod stat; 33 | pub mod status; 34 | 35 | /// Reads a range of memory from `mem_space` and writes it to `f`. 36 | /// 37 | /// `begin` and `end` represent the range of memory to read. 38 | pub fn read_memory( 39 | mem_space: &Arc<MemSpace>, 40 | begin: VirtAddr, 41 | end: VirtAddr, 42 | ) -> AllocResult<Vec<u8>> { 43 | let len = end.0.saturating_sub(begin.0); 44 | let mut buf = vec![0; len]?; 45 | let Ok(slice) = UserSlice::from_user(begin.as_ptr(), len) else { 46 | // Slice is out of range: return zeros 47 | return Ok(buf); 48 | }; 49 | unsafe { 50 | MemSpace::switch(mem_space, |_| { 51 | let mut i = 0; 52 | while i < len { 53 | let Ok(len) = slice.copy_from_user(i, &mut buf[i..]) else { 54 | break; 55 | }; 56 | i += len; 57 | } 58 | }); 59 | } 60 | Ok(buf) 61 | } 62 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/proc_dir/mounts.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the `mounts` node which allows to get the list of mountpoint. 20 | 21 | use crate::{ 22 | file::{File, fs::FileOps, vfs, vfs::mountpoint}, 23 | format_content, 24 | memory::user::UserSlice, 25 | process::pid::Pid, 26 | }; 27 | use core::{fmt, fmt::Formatter}; 28 | use utils::{DisplayableStr, errno::EResult}; 29 | 30 | /// The `mounts` node. 31 | #[derive(Debug)] 32 | pub struct Mounts(pub Pid); 33 | 34 | impl FileOps for Mounts { 35 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 36 | format_content!(off, buf, "{self}") 37 | } 38 | } 39 | 40 | impl fmt::Display for Mounts { 41 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 42 | let mps = mountpoint::MOUNT_POINTS.lock(); 43 | for (_, mp) in mps.iter() { 44 | let Ok(target) = vfs::Entry::get_path(&mp.root_entry) else { 45 | continue; 46 | }; 47 | let fs_type = mp.fs.ops.get_name(); 48 | let flags = "TODO"; // TODO 49 | writeln!( 50 | f, 51 | "{source} {target} {fs_type} {flags} 0 0", 52 | source = mp.source, 53 | target = target, 54 | fs_type = DisplayableStr(fs_type) 55 | )?; 56 | } 57 | Ok(()) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/proc_dir/stat.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the `stat` file, which allows to retrieve the current 20 | //! status of the process. 21 | 22 | use crate::{ 23 | file::{File, fs::FileOps}, 24 | format_content, 25 | memory::{VirtAddr, user::UserSlice}, 26 | process::{Process, pid::Pid}, 27 | }; 28 | use core::fmt; 29 | use utils::{DisplayableStr, errno, errno::EResult}; 30 | 31 | /// The `stat` node of the proc. 32 | #[derive(Debug)] 33 | pub struct StatNode(pub Pid); 34 | 35 | impl FileOps for StatNode { 36 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 37 | let proc = Process::get_by_pid(self.0).ok_or_else(|| errno!(ENOENT))?; 38 | let disp = fmt::from_fn(|f| { 39 | let (name, vmem_usage) = proc 40 | .mem_space 41 | .as_ref() 42 | .map(|m| (m.exe_info.exe.name.as_bytes(), m.get_vmem_usage())) 43 | .unwrap_or_default(); 44 | let user_regs = proc.user_regs(); 45 | // TODO Fill every fields with process's data 46 | write!( 47 | f, 48 | "{pid} ({name}) {state_char} {ppid} {pgid} {sid} TODO TODO 0 \ 49 | 0 0 0 0 {user_jiffies} {kernel_jiffies} TODO TODO {priority} {nice} {num_threads} 0 {vmem_usage} \ 50 | TODO TODO TODO TODO {sp:?} {pc:?} TODO TODO TODO TODO 0 0 0 TODO TODO TODO TODO TODO TODO TODO TODO \ 51 | TODO TODO TODO TODO TODO TODO TODO TODO TODO", 52 | pid = self.0, 53 | name = DisplayableStr(name), 54 | state_char = proc.get_state().as_char(), 55 | ppid = proc.get_parent_pid(), 56 | pgid = proc.get_pgid(), 57 | sid = 0, // TODO 58 | user_jiffies = 0, // TODO 59 | kernel_jiffies = 0, // TODO 60 | priority = 0, // TODO 61 | nice = 0, // TODO 62 | num_threads = 1, // TODO 63 | sp = VirtAddr(user_regs.get_stack_address() as _), 64 | pc = VirtAddr(user_regs.get_program_counter() as _), 65 | ) 66 | }); 67 | format_content!(off, buf, "{disp}") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/self_link.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of the `self` symlink, which points to the current process's directory. 20 | 21 | use crate::{ 22 | file::{fs::NodeOps, vfs::node::Node}, 23 | format_content, 24 | memory::user::UserSlice, 25 | process::Process, 26 | }; 27 | use utils::errno::EResult; 28 | 29 | /// The `self` symlink. 30 | #[derive(Debug, Default)] 31 | pub struct SelfNode; 32 | 33 | impl NodeOps for SelfNode { 34 | fn readlink(&self, _node: &Node, buf: UserSlice<u8>) -> EResult<usize> { 35 | let pid = Process::current().get_pid(); 36 | format_content!(0, buf, "{pid}") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/sys_dir/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! TODO doc 20 | 21 | use crate::{ 22 | file::{File, FileType, Stat, fs::FileOps}, 23 | format_content, 24 | memory::user::UserSlice, 25 | }; 26 | use utils::errno::EResult; 27 | 28 | /// The `osrelease` file. 29 | #[derive(Debug, Default)] 30 | pub struct OsRelease; 31 | 32 | impl FileOps for OsRelease { 33 | fn get_stat(&self, _file: &File) -> EResult<Stat> { 34 | Ok(Stat { 35 | mode: FileType::Regular.to_mode() | 0o444, 36 | ..Default::default() 37 | }) 38 | } 39 | 40 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 41 | format_content!(off, buf, "{}\n", crate::VERSION) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/uptime.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The uptime file returns the amount of time elapsed since the system started up. 20 | 21 | use crate::{ 22 | file::{File, fs::FileOps}, 23 | format_content, 24 | memory::user::UserSlice, 25 | time::clock::{Clock, current_time_ns}, 26 | }; 27 | use utils::errno::EResult; 28 | 29 | /// The `uptime` file. 30 | #[derive(Debug, Default)] 31 | pub struct Uptime; 32 | 33 | impl FileOps for Uptime { 34 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 35 | let uptime = current_time_ns(Clock::Boottime) / 10_000_000; 36 | let uptime_upper = uptime / 100; 37 | let uptime_lower = uptime % 100; 38 | // TODO second value is the total amount of time each core has spent idle 39 | format_content!(off, buf, "{uptime_upper}.{uptime_lower:02} 0.00\n") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /kernel/src/file/fs/proc/version.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The `version` file returns the version of the kernel. 20 | 21 | use crate::{ 22 | file::{File, fs::FileOps}, 23 | format_content, 24 | memory::user::UserSlice, 25 | }; 26 | use utils::errno::EResult; 27 | 28 | /// Kernel version file. 29 | #[derive(Debug, Default)] 30 | pub struct Version; 31 | 32 | impl FileOps for Version { 33 | fn read(&self, _file: &File, off: u64, buf: UserSlice<u8>) -> EResult<usize> { 34 | format_content!(off, buf, "{} version {}\n", crate::NAME, crate::VERSION) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /kernel/src/file/util.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements utility functions for files manipulations. 20 | 21 | use crate::file::{FileType, Stat, perm::AccessProfile, vfs, vfs::ResolutionSettings}; 22 | use utils::{ 23 | collections::path::{Component, Path, PathBuf}, 24 | errno, 25 | errno::EResult, 26 | }; 27 | 28 | /// Creates the directories necessary to reach path `path`. 29 | /// 30 | /// If relative, the path is taken from the root. 31 | pub fn create_dirs(path: &Path) -> EResult<()> { 32 | // Path of the parent directory 33 | let mut p = PathBuf::root()?; 34 | for comp in path.components() { 35 | let Component::Normal(name) = &comp else { 36 | continue; 37 | }; 38 | if let Ok(parent) = vfs::get_file_from_path(&p, &ResolutionSettings::kernel_follow()) { 39 | let res = vfs::create_file( 40 | parent, 41 | name, 42 | &AccessProfile::KERNEL, 43 | Stat { 44 | mode: FileType::Directory.to_mode() | 0o755, 45 | ..Default::default() 46 | }, 47 | ); 48 | match res { 49 | Err(e) if e.as_int() != errno::EEXIST => return Err(e), 50 | _ => {} 51 | } 52 | } 53 | p = p.join(comp)?; 54 | } 55 | Ok(()) 56 | } 57 | -------------------------------------------------------------------------------- /kernel/src/libc/memcmp.c: -------------------------------------------------------------------------------- 1 | // Code taken from musl. License: https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT 2 | 3 | #include <stddef.h> 4 | 5 | int memcmp(const void *vl, const void *vr, size_t n) 6 | { 7 | const unsigned char *l = vl, *r = vr; 8 | for (; n && *l == *r; n--, l++, r++); 9 | return n ? *l - *r : 0; 10 | } 11 | -------------------------------------------------------------------------------- /kernel/src/libc/strlen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | #include <stdint.h> 20 | #include <stddef.h> 21 | 22 | #define LOW ((size_t) -1 / 0xff) 23 | #define HIGH (LOW * 0x80) 24 | #define ZERO(w) (((w) - LOW) & (~(w) & HIGH)) 25 | 26 | size_t strlen(const char *s) 27 | { 28 | const char *n = s; 29 | 30 | // Align 31 | for (; (uintptr_t) n % sizeof(size_t); ++n) if (!*n) return n - s; 32 | // Check word-by-word 33 | const size_t *word = (size_t *) n; 34 | for (; !ZERO(*word); ++word); 35 | n = (const char *) word; 36 | // Count remaining 37 | for (; *n; ++n) 38 | ; 39 | return n - s; 40 | } 41 | -------------------------------------------------------------------------------- /kernel/src/memory/malloc/block.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! In the malloc allocator, a block is a memory allocation performed from 20 | //! another allocator, which is too big to be used directly for allocation, so 21 | //! it has to be divided into chunks. 22 | 23 | use super::chunk::{Chunk, FreeChunk}; 24 | use crate::memory::buddy; 25 | use core::{ 26 | mem::{offset_of, size_of}, 27 | num::NonZeroUsize, 28 | ptr, 29 | }; 30 | use utils::{errno::AllocResult, limits::PAGE_SIZE}; 31 | 32 | /// A frame of memory allocated using the buddy allocator, storing memory chunks. 33 | #[repr(C, align(8))] 34 | pub struct Block { 35 | /// The order of the frame for the buddy allocator 36 | order: buddy::FrameOrder, 37 | /// The first chunk of the block 38 | pub first_chunk: Chunk, 39 | } 40 | 41 | impl Block { 42 | /// Allocates a new block of memory with the minimum available size 43 | /// `min_size` in bytes. 44 | /// 45 | /// The buddy allocator must be initialized before using this function. 46 | /// 47 | /// The underlying chunk created by this function is **not** inserted into the free list. 48 | pub fn new(min_size: NonZeroUsize) -> AllocResult<&'static mut Self> { 49 | let min_total_size = size_of::<Block>() + min_size.get(); 50 | let block_order = buddy::get_order(min_total_size.div_ceil(PAGE_SIZE)); 51 | // The size of the first chunk 52 | let first_chunk_size = buddy::get_frame_size(block_order) - size_of::<Block>(); 53 | debug_assert!(first_chunk_size >= min_size.get()); 54 | // Allocate the block 55 | let block = unsafe { 56 | let mut ptr = buddy::alloc_kernel(block_order, 0)?.cast(); 57 | ptr::write_volatile( 58 | ptr.as_mut(), 59 | Self { 60 | order: block_order, 61 | first_chunk: Chunk::new(), 62 | }, 63 | ); 64 | ptr.as_mut() 65 | }; 66 | *block.first_chunk.as_free_chunk().unwrap() = FreeChunk::new(first_chunk_size); 67 | Ok(block) 68 | } 69 | 70 | /// Returns a mutable reference to the block whose first chunk's reference 71 | /// is passed as argument. 72 | pub unsafe fn from_first_chunk(chunk: *mut Chunk) -> &'static mut Block { 73 | let first_chunk_off = offset_of!(Block, first_chunk); 74 | let ptr = chunk.byte_sub(first_chunk_off) as *mut Self; 75 | debug_assert!(ptr.is_aligned_to(PAGE_SIZE)); 76 | &mut *ptr 77 | } 78 | } 79 | 80 | impl Drop for Block { 81 | fn drop(&mut self) { 82 | unsafe { 83 | buddy::free_kernel(self as *mut _ as _, self.order); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /kernel/src/memory/oom.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! OOM killing is a procedure which is invoked when the kernel runs out of 20 | //! memory. 21 | //! 22 | //! The OOM killer terminates one or more processes according to a score computed for 23 | //! each of them. 24 | //! 25 | //! This is an emergency procedure which is not supposed to be used under normal conditions. 26 | 27 | use crate::{file::vfs, memory::cache}; 28 | use utils::errno::AllocResult; 29 | 30 | /// Attempts to reclaim memory from different places, or panics on failure. 31 | pub fn reclaim() { 32 | // Attempt to shrink the page cache 33 | if cache::shrink() { 34 | return; 35 | } 36 | // Attempt to shrink the directory entries cache 37 | if vfs::shrink_entries() { 38 | return; 39 | } 40 | // TODO Attempt to: 41 | // - swap memory to disk 42 | // - if the kernel is configured for it, prompt the user to select processes to kill 43 | // - if the kernel is configured for it, kill the process with the highest OOM score (ignore 44 | // init process) 45 | // - else, panic: 46 | panic!("Out of memory"); 47 | } 48 | 49 | /// Executes the given function. On failure due to a lack of memory, the function runs the OOM 50 | /// killer, then tries again. 51 | /// 52 | /// If the OOM killer is unable to free enough memory, the kernel may panic. 53 | pub fn wrap<T, F: FnMut() -> AllocResult<T>>(mut f: F) -> T { 54 | loop { 55 | if let Ok(r) = f() { 56 | return r; 57 | } 58 | reclaim(); 59 | // TODO Check if current process has been killed 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /kernel/src/memory/stats.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Statistics about memory usage. 20 | 21 | use crate::sync::mutex::Mutex; 22 | use core::{ 23 | fmt, 24 | fmt::{Display, Formatter}, 25 | }; 26 | 27 | /// Stores memory usage information. Each field is in KiB. 28 | #[derive(Clone)] 29 | pub struct MemInfo { 30 | /// The total amount of memory on the system. 31 | pub mem_total: usize, 32 | /// The total amount of free physical memory. 33 | pub mem_free: usize, 34 | /// The total amount of free + reclaimable memory. 35 | pub mem_available: usize, 36 | /// The total amount of active (mapped) memory. 37 | pub active: usize, 38 | /// The total amount of inactive (not mapped but cached) memory. 39 | pub inactive: usize, 40 | } 41 | 42 | impl Display for MemInfo { 43 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 44 | writeln!( 45 | f, 46 | "MemTotal: {} kB 47 | MemFree: {} kB 48 | MemAvailable: {} kB 49 | Active: {} kB 50 | Inactive: {} kB", 51 | self.mem_total, self.mem_free, self.mem_available, self.active, self.inactive 52 | ) 53 | } 54 | } 55 | 56 | /// Memory usage statistics. 57 | pub static MEM_INFO: Mutex<MemInfo> = Mutex::new(MemInfo { 58 | mem_total: 0, 59 | mem_free: 0, 60 | mem_available: 0, 61 | active: 0, 62 | inactive: 0, 63 | }); 64 | -------------------------------------------------------------------------------- /kernel/src/memory/trace.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Memory usage tracing utility functions. 20 | 21 | use crate::{debug, device::serial, memory::VirtAddr, register_get}; 22 | use core::ptr; 23 | 24 | /// The operation being sampled. 25 | #[repr(u8)] 26 | pub enum SampleOp { 27 | Alloc = 0, 28 | Realloc = 1, 29 | Free = 2, 30 | } 31 | 32 | /// Writes a memory tracing sample to the **COM2** serial port. 33 | /// 34 | /// Arguments: 35 | /// - `allocator` is the name of the allocator. 36 | /// - `op` is the operation number. 37 | /// - `ptr` is the affected pointer. 38 | /// - `size` is the new size of the allocation. The unit is dependent on the allocator. 39 | pub fn sample(allocator: &str, op: SampleOp, addr: usize, size: usize) { 40 | // Dump callstack 41 | #[cfg(target_arch = "x86")] 42 | let frame = register_get!("ebp"); 43 | #[cfg(target_arch = "x86_64")] 44 | let frame = register_get!("rbp"); 45 | let frame = ptr::with_exposed_provenance::<usize>(frame); 46 | let mut callstack: [VirtAddr; 64] = [VirtAddr::default(); 64]; 47 | unsafe { 48 | debug::get_callstack(frame, &mut callstack); 49 | } 50 | // COM2 51 | let mut serial = serial::PORTS[1].lock(); 52 | // Write name of allocator 53 | serial.write(&[allocator.len() as u8]); 54 | serial.write(allocator.as_bytes()); 55 | // Write op 56 | serial.write(&[op as u8]); 57 | // Write ptr and size 58 | serial.write(&(addr as u64).to_le_bytes()); 59 | serial.write(&(size as u64).to_le_bytes()); 60 | // Write callstack 61 | let len = callstack 62 | .iter() 63 | .enumerate() 64 | .find(|(_, p)| p.is_null()) 65 | .map(|(i, _)| i) 66 | .unwrap_or(callstack.len()); 67 | serial.write(&[len as u8]); 68 | for f in &callstack[..len] { 69 | serial.write(&(f.0 as u64).to_le_bytes()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /kernel/src/net/buff.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! TODO doc 20 | 21 | use core::ptr::NonNull; 22 | 23 | /// A linked-list of buffers representing a packet being built. 24 | /// 25 | /// This structure works without any memory allocations and relies entirely on lifetimes. 26 | pub struct BuffList<'b> { 27 | /// The buffer. 28 | b: &'b [u8], 29 | 30 | /// The next buffer in the list. 31 | next: Option<NonNull<BuffList<'b>>>, 32 | /// The length of following buffers combined. 33 | next_len: usize, 34 | } 35 | 36 | impl<'b> From<&'b [u8]> for BuffList<'b> { 37 | fn from(b: &'b [u8]) -> Self { 38 | Self { 39 | b, 40 | 41 | next: None, 42 | next_len: 0, 43 | } 44 | } 45 | } 46 | 47 | impl<'b> BuffList<'b> { 48 | /// Returns the length of the buffer, plus following buffers. 49 | #[allow(clippy::len_without_is_empty)] 50 | pub fn len(&self) -> usize { 51 | self.b.len() + self.next_len 52 | } 53 | 54 | /// Pushes another buffer at the front of the current list. 55 | /// 56 | /// The function returns the new head of the list (which is the given `front`). 57 | pub fn push_front<'o>(&mut self, mut front: BuffList<'o>) -> BuffList<'o> 58 | where 59 | 'b: 'o, 60 | { 61 | front.next = NonNull::new(self); 62 | front.next_len = self.b.len() + self.next_len; 63 | 64 | front 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /kernel/src/net/icmp.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements the Internet Control Message Protocol. 20 | //! 21 | //! This procotol is defined by the following RFCs: 22 | //! - With IPv4: RFC 792 23 | //! - With IPv6 (ICMPv6): RFC 4443 24 | 25 | /// An enumeration of ICMP packet types. 26 | pub enum ICMPType { 27 | /// Used by ping to reply to an echo request. 28 | EchoReply, 29 | /// TODO doc 30 | DestinationUnreachable, 31 | /// TODO doc 32 | SourceQuench, 33 | /// TODO doc 34 | RedirectMessage, 35 | /// Used by ping to request an echo. 36 | EchoRequest, 37 | /// TODO doc 38 | RouterAdvertisement, 39 | /// TODO doc 40 | RouterSolicitation, 41 | /// TODO doc 42 | TimeExceeded, 43 | /// TODO doc 44 | ParameterProblem, 45 | /// TODO doc 46 | Timestamp, 47 | /// TODO doc 48 | TimestampReply, 49 | /// TODO doc 50 | InformationRequest, 51 | /// TODO doc 52 | InformationReply, 53 | /// TODO doc 54 | AddressMaskRequest, 55 | /// TODO doc 56 | AddressMaskReply, 57 | /// TODO doc 58 | Traceroute, 59 | /// TODO doc 60 | ExtendedEchoRequest, 61 | /// TODO doc 62 | ExtendedEchoReply, 63 | } 64 | 65 | impl ICMPType { 66 | /// Returns a type from its ID. 67 | /// 68 | /// If no type match, the function returns None. 69 | pub fn from_type(id: u8) -> Option<Self> { 70 | match id { 71 | 0 => Some(Self::EchoReply), 72 | 3 => Some(Self::DestinationUnreachable), 73 | 4 => Some(Self::SourceQuench), 74 | 5 => Some(Self::RedirectMessage), 75 | 8 => Some(Self::EchoRequest), 76 | 9 => Some(Self::RouterAdvertisement), 77 | 10 => Some(Self::RouterSolicitation), 78 | 11 => Some(Self::TimeExceeded), 79 | 12 => Some(Self::ParameterProblem), 80 | 13 => Some(Self::Timestamp), 81 | 14 => Some(Self::TimestampReply), 82 | 15 => Some(Self::InformationRequest), 83 | 16 => Some(Self::InformationReply), 84 | 17 => Some(Self::AddressMaskRequest), 85 | 18 => Some(Self::AddressMaskReply), 86 | 30 => Some(Self::Traceroute), 87 | 42 => Some(Self::ExtendedEchoRequest), 88 | 43 => Some(Self::ExtendedEchoReply), 89 | 90 | _ => None, 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /kernel/src/net/lo.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements the local loopback. 20 | 21 | use super::{Address, BindAddress, Interface, MAC, buff::BuffList}; 22 | use utils::errno::EResult; 23 | 24 | /// Local loopback interfaces allows the system to write data to itself. 25 | pub struct LocalLoopback {} 26 | 27 | impl Interface for LocalLoopback { 28 | fn get_name(&self) -> &[u8] { 29 | b"lo" 30 | } 31 | 32 | fn is_up(&self) -> bool { 33 | true 34 | } 35 | 36 | fn get_mac(&self) -> &MAC { 37 | &[0x00; 6] 38 | } 39 | 40 | fn get_addresses(&self) -> &[BindAddress] { 41 | &[ 42 | BindAddress { 43 | addr: Address::IPv4([127, 0, 0, 1]), 44 | subnet_mask: 8, 45 | }, 46 | BindAddress { 47 | addr: Address::IPv6([ 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x01, 50 | ]), 51 | subnet_mask: 128, 52 | }, 53 | ] 54 | } 55 | 56 | fn read(&mut self, _buff: &mut [u8]) -> EResult<u64> { 57 | // TODO Write to ring buffer 58 | todo!(); 59 | } 60 | 61 | fn write(&mut self, _buff: &BuffList<'_>) -> EResult<u64> { 62 | // TODO Read from ring buffer 63 | todo!(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /kernel/src/net/sockaddr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module defines sockaddr structures used by system calls to define connection informations 20 | //! on sockets. 21 | 22 | use super::Address; 23 | use core::ffi::c_short; 24 | 25 | /// Structure providing connection informations for sockets with IPv4. 26 | #[repr(C)] 27 | #[derive(Clone)] 28 | pub struct SockAddrIn { 29 | /// The family of the socket. 30 | sin_family: c_short, 31 | /// The port on which the connection is to be opened. 32 | sin_port: c_short, 33 | /// The destination address of the connection. 34 | sin_addr: u32, 35 | /// Padding. 36 | sin_zero: [u8; 8], 37 | } 38 | 39 | /// Structure representing an IPv6 address. 40 | #[repr(C)] 41 | #[derive(Clone, Copy)] 42 | pub union In6Addr { 43 | __s6_addr: [u8; 16], 44 | __s6_addr16: [u16; 8], 45 | __s6_addr32: [u32; 4], 46 | } 47 | 48 | /// Structure providing connection informations for sockets with IPv6. 49 | #[repr(C)] 50 | #[derive(Clone)] 51 | pub struct SockAddrIn6 { 52 | /// The family of the socket. 53 | sin6_family: c_short, 54 | /// The port on which the connection is to be opened. 55 | sin6_port: c_short, 56 | /// TODO doc 57 | sin6_flowinfo: u32, 58 | /// The destination address of the connection. 59 | sin6_addr: In6Addr, 60 | /// TODO doc 61 | sin6_scope_id: u32, 62 | } 63 | 64 | /// A unified structure which contains data passed from userspace. 65 | #[derive(Debug)] 66 | pub struct SockAddr { 67 | /// The port used by the socket. 68 | pub port: u16, 69 | /// The destination address of the socket. 70 | pub addr: Address, 71 | } 72 | 73 | impl From<SockAddrIn> for SockAddr { 74 | fn from(val: SockAddrIn) -> Self { 75 | let addr: [u8; 4] = [ 76 | ((val.sin_addr >> 24) & 0xff) as u8, 77 | ((val.sin_addr >> 16) & 0xff) as u8, 78 | ((val.sin_addr >> 8) & 0xff) as u8, 79 | (val.sin_addr & 0xff) as u8, 80 | ]; 81 | 82 | Self { 83 | port: val.sin_port as _, 84 | addr: Address::IPv4(addr), 85 | } 86 | } 87 | } 88 | 89 | impl From<SockAddrIn6> for SockAddr { 90 | fn from(val: SockAddrIn6) -> Self { 91 | let addr = unsafe { val.sin6_addr.__s6_addr }; 92 | 93 | Self { 94 | port: val.sin6_port as _, 95 | addr: Address::IPv6(addr), 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /kernel/src/net/tcp.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The Transmission Control Protocol (TCP) is a protocol transmitting sequenced, reliable, 20 | //! two-way, connection-based byte streams. 21 | 22 | use super::{buff::BuffList, osi::Layer}; 23 | use crate::file::socket::Socket; 24 | use utils::errno::EResult; 25 | 26 | /// The TCP segment header. 27 | #[repr(C, packed)] 28 | pub struct TCPHdr { 29 | /// Source port. 30 | src_port: u16, 31 | /// Destination port. 32 | dst_port: u16, 33 | 34 | /// Sequence number. 35 | seq_nbr: u32, 36 | 37 | /// TODO doc 38 | ack_nbr: u32, 39 | 40 | /// The size of the header in units of 4 bytes. 41 | /// 42 | /// Since the first 4 bits are reserved, the value must be shifted by 4 bits. 43 | data_offset: u8, 44 | /// The segment's flags. 45 | flags: u8, 46 | /// TODO doc 47 | win_size: u16, 48 | 49 | /// TODO doc 50 | checksum: u16, 51 | /// TODO doc 52 | urg_ptr: u16, 53 | } 54 | 55 | /// The network layer for the TCP protocol. 56 | #[derive(Debug)] 57 | pub struct TCPLayer {} 58 | 59 | impl Layer for TCPLayer { 60 | fn transmit<'c, F>(&self, _buff: BuffList<'c>, _next: F) -> EResult<()> 61 | where 62 | F: Fn(BuffList<'c>) -> EResult<()>, 63 | { 64 | // TODO 65 | todo!(); 66 | } 67 | } 68 | 69 | /// Initiates a TCP connection on the given socket `sock`. 70 | pub fn init_connection(_sock: &mut Socket) -> EResult<()> { 71 | // TODO 72 | todo!(); 73 | } 74 | -------------------------------------------------------------------------------- /kernel/src/panic.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements kernel panics handling. 20 | //! 21 | //! A kernel panic occurs when an error is raised that the kernel cannot recover 22 | //! from. This is an undesirable state which requires to reboot the host 23 | //! machine. 24 | 25 | #[cfg(config_debug_qemu)] 26 | use crate::debug::qemu; 27 | use crate::{arch::x86::cli, logger, memory::VirtAddr, power, register_get}; 28 | use core::panic::PanicInfo; 29 | 30 | /// Called on Rust panic. 31 | #[panic_handler] 32 | fn panic(panic_info: &PanicInfo) -> ! { 33 | cli(); 34 | logger::LOGGER.lock().silent = false; 35 | 36 | #[cfg(test)] 37 | { 38 | use crate::selftest; 39 | if selftest::is_running() { 40 | crate::println!("FAILED\n"); 41 | crate::println!("Error: {panic_info}\n"); 42 | #[cfg(config_debug_qemu)] 43 | qemu::exit(qemu::FAILURE); 44 | power::halt(); 45 | } 46 | } 47 | 48 | crate::println!("--- KERNEL PANIC ---\n"); 49 | crate::println!("Kernel has been forced to halt due to internal problem, sorry :/"); 50 | crate::print!("Reason: {}", panic_info.message()); 51 | if let Some(loc) = panic_info.location() { 52 | crate::println!(" (location: {loc})"); 53 | } else { 54 | crate::println!(); 55 | } 56 | crate::println!( 57 | "If you believe this is a bug on the kernel side, please feel free to report it." 58 | ); 59 | 60 | crate::println!("cr2: {:?}\n", VirtAddr(register_get!("cr2"))); 61 | 62 | #[cfg(debug_assertions)] 63 | { 64 | use crate::debug; 65 | use core::ptr; 66 | 67 | crate::println!("--- Callstack ---"); 68 | #[cfg(target_arch = "x86")] 69 | let frame = register_get!("ebp"); 70 | #[cfg(target_arch = "x86_64")] 71 | let frame = register_get!("rbp"); 72 | let ebp = ptr::with_exposed_provenance(frame); 73 | let mut callstack: [VirtAddr; 8] = [VirtAddr::default(); 8]; 74 | unsafe { 75 | debug::get_callstack(ebp, &mut callstack); 76 | } 77 | debug::print_callstack(&callstack); 78 | } 79 | #[cfg(config_debug_qemu)] 80 | qemu::exit(qemu::FAILURE); 81 | power::halt(); 82 | } 83 | 84 | // TODO check whether this can be removed since the kernel uses panic=abort 85 | /// Function that is required to be implemented by the Rust compiler and is used 86 | /// only when panicking. 87 | #[lang = "eh_personality"] 88 | fn eh_personality() {} 89 | -------------------------------------------------------------------------------- /kernel/src/power.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module handles system power. 20 | 21 | use crate::arch::x86::{ 22 | cli, hlt, 23 | io::{inb, outb}, 24 | }; 25 | use core::arch::asm; 26 | 27 | /// Halts the kernel until reboot. 28 | pub fn halt() -> ! { 29 | // TODO Send a signal to all other cores to stop them 30 | loop { 31 | cli(); 32 | hlt(); 33 | } 34 | } 35 | 36 | /// Powers the system down. 37 | pub fn shutdown() -> ! { 38 | // TODO Use ACPI to power off the system 39 | todo!() 40 | } 41 | 42 | /// Reboots the system. 43 | pub fn reboot() -> ! { 44 | cli(); 45 | // First try: ACPI 46 | // TODO Use ACPI reset to ensure everything reboots 47 | // Second try: PS/2 48 | loop { 49 | let tmp = unsafe { inb(0x64) }; 50 | // Empty keyboard buffer 51 | if tmp & 0b1 != 0 { 52 | unsafe { 53 | inb(0x60); 54 | } 55 | } 56 | // If buffer is empty, break 57 | if tmp & 0b10 == 0 { 58 | break; 59 | } 60 | } 61 | // PS/2 CPU reset command 62 | unsafe { 63 | outb(0x64, 0xfe); 64 | } 65 | // Third try: triple fault 66 | unsafe { 67 | asm!("push 0xffff", "push 0", "retf"); 68 | } 69 | unreachable!(); 70 | } 71 | -------------------------------------------------------------------------------- /kernel/src/print.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of printing/logging macros. 20 | //! 21 | //! Unlike the print macros from Rust's standard library, these are used to log informations 22 | //! instead of only printing. 23 | //! 24 | //! Printing can be silenced at boot using the `-silent` command line argument, but logs remain in 25 | //! memory. 26 | 27 | use crate::logger::LOGGER; 28 | use core::fmt; 29 | 30 | /// Prints/logs the given message. 31 | /// 32 | /// This function is meant to be used through [`print!`] and [`println!`] macros only. 33 | #[doc(hidden)] 34 | pub fn _print(args: fmt::Arguments) { 35 | let mut logger = LOGGER.lock(); 36 | fmt::write(&mut *logger, args).ok(); 37 | } 38 | 39 | /// Prints the given formatted string with the given values. 40 | #[allow_internal_unstable(print_internals)] 41 | #[macro_export] 42 | macro_rules! print { 43 | ($($arg:tt)*) => {{ 44 | $crate::print::_print(format_args!($($arg)*)); 45 | }}; 46 | } 47 | 48 | /// Same as [`crate::print!`], except it appends a newline at the end. 49 | #[allow_internal_unstable(print_internals, format_args_nl)] 50 | #[macro_export] 51 | macro_rules! println { 52 | () => ($crate::print!("\n")); 53 | ($($arg:tt)*) => {{ 54 | $crate::print::_print(format_args_nl!($($arg)*)); 55 | }}; 56 | } 57 | -------------------------------------------------------------------------------- /kernel/src/process/pid.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! PIDs handling. 20 | //! 21 | //! Each process must have a unique PID, thus they have to be allocated. 22 | //! A bitfield is used to store the used PIDs. 23 | 24 | use crate::sync::mutex::Mutex; 25 | use core::{alloc::AllocError, ops::Deref}; 26 | use utils::{collections::id_allocator::IDAllocator, errno::AllocResult}; 27 | 28 | /// Type representing a Process ID. This ID is unique for every running 29 | /// processes. 30 | pub type Pid = u16; 31 | 32 | /// The maximum possible PID. 33 | const MAX_PID: Pid = 32768; 34 | /// Special PID for the idle task. 35 | pub const IDLE_PID: Pid = 0; 36 | /// PID of the init process. 37 | pub const INIT_PID: Pid = 1; 38 | 39 | /// The PID allocator. 40 | static ALLOCATOR: Mutex<Option<IDAllocator>> = Mutex::new(None); 41 | 42 | /// Perform an operation with the allocator. 43 | fn allocator_do<F: Fn(&mut IDAllocator) -> AllocResult<T>, T>(f: F) -> AllocResult<T> { 44 | let mut allocator = ALLOCATOR.lock(); 45 | let allocator = match &mut *allocator { 46 | Some(a) => a, 47 | None => allocator.insert(IDAllocator::new(MAX_PID as _)?), 48 | }; 49 | f(allocator) 50 | } 51 | 52 | /// Wrapper for a PID, freeing it on drop. 53 | #[derive(Debug)] 54 | pub struct PidHandle(Pid); 55 | 56 | impl PidHandle { 57 | /// Allocates the given `pid`. 58 | /// 59 | /// If already allocated, the function returns an error. 60 | pub(super) fn mark_used(pid: Pid) -> AllocResult<Self> { 61 | let Some(id) = pid.checked_sub(1) else { 62 | // Pid `0` is not allocated, just return a handle 63 | return Ok(Self(pid)); 64 | }; 65 | allocator_do(|a| { 66 | if !a.is_used(id as _) { 67 | a.set_used(id as _); 68 | Ok(Self(pid)) 69 | } else { 70 | Err(AllocError) 71 | } 72 | }) 73 | } 74 | 75 | /// Returns an unused PID and marks it as used. 76 | pub fn unique() -> AllocResult<PidHandle> { 77 | allocator_do(|allocator| allocator.alloc(None)).map(|i| PidHandle((i + 1) as _)) 78 | } 79 | } 80 | 81 | impl Deref for PidHandle { 82 | type Target = Pid; 83 | 84 | fn deref(&self) -> &Self::Target { 85 | &self.0 86 | } 87 | } 88 | 89 | impl Drop for PidHandle { 90 | fn drop(&mut self) { 91 | // Cannot free PID `0` 92 | let Some(i) = self.0.checked_sub(1) else { 93 | return; 94 | }; 95 | // Cannot fail 96 | let _ = allocator_do(|a| { 97 | a.free(i as _); 98 | Ok(()) 99 | }); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /kernel/src/process/rusage.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Monitoring of the resource usage of processes. 20 | 21 | use crate::time::unit::Timeval; 22 | 23 | // TODO Place calls in kernel's code to update usage 24 | 25 | /// Usage of each resource by a process. 26 | #[derive(Clone, Debug, Default)] 27 | pub struct Rusage { 28 | /// User CPU time used. 29 | pub ru_utime: Timeval, 30 | /// System CPU time used. 31 | pub ru_stime: Timeval, 32 | /// Maximum resident set size. 33 | pub ru_maxrss: i64, 34 | /// Integral shared memory size. 35 | pub ru_ixrss: i64, 36 | /// Integral unshared data size. 37 | pub ru_idrss: i64, 38 | /// Integral unshared stack size. 39 | pub ru_isrss: i64, 40 | /// Page reclaims (soft page faults). 41 | pub ru_minflt: i64, 42 | /// Page faults (hard page faults). 43 | pub ru_majflt: i64, 44 | /// Swaps. 45 | pub ru_nswap: i64, 46 | /// Block input operations. 47 | pub ru_inblock: i64, 48 | /// Block output operations. 49 | pub ru_oublock: i64, 50 | /// IPC messages sent. 51 | pub ru_msgsnd: i64, 52 | /// IPC messages received. 53 | pub ru_msgrcv: i64, 54 | /// Signals received. 55 | pub ru_nsignals: i64, 56 | /// Voluntary context switches. 57 | pub ru_nvcsw: i64, 58 | /// Involuntary context switches. 59 | pub ru_nivcsw: i64, 60 | } 61 | -------------------------------------------------------------------------------- /kernel/src/selftest.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Selftesting are unit tests or integration tests that run on the kernel itself. 20 | //! 21 | //! # Issues 22 | //! 23 | //! Since the kernel cannot reset itself between each test, this method of testing might not be 24 | //! entirely trustable because a test might corrupt the environment for the next tests, which might 25 | //! make them pass even though they should not. Even if this scenario is unlikely, this remains a 26 | //! concern since the kernel has to be as reliable as possible. 27 | 28 | #[cfg(config_debug_qemu)] 29 | use crate::debug::qemu; 30 | use crate::power; 31 | use core::{ 32 | any::type_name, 33 | sync::{atomic, atomic::AtomicBool}, 34 | }; 35 | 36 | /// Boolean value telling whether selftesting is running. 37 | static RUNNING: AtomicBool = AtomicBool::new(false); 38 | 39 | /// Trait for any testable feature. 40 | pub trait Testable { 41 | /// Function called to run the corresponding test. 42 | fn run(&self); 43 | } 44 | 45 | impl<T> Testable for T 46 | where 47 | T: Fn(), 48 | { 49 | fn run(&self) { 50 | let name = type_name::<T>(); 51 | crate::print!("test {name} ... "); 52 | self(); 53 | crate::println!("ok"); 54 | } 55 | } 56 | 57 | /// The test runner for the kernel. 58 | /// 59 | /// This function runs every tests for the kernel and halts the kernel or exits the emulator if 60 | /// possible. 61 | pub fn runner(tests: &[&dyn Testable]) { 62 | crate::println!("Running {} tests", tests.len()); 63 | RUNNING.store(true, atomic::Ordering::Relaxed); 64 | for test in tests { 65 | test.run(); 66 | } 67 | RUNNING.store(false, atomic::Ordering::Relaxed); 68 | crate::println!("No more tests to run"); 69 | #[cfg(config_debug_qemu)] 70 | qemu::exit(qemu::SUCCESS); 71 | power::halt(); 72 | } 73 | 74 | /// Tells whether selftesting is running. 75 | pub fn is_running() -> bool { 76 | RUNNING.load(atomic::Ordering::Relaxed) 77 | } 78 | -------------------------------------------------------------------------------- /kernel/src/sync/atomic.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Implementation of [`AtomicU64`]. 20 | 21 | #[cfg(not(target_has_atomic = "64"))] 22 | use super::mutex::IntMutex; 23 | use core::{fmt, fmt::Formatter, sync::atomic}; 24 | 25 | /// Fulfills the role of `AtomicU64`, while being available on 32 bits platforms. 26 | #[cfg(target_has_atomic = "64")] 27 | #[derive(Default)] 28 | pub struct AtomicU64(core::sync::atomic::AtomicU64); 29 | 30 | /// Fulfills the role of `AtomicU64`, while being available on 32 bits platforms. 31 | #[cfg(not(target_has_atomic = "64"))] 32 | #[derive(Default)] 33 | pub struct AtomicU64(IntMutex<u64>); 34 | 35 | impl AtomicU64 { 36 | /// Creates a new instance with the given value. 37 | pub const fn new(val: u64) -> Self { 38 | #[cfg(target_has_atomic = "64")] 39 | { 40 | Self(core::sync::atomic::AtomicU64::new(val)) 41 | } 42 | #[cfg(not(target_has_atomic = "64"))] 43 | { 44 | Self(IntMutex::new(val)) 45 | } 46 | } 47 | 48 | /// Loads a value from the atomic integer. 49 | #[allow(unused_variables)] 50 | pub fn load(&self, order: atomic::Ordering) -> u64 { 51 | #[cfg(target_has_atomic = "64")] 52 | { 53 | self.0.load(order) 54 | } 55 | #[cfg(not(target_has_atomic = "64"))] 56 | { 57 | *self.0.lock() 58 | } 59 | } 60 | 61 | /// Stores a value into the atomic integer. 62 | #[allow(unused_variables)] 63 | pub fn store(&self, val: u64, order: atomic::Ordering) { 64 | #[cfg(target_has_atomic = "64")] 65 | { 66 | self.0.store(val, order) 67 | } 68 | #[cfg(not(target_has_atomic = "64"))] 69 | { 70 | *self.0.lock() = val; 71 | } 72 | } 73 | 74 | /// Adds to the current value, returning the previous value. 75 | #[allow(unused_variables)] 76 | pub fn fetch_add(&self, val: u64, order: atomic::Ordering) -> u64 { 77 | #[cfg(target_has_atomic = "64")] 78 | { 79 | self.0.fetch_add(val, order) 80 | } 81 | #[cfg(not(target_has_atomic = "64"))] 82 | { 83 | let mut guard = self.0.lock(); 84 | let prev = *guard; 85 | *guard = guard.wrapping_add(val); 86 | prev 87 | } 88 | } 89 | } 90 | 91 | impl fmt::Debug for AtomicU64 { 92 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 93 | fmt::Debug::fmt(&self.0, f) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /kernel/src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Kernel synchronization primitives. 20 | 21 | pub mod atomic; 22 | pub mod mutex; 23 | pub mod once; 24 | pub mod rcu; 25 | pub mod rwlock; 26 | pub mod spinlock; 27 | -------------------------------------------------------------------------------- /kernel/src/sync/once.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Once-initialized objects. 20 | 21 | use core::{cell::UnsafeCell, mem::MaybeUninit, ops::Deref}; 22 | 23 | /// An object that is meant to be initialized once at boot, then accessed in read-only. 24 | /// 25 | /// The value **must** be initialized with `init` before calling `get`. Failure to do so results in 26 | /// an undefined behavior. 27 | pub struct OnceInit<T>(UnsafeCell<MaybeUninit<T>>); 28 | 29 | impl<T> OnceInit<T> { 30 | /// Creates a new instance waiting to be initialized. 31 | /// 32 | /// # Safety 33 | /// 34 | /// The value **must** be initialized with before calling `get`. 35 | pub const unsafe fn new() -> Self { 36 | Self(UnsafeCell::new(MaybeUninit::uninit())) 37 | } 38 | 39 | /// Initializes with the given value. 40 | /// 41 | /// If already initialized, the previous value is **not** dropped. 42 | /// 43 | /// # Safety 44 | /// 45 | /// It is the caller's responsibility to enforce concurrency rules. 46 | pub unsafe fn init(this: &Self, val: T) -> &T { 47 | unsafe { 48 | let inner = &mut *this.0.get(); 49 | inner.write(val); 50 | inner.assume_init_ref() 51 | } 52 | } 53 | } 54 | 55 | impl<T> Deref for OnceInit<T> { 56 | type Target = T; 57 | 58 | fn deref(&self) -> &Self::Target { 59 | unsafe { (*self.0.get()).assume_init_ref() } 60 | } 61 | } 62 | 63 | unsafe impl<T> Sync for OnceInit<T> {} 64 | -------------------------------------------------------------------------------- /kernel/src/sync/spinlock.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Spinlock implementation. 20 | 21 | use core::{ 22 | hint, 23 | sync::{atomic, atomic::AtomicBool}, 24 | }; 25 | 26 | /// Locking primitive spinning until the resource can be acquired. 27 | /// 28 | /// It works by storing a value telling whether a thread is already in that piece of code. 29 | /// 30 | /// To avoid race conditions, the implementation uses an atomic exchange instruction. If a threads 31 | /// tries to acquire the lock while already in use, the thread shall wait in a loop (spin) until 32 | /// the lock is released. 33 | pub struct Spinlock(AtomicBool); 34 | 35 | impl Spinlock { 36 | /// Creates a new spinlock. 37 | #[allow(clippy::new_without_default)] 38 | pub const fn new() -> Self { 39 | Self(AtomicBool::new(false)) 40 | } 41 | 42 | /// Locks the spinlock. 43 | #[inline(always)] 44 | pub fn lock(&mut self) { 45 | while self.0.swap(true, atomic::Ordering::Acquire) { 46 | hint::spin_loop(); 47 | } 48 | } 49 | 50 | /// Unlocks the spinlock. 51 | #[inline(always)] 52 | pub fn unlock(&mut self) { 53 | self.0.store(false, atomic::Ordering::Release); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /kernel/src/syscall/getrandom.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The `getrandom` system call allows to get random bytes. 20 | 21 | use crate::{crypto::rand, memory::user::UserSlice, syscall::Args}; 22 | use core::ffi::c_uint; 23 | use utils::errno::EResult; 24 | 25 | pub fn getrandom(Args((buf, buflen, flags)): Args<(*mut u8, usize, c_uint)>) -> EResult<usize> { 26 | let buf = UserSlice::from_user(buf, buflen)?; 27 | rand::getrandom(buf, flags) 28 | } 29 | -------------------------------------------------------------------------------- /kernel/src/syscall/module.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Kernel module system calls. 20 | 21 | use crate::{ 22 | file::{fd::FileDescriptorTable, perm::AccessProfile}, 23 | memory::user::{UserSlice, UserString}, 24 | module, 25 | module::Module, 26 | sync::mutex::Mutex, 27 | syscall::Args, 28 | }; 29 | use core::{ 30 | ffi::{c_int, c_uint, c_ulong}, 31 | hint::unlikely, 32 | }; 33 | use utils::{errno, errno::EResult, ptr::arc::Arc}; 34 | 35 | pub fn init_module( 36 | Args((module_image, len, _param_values)): Args<(*mut u8, c_ulong, UserString)>, 37 | ap: AccessProfile, 38 | ) -> EResult<usize> { 39 | let module_image = UserSlice::from_user(module_image, len as _)?; 40 | if unlikely(!ap.is_privileged()) { 41 | return Err(errno!(EPERM)); 42 | } 43 | let image = module_image 44 | .copy_from_user_vec(0)? 45 | .ok_or_else(|| errno!(EFAULT))?; 46 | let module = Module::load(&image)?; 47 | module::add(module)?; 48 | Ok(0) 49 | } 50 | 51 | pub fn finit_module( 52 | Args((fd, _param_values, _flags)): Args<(c_int, UserString, c_int)>, 53 | ap: AccessProfile, 54 | fds: Arc<Mutex<FileDescriptorTable>>, 55 | ) -> EResult<usize> { 56 | if !ap.is_privileged() { 57 | return Err(errno!(EPERM)); 58 | } 59 | // Read file 60 | let image = fds.lock().get_fd(fd)?.get_file().read_all()?; 61 | let module = Module::load(&image)?; 62 | module::add(module)?; 63 | Ok(0) 64 | } 65 | 66 | // TODO handle flags 67 | pub fn delete_module( 68 | Args((name, _flags)): Args<(UserString, c_uint)>, 69 | ap: AccessProfile, 70 | ) -> EResult<usize> { 71 | if !ap.is_privileged() { 72 | return Err(errno!(EPERM)); 73 | } 74 | let name = name.copy_from_user()?.ok_or_else(|| errno!(EFAULT))?; 75 | // TODO handle dependency (don't unload a module that is required by another) 76 | module::remove(&name)?; 77 | Ok(0) 78 | } 79 | -------------------------------------------------------------------------------- /kernel/src/syscall/mount.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Mountpoint system calls. 20 | 21 | use crate::{ 22 | file::{ 23 | FileType, fs, vfs, 24 | vfs::{ResolutionSettings, mountpoint, mountpoint::MountSource}, 25 | }, 26 | memory::user::{UserPtr, UserString}, 27 | syscall::Args, 28 | }; 29 | use core::ffi::{c_int, c_ulong, c_void}; 30 | use utils::{collections::path::PathBuf, errno, errno::EResult}; 31 | 32 | pub fn mount( 33 | Args((source, target, filesystemtype, mountflags, _data)): Args<( 34 | UserString, 35 | UserString, 36 | UserString, 37 | c_ulong, 38 | UserPtr<c_void>, 39 | )>, 40 | rs: ResolutionSettings, 41 | ) -> EResult<usize> { 42 | if !rs.access_profile.is_privileged() { 43 | return Err(errno!(EPERM)); 44 | } 45 | // Read arguments 46 | let source_slice = source.copy_from_user()?.ok_or(errno!(EFAULT))?; 47 | let mount_source = MountSource::new(&source_slice)?; 48 | let target_slice = target.copy_from_user()?.ok_or(errno!(EFAULT))?; 49 | let target_path = PathBuf::try_from(target_slice)?; 50 | let filesystemtype_slice = filesystemtype.copy_from_user()?.ok_or(errno!(EFAULT))?; 51 | let fs_type = fs::get_type(&filesystemtype_slice).ok_or(errno!(ENODEV))?; 52 | // Get target file 53 | let target = vfs::get_file_from_path(&target_path, &rs)?; 54 | // Check the target is a directory 55 | if target.get_type()? != FileType::Directory { 56 | return Err(errno!(ENOTDIR)); 57 | } 58 | // TODO Use `data` 59 | // Create mountpoint 60 | mountpoint::create(mount_source, Some(fs_type), mountflags as _, Some(target))?; 61 | Ok(0) 62 | } 63 | 64 | pub fn umount(Args(target): Args<UserString>, rs: ResolutionSettings) -> EResult<usize> { 65 | umount2(Args((target, 0)), rs) 66 | } 67 | 68 | pub fn umount2( 69 | Args((target, _flags)): Args<(UserString, c_int)>, 70 | rs: ResolutionSettings, 71 | ) -> EResult<usize> { 72 | // TODO handle flags 73 | // Check permission 74 | if !rs.access_profile.is_privileged() { 75 | return Err(errno!(EPERM)); 76 | } 77 | // Get target directory 78 | let target_slice = target.copy_from_user()?.ok_or(errno!(EFAULT))?; 79 | let target_path = PathBuf::try_from(target_slice)?; 80 | let target = vfs::get_file_from_path(&target_path, &rs)?; 81 | // Remove mountpoint 82 | mountpoint::remove(target)?; 83 | Ok(0) 84 | } 85 | -------------------------------------------------------------------------------- /kernel/src/syscall/pipe.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The `pipe` system call allows to create a pipe. 20 | 21 | use crate::{ 22 | file, 23 | file::{File, fd::FileDescriptorTable, pipe::PipeBuffer}, 24 | memory::user::UserPtr, 25 | sync::mutex::Mutex, 26 | syscall::Args, 27 | }; 28 | use core::ffi::c_int; 29 | use utils::{errno, errno::EResult, ptr::arc::Arc}; 30 | 31 | pub fn pipe( 32 | Args(pipefd): Args<UserPtr<[c_int; 2]>>, 33 | fds: Arc<Mutex<FileDescriptorTable>>, 34 | ) -> EResult<usize> { 35 | let ops = Arc::new(PipeBuffer::new()?)?; 36 | let file0 = File::open_floating(ops.clone(), file::O_RDONLY)?; 37 | let file1 = File::open_floating(ops, file::O_WRONLY)?; 38 | let (fd0_id, fd1_id) = fds.lock().create_fd_pair(file0, file1)?; 39 | pipefd.copy_to_user(&[fd0_id as _, fd1_id as _])?; 40 | Ok(0) 41 | } 42 | 43 | pub fn pipe2( 44 | Args((pipefd, flags)): Args<(UserPtr<[c_int; 2]>, c_int)>, 45 | fds: Arc<Mutex<FileDescriptorTable>>, 46 | ) -> EResult<usize> { 47 | // Validation 48 | let accepted_flags = file::O_CLOEXEC | file::O_DIRECT | file::O_NONBLOCK; 49 | if flags & !accepted_flags != 0 { 50 | return Err(errno!(EINVAL)); 51 | } 52 | let ops = Arc::new(PipeBuffer::new()?)?; 53 | let file0 = File::open_floating(ops.clone(), flags | file::O_RDONLY)?; 54 | let file1 = File::open_floating(ops, flags | file::O_WRONLY)?; 55 | let (fd0_id, fd1_id) = fds.lock().create_fd_pair(file0, file1)?; 56 | pipefd.copy_to_user(&[fd0_id as _, fd1_id as _])?; 57 | Ok(0) 58 | } 59 | -------------------------------------------------------------------------------- /kernel/src/syscall/util/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Utility functions for system calls. 20 | 21 | pub mod at; 22 | -------------------------------------------------------------------------------- /kernel/src/time/hw/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements hardware clocks. 20 | 21 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 22 | pub mod pit; 23 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 24 | pub mod rtc; 25 | 26 | use crate::{sync::mutex::Mutex, time::unit::Timestamp}; 27 | use utils::{ 28 | boxed::Box, 29 | collections::{hashmap::HashMap, string::String}, 30 | }; 31 | 32 | /// Trait representing a hardware clock. 33 | pub trait HwClock { 34 | /// Enables or disable the clock. 35 | fn set_enabled(&mut self, enable: bool); 36 | /// Sets the clock's frequency in hertz. 37 | /// 38 | /// The actual frequency is the closest possible rounded down according to the clock's 39 | /// resolution. 40 | fn set_frequency(&mut self, freq: u32); 41 | 42 | /// Returns the value of the clock, if applicable. 43 | fn get_value(&self) -> Option<Timestamp> { 44 | None 45 | } 46 | 47 | /// Returns the interrupt vector of the timer. 48 | fn get_interrupt_vector(&self) -> u32; 49 | } 50 | 51 | /// The list of hardware clock sources. 52 | /// 53 | /// The key is the name of the clock. 54 | pub static CLOCKS: Mutex<HashMap<String, Box<dyn HwClock>>> = Mutex::new(HashMap::new()); 55 | -------------------------------------------------------------------------------- /kernel/src/time/hw/pit.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module handles the PIT (Programmable Interrupt Timer) which allows to 20 | //! trigger interruptions at a fixed interval. 21 | 22 | use super::HwClock; 23 | use crate::arch::x86::{idt, io::outb, pic}; 24 | 25 | /// PIT channel number 0. 26 | const CHANNEL_0: u16 = 0x40; 27 | /// PIT channel number 2. 28 | const CHANNEL_2: u16 = 0x42; 29 | /// The port to send a command to the PIT. 30 | const PIT_COMMAND: u16 = 0x43; 31 | 32 | /// The command to enable the PC speaker. 33 | const BEEPER_ENABLE_COMMAND: u8 = 0x61; 34 | 35 | /// Select PIT channel 0. 36 | const SELECT_CHANNEL_0: u8 = 0b00 << 6; 37 | /// Select PIT channel 2. 38 | const SELECT_CHANNEL_2: u8 = 0b10 << 6; 39 | 40 | /// Tells the PIT to read the whole counter value. 41 | const ACCESS_LOBYTE_HIBYTE: u8 = 0b11 << 4; 42 | 43 | /// Square wave generator. 44 | const MODE_3: u8 = 0b011 << 1; 45 | 46 | /// The base frequency of the PIT. 47 | const BASE_FREQUENCY: u32 = 1193182; 48 | 49 | // FIXME prevent having several instances at the same time 50 | 51 | /// The PIT. 52 | pub struct PIT {} 53 | 54 | impl PIT { 55 | /// Creates a new instance. 56 | /// 57 | /// By default, the timer is disabled and its frequency is undefined. 58 | #[allow(clippy::new_without_default)] 59 | pub fn new() -> Self { 60 | let mut s = Self {}; 61 | s.set_enabled(false); 62 | idt::wrap_disable_interrupts(|| unsafe { 63 | outb( 64 | PIT_COMMAND, 65 | SELECT_CHANNEL_0 | ACCESS_LOBYTE_HIBYTE | MODE_3, 66 | ); 67 | s.set_frequency(1); 68 | }); 69 | s 70 | } 71 | } 72 | 73 | impl HwClock for PIT { 74 | fn set_enabled(&mut self, enable: bool) { 75 | if enable { 76 | pic::enable_irq(0x0); 77 | } else { 78 | pic::disable_irq(0x0); 79 | } 80 | } 81 | 82 | fn set_frequency(&mut self, frequency: u32) { 83 | let mut count = if frequency != 0 { 84 | (BASE_FREQUENCY / frequency) as u16 85 | } else { 86 | 0 87 | }; 88 | if count == 0xffff { 89 | count = 0; 90 | } 91 | 92 | // Update frequency divider's value 93 | idt::wrap_disable_interrupts(|| unsafe { 94 | outb(CHANNEL_0, (count & 0xff) as u8); 95 | outb(CHANNEL_0, ((count >> 8) & 0xff) as u8); 96 | }); 97 | } 98 | 99 | fn get_interrupt_vector(&self) -> u32 { 100 | 0x20 101 | } 102 | } 103 | 104 | impl Drop for PIT { 105 | fn drop(&mut self) { 106 | self.set_enabled(false); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /kernel/src/time/hw/rtc.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The Real Time Clock (RTC) is the clock used by the CMOS to maintain system time. 20 | 21 | use super::HwClock; 22 | use crate::arch::x86::{ 23 | idt, 24 | io::{inb, outb}, 25 | }; 26 | 27 | /// The ID of the port used to select the CMOS register to read. 28 | const SELECT_PORT: u16 = 0x70; 29 | /// The ID of the port to read or write a CMOS port previously selected. 30 | const VALUE_PORT: u16 = 0x71; 31 | 32 | /// The ID of the status register A. 33 | const STATUS_A_REGISTER: u8 = 0x0a; 34 | /// The ID of the status register B. 35 | const STATUS_B_REGISTER: u8 = 0x0b; 36 | /// The ID of the status register C. 37 | const STATUS_C_REGISTER: u8 = 0x0c; 38 | 39 | // FIXME prevent having several instances at the same time 40 | 41 | /// The RTC. 42 | /// 43 | /// **Note**: the RTC needs a call to `reset` to allow the next tick to be fired. 44 | pub struct RTC {} 45 | 46 | impl RTC { 47 | /// Creates a new instance. 48 | /// 49 | /// By default, the timer is disabled and its frequency is undefined. 50 | #[allow(clippy::new_without_default)] 51 | pub fn new() -> Self { 52 | let mut s = Self {}; 53 | s.set_enabled(false); 54 | s 55 | } 56 | 57 | /// Resets the timer to make it ready for the next tick. 58 | #[inline] 59 | pub fn reset() { 60 | unsafe { 61 | outb(SELECT_PORT, STATUS_C_REGISTER); 62 | inb(VALUE_PORT); 63 | } 64 | } 65 | } 66 | 67 | impl HwClock for RTC { 68 | fn set_enabled(&mut self, enable: bool) { 69 | idt::wrap_disable_interrupts(|| unsafe { 70 | outb(SELECT_PORT, STATUS_B_REGISTER | 0x80); 71 | let prev = inb(VALUE_PORT); 72 | outb(SELECT_PORT, STATUS_B_REGISTER | 0x80); 73 | if enable { 74 | outb(VALUE_PORT, prev | 0x40); 75 | } else { 76 | outb(VALUE_PORT, prev & !0x40); 77 | } 78 | }); 79 | } 80 | 81 | fn set_frequency(&mut self, freq: u32) { 82 | let rate = (32768u32 / freq).trailing_zeros() as u8 + 1; 83 | idt::wrap_disable_interrupts(|| unsafe { 84 | outb(SELECT_PORT, STATUS_A_REGISTER | 0x80); 85 | let prev = inb(VALUE_PORT); 86 | outb(SELECT_PORT, STATUS_A_REGISTER | 0x80); 87 | outb(VALUE_PORT, (prev & 0xf0) | (rate & 0x0f)); 88 | }); 89 | } 90 | 91 | fn get_interrupt_vector(&self) -> u32 { 92 | 0x28 93 | } 94 | } 95 | 96 | impl Drop for RTC { 97 | fn drop(&mut self) { 98 | self.set_enabled(false); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /kernel/vdso/linker.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | SECTIONS 20 | { 21 | ENTRY(__kernel_vsyscall) 22 | 23 | . = 0x1000; 24 | 25 | .text BLOCK(4K) : ALIGN(4K) 26 | { 27 | *(.text) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /kernel/vdso/x86.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | .section .text 20 | 21 | .global __kernel_vsyscall 22 | .global __kernel_rt_sigreturn 23 | .global __kernel_sigreturn 24 | .global __vdso_clock_gettime 25 | .global __vdso_gettimeofday 26 | .global __vdso_time 27 | 28 | __kernel_vsyscall: 29 | int $0x80 30 | ret 31 | 32 | __kernel_rt_sigreturn: 33 | # TODO 34 | ud2 35 | 36 | __kernel_sigreturn: 37 | # TODO 38 | ud2 39 | 40 | __vdso_clock_gettime: 41 | # TODO 42 | ud2 43 | 44 | __vdso_gettimeofday: 45 | # TODO 46 | ud2 47 | 48 | __vdso_time: 49 | # TODO 50 | ud2 51 | -------------------------------------------------------------------------------- /kernel/vdso/x86_64.s: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | .section .text 20 | 21 | .global __vdso_clock_gettime 22 | .global __vdso_getcpu 23 | .global __vdso_gettimeofday 24 | .global __vdso_time 25 | 26 | __vdso_clock_gettime: 27 | # TODO 28 | ud2 29 | 30 | __vdso_getcpu: 31 | # TODO 32 | ud2 33 | 34 | __vdso_gettimeofday: 35 | # TODO 36 | ud2 37 | 38 | __vdso_time: 39 | # TODO 40 | ud2 41 | -------------------------------------------------------------------------------- /macros/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /macros/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "macros" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "proc-macro2", 10 | "quote", 11 | "syn", 12 | ] 13 | 14 | [[package]] 15 | name = "proc-macro2" 16 | version = "1.0.95" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 19 | dependencies = [ 20 | "unicode-ident", 21 | ] 22 | 23 | [[package]] 24 | name = "quote" 25 | version = "1.0.40" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 28 | dependencies = [ 29 | "proc-macro2", 30 | ] 31 | 32 | [[package]] 33 | name = "syn" 34 | version = "2.0.101" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "unicode-ident", 41 | ] 42 | 43 | [[package]] 44 | name = "unicode-ident" 45 | version = "1.0.18" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 48 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macros" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [lib] 7 | proc-macro = true 8 | 9 | [dependencies] 10 | proc-macro2 = "1.0.95" 11 | quote = "1.0.40" 12 | syn = { version = "2.0.101", features = ["full", "extra-traits"] } 13 | 14 | [features] 15 | default = [] 16 | strace = [] 17 | -------------------------------------------------------------------------------- /macros/rustfmt.toml: -------------------------------------------------------------------------------- 1 | ../rustfmt.toml -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This crate implements derive macros for the Maestro kernel. 20 | 21 | #![feature(iter_intersperse)] 22 | 23 | extern crate proc_macro; 24 | 25 | mod aml; 26 | mod util; 27 | 28 | use crate::util::has_repr_c; 29 | use proc_macro::TokenStream; 30 | use quote::quote; 31 | use syn::{DeriveInput, parse_macro_input}; 32 | 33 | /// Implements `AnyRepr`, making necessary safety checks. 34 | #[proc_macro_derive(AnyRepr)] 35 | pub fn any_repr(input: TokenStream) -> TokenStream { 36 | let input = parse_macro_input!(input as DeriveInput); 37 | let ident = input.ident; 38 | if !has_repr_c(&input.attrs) { 39 | panic!("{ident} is not suitable for the trait `AnyRepr`"); 40 | } 41 | let toks = quote! { 42 | unsafe impl utils::bytes::AnyRepr for #ident {} 43 | }; 44 | TokenStream::from(toks) 45 | } 46 | 47 | /// Definition of a derive macro used to turn a structure into a parsable object for the AML 48 | /// bytecode. 49 | /// 50 | /// TODO further document 51 | #[proc_macro_derive(Parseable)] 52 | pub fn aml_parseable(input: TokenStream) -> TokenStream { 53 | aml::derive_parseable(input) 54 | } 55 | -------------------------------------------------------------------------------- /macros/src/util.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions. 2 | 3 | use proc_macro2::TokenTree; 4 | use syn::{AttrStyle, Attribute, Meta, MetaList, Path}; 5 | 6 | /// Tells whether the list of attributes contains `repr(C)`. 7 | pub fn has_repr_c(attrs: &[Attribute]) -> bool { 8 | attrs 9 | .iter() 10 | .filter_map(|attr| { 11 | if !matches!(attr.style, AttrStyle::Outer) { 12 | return None; 13 | } 14 | let Path { 15 | leading_colon: None, 16 | segments, 17 | .. 18 | } = attr.path() 19 | else { 20 | return None; 21 | }; 22 | if segments.len() != 1 { 23 | return None; 24 | } 25 | let seg = segments.first().unwrap(); 26 | if !seg.arguments.is_empty() { 27 | return None; 28 | } 29 | if seg.ident != "repr" { 30 | return None; 31 | } 32 | let Meta::List(MetaList { 33 | ref tokens, .. 34 | }) = attr.meta 35 | else { 36 | return None; 37 | }; 38 | Some(tokens.clone()) 39 | }) 40 | .flat_map(|tokens| tokens.into_iter()) 41 | .filter_map(|tok| { 42 | if let TokenTree::Ident(ident) = tok { 43 | Some(ident) 44 | } else { 45 | None 46 | } 47 | }) 48 | .any(|ident| ident == "C") 49 | } 50 | -------------------------------------------------------------------------------- /mod/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script builds kernel modules. 4 | 5 | KERN_SRC=$(realpath $(dirname $0)/..) 6 | if [ -z "$ARCH" ]; then 7 | ARCH="x86_64" 8 | fi 9 | export CARGOFLAGS="--target $KERN_SRC/kernel/arch/$ARCH/$ARCH.json $CARGOFLAGS" 10 | 11 | if [ ! -z "$PROFILE" ] && [ "$PROFILE" != "debug" ]; then 12 | CARGOFLAGS="$CARGOFLAGS --profile $PROFILE" 13 | else 14 | export PROFILE="debug" 15 | fi 16 | export RUSTFLAGS="--extern kernel=$KERN_SRC/kernel/target/$ARCH/$PROFILE/libkernel.rlib -L $KERN_SRC/kernel/target/$ARCH/$PROFILE/deps -L $KERN_SRC/kernel/target/$PROFILE/deps $RUSTFLAGS" 17 | 18 | cargo build $CARGOFLAGS $@ 19 | -------------------------------------------------------------------------------- /mod/template/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["profile-rustflags"] 2 | 3 | [package] 4 | name = "hello" 5 | version = "0.1.0" 6 | edition = "2024" 7 | 8 | [lib] 9 | path = "src/mod.rs" 10 | crate-type = ["dylib"] 11 | 12 | [dependencies] 13 | 14 | [profile.release] 15 | panic = "abort" 16 | 17 | [profile.dev] 18 | rustflags = [ 19 | "-Cforce-frame-pointers=yes" 20 | ] 21 | -------------------------------------------------------------------------------- /mod/template/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | ../../rust-toolchain.toml -------------------------------------------------------------------------------- /mod/template/src/mod.rs: -------------------------------------------------------------------------------- 1 | //! <Add documentation for your module here> 2 | 3 | #![no_std] 4 | #![no_main] 5 | 6 | // Do not include kernel symbols in the module 7 | #[no_link] 8 | extern crate kernel; 9 | 10 | // Declare the module, with its dependencies 11 | kernel::module!([]); 12 | 13 | /// Called on module load 14 | #[unsafe(no_mangle)] 15 | pub extern "C" fn init() -> bool { 16 | kernel::println!("Hello world!"); 17 | true 18 | } 19 | 20 | /// Called on module unload 21 | #[unsafe(no_mangle)] 22 | pub extern "C" fn fini() { 23 | kernel::println!("Goodbye!"); 24 | } 25 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-05-10" 3 | components = ["rustfmt", "rustc-dev", "rust-src", "clippy", "miri"] 4 | targets = ["i686-unknown-linux-musl", "x86_64-unknown-linux-musl"] 5 | profile = "minimal" 6 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | blank_lines_upper_bound = 1 2 | comment_width = 99 3 | condense_wildcard_suffixes = true 4 | format_code_in_doc_comments = true 5 | group_imports = "One" 6 | hard_tabs = true 7 | hex_literal_case = "Lower" 8 | imports_granularity = "Crate" 9 | imports_indent = "Block" 10 | max_width = 99 11 | newline_style = "Unix" 12 | reorder_impl_items = true 13 | struct_lit_single_line = false 14 | use_field_init_shorthand = true 15 | wrap_comments = true -------------------------------------------------------------------------------- /utils/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /utils/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "macros" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "proc-macro2", 10 | "quote", 11 | "syn", 12 | ] 13 | 14 | [[package]] 15 | name = "proc-macro2" 16 | version = "1.0.95" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 19 | dependencies = [ 20 | "unicode-ident", 21 | ] 22 | 23 | [[package]] 24 | name = "quote" 25 | version = "1.0.40" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 28 | dependencies = [ 29 | "proc-macro2", 30 | ] 31 | 32 | [[package]] 33 | name = "syn" 34 | version = "2.0.101" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "unicode-ident", 41 | ] 42 | 43 | [[package]] 44 | name = "unicode-ident" 45 | version = "1.0.18" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 48 | 49 | [[package]] 50 | name = "utils" 51 | version = "0.1.0" 52 | dependencies = [ 53 | "macros", 54 | ] 55 | -------------------------------------------------------------------------------- /utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "utils" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | macros = { path = "../macros" } 8 | 9 | [features] 10 | default = [] 11 | std = [] 12 | healthcheck = [] 13 | -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | # util 2 | 3 | Utilities are implemented in a separate library to allow easier debugging, by running tests in userspace. 4 | 5 | 6 | 7 | ## Run tests 8 | 9 | You can use the command: 10 | 11 | ```sh 12 | cargo test 13 | ``` 14 | 15 | to run unit tests in userspace. 16 | 17 | Tests can also be run with [Miri](https://github.com/rust-lang/miri) using: 18 | 19 | ```sh 20 | cargo miri test 21 | ``` -------------------------------------------------------------------------------- /utils/src/collections/id_allocator.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements an identifier allocator, allowing to allocate and 20 | //! free indexes in range `0..=max`, where `max` is given. 21 | 22 | use crate::{collections::bitfield::Bitfield, errno::AllocResult}; 23 | use core::alloc::AllocError; 24 | 25 | /// Structure representing an identifier allocator. 26 | pub struct IDAllocator { 27 | /// The bitfield keeping track of used identifiers. 28 | used: Bitfield, 29 | } 30 | 31 | impl IDAllocator { 32 | /// Creates a new instance. 33 | /// 34 | /// `max` is the maximum ID. 35 | pub fn new(max: u32) -> AllocResult<Self> { 36 | Ok(Self { 37 | used: Bitfield::new((max + 1) as _)?, 38 | }) 39 | } 40 | 41 | /// Tells whether `id` is marked as used. 42 | /// 43 | /// If out of bounds, the function returns `true`. 44 | pub fn is_used(&self, id: u32) -> bool { 45 | if id <= self.used.len() as _ { 46 | self.used.is_set(id as _) 47 | } else { 48 | true 49 | } 50 | } 51 | 52 | /// Sets `id` as used. 53 | pub fn set_used(&mut self, id: u32) { 54 | if id <= self.used.len() as _ { 55 | self.used.set(id as _); 56 | } 57 | } 58 | 59 | /// Allocates an identifier. 60 | /// 61 | /// If `id` is not `None`, the function shall allocate the given id. 62 | /// 63 | /// If the allocation fails, the function returns `None`. 64 | #[must_use = "not freeing a PID shall cause a leak"] 65 | pub fn alloc(&mut self, id: Option<u32>) -> AllocResult<u32> { 66 | if let Some(i) = id { 67 | if !self.used.is_set(i as _) { 68 | self.used.set(i as _); 69 | Ok(i) 70 | } else { 71 | Err(AllocError) 72 | } 73 | } else if let Some(i) = self.used.find_clear() { 74 | self.used.set(i); 75 | Ok(i as _) 76 | } else { 77 | Err(AllocError) 78 | } 79 | } 80 | 81 | /// Frees the given identifier `id`. 82 | pub fn free(&mut self, id: u32) { 83 | if id <= self.used.len() as _ { 84 | self.used.clear(id as _); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /utils/src/collections/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements collections. 20 | 21 | pub mod bitfield; 22 | pub mod btreemap; 23 | pub mod hashmap; 24 | pub mod hashset; 25 | pub mod id_allocator; 26 | pub mod list; 27 | pub mod path; 28 | pub mod string; 29 | pub mod vec; 30 | -------------------------------------------------------------------------------- /utils/src/math.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! The functions in this module implement utilities for integer mathematics. 20 | //! 21 | //! Since floating point numbers are slow, imprecise and may even be disabled by 22 | //! default, the kernel uses only integers. 23 | 24 | use core::ops::{Rem, Shl}; 25 | 26 | /// Computes `pow(2, n)` where `n` is unsigned. 27 | /// 28 | /// The behaviour is undefined for n < 0. 29 | #[inline(always)] 30 | pub fn pow2<T>(n: T) -> T 31 | where 32 | T: From<u8> + Shl<Output = T>, 33 | { 34 | T::from(1) << n 35 | } 36 | 37 | /// Pseudo random number generation based on linear congruential generator. 38 | /// 39 | /// Arguments: 40 | /// - `x` is the value to compute the next number from. It should either be a seed, or the previous 41 | /// value returned from this function. 42 | /// - `a`, `c` and `m` are hyperparameters use as follows: (a * x + c) % m. 43 | pub fn pseudo_rand(x: u32, a: u32, c: u32, m: u32) -> u32 { 44 | a.wrapping_mul(x).wrapping_add(c) % m 45 | } 46 | 47 | /// Returns the Greatest Common Divider of the two given numbers. 48 | pub fn gcd<T>(mut a: T, mut b: T) -> T 49 | where 50 | T: Clone + From<u8> + PartialEq + Rem<Output = T>, 51 | { 52 | while b != T::from(0) { 53 | let r = a % b.clone(); 54 | a = b; 55 | b = r; 56 | } 57 | a 58 | } 59 | 60 | #[cfg(test)] 61 | mod test { 62 | use super::*; 63 | 64 | #[test] 65 | fn gcd0() { 66 | assert_eq!(gcd(2, 2), 2); 67 | assert_eq!(gcd(4, 2), 2); 68 | assert_eq!(gcd(4, 4), 4); 69 | assert_eq!(gcd(8, 12), 4); 70 | assert_eq!(gcd(48, 18), 6); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /utils/src/ptr/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! This module implements smart pointers. 20 | 21 | pub mod arc; 22 | pub mod cow; 23 | -------------------------------------------------------------------------------- /utils/src/unsafe_mut.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Luc Lenôtre 3 | * 4 | * This file is part of Maestro. 5 | * 6 | * Maestro is free software: you can redistribute it and/or modify it under the 7 | * terms of the GNU General Public License as published by the Free Software 8 | * Foundation, either version 3 of the License, or (at your option) any later 9 | * version. 10 | * 11 | * Maestro is distributed in the hope that it will be useful, but WITHOUT ANY 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along with 16 | * Maestro. If not, see <https://www.gnu.org/licenses/>. 17 | */ 18 | 19 | //! Unsafe mutable wrapper. 20 | 21 | use core::{cell::UnsafeCell, ops::Deref}; 22 | 23 | /// Wrapper allowing safe immutable accesses, or unsafe mutable accesses at the same time. 24 | #[derive(Default)] 25 | pub struct UnsafeMut<T>(UnsafeCell<T>); 26 | 27 | impl<T> UnsafeMut<T> { 28 | /// Creates a new instance. 29 | pub fn new(val: T) -> Self { 30 | Self(UnsafeCell::new(val)) 31 | } 32 | 33 | /// Returns an immutable reference. 34 | pub fn get(&self) -> &T { 35 | unsafe { &*self.0.get() } 36 | } 37 | 38 | /// Returns a mutable reference. 39 | /// 40 | /// # Safety 41 | /// 42 | /// The caller must ensure no other thread is accessing the value at the same time. 43 | #[allow(clippy::mut_from_ref)] 44 | pub unsafe fn get_mut(&self) -> &mut T { 45 | unsafe { &mut *self.0.get() } 46 | } 47 | } 48 | 49 | impl<T> Deref for UnsafeMut<T> { 50 | type Target = T; 51 | 52 | fn deref(&self) -> &Self::Target { 53 | self.get() 54 | } 55 | } 56 | 57 | impl<T: Clone> Clone for UnsafeMut<T> { 58 | fn clone(&self) -> Self { 59 | let inner = unsafe { &*self.0.get() }; 60 | Self(UnsafeCell::new(inner.clone())) 61 | } 62 | } 63 | --------------------------------------------------------------------------------