First version
This commit is contained in:
parent
61ec7bb47b
commit
97cd6cdd98
|
@ -1,2 +0,0 @@
|
|||
[unstable]
|
||||
bindeps = true
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1 +1,5 @@
|
|||
/target
|
||||
/kernel/target
|
||||
/build
|
||||
/binutils
|
||||
/gcc
|
||||
/opt
|
||||
|
|
23
Makefile
Normal file
23
Makefile
Normal file
|
@ -0,0 +1,23 @@
|
|||
.PHONY: all build test clean
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
rm -rf build
|
||||
rm -rf kernel/target
|
||||
mkdir build
|
||||
i686-elf-as -o build/boot.o boot.s
|
||||
cd kernel; cargo build
|
||||
cp kernel/target/i686-unknown-bare/debug/kernel.elf build/geos.bin
|
||||
mkdir -p build/isodir/boot/grub
|
||||
cp build/geos.bin build/isodir/boot/geos.bin
|
||||
cp grub.cfg build/isodir/boot/grub/grub.cfg
|
||||
grub-mkrescue -o build/geos.iso build/isodir
|
||||
|
||||
test: build
|
||||
qemu-system-i386 -cdrom build/geos.iso
|
||||
|
||||
clean:
|
||||
rm -rf kernel/target
|
||||
rm -rf build
|
||||
|
109
boot.s
Normal file
109
boot.s
Normal file
|
@ -0,0 +1,109 @@
|
|||
/* Declare constants for the multiboot header. */
|
||||
.set ALIGN, 1<<0 /* align loaded modules on page boundaries */
|
||||
.set MEMINFO, 1<<1 /* provide memory map */
|
||||
.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */
|
||||
.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
|
||||
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */
|
||||
|
||||
/*
|
||||
Declare a multiboot header that marks the program as a kernel. These are magic
|
||||
values that are documented in the multiboot standard. The bootloader will
|
||||
search for this signature in the first 8 KiB of the kernel file, aligned at a
|
||||
32-bit boundary. The signature is in its own section so the header can be
|
||||
forced to be within the first 8 KiB of the kernel file.
|
||||
*/
|
||||
.section .multiboot
|
||||
.align 4
|
||||
.long MAGIC
|
||||
.long FLAGS
|
||||
.long CHECKSUM
|
||||
|
||||
/*
|
||||
The multiboot standard does not define the value of the stack pointer register
|
||||
(esp) and it is up to the kernel to provide a stack. This allocates room for a
|
||||
small stack by creating a symbol at the bottom of it, then allocating 16384
|
||||
bytes for it, and finally creating a symbol at the top. The stack grows
|
||||
downwards on x86. The stack is in its own section so it can be marked nobits,
|
||||
which means the kernel file is smaller because it does not contain an
|
||||
uninitialized stack. The stack on x86 must be 16-byte aligned according to the
|
||||
System V ABI standard and de-facto extensions. The compiler will assume the
|
||||
stack is properly aligned and failure to align the stack will result in
|
||||
undefined behavior.
|
||||
*/
|
||||
.section .bss
|
||||
.align 16
|
||||
stack_bottom:
|
||||
.skip 16384 # 16 KiB
|
||||
stack_top:
|
||||
|
||||
/*
|
||||
The linker script specifies _start as the entry point to the kernel and the
|
||||
bootloader will jump to this position once the kernel has been loaded. It
|
||||
doesn't make sense to return from this function as the bootloader is gone.
|
||||
*/
|
||||
.section .text
|
||||
.global _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
/*
|
||||
The bootloader has loaded us into 32-bit protected mode on a x86
|
||||
machine. Interrupts are disabled. Paging is disabled. The processor
|
||||
state is as defined in the multiboot standard. The kernel has full
|
||||
control of the CPU. The kernel can only make use of hardware features
|
||||
and any code it provides as part of itself. There's no printf
|
||||
function, unless the kernel provides its own <stdio.h> header and a
|
||||
printf implementation. There are no security restrictions, no
|
||||
safeguards, no debugging mechanisms, only what the kernel provides
|
||||
itself. It has absolute and complete power over the
|
||||
machine.
|
||||
*/
|
||||
|
||||
/*
|
||||
To set up a stack, we set the esp register to point to the top of the
|
||||
stack (as it grows downwards on x86 systems). This is necessarily done
|
||||
in assembly as languages such as C cannot function without a stack.
|
||||
*/
|
||||
mov $stack_top, %esp
|
||||
|
||||
/*
|
||||
This is a good place to initialize crucial processor state before the
|
||||
high-level kernel is entered. It's best to minimize the early
|
||||
environment where crucial features are offline. Note that the
|
||||
processor is not fully initialized yet: Features such as floating
|
||||
point instructions and instruction set extensions are not initialized
|
||||
yet. The GDT should be loaded here. Paging should be enabled here.
|
||||
C++ features such as global constructors and exceptions will require
|
||||
runtime support to work as well.
|
||||
*/
|
||||
|
||||
/*
|
||||
Enter the high-level kernel. The ABI requires the stack is 16-byte
|
||||
aligned at the time of the call instruction (which afterwards pushes
|
||||
the return pointer of size 4 bytes). The stack was originally 16-byte
|
||||
aligned above and we've pushed a multiple of 16 bytes to the
|
||||
stack since (pushed 0 bytes so far), so the alignment has thus been
|
||||
preserved and the call is well defined.
|
||||
*/
|
||||
call kernel_main
|
||||
|
||||
/*
|
||||
If the system has nothing more to do, put the computer into an
|
||||
infinite loop. To do that:
|
||||
1) Disable interrupts with cli (clear interrupt enable in eflags).
|
||||
They are already disabled by the bootloader, so this is not needed.
|
||||
Mind that you might later enable interrupts and return from
|
||||
kernel_main (which is sort of nonsensical to do).
|
||||
2) Wait for the next interrupt to arrive with hlt (halt instruction).
|
||||
Since they are disabled, this will lock up the computer.
|
||||
3) Jump to the hlt instruction if it ever wakes up due to a
|
||||
non-maskable interrupt occurring or due to system management mode.
|
||||
*/
|
||||
cli
|
||||
1: hlt
|
||||
jmp 1b
|
||||
|
||||
/*
|
||||
Set the size of the _start symbol to the current location '.' minus its start.
|
||||
This is useful when debugging or when you implement call tracing.
|
||||
*/
|
||||
.size _start, . - _start
|
9
kernel/.cargo/config.toml
Normal file
9
kernel/.cargo/config.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[build]
|
||||
target = "i686-unknown-bare.json"
|
||||
|
||||
[unstable]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
build-std = ["core", "compiler_builtins"]
|
||||
|
||||
[target."i686-unknown-bare"]
|
||||
linker = "i686-elf-gcc"
|
7
kernel/Cargo.lock
generated
Normal file
7
kernel/Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
6
kernel/Cargo.toml
Normal file
6
kernel/Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
25
kernel/i686-unknown-bare.json
Normal file
25
kernel/i686-unknown-bare.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"llvm-target": "i686-unknown-none",
|
||||
"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",
|
||||
"arch": "x86",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "32",
|
||||
"target-c-int-width": "32",
|
||||
"os": "none",
|
||||
"executables": true,
|
||||
"linker-flavor": "gcc",
|
||||
"linker": "i686-elf-gcc",
|
||||
"panic-strategy": "abort",
|
||||
"disable-redzone": true,
|
||||
"features": "-sse,+soft-float",
|
||||
"dynamic-linking": false,
|
||||
"relocation-model": "pic",
|
||||
"code-model": "kernel",
|
||||
"exe-suffix": ".elf",
|
||||
"has-rpath": false,
|
||||
"no-default-libraries": true,
|
||||
"position-independent-executables": false,
|
||||
"pre-link-args": {
|
||||
"gcc": ["-T", "../linker.ld", "-ffreestanding", "-nostdlib", "-lgcc", "../build/boot.o"]
|
||||
}
|
||||
}
|
23
kernel/src/main.rs
Normal file
23
kernel/src/main.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn kernel_main() -> ! {
|
||||
let vga_buffer = 0xb8000 as *mut u8;
|
||||
|
||||
let string: &[u8] = b":3";
|
||||
for (i, &byte) in string.iter().enumerate() {
|
||||
unsafe {
|
||||
*vga_buffer.offset(i as isize * 2) = byte;
|
||||
*vga_buffer.offset(i as isize * 2 + 1) = 0xf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
52
linker.ld
Normal file
52
linker.ld
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* The bootloader will look at this image and start execution at the symbol
|
||||
designated as the entry point. */
|
||||
ENTRY(_start)
|
||||
|
||||
/* Tell where the various sections of the object files will be put in the final
|
||||
kernel image. */
|
||||
SECTIONS
|
||||
{
|
||||
/* It used to be universally recommended to use 1M as a start offset,
|
||||
as it was effectively guaranteed to be available under BIOS systems.
|
||||
However, UEFI has made things more complicated, and experimental data
|
||||
strongly suggests that 2M is a safer place to load. In 2016, a new
|
||||
feature was introduced to the multiboot2 spec to inform bootloaders
|
||||
that a kernel can be loaded anywhere within a range of addresses and
|
||||
will be able to relocate itself to run from such a loader-selected
|
||||
address, in order to give the loader freedom in selecting a span of
|
||||
memory which is verified to be available by the firmware, in order to
|
||||
work around this issue. This does not use that feature, so 2M was
|
||||
chosen as a safer option than the traditional 1M. */
|
||||
. = 2M;
|
||||
|
||||
/* First put the multiboot header, as it is required to be put very early
|
||||
in the image or the bootloader won't recognize the file format.
|
||||
Next we'll put the .text section. */
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.multiboot)
|
||||
*(.text)
|
||||
}
|
||||
|
||||
/* Read-only data. */
|
||||
.rodata BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
/* Read-write data (initialized) */
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.data)
|
||||
}
|
||||
|
||||
/* Read-write data (uninitialized) and stack */
|
||||
.bss BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
/* The compiler may produce other sections, by default it will put them in
|
||||
a segment with the same name. Simply add stuff here as needed. */
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
nightly
|
21
shell.nix
21
shell.nix
|
@ -1,20 +1,35 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
pkgs.mkShell rec {
|
||||
|
||||
let
|
||||
crossPkgs = pkgs.pkgsCross.aarch64-multiplatform;
|
||||
in pkgs.mkShell rec {
|
||||
buildInputs = with pkgs; [
|
||||
clang
|
||||
llvmPackages_17.bintools
|
||||
rustup
|
||||
qemu
|
||||
grub2
|
||||
libisoburn
|
||||
bison
|
||||
flex
|
||||
gmp
|
||||
libmpc
|
||||
mpfr
|
||||
texinfo
|
||||
isl
|
||||
];
|
||||
RUSTC_VERSION = pkgs.lib.readFile ./rust-toolchain;
|
||||
RUSTC_VERSION = "nightly";
|
||||
hardeningDisable = [ "all" ];
|
||||
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
||||
LIBCLANG_PATH = pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
||||
shellHook = ''
|
||||
rustup default nightly
|
||||
rustup component add llvm-tools-preview
|
||||
rustup component add rust-analyzer
|
||||
rustup target add x86_64-unknown-none
|
||||
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
||||
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/nightly-x86_64-unknown-linux-gnu/bin/
|
||||
export PATH=$PATH:$PWD/opt/bin
|
||||
'';
|
||||
# Add precompiled library to rustc search path
|
||||
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
|
||||
|
|
Loading…
Reference in a new issue