Git Product home page Git Product logo

emerald's Introduction

emerald OS logo

Emerald OS

OS Build Documentation

Emerald is an OS written in Rust from scratch.

The plan is to learn everything about the kernel and low level details, so I'm implementing as much as possible without using any libraries. But maybe I'll add those just to make the code smaller and better to work with.

Running

If you don't want to build the project, you can download the latest artifacts from:

You get ISO file containing the kernel and compressed filesystem directory containing the userspace programs.

The current command is what we use normally to run the OS, but can be run by any VM with some setup.

qemu-system-x86_64 -cdrom <kernel.iso> -serial mon:stdio -m 512 -boot d -drive format=raw,file=fat:rw:<filesystem>

where <kernel.iso> is the path to the ISO file, and <filesystem> is the path to the filesystem directory decompressed.

Some extra info:

  • -serial mon:stdio is used to redirect the serial output to the terminal.
  • -m 512 is the amount of memory to allocate for the VM, 512MB.
  • -boot d is to boot from the CD-ROM we just loaded.
  • -drive format=raw,file=fat:rw:<filesystem> is to pass the filesystem directory to the kernel as a disk.

Here we use a feature of QEMU, virtual fat, where it will treat the directory as a FAT filesystem, and being passed to the kernel as a disk.

Building

The whole building and packaging is done by xtask

The ISO file can be used to run on other VMs/hardware(not tested)

For building the ISO image, you can use make but you need to have other dependencies installed to build and run the ISO:

xorriso mtools grub-pc-bin qemu-system-x86

Build kernel iso:

cargo xtask build-iso

Building userspace programs

This builds userspace programs into filesystem directory (used by qemu):

The userspace programs are built using a custom rust toolchain (See more info here)

Anyway, there are 2 options to build our userspace programs and in general any other program.

Using the prebuilt toolchain

We distribute a prebuilt toolchain in:

bash tools/install_toolchain_and_link.sh <path_to_toolchain.zip>

This will install the toolchain into extern/toolchain and link it to rustup as emerald.

Then, xtask will use the installed toolchain to build userspace programs, if its not installed it will give an error.

cargo xtask userspace build

Building the toolchain

We don't build the toolchain automatically, i.e. if you don't have the toolchain you can build the toolchain yourself from source if you don't want to installed prebuilt.

cargo xtask toolchain

If you want to build and install from source into extern/toolchain directory

You can then use rustup toolchain link ... to link to this folder

cargo xtask toolchain --install

Building and running

To build and run kernel and userspace programs:

cargo xtask run

You need to have qemu-system-x86_64 installed.

Debugging

You can use gdb or lldb to debug this.

But I have included vscode configs to enable easily debugging with CodeLLDB extension.

And to boot QEMU in debug mode you can use (it will wait for debugger on port :1234)

cargo xtask run --gdb

Documentation

The main documentation is in the book directory, you can build it using mdbook:

mdbook build book

Its also served in https://amjad.alsharafi.dev/Emerald/

Kernel

Booting

Currently, this project compiles a multiboot2 ELF64 kernel that can be booted by several bootloaders, I'm using GRUB using a bootloader like GRUB.

GRUB and probably other bootloaders, will setup protected-mode (32bit) and then pass execution to the kernel starting in kernel/src/boot.S.

Note here, since we have moved to multiboot2 in #2, we can directly start in 64bit with EFI, but right now since we already have kernel/src/boot.S running in 32bit in boot, let's keep on that, so that we can support both BIOS and EFI easily from the same entry point.

In the start of the kernel, we only do basic setup to switch to long-mode (64bit), this is done in assembly in kernel/src/boot.S. After setting up long-mode, we jump to rust code, and start executing the kernel_main function.

when we jump to the kernel_main, we have mapped some basic parts of the kernel to virtual memory, a basic GDT with no IDT, and we have interrupts still disabled. So we setup all of those and the rest of the OS then.

Userland

Currently, the main focus for running userspace applications is by having std in rust, as all userspace applications are build in rust, this is the primary support. Thus, we don't have libc for now. We have emerald_std which is the main dependency for that std uses.

We have our own target x86_64-unknown-emerald which is a custom target for our OS, added to custom fork of rustc in here: rust.

Demo to userspace programs

Here is a demo of the shell as is here. Another more complex application I can run is lprs-fork (not included in the demo below, but there is a demo in the README of that project).

demo

License

This project is licensed under the MIT license, see LICENSE for more information.

emerald's People

Contributors

amjad50 avatar

Stargazers

 avatar Fahad Alashari avatar  avatar Txqi avatar baxiry avatar Jannes (思明) avatar Mohamed Emad avatar

Watchers

Lucian avatar  avatar  avatar

emerald's Issues

Better handling of virtual space and raw pointers

make a better safe API for using the virtual_space, we should return a valid pointer or a struct that will always point to a valid pointer. It will handle dropping and so on.

