intro post
This commit is contained in:
parent
aa98b907f4
commit
83d1ce9b98
|
|
@ -259,3 +259,90 @@ We will create a =run.sh= script to automate hide away the details of launching
|
|||
-vga std
|
||||
|
||||
#+end_src
|
||||
|
||||
Lets go through the script step by step:
|
||||
The first like after the shebang that tells the loader to use =bash= as the interpreter is =set -e=, which means the script will exit immediately if any command returns a failure exit code.
|
||||
This means that, if we for example don't have limine installed, the script won't try to create the image or run qemu with an incomplete setup.
|
||||
|
||||
Our script currently takes a single argument, the path to the kernel binary.
|
||||
Cargo allows us to set a "runner" command in the =.cargo/config.toml= file, which it will call with the path to the binary as the first argument instead of the calling the binary itself. We will make use of this feature to automatically start our kernel in qemu with the help of cargo.
|
||||
|
||||
We take the first argument and extract the proper parent directory and the filename and use both to construct the path to the image file we create in the next step:
|
||||
#+begin_src bash
|
||||
bin="$1"
|
||||
|
||||
canonicalised=$(realpath -- "$bin")
|
||||
parent=$(dirname -- "$canonicalised")
|
||||
bin_name=$(basename -- "$exe")
|
||||
|
||||
image="$parent/$bin_name.img"
|
||||
#+end_src
|
||||
|
||||
Next, we check if a file exists at the path we constructed for the image file, and whether it is older than the kernel binary:
|
||||
#+begin_src bash
|
||||
if [[ "$canonicalised" -nt "$image" ]]; then
|
||||
#+end_src
|
||||
|
||||
If so, we will want to (re)create the image, starting with the =limine.conf= file the limine bootloader will read to know where to find our kernel binary:
|
||||
|
||||
#+begin_src bash
|
||||
cat > /tmp/limine.conf <<EOF
|
||||
timeout: 0
|
||||
serial: yes
|
||||
serial_baudrate: 9600
|
||||
|
||||
/Kernel
|
||||
protocol: limine
|
||||
path: boot():/boot/kernel.elf
|
||||
EOF
|
||||
#+end_src
|
||||
|
||||
We also tell limine to use the serial port for output, which means any error messages will be printed in the terminal we run our script (or =cargo run= ) in.
|
||||
|
||||
Next, we create a 64 MiB image file filled with zeroes, and format it with =mformat= to create a FAT32 filesystem.
|
||||
After creating the directory structure using =mmd=, we copy the relevant files, including the =BOOTX64.EFI= bootloader binary from limine into the respective paths in the image file using =mcopy=.
|
||||
#+begin_src bash
|
||||
limine_datadir="$(limine --print-datadir)"
|
||||
limine_uefi_path="$limine_datadir/BOOTX64.EFI"
|
||||
|
||||
dd if=/dev/zero of="$image" bs=1M count=64
|
||||
mformat -i "$image" -F ::
|
||||
|
||||
mmd -i "$image" ::/boot
|
||||
mmd -i "$image" ::/EFI
|
||||
mmd -i "$image" ::/EFI/BOOT
|
||||
|
||||
mcopy -i "$image" /tmp/limine.conf ::/boot/limine.conf
|
||||
mcopy -i "$image" "$canonicalised" ::/boot/kernel.elf
|
||||
mcopy -i "$image" "$limine_uefi_path" ::/EFI/BOOT/BOOTX64.EFI
|
||||
#+end_src
|
||||
|
||||
Finally, we run qemu with the image attached as a drive:
|
||||
|
||||
#+begin_src bash
|
||||
qemu-system-x86_64 \
|
||||
-drive file="$image",format=raw \
|
||||
-machine q35,accel=kvm -enable-kvm \
|
||||
-drive if=pflash,format=raw,readonly=on,file="$OVMF_PATH/FV/OVMF_CODE.fd" \
|
||||
-serial stdio
|
||||
#+end_src
|
||||
|
||||
We tell qemu to use the =q35= machine type, which is a more modern virtual motherboard and is noticably faster at startup in my experience. Additionally, we tell qemu to use KVM acceleration, which will allow the virtual machine to run at near-native speed by utilising the CPUs hardware virtualization features rather than emulating the CPU in software. This requires that the host CPU supports virtualization and that KVM is enabled in the host linux system.
|
||||
We also attach the UEFI firmware and the serial port we've told limine to write to.
|
||||
|
||||
Finally, we make a few changes to our =.cargo/config.toml= file to tell cargo to use our =run.sh= script as the runner for our kernel binary:
|
||||
#+begin_src toml
|
||||
[build]
|
||||
target = "x86_64-unknown-kernel.json"
|
||||
|
||||
[target.x86_64-unknown-kernel]
|
||||
runner = "./run.sh"
|
||||
#+end_src
|
||||
|
||||
This tells cargo to use our custom target specification file by default when building, and to use our =run.sh= script as the runner for any binary built for our custom target.
|
||||
|
||||
If we now run =cargo run=, cargo will build our kernel and automatically start qemu.
|
||||
Unfortunately, since our kernel currently does nothing, this is hard to discern.
|
||||
Therefore, our next goal will be to get some output from our kernel, which we will do by making use of limine’s framebuffer.
|
||||
|
||||
* Getting the Framebuffer
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ bin_name="$(basename -- "$canonicalised")"
|
|||
image="$parent/$bin_name.img"
|
||||
|
||||
|
||||
cat > /tmp/limine.conf <<EOF
|
||||
if [[ "$canonicalised" -nt "$image" ]]; then
|
||||
echo "Creating image $image"
|
||||
|
||||
cat > /tmp/limine.conf <<EOF
|
||||
timeout: 0
|
||||
serial: yes
|
||||
serial_baudrate: 9600
|
||||
|
|
@ -21,9 +24,6 @@ serial_baudrate: 9600
|
|||
path: boot():/boot/kernel.elf
|
||||
EOF
|
||||
|
||||
if [[ "$canonicalised" -nt "$image" ]]; then
|
||||
echo "Creating image $image"
|
||||
|
||||
limine_datadir="$(limine --print-datadir)"
|
||||
limine_uefi_path="$limine_datadir/BOOTX64.EFI"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue