{"id":20961225,"url":"https://github.com/jbouron/x86-kernel","last_synced_at":"2025-04-11T19:42:04.905Z","repository":{"id":211799604,"uuid":"194021919","full_name":"JBouron/x86-kernel","owner":"JBouron","description":"A 32-bit x86 kernel written from scratch in C supporting multicore cpus and preemptible scheduling.","archived":false,"fork":false,"pushed_at":"2023-12-10T21:53:15.000Z","size":864,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-25T15:48:57.459Z","etag":null,"topics":["assembly","kernel","operating-system","osdev","x86"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JBouron.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-27T04:20:19.000Z","updated_at":"2024-05-10T18:44:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"9debe7d3-bbb3-45f5-965e-5ad67196e68c","html_url":"https://github.com/JBouron/x86-kernel","commit_stats":null,"previous_names":["jbouron/x86-kernel"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBouron%2Fx86-kernel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBouron%2Fx86-kernel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBouron%2Fx86-kernel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JBouron%2Fx86-kernel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JBouron","download_url":"https://codeload.github.com/JBouron/x86-kernel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248468471,"owners_count":21108822,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assembly","kernel","operating-system","osdev","x86"],"created_at":"2024-11-19T02:01:56.675Z","updated_at":"2025-04-11T19:42:04.879Z","avatar_url":"https://github.com/JBouron.png","language":"C","readme":"# x86-kernel\nA 32-bit x86 kernel written from scratch in C.\n\n## What is it?\nThis project was meant to be a learning exercise about kernel and OS\ndevelopment. It started as a simple \"Hello world\" kernel, using the [Bare Bones\nosdev tutorial](https://wiki.osdev.org/Bare_Bones) and naturally grew from\nthere.\n\nHere are the most notable features of this kernel:\n\n* Multiboot compliant and bootable by GRUB\n* Runs on physical machines (YMMV, only tested on a Thinkpad t440s with an Intel\n  CPU)\n* Paging support with user and kernel address space isolation\n* Multicore support, up to 255 cores\n* Capable of running processes in user-space\n* Preemptible round-robin scheduling using the LAPIC timer\n* LAPIC and IO-APIC support\n* Inter-Processor-Interrupts for synchronization between cores and TLB shootdown\n* ELF loader (statically compiled binaries only)\n* Syscall interface using software interrupts\n* Rudimentary file-system interface to open, read and write files\n* Basic block device support\n* Serial and VGA outputs\n\nI've mostly focused on the kernel side of things, that is hardware interaction,\nprocess \u0026 memory management, ... there is no user-space per se (i.e. no console\nor tty) although the kernel does support running user programs and has a couple\nof syscalls to play with. While there is a file-system interface, it is pretty\nrudimentary and for now it is only able to mount a TAR archive as a file-system\nand read and write the files it contains. It does not yet support appending\ndata to files, only overwriting them (mostly because the TAR format was never\nmeant to be used in this way).\n\nThe decision to use C was mostly for simplicity, kernel programming is already\nhard enough by itself and the programming language you use may make things more\ncomplicated. Not all programming languages were meant to be used in a\nbaremetal/kernel environment, in this regard C is perfectly suited for this kind\nof task. Less fighting against the language to make it work in a kernel\nenvironment meant more focus on the actual project.\n\nLimiting myself to 32-bit x86 was also intentional and again in the name of\nsimplicity. x86_64 is much more complex than 32-bit x86 and I wanted to first\nhave some experience with the latter before eventually looking into 64-bit mode.\n\n## Design\nThere is not much to be said here. Monolithic kernels are the simplest to\nunderstand and write and so I naturally chose that route. Most of the\narchitecture and interfaces are my own creation, although sometimes I took\ninspiration from the Linux kernel (the `struct sched` being a good example of\nthis).\n\nAt the time of this project, I only had access to physical machines with Intel\nCPUs therefore the kernel was developed with the assumption that it would run on\nan Intel CPU (or on a VM running on an Intel host). That being said I have not,\nif I recall correctly, used any Intel-specific features/MSRs and running the\nkernel on a VM on an AMD host seems to be working just fine.\n\nThe code is heavily commented, to the point that someone may actually use this\nto learn a bit more about kernel/OS development. Additionally a lot of effort\nhas been put into unit testing. All tests are run within the kernel upon booting\nup, as a matter of fact this is pretty much the only thing the kernel does at\nthis point since there is no console / tty to login into.\n\n## State of the project\nI am no longer working on this project for a few different reasons.\n\nFirst, most of the \"easy\" stuff (i.e. well documented on osdev and / or Intel\nmanuals) has been implemented, what I would like to have now is support for\nactual hardware devices such as hard-drives and/or network cards. However\nsupporting those requires supporting AHCI and/or PCIe first both of which\nrequire substantial effort to implement and could be projects of their own.\nThere is a branch attempting to add PCIe support, however I quickly realised\nthat there is no good way to properly test PCIe support other than trying to\ncommunicate with a device.\n\nThe second reason had more to do with 32-bit limitations. I reached a point\nwhere I learned all that I wanted about the 32-bit x86 architecture and started\nto get interested in 64-bit mode. However, adding support for 64-bit mode would\nmean pretty much starting over, as this kernel was always developed with 32-bit\nmode in mind.\n\nLastly, as the scope of the project grew, I started to feel somewhat limited by\nC. Sure you can get very far with C (the Linux kernel is a good example of this)\nbut at some point I started to miss the niceties of higher-level language.\nMacros and their weird tricks can only get you so far ...\n\nThese days, I am working on another kernel targeting x86_64 and written in C++.\nIt is currently in a private repository but I plan to make it public at some\npoint.\n\n## Building and Running\nThere are few dependencies required to build and run this kernel, your machine\nneeds `make`, `docker` and `qemu` (more specifically `qemu-system-x86`) to be\ninstalled.\n\n### Docker container\nThe first thing [osdev](https://wiki.osdev.org/Main_Page) teaches you is that\nkernel development requires using a cross-compiler. Rather than polluting my\nmachine (and yours!) with the cross-compiling toolchain, the toolchain lives\nwithin a docker image. The Makefile takes care of creating this image for you if\nit is not available and then uses it to build the kernel.\n\nUsing a docker container to build the kernel has one draw-back however: it\nrequires root privileges. Therefore you might need to execute `make` using sudo,\nor be ready to enter your password during the building process (which isn't long\nanyway).\n\n### Building\nTo build the kernel simply execute `make` in the top directory. If this is the\nfirst time building the kernel, the Makefile will create the docker image\ncontaining the cross-compiler first. Note that this may take a while as it needs\nto build the entire toolchain from sources, this only need to be done once\nhowever.\n\nOnce the docker image is created, or if it is already available, the Makefile\nuses the docker image to build the kernel from sources, it then creates an image\n(e.g. an ELF) for the kernel (`build_dir/kernel.bin` by default) which can be\nused by `qemu`.\n\n### Running\nTo run the kernel, execute `make run`. If the kernel was not already built, the\nrecipe builds it first. `make run` then starts `qemu` passing it the kernel\nimage `build_dir/kernel.bin`.\n\nQemu is run with a configurable number of cpus and amount of memory. The\nMakefile has variables for both of those that you can modify if needed.\n\nBy default the kernel outputs its log messages in the serial console and `qemu`\nis started in _nographics_ mode in which it shows the serial console (i.e. no\nGUI).  This behaviour can be changed by setting the `OUTPUT` variable in the\nMakefile accordingly, available values are `SERIAL` and `VGA`. Note that if you\nwant to use `VGA` you will need to modify the `qemu` command line to run in GUI\nmode.\n\n#### Debugging\nYou can also debug the kernel using `gdb`. If the kernel is already running\nunder `qemu` simply execute:\n\n```\ngdb -ex \"source kernel.gdb\" -ex \"target remote localhost:1234\" -ex \"add-symbol-file build_dir/kernel.bin\"\n```\n\nThis will drop you into a gdb session debugging the kernel running within the\nqemu VM.\n\nAdditionally you can tell qemu to _wait_ for a GDB session before starting\nexecution by running `make runs`. In this target, qemu will only start execution\nonce you've connected to it with GDB and typed `continue`. This can be useful as\nit gives you time to set up breakpoint(s) before starting the kernel.\n\n### Other Makefile Targets\nA few other targets are available in the Makefile:\n\n* `all` (default): Build the kernel in debug mode.\n* `run`: Build the kernel in debug mode and start it using qemu.\n* `runs`: Build the kernel in debug mode and start it using qemu. Qemu is\n  started with the -S flag, e.g. waiting for gdb to start the VM's execution.\n* `debug`: Build the kernel in debug mode with -O0 and -g.\n* `release`: Build the kernel in release mode with -O2\n* `baremetal_debug`: Build the kernel in debug mode and create an .iso file that\n  is meant to be run on a physical machine.\n* `baremetal_release`: Build the kernel in release mode and create an .iso file\n  that is meant to be run on a physical machine.\n* `clean`: Remove all compilation artifacts.\n\n### Running on a physical machine\nExecuting `make baremetal_debug` or `make baremetal_release` create a bootable\n.iso file that can be `dd`'ed onto a USB stick in order to run on a physical\nmachine.\n\nBuilding the baremetal .iso requires a few more dependencies: `xorriso` and\n`mformat` (from the `mtools` package on Ubuntu).\n\nI was only able to test on a Thinkpad t440s with an Intel CPU, hence cannot\nguarantee that it will run on all machines.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbouron%2Fx86-kernel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjbouron%2Fx86-kernel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjbouron%2Fx86-kernel/lists"}