Also fix the issue where we store the mmio value in APIC as a raw pointer, and maybe replace it with this concrete type (probably unsafecell or something, but gotta be safe)

Improve locks

Use a better locking, like test loop, that only uses load and only try compare_exchange once it can actually do it. Reduce contention.

Also other improvements

Tasks

  • reduce contention in locking
  • Keep track of time holding the lock
  • Keep track of time needed to wait for the lock
  • Assert several metrics (go back to reddit for the suggestions ppl mentioned)
  • (Need to check if its good) Keep track of owner CPU in the Lock itself and use Usize and not Bool
    • We won't do this
  • Add more functionalities and structs around locks
    • Rwlock

Add mouse

  • mouse driver
  • mouse movement updates
  • mouse rendering

Creates video player

Use the functionalities of keyboard driver, mouse driver, graphics to display video frames on the graphics buffer from usermode and control the playback, just pause/play, forward, backward is enough.

For the mouse, we can have a progress bar that we can control with mouse.

New name: Emerald

Change all occurances of amjad_os to emerald, add new logo and also maybe ASCII art on OS boot on the shell process would be cool

Unit testing

Not really sure what to do tests for exactly

Think of fields to test, for now could be on:

  • Filesystem in general
  • Fat
  • virtual space
  • virtual memory
  • allocations
  • scheduling
  • other?

Try to do extreme tests that catch edge cases.

Save logs to file

Save up the logs from Console Early and Late and then save them in the filesystem after/during runtime of the OS.

We can have it either:

  • remove old logs on every new boot
  • create a new log file/append on every boot

shared memory maps

shared memory between processes, for now, not sure if we need to implement memory mapped files and paging. Maybe not, as its kinda different, we just want to share memory between processes as a form of IPC #13

Fix mapping memory for ACPI and framebuffers

Currently there is an issue with dealing with ACPI, framebuffer and other memory regions that are fixed in physical space.

We need to have some kind of "virtual address allocator", where we can get a segment of N pages and map them to our desired physical space.
This is only used in startup and such.

Currently for ACPI, we have a very dumb method to reserve space in kernel virtual space based on the first address we get, but that's not enough XD. as in some cases this address is very large (several MB) so we can't just get a memory around some place random.

https://github.com/Amjad50/OS/blob/0a28e58e9e3d19e6c126bbde23bd2378bf422596/kernel/src/acpi/tables.rs#L22-L93

Idea

  • The idea I have is to have a virtual page allocator, similar to the physical page allocator, it will contain a range of free blocks, then we can request a number of pages, get a region, then we can probably free in the future as well.
  • Since unlike the physical page allocator, we are dealing with addresses pages that must be following each other, we might get fragmentation easily. So will have to look into how to minimize it.

Do note: if its not clear already, this is not allocating any memory at all, this is just virtual space, so only ram is used is to store the metadata about free ranges and that's it.

Add ACPI AML code parsing

This will help us be able to get the details of the ACPI implementation, like the interrupt lines used and the sleep modes supported and the values we should pass to ACPI functions to shutdown/sleep etc.

This is by no means needed at this point or even necessary to do, there is ACPICA and other libraries we can use instead of doing from scratch, but I got interested in it, so we are here.

User input

Now we have keyboard, add mouse input, and a way for devices + usermode to read that.

For this roadmap, we can focus on keyboard input, and make it accessible to the shell somehow in a console device for example. But in the future it will be accessible from GUI applications

Add list-dir in user space

In order to build a shell, we must somehow be able to search for programs in the disk and execute them.

  • build ls

Create an ethernet network driver

For now, this should work for qemu, I'm not sure how general we can be, but lets focus on that, and test on other platforms later and improve/make new drivers when needed.

Implement IPC

This issue is first considered as part of the roadmap for making a shell, so probably we don't need all types of IPCs and just something like a pipe will be enough.
In that case, the rest can be moved to another issue

  • #83
  • Pipes #23
  • Messages sharing (not sure how yet)

Add clock support and sleep

need to do 2 things:

  • use the current HPET (for calibration) along with low overhear CPU instructions to get clock speed, to track the time on the kernel.
  • we can use RTC to get the start time
  • implement userspace sleep with that.

Implement IO waiting

In the idea dump: https://github.com/users/Amjad50/projects/3/views/1?filterQuery=-milestone%3A%22Rust+Hello+World%22%2C%22Userspace+graphics%3A+Video+player%22&pane=issue&itemId=47273788

I talked about async kernel, and other implementations.

The goal of this issue/ticket, is just to improve performance, by smartly waiting for IO and not spin looping (which is what is still done until now).

We can change the implementation of the kernel to be fully async (rust async), and use those features to implement waiting.
Or we implement them manually.

Boot in UEFI

Currently there are some issues that doesn't allow us to boot into UEFI.

  1. The way we take the memory layout is, take the second "Available" memory, and it must be at least 128 MB. UEFI and even BIOS doesn't guarentee that the second memory area is the main one. So we need to be more dynamic.

Tasks

  • Implement dynamic physical page allocator from the multiboot info

Initial CPU state

I noticed in UEFI and BIOS, the state of the CPU is different, namely the MSR and C0,4 registers.
for example, as of writing this issue, with UEFI, the kernel cannot write to protected user pages, but with BIOS it can. (tested in qemu only).
Which resulted in a crash in the ELF loader for UEFI, because we are writing to a read-only user pages.

Would be good to have a standard known from boot which features we have support for.

Profiling + stack unwinding

Add debugging and profiling capabilities, and this will include stack unwinding so that we can create a flamegrpah kind of stuff, among other stuff

Create keyboard usermode device

Find a way to send keypresses to usermode without going to the console

Of course also, if we spawn the player from the shell we want to hijack and take control of the keyboard device data

Added auto growing stack

We can added special growing for the stack alone (interact with the exception handler)
or add mmap with a mode that allows any memory to grow (upon reserved size).

Add Process heap

Depend on some part of #11.

Think of how to manage the heap, is its from the kernel with something like brk? or make the userspace allocate its own pages?

If we choose the second option, need to make sure the location where the userspace program choose to use is safe to use and won't get in some memory in the middle.

Create TCP stack

For now, the idea is to have this in the kernel, similar to linux, not sure if its the best idea, if there are better, we can implement them instead.

Better VGA buffer management

Kinda continuation after #36 .

Have the VGA buffer be a static buffer owned by the kernel, that the console can use (like what we do in keyboard)

Beside that, have it so that the userspace can take control over it somehow, and stop the console.

The output of this would be to display something from usermode on the vga buffer.

allocate high memory in the physical page allocator

Currently we only use max 128MB memory, this memory is mapped from 0 into the KERNEL_BASE and that's good since we can have something like 1:1 mapping and that's how physical2virtual function works.

But for more memory, we need to have a special physical page allocator, that doesn't have the memory virtually mapped, and thus we can't store the free pages as a linked list connected to one another by we can store the metadata about free pages in the heap somewhere since we have that.

The setup can be done as so.

  • initial physical page allocator setup
  • virtual memory setup
  • [implied] heap setup (for us heap just works after virtual memory is initialized)
  • allocate rest of ram into high physical page allocator
  • Can use any amount of memory from ram

Add ability to allocate contiguous range of physical memory

currently the physical page allocator only allocate pages, and because we free them start to finish they are stored in reverse order, so it may be hard to easily get contiguous size of how many we want.

Currently we don't need this, but I think this will become very important later on when dealing with dma for example and other hardware devices that require physical memory.

We should find a better method to organize physical memory.

I like how linux does it, it stores it as tree structure, where you have small amount of 4K pages, so when you want 4K pages and you don't have any, you allocate from the 8K pages above, and so on, each step is 2x the step before it, so you allocate 1 block and split it into 2 for the steps below.
Not sure with this approach how many steps we should take though. And how we would handle freeing memory, would it merge back up?

Implement needed syscalls for shell and creating a process

Here are the list of syscalls or functionalities we will need to implement shell and loading other processes:

  • execute/spawn (fork, not sure if I need to implement it, its an awkward api)
  • mmap? or maybe brk? or something else for heap (diff name)
  • exit (process exit)

There is more things to add, such as mprotect/VirtualProtect, Futex/Mutex related stuff, others, but this is just to get something going.

Better handling of PCI interface

We have a very basic API to deal with PCI, we should improve it.

For example, one of the main issue in the design now, is that we don't handle 32/64 memory BARs correctly I think.

There is no need to allocate time specifically for this issue, its just being recorded so that we notice the issue when implementing the next PCI device.

Currently, we only have IDE device, and its quite basic as it uses IO BARs

Add handling for cwd and cd

current working directory for the process, and file commands will understand the relative path.
And ability to change this path

Add framebuffer and VGA display and some graphics (graphical console)

Not sure exactly when to put this?

should we implement this very early and have like two variants (Basic, Advanced) display.

  • one will be initialized before anything else (to allow to display logs), similar to EarlyConsole
  • the other will use heap and whatnot.

Have only one, and set it up as soon as we can (probably after virtual memory).
We can print the logs with this method and for old logs, we can just keep it in the EarlyConsole for example in a static sized array big enough (few KBs) and copy it in the display once its ready.

Not sure exactly about the startup, but that's not the most important part. This issue focuses on getting easy API to draw to the screen.

The output of this issue is to not change anything really :D visually, currently on bios mode, we get Text mode graphics, and that's what we use.

So after this, we should get this same/similar output in display, but it would work in uefi/bios etc...
This would make it easier for future work on gui.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.