From af3e575dd74b369b134460b3bb9b3750a6d9ab59 Mon Sep 17 00:00:00 2001 From: Berke Sinar <berke2.sinar@live.uwe.ac.uk> Date: Fri, 6 Dec 2024 11:54:39 +0000 Subject: [PATCH] files --- README.md | 979 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 923 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index b34fe92..1f5b8ea 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,960 @@ -# osws2 +# Tiny Operating System - Worksheet 2 (Part 1) +## Introduction +This project demonstrates the core components of an operating system by implementing: +1. A bootloader (`loader.asm`) to initialise the system and pass control to the kernel. +2. A kernel (`kernel.elf`) written in C and assembly, handling basic system functionality. +3. Essential drivers like a framebuffer driver for screen output. -## Getting started +The OS is loaded using GRUB and executed in QEMU, providing a platform for learning and experimenting with low-level programming. -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +--- -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +## Features +- Multiboot-compliant bootloader. +- Basic kernel functionality (e.g., initialisation and infinite loop). +- Framebuffer driver for managing screen output. +- Support for running in QEMU for emulation. -## Add your files +--- -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +## Approach: How the Operating System Works +This section outlines the overall approach used in the development of the Tiny OS as part of Worksheet 2 for the Operating Systems course. + +### 1. Initial Setup: Kernel Creation + - **Objective**: Develop a simple kernel in assembly, which will serve as the foundation of the OS. + - **Process**: The kernel begins by setting up essential hardware resources, such as the framebuffer for display output, and initializing basic I/O operations. + - **Key Functionality**: At this stage, the kernel simply writes a "Hello" message to the screen. + +### 2. Framebuffer Integration + - **Objective**: Integrate a framebuffer driver to display text on the screen. + - **Process**: A simple framebuffer driver is written in C (`drivers/framebuffer.c`) to interact with the display memory directly. + - **Key Functionality**: It allows the kernel to write characters to specific positions on the screen and clear the screen when necessary. + - **Details**: The framebuffer is accessed directly by writing to a known memory address (`0x000B8000`), which represents the VGA text buffer. + +### 3. Arithmetic Operations in Kernel + - **Objective**: Implement simple arithmetic operations (sum, multiplication, subtraction) to demonstrate the execution of functions in the kernel. + - **Process**: These operations are implemented as C functions, which are called from the `kernel_main()` function. + - **Key Functionality**: After performing the operations, the results are displayed on the screen using the framebuffer functions (`fb_write_char()` and `print_number()`). + +### 4. Displaying Output + - **Objective**: Show the results of arithmetic operations on the screen. + - **Process**: The kernel prints a welcome message, followed by the results of the arithmetic functions, one after the other. + - **Key Functionality**: The display is updated line by line, ensuring that results are legible and well-organized on an 80x25 character screen. + +### 5. System Continuity + - **Objective**: Ensure that the system keeps running without exiting. + - **Process**: At the end of `kernel_main()`, the kernel enters an infinite loop to simulate an operational system. + - **Key Functionality**: The infinite loop keeps the kernel running indefinitely, awaiting further instructions or user input (in a real OS, this could be more complex, with process scheduling, interrupts, etc.). + +### 6. GRUB Bootloader Configuration + - **Objective**: Configure the GRUB bootloader to load the kernel automatically. + - **Process**: The bootloader configuration is set up using the `menu.lst` file to define the kernel entry and loading parameters. + - **Key Functionality**: The `menu.lst` file tells GRUB to load the `kernel.elf` file and pass control to the kernel with no delay (`timeout=0`). + +### 7. Compile and Link Kernel + - **Objective**: Build the kernel and its components using a `Makefile`. + - **Process**: The kernel is compiled using a cross-compiler and linked to create a bootable `kernel.elf` file. + - **Key Functionality**: The `Makefile` automates the build process, compiling the source files (`source/loader.asm`, `source/kernel.c`, `drivers/framebuffer.c`) and linking them together to form the final kernel binary. + +## Build Process + +To build and run the OS, use the `Makefile` included in the project. Below is an overview of the steps: + +1. **Compilation**: The `Makefile` compiles assembly and C source files into object files (`.o`). +2. **Linking**: The linker script (`link.ld`) links the object files to produce a kernel ELF file (`kernel.elf`). +3. **ISO Creation**: A bootable ISO image (`os.iso`) is generated using GRUB. +4. **Execution**: The ISO can be executed in QEMU. + +--- + + +## Overview +This repository contains the implementation of a minimal Tiny OS that boots a machine from scratch and writes `0xCAFEBABE` to the `eax` register. The OS is built using an assembly bootloader and kernel, and we use GRUB as the bootloader. The goal of this part of the project was to develop an OS that can boot, load the kernel, and provide a foundation for extending functionality in future tasks. + +### Tasks +1. **Task 1:** Develop a bootloader and kernel to boot the system and write `0xCAFEBABE` to the `eax` register. +2. **Task 2:** Extend the kernel to call C functions from assembly. Implement `sum_of_three` and two additional functions in C. + +## Directory Structure +``` +. +├── Makefile # Makefile to build the kernel and ISO image +├── README.md # This file +├── cafebabe.png # Screenshot showing 0xCAFEBABE in logQ.txt +├── drivers/ # Contains drivers (e.g., framebuffer driver) +│ ├── framebuffer.c # Framebuffer driver C code +│ └── framebuffer.o # Compiled framebuffer driver object +├── iso/ # ISO directory for creating bootable image +│ ├── boot/ +│ │ ├── grub # GRUB configuration and bootloader files +│ │ │ ├── menu.lst # GRUB menu configuration +│ │ │ └── stage2_eltorito # GRUB boot stage +│ ├── kernel.elf # The compiled kernel file +│ └── logQ.txt # QEMU execution log +├── logQ.txt # Detailed QEMU execution log +├── os.iso # Final ISO image to boot the OS +└── source/ # Source code for the bootloader and kernel + ├── kernel.c # Kernel C code (if any) + ├── kernel.o # Compiled kernel object + ├── link.ld # Linker script + ├── loader.asm # Bootloader in assembly + └── loader.o # Compiled loader object +``` + +Here is my directory sturcture below + +## Requirements +- `nasm`: Assembler for creating object files from assembly code. +- `ld`: GNU linker to create the final kernel ELF file. +- `genisoimage`: Tool to create an ISO image for booting with QEMU. +- `qemu`: Emulator for running the OS in a virtual environment. + +## Task 1: Kernel and Bootloader +### Description +In this task, we developed a basic bootloader using GRUB to load the Tiny OS kernel. The kernel was written in assembly and designed to write `0xCAFEBABE` to the `eax` register. The code is structured with a bootloader and a simple kernel that loops indefinitely after performing the action. + +Below is an example log of `0xCAFEBABE` appearing in the `logQ.txt` file during execution: + + + +### Files +- **loader.asm**: The assembly file that contains the bootloader and kernel code. It includes the `MAGIC_NUMBER`, `FLAGS`, and `CHECKSUM` for multiboot compatibility. + +## loader.asm + +### **Explaining `loader.asm`**: +The `loader.asm` file contains the code for the bootloader of the Tiny OS. It is a simple program that starts execution when the system boots and does the following: + +1. **Multiboot Header**: + At the beginning of the file, there’s a Multiboot-compliant header. Multiboot is a standard used by bootloaders (like GRUB) to load a kernel. The header includes a **magic number**, **flags**, and a **checksum**. These are used to validate that the kernel is correctly loaded by GRUB. The header is defined as follows: + +```asm +MAGIC_NUMBER equ 0x1BADB002 ; Define the magic number constant +FLAGS equ 0x0 ; Multiboot flags (indicates the kernel doesn't use special flags) +CHECKSUM equ -MAGIC_NUMBER ; Calculate the checksum (to ensure the header is correct) +``` + +- **MAGIC_NUMBER**: A unique identifier (0x1BADB002) that tells the bootloader this is a valid Multiboot kernel. +- **FLAGS**: Set to `0x0`, which means the kernel doesn't need any special flags. +- **CHECKSUM**: Ensures the sum of the magic number, flags, and checksum equals zero. This is part of the Multiboot specification for kernel integrity. + +These values are written to the memory in the following lines: + +```asm +dd MAGIC_NUMBER ; Write the magic number to the machine code +dd FLAGS ; Write the flags +dd CHECKSUM ; Write the checksum +``` + +2. **Loader Label**: + The label `loader` defines the entry point of the program, which is where execution starts after the bootloader hands over control to the kernel. + +```asm +loader: ; The loader label (entry point) +``` + +3. **Setting the `eax` Register**: + The core functionality of the loader is to place a specific value (`0xCAFEBABE`) into the `eax` register. This is just a placeholder action that ensures the OS is running and performing operations. + +```asm +mov eax, 0xCAFEBABE ; Place the number 0xCAFEBABE in the eax register +``` + +4. **Infinite Loop**: + The `jmp .loop` instruction is an infinite loop that keeps the kernel running. This loop is essential because, after setting the register value, the system should continue running without halting. It effectively prevents the system from crashing and allows you to test that the kernel is correctly loaded into memory. + +```asm +.loop: + jmp .loop ; Loop forever +``` + +### **Summary**: +The `loader.asm` file performs the following tasks: +- Defines the Multiboot header with a magic number, flags, and checksum to ensure proper loading by GRUB. +- Sets the entry point (`loader`), where execution begins. +- Places the value `0xCAFEBABE` into the `eax` register to indicate that the kernel is running. +- Enters an infinite loop to keep the kernel running and prevent the system from halting. + +This simple bootloader serves as a foundation for more complex OS development, where additional features like memory management, I/O handling, and multitasking can be added. + + +- **link.ld**: The linker script that ensures the kernel is loaded at memory address `0x00100000`. + +## Explanation of `link.ld` + +The `link.ld` file is a linker script used to control the placement of different sections in memory during the linking process. It ensures that the kernel and other sections are loaded at the correct addresses in memory. Below is a breakdown of the sections in the script: + +### Code Snippet: `link.ld` + +```ld +ENTRY(loader) /* The name of the entry label */ +SECTIONS { + . = 0x00100000; /* The code should be loaded at 1 MB */ + + .text ALIGN(0x1000) : /* Align the .text section at 4 KB boundaries */ + { + *(.text) /* All text sections from all files */ + } + + .rodata ALIGN(0x1000) : /* Align the read-only data section at 4 KB boundaries */ + { + *(.rodata*) /* All read-only data sections from all files */ + } + + .data ALIGN(0x1000) : /* Align the .data section at 4 KB boundaries */ + { + *(.data) /* All data sections from all files */ + } + + .bss ALIGN(0x1000) : /* Align the .bss section at 4 KB boundaries */ + { + *(COMMON) /* All COMMON sections from all files */ + *(.bss) /* All .bss sections from all files */ + } +} +``` + +# kernel.c Explanation + +The `kernel.c` file is the core of the operating system, containing the main logic for running basic operations, displaying output to the framebuffer, and performing simple arithmetic operations. + +## Code Breakdown + +### 1. Declare framebuffer functions directly + +The kernel includes the declaration of framebuffer functions (`fb_write_char` and `fb_clear`) which are used for writing characters and clearing the screen. + +```c +void fb_write_char(unsigned int position, char character); +void fb_clear(); +``` + +### 2. Sum of Three Numbers + +This function calculates the sum of three integer arguments. The arguments are set to predefined values inside the function, and their sum is returned. + +**Parameters:** `arg1`, `arg2`, `arg3` (input integers) +**Returns:** The sum of the three numbers. + +```c +int sum_of_three(int arg1, int arg2, int arg3) { + arg1 = 3; + arg2 = 5; + arg3 = 7; + return arg1 + arg2 + arg3; +} +``` + +### 3. Multiply Two Numbers + +This function multiplies two integers. The arguments are set to predefined values within the function, and the result is returned. + +**Parameters:** `arg1`, `arg2` (input integers) +**Returns:** The product of the two numbers. + +```c +int multiply_two(int arg1, int arg2) { + arg1 = 4; + arg2 = 6; + return arg1 * arg2; +} ``` -cd existing_repo -git remote add origin https://gitlab.uwe.ac.uk/b2-sinar/osws2.git -git branch -M main -git push -uf origin main + +### 4. Subtract Two Numbers + +This function subtracts the second integer (`arg2`) from the first integer (`arg1`) and returns the result. + +**Parameters:** `arg1`, `arg2` (input integers) +**Returns:** The result of the subtraction (`arg1 - arg2`). + +```c +int subtract_two(int arg1, int arg2) { + arg1 = 10; + arg2 = 3; + return arg1 - arg2; +} ``` -## Integrate with your tools +### 5. Print a Number + +This function prints an integer `num` to the framebuffer at the given position `start_pos`. The number is converted into individual characters and printed one by one. + +**Parameters:** +- `num`: The number to be printed. +- `start_pos`: The starting position on the screen where the number will be displayed. + +**How it works:** +- The number is divided by 10 repeatedly to extract each digit (from least significant to most significant). +- The digits are stored in a buffer and then printed in reverse order. + +```c +void print_number(int num, int start_pos) { + char buffer[16]; + int pos = 0; + if (num == 0) { + fb_write_char(start_pos, '0'); + return; + } + int temp = num; + while (temp > 0) { + buffer[pos++] = '0' + (temp % 10); + temp /= 10; + } + for (int i = pos - 1; i >= 0; i--) { + fb_write_char(start_pos++, buffer[i]); + } +} +``` -- [ ] [Set up project integrations](https://gitlab.uwe.ac.uk/b2-sinar/osws2/-/settings/integrations) +### 6. Kernel Main Function + +This is the main entry point of the kernel, where all operations are initiated. + +**How it works:** +- The framebuffer is cleared at the start. +- The string "Hello Berkeee" is printed on the screen. +- Results of the arithmetic functions (`sum_of_three`, `multiply_two`, and `subtract_two`) are calculated and printed with appropriate labels. +- An infinite loop is used to keep the kernel running. + +```c +void kernel_main() { + fb_clear(); + + // Print "Hello OS" + char *message = "Hello Berkeee"; + int pos = 0; + for (int i = 0; message[i] != '\0'; i++) { + fb_write_char(pos++, message[i]); + } + + // Move to a new line for the results + pos = 80; // Starting position for the next line on an 80x25 screen + + // Print function results with labels + int sum_result = sum_of_three(0, 0, 0); + char *sum_label = "Sum: "; + for (int i = 0; sum_label[i] != '\0'; i++) { + fb_write_char(pos++, sum_label[i]); + } + print_number(sum_result, pos); + pos += 5; // Adjust as needed for spacing + + int multiply_result = multiply_two(0, 0); + char *mul_label = " Mul: "; + for (int i = 0; mul_label[i] != '\0'; i++) { + fb_write_char(pos++, mul_label[i]); + } + print_number(multiply_result, pos); + pos += 5; + + int subtract_result = subtract_two(0, 0); + char *sub_label = " Sub: "; + for (int i = 0; sub_label[i] != '\0'; i++) { + fb_write_char(pos++, sub_label[i]); + } + print_number(subtract_result, pos); + + // Simple infinite loop to keep the kernel running + while(1){} +} +``` -## Collaborate with your team +## Purpose -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) +The `kernel.c` file demonstrates basic kernel functionality such as: -## Test and Deploy +1. **Output to the framebuffer**: The kernel writes messages and results to the screen using the framebuffer functions. +2. **Arithmetic Operations**: Simple mathematical functions are implemented (`sum_of_three`, `multiply_two`, `subtract_two`) to demonstrate computation. +3. **Displaying Results**: The results of the functions are displayed on the screen using the `print_number` function. +4. **Infinite Loop**: The kernel enters an infinite loop at the end to keep the system running. -Use the built-in continuous integration in GitLab. +This structure is foundational for creating a simple operating system, where you can add more functionality as needed. + + +- **Makefile**: The Makefile builds the kernel (`kernel.elf`), copies necessary files into the GRUB directory, and creates the ISO image (`os.iso`). + + +# GRUB `menu.lst` File + +The `menu.lst` file is a configuration file used by GRUB (the bootloader) to define how the operating system is booted. Below is the content and explanation of the file. + +--- + +## **File Contents** +```plaintext +default=0 +timeout=0 +title os +kernel /boot/kernel.elf +``` -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) +--- -*** +## **Explanation** -# Editing this README +### **1. `default=0`** +- Sets the default boot entry to the first option (index starts at 0). +- Since there’s only one entry labeled `os`, this ensures it is selected automatically. -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. +### **2. `timeout=0`** +- Sets the boot delay to 0 seconds. +- The operating system will boot immediately without showing the GRUB menu. -## Suggestions for a good README +### **3. `title os`** +- Defines the label displayed in the GRUB menu for this entry. +- In this case, the operating system is labeled as `os`. -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. +### **4. `kernel /boot/kernel.elf`** +- Specifies the kernel file to be loaded and executed. +- `/boot/kernel.elf` is the location of the kernel ELF binary created during the build process. -## Name -Choose a self-explaining name for your project. +--- + +## **Purpose** +The `menu.lst` ensures: +1. GRUB boots the operating system automatically without user interaction. +2. The specified kernel file (`kernel.elf`) is loaded into memory and executed. + + + +# framebuffer.c + +This file provides essential functions for working with the framebuffer in on x86 systems. It enables basic operations like writing characters and clearing the screen. + +--- + +## Code Overview + +### 1. Framebuffer Address + +The framebuffer is memory-mapped to the address `0x000B8000` on x86 systems. This memory is used for text mode video output. + +```c +#define FB_ADDRESS 0x000B8000 +``` + +--- + +### 2. Text Attributes + +The text color and background are controlled using attributes. `WHITE_ON_BLACK` specifies white text on a black background. + +```c +#define WHITE_ON_BLACK 0x0F +``` -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. +--- -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. +### 3. Writing a Character -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. +The `fb_write_char` function writes a character at a specified position in the framebuffer. + +#### Function Definition + +```c +void fb_write_char(unsigned int position, char character) { + volatile char *fb = (char *) FB_ADDRESS; + fb[position * 2] = character; // Character byte + fb[position * 2 + 1] = WHITE_ON_BLACK; // Attribute byte (text color) +} +``` + +#### Usage + +To write the letter 'A' at the top-left corner of the screen: + +```c +fb_write_char(0, 'A'); +``` + +--- + +### 4. Clearing the Screen + +The `fb_clear` function clears the entire screen by filling it with blank spaces (`' '`) and resetting the attributes. + +#### Function Definition + +```c +void fb_clear() { + volatile char *fb = (char *) FB_ADDRESS; + for (int i = 0; i < 80 * 25; i++) { // Assuming an 80x25 screen size + fb[i * 2] = ' '; // Clear character + fb[i * 2 + 1] = WHITE_ON_BLACK; // Set background color + } +} +``` + +#### Usage + +To clear the screen: + +```c +fb_clear(); +``` + +--- + +## Purpose + +This file enables basic text output in early-stage system programming. It is particularly useful for: + +- Writing characters at specific screen positions. +- Clearing the screen in preparation for new output. + +--- -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. ## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. +To include this in a project: + +1. Include `framebuffer.c` in your build process. +2. Call `fb_write_char` to write characters or `fb_clear` to clear the screen. + +--- + +## Example + +Here’s a simple example that writes "Hello" to the screen: + +```c +void display_hello() { + fb_write_char(0, 'H'); + fb_write_char(1, 'e'); + fb_write_char(2, 'l'); + fb_write_char(3, 'l'); + fb_write_char(4, 'o'); +} + +int main() { + fb_clear(); // Clear the screen + display_hello(); // Display "Hello" + return 0; +} +``` + + + + +## Makefile Overview + +The `Makefile` simplifies the process of compiling, linking, and creating the ISO image for the operating system. Below are the details for each section of the `Makefile`. + +### Variables + +```makefile +OBJECTS = source/loader.o source/kernel.o drivers/framebuffer.o # Add other .o files as needed +CC = gcc +CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \ + -nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c +LDFLAGS = -T source/link.ld -melf_i386 +AS = nasm +ASFLAGS = -f elf32 +``` + +- **`OBJECTS`**: Specifies the object files to be linked into the kernel. +- **`CC`**: Sets the C compiler to `gcc`. +- **`CFLAGS`**: Compiler flags for building 32-bit bare-metal code. +- **`LDFLAGS`**: Linker flags for controlling memory layout and architecture. +- **`AS`**: Assembler (`nasm`) used for compiling `.asm` files. +- **`ASFLAGS`**: Flags for the assembler to produce 32-bit ELF object files. + +--- + +### Build Targets + +#### Build the ISO Image +```makefile +all: os.iso +``` +The default target creates a bootable ISO image. + +#### Build the Kernel ELF +```makefile +kernel.elf: $(OBJECTS) + ld $(LDFLAGS) $(OBJECTS) -o iso/boot/kernel.elf +``` +Links all object files into a kernel ELF binary, placing it in the `iso/boot` directory. + +#### Create the ISO Image +```makefile +os.iso: kernel.elf + mkdir -p iso/boot/grub + cp /opt/os/stage2_eltorito iso/boot/grub/ + echo "default=0" > iso/boot/grub/menu.lst + echo "timeout=0" >> iso/boot/grub/menu.lst + echo "title os" >> iso/boot/grub/menu.lst + echo "kernel /boot/kernel.elf" >> iso/boot/grub/menu.lst + genisoimage -R \ + -b boot/grub/stage2_eltorito \ + -no-emul-boot \ + -boot-load-size 4 \ + -A os \ + -input-charset utf8 \ + -quiet \ + -boot-info-table \ + -o os.iso \ + iso +``` +- Creates directories and copies GRUB files. +- Configures GRUB with `menu.lst`. +- Uses `genisoimage` to create a bootable ISO. + +--- + +#### Run Targets + +##### Run in Regular QEMU Mode +```makefile +run: os.iso + qemu-system-i386 -curses -monitor telnet::46669,server,nowait -serial mon:stdio -boot d -cdrom os.iso -m 32 -d cpu -D logQ.txt +``` + +##### Run in QEMU with Curses Mode +```makefile +run-curses: os.iso + qemu-system-i386 -curses -monitor telnet::45454,server,nowait -serial mon:stdio -boot d -cdrom os.iso +``` +Runs the ISO in QEMU for testing. + +--- + +#### Object File Compilation Rules + +##### Compile C Files +```makefile +%.o: %.c + $(CC) $(CFLAGS) $< -o $@ # Indented with a tab +``` + +##### Compile Assembly Files +```makefile +%.o: %.asm + $(AS) $(ASFLAGS) $< -o $@ # Indented with a tab +``` + +##### Specific Rules +```makefile +source/loader.o: source/loader.asm + $(AS) $(ASFLAGS) source/loader.asm -o source/loader.o # Indented with a tab + +drivers/framebuffer.o: drivers/framebuffer.c + $(CC) $(CFLAGS) drivers/framebuffer.c -o drivers/framebuffer.o # Indented with a tab +``` + +--- + +### Clean Target + +```makefile +clean: + rm -rf source/.o drivers/.o iso/boot/kernel.elf os.iso iso/boot/grub/menu.lst +``` +Deletes all generated files to clean the build directory. + +--- + +## Steps to Build and Run + +1. **Build the ISO Image**: + ```bash + make + ``` + +2. **Run in QEMU**: + - Regular mode: + ```bash + make run + ``` + + +3. **Clean the Project**: + ```bash + make clean + ``` + +--- + +### Prerequisites + +Ensure the following are installed: +- GCC (`gcc`) for C compilation. +- NASM (`nasm`) for assembly. +- LD (`ld`) for linking. +- `genisoimage` for creating bootable ISO images. +- QEMU for testing the OS. + +--- + +# Kernel Initialization Flowchart + +The **Kernel Initialization Flowchart** represents the series of steps the kernel follows during its initialization process. After the initialization, it enters an infinite loop to keep the system running. Here's a breakdown of each step in the flowchart: + +## Steps + +1. **Start** + The process begins. + +2. **Set up framebuffer** + The framebuffer is set up to prepare the screen for displaying output. This ensures that we can display characters and other data on the screen. + +3. **Clear framebuffer** + The framebuffer is cleared to provide a clean slate for displaying the results. This is necessary to ensure no residual data is left on the screen from previous operations. + +4. **Perform Arithmetic Operations** + The kernel performs several arithmetic operations, such as: + - `sum_of_three()` which adds three numbers. + - `multiply_two()` which multiplies two numbers. + - `subtract_two()` which subtracts one number from another. + +5. **Display Results** + After performing the calculations, the results are displayed on the framebuffer. The results are shown in a readable format on the screen. + +6. **Infinite Loop** + After the results are displayed, the kernel enters an **infinite loop**. This ensures that the kernel keeps running indefinitely without terminating. The infinite loop is necessary to maintain control of the system and avoid the kernel shutting down. + +7. **End** + This step is never reached because the kernel is in the infinite loop. The system continues running until it is shut down manually or by an external process. + +## Purpose + +This flowchart serves to explain the **initialization sequence** of the kernel. After performing necessary setup and arithmetic operations, the kernel enters an infinite loop to keep the system operational and maintain control over the hardware. + +The flowchart does not have an "End" point because the infinite loop ensures that the process never terminates on its own. + + + + + +# fb_write_char Function Flowchart + +The **`fb_write_char` Function Flowchart** illustrates the steps involved in writing a character to the framebuffer. + +## Steps + +1. **Start** + The function is called with the `position` and `character` as inputs. + +2. **Calculate Character Position** + The framebuffer position is calculated by multiplying the provided position by 2 (since each character occupies two bytes: one for the character and one for the attribute). + +3. **Write Character to Framebuffer** + The character is written to the calculated position in the framebuffer. + +4. **Write Attribute** + The attribute byte is written next. This defines the text color (e.g., `WHITE_ON_BLACK`). + +5. **End** + The process ends after writing both the character and the attribute byte to the framebuffer. + +## Purpose + +This flowchart explains how the kernel's `fb_write_char` function writes a character and its associated attributes to the framebuffer for display on the screen. + + + + + +### Flowchart 3: `fb_clear` Function (Framebuffer Clear) + +The purpose of this flowchart is to illustrate the flow of the `fb_clear` function, which clears the framebuffer (screen). This function iterates over every character cell of the framebuffer and sets each character to a blank space with a default text color. Here's how the function works step by step: + +#### Steps: + +1. **Start** + The process begins here. + +2. **Initialize framebuffer pointer (`fb`)** + Set the framebuffer pointer `fb` to the memory address `0x000B8000`, where the framebuffer is located. + +3. **Start Loop** + A loop starts, where we will iterate through each position of the framebuffer. For an 80x25 screen, this loop will run for 2000 iterations (since 80 * 25 = 2000). + +4. **Set character to ' ' (blank space)** + In each iteration of the loop, set the character byte at the current framebuffer position `i` to a blank space (`' '`). This clears the text on the screen. + +5. **Set attribute byte to `WHITE_ON_BLACK`** + In the same iteration, set the attribute byte (which controls the text color) at position `i` to `WHITE_ON_BLACK`. This ensures the background is black, and the text is white. + +6. **Increment position (i++)** + Move to the next position in the framebuffer (`i++`), continuing the process of clearing the screen. + +7. **Check Loop Condition** + The loop condition is checked. If `i` is less than 2000, the loop continues, and the process goes back to step 4. If `i` reaches 2000, the loop ends. + +8. **End** + The function completes once all the framebuffer positions have been cleared, and the process ends. + +#### Key Points: +- The loop ensures that all 2000 character positions on the screen are cleared. +- Each position is set to a blank space `' '`, and the color attribute is set to `WHITE_ON_BLACK`. +- The framebuffer pointer `fb` is used to access the memory locations directly. +- The function doesn't take any parameters and doesn't return any value. It simply clears the screen. + +This flowchart and the `fb_clear` function help reset the screen when the operating system or application needs to start with a clean display. + + + + +## Flowchart 4 - Kernel Main Function Execution Flow + +This flowchart illustrates the execution flow of the `kernel_main` function. The kernel starts execution from the `Start` point and proceeds through the steps outlined below. Once the kernel has executed all the necessary steps to display the message and perform calculations, it enters an infinite loop to keep running. + +### Flowchart Breakdown + +1. **Start**: This is the entry point of the kernel program. + - The execution begins here, initiating the kernel. + +2. **Call `fb_clear()`**: The framebuffer is cleared at the start of the kernel execution. + - This function prepares the screen by clearing any previous content and setting the background color. + +3. **Initialize 'Hello Berkeee' message**: The message to be displayed on the screen is set up. + - The message "Hello Berkeee" is prepared for output. + +4. **Write character to framebuffer**: Each character of the message is written to the screen. + - This step uses the `fb_write_char` function to display each character of the message on the framebuffer at the correct position. + +5. **Message still to write?**: A decision point checks if there are more characters in the message to be written. + - **Yes**: The flow goes back to "Write character to framebuffer" to display the next character. + - **No**: The flow moves on to the next step. + +6. **Move cursor to next line**: After the message is displayed, the cursor is moved to the next line. + - This prepares the screen for displaying the next set of results, such as the calculation outputs. + +7. **Print function results (sum, multiply, subtract)**: The kernel prints the results of the functions `sum_of_three`, `multiply_two`, and `subtract_two`. + - The output of these functions is displayed on the screen one after another, including labels and the results. + +8. **Infinite Loop**: After displaying all the messages and function results, the program enters an infinite loop. + - The kernel stays in this loop, ensuring the program keeps running. This loop does not return control to the beginning of the flow; it simply keeps the system in operation. + +### No Loops or Endpoints +- The flowchart does not loop back to the start; instead, it ends at the **Infinite Loop** step. +- This infinite loop prevents the kernel from exiting or restarting, ensuring that the system remains running. + +The flowchart captures how the kernel function continuously operates until manually halted, with no explicit exit condition. + + + +### Conclusion + +This `Makefile` simplifies the process of building a bootable operating system and provides a quick way to test it in QEMU. + + +### Steps +1. **Building the Kernel**: Use `nasm` to assemble the `loader.asm` file and `ld` to link the object file into the final `kernel.elf`. + ``` + nasm -f elf loader.asm + ld -T ./source/link.ld -melf_i386 loader.o -o kernel.elf + ``` +2. **Creating the ISO Image**: The Makefile automates the creation of the ISO image (`os.iso`) by using `genisoimage`. + ``` + make iso + ``` +3. **Running with QEMU**: The ISO image can be run with QEMU using the following command: + ``` + qemu-system-i386 -nographic -boot d -cdrom os.iso -m 32 -d cpu -D logQ.txt + ``` + This will boot the OS and log the CPU execution to `logQ.txt`, where you will see `CAFEBABE`. + +## Task 2: Calling C Functions from Assembly +### Description +In Task 2, the kernel was extended to call C functions from assembly. We implemented the `sum_of_three` function along with two additional custom functions in C. These functions are called from the assembly kernel, and the results are displayed via the framebuffer. + + + +### Files +- **sum_of_three.c**: The C implementation of the `sum_of_three` function. +- **Makefile**: Extended to compile the C functions and link them with the kernel. + +### Steps +1. **Creating C Functions**: Implement C functions like `sum_of_three` that take arguments and return values. +2. **Calling C from Assembly**: Use the `extern` keyword in assembly to declare C functions and call them from assembly. +3. **Compiling and Linking**: Extend the Makefile to include rules for compiling C code and linking it with the kernel. For example: + ``` + gcc -m32 -ffreestanding -c sum_of_three.c -o sum_of_three.o + ld -T ./source/link.ld -melf_i386 loader.o sum_of_three.o -o kernel.elf + ``` + +## Task 3: Adding Simple I/O (Framebuffer Driver) +### Description +For Task 3, we focused on adding basic I/O support using memory-mapped I/O to write to the framebuffer. This allows the OS to display output (e.g., text) to the screen. The framebuffer is configured at memory address `0x000B8000`, and writing to this address updates the display. + +Below is an example of text output via the framebuffer: + +# Framebuffer Module + +We have modularised the framebuffer functionality by separating it into a dedicated header file, `framebuffer.h`. This structure helps in organising the codebase and allows easier extension and reuse of framebuffer-related functions. + +--- + +## `framebuffer.h` + +The `framebuffer.h` header includes: + +- **Framebuffer Address and Attributes**: + - Defined constants such as `FB_ADDRESS` (`0x000B8000`) and `WHITE_ON_BLACK` (`0x0F`) for default text colour. + +- **I/O Ports and Commands**: + - Ports (`FB_COMMAND_PORT` and `FB_DATA_PORT`) and commands (`FB_HIGH_BYTE_COMMAND` and `FB_LOW_BYTE_COMMAND`) for cursor control. + +- **Functions**: + - `fb_write_char`: Writes a character at a specific position. + - `fb_clear`: Clears the framebuffer with a specified colour. + - `fb_move_cursor`: Moves the cursor to a given position. + - `fb_move`: Positions the cursor using x and y coordinates. + +This modular approach ensures a clear separation of concerns and sets a strong foundation for further enhancements. + +--- + +## Cursor Control + +We have added functionality to move the cursor using two approaches: +1. **Position-based movement**: Directly specifying a position in the framebuffer. +2. **Coordinate-based movement**: Using `x` and `y` coordinates for readability. + +--- + +### Functions in `framebuffer.c` + +#### Move Cursor by Position + +The `fb_move_cursor` function moves the cursor to a specific position within the framebuffer: + +```c +void fb_move_cursor(unsigned short pos) +{ + outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND); + outb(FB_DATA_PORT, ((pos >> 8) & 0x00FF)); // High byte + outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND); + outb(FB_DATA_PORT, pos & 0x00FF); // Low byte +} + +### Files +- **framebuffer.c**: The C implementation of the framebuffer driver. + -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. +## How to Build and Run +1. **Build the Kernel and ISO Image**: + ``` + make + ``` +2. **Run the OS**: + ``` + make run + ``` -## Contributing -State if you are open to contributions and what your requirements are for accepting them. +## Screenshots +- **Running the OS on QEMU**: A screenshot or log output showing the OS in action -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. -## License -For open source projects, say how it is licensed. -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +## Conclusion +In this part of the worksheet, we successfully created a bootable Tiny OS, implemented basic kernel functionality, and extended it to call C functions from assembly. The next steps involve adding more advanced I/O support and keyboard handling. -- GitLab