wrote a basic bootloader
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
ENTRY(_start)
|
||||
OUTPUT_FORMAT(binary)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x7C00; /* Where BIOS loads us */
|
||||
.text : {
|
||||
*(.text)
|
||||
. = 446; /* Pad to 510 bytes */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
section .text
|
||||
global _start
|
||||
|
||||
[BITS 16]
|
||||
_start:
|
||||
; Set video mode (clear screen)
|
||||
mov ah, 0x00
|
||||
mov al, 0x03
|
||||
int 0x10
|
||||
|
||||
; Print loading message
|
||||
mov ax, 0xb800
|
||||
mov ds, ax
|
||||
|
||||
mov dword [ 0], "L O "
|
||||
mov dword [ 4], "A D "
|
||||
mov dword [ 8], "I N "
|
||||
mov dword [ 12], "G . "
|
||||
mov dword [ 16], ". . "
|
||||
|
||||
; setup a stack
|
||||
cli
|
||||
mov ax, 0x9000
|
||||
mov ss, ax
|
||||
mov sp, 0x0000
|
||||
mov bp, sp
|
||||
|
||||
mov bx, 0x0800 ; this is the first memory location we're interested in.
|
||||
mov es, bx
|
||||
xor bx, bx
|
||||
|
||||
mov cl, 0x02 ; this is the first sector we're interested in.
|
||||
|
||||
call load_sector
|
||||
|
||||
mov ax, [es:6] ; load last two bytes (0x1FE = 510)
|
||||
cmp ax, 0xAA55 ; check if it matches boot signature
|
||||
jne error ; if not equal, not a valid boot sector
|
||||
|
||||
; jmp success
|
||||
|
||||
mov ax, [es:4] ; load sector count
|
||||
|
||||
next_sector:
|
||||
dec ax ; decrement sectors left to write
|
||||
inc cl
|
||||
|
||||
cmp ax, 0
|
||||
je success
|
||||
|
||||
add bx, 0x200 ; Add 512 to offset
|
||||
|
||||
call load_sector
|
||||
jmp next_sector
|
||||
|
||||
load_sector:
|
||||
pusha
|
||||
|
||||
; setup registers for disk read
|
||||
mov ah, 0x02 ; read sectors function
|
||||
mov dl, 0x80 ; first hard disk
|
||||
mov ch, 0x00 ; cylinder 0
|
||||
mov dh, 0x00 ; head 0
|
||||
mov al, 1 ; read 1 sector
|
||||
|
||||
; do the read
|
||||
int 0x13
|
||||
jc error ; if carry set, read failed
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
hang:
|
||||
jmp hang
|
||||
|
||||
error:
|
||||
; error getting the magic number
|
||||
mov ah, 0x00
|
||||
mov al, 0x03
|
||||
int 0x10
|
||||
|
||||
mov ax, 0xb800
|
||||
mov ds, ax
|
||||
|
||||
mov dword [ 0], "E R "
|
||||
mov dword [ 4], "R O "
|
||||
mov dword [ 8], "R . "
|
||||
|
||||
jmp hang
|
||||
|
||||
success:
|
||||
jmp 0x0800:0x0016 ; jump to start of stage 2
|
||||
@@ -0,0 +1,118 @@
|
||||
section .text.boot
|
||||
extern stage2_main
|
||||
extern STAGE2_SIZE
|
||||
extern STAGE2_SECTORS
|
||||
|
||||
section .header
|
||||
global _header_start
|
||||
_header_start:
|
||||
dd STAGE2_SIZE
|
||||
dw STAGE2_SECTORS
|
||||
db 0x55, 0xAA
|
||||
|
||||
global _start
|
||||
[BITS 16]
|
||||
_start:
|
||||
mov ah, 0x00
|
||||
mov al, 0x03
|
||||
int 0x10
|
||||
|
||||
mov ah, 0x06
|
||||
mov al, 0
|
||||
mov bh, 0x07 ; normal attribute (white on black)
|
||||
mov cx, 0 ; upper left = (0,0)
|
||||
mov dx, 0x184F ; lower right = (24,79) - full screen
|
||||
int 0x10
|
||||
|
||||
; print "loaded"
|
||||
mov ax, 0xb800
|
||||
mov ds, ax
|
||||
|
||||
mov byte [0], 'L'
|
||||
mov byte [1], 0x07
|
||||
mov byte [2], 'O'
|
||||
mov byte [3], 0x07
|
||||
mov byte [4], 'A'
|
||||
mov byte [5], 0x07
|
||||
mov byte [6], 'D'
|
||||
mov byte [7], 0x07
|
||||
mov byte [8], 'E'
|
||||
mov byte [9], 0x07
|
||||
mov byte [10], 'D'
|
||||
mov byte [11], 0x07
|
||||
|
||||
; Set up stack
|
||||
mov ax, 0x7000
|
||||
mov ss, ax
|
||||
mov sp, 0xFFFF
|
||||
|
||||
; Enable A20 line
|
||||
in al, 0x92
|
||||
or al, 2
|
||||
out 0x92, al
|
||||
|
||||
; Load GDT and switch to protected mode
|
||||
cli
|
||||
lgdt [GDT.pointer]
|
||||
mov eax, cr0
|
||||
or eax, 1
|
||||
mov cr0, eax
|
||||
|
||||
hlt
|
||||
|
||||
; Jump to 32-bit code
|
||||
jmp GDT.code32:protected_mode
|
||||
|
||||
[BITS 32]
|
||||
protected_mode:
|
||||
; Set up segment registers
|
||||
mov ax, GDT.data32
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
; Clear BSS section
|
||||
mov edi, bss_start
|
||||
mov ecx, bss_end
|
||||
sub ecx, edi
|
||||
xor eax, eax
|
||||
rep stosb
|
||||
|
||||
; Call C code
|
||||
call stage2_main
|
||||
|
||||
; Should never return
|
||||
cli
|
||||
hlt
|
||||
|
||||
section .rodata
|
||||
GDT:
|
||||
; Null descriptor
|
||||
dq 0
|
||||
|
||||
.code32: equ $ - GDT
|
||||
; 32-bit code segment
|
||||
dw 0xFFFF ; Limit 0:15
|
||||
dw 0x0000 ; Base 0:15
|
||||
db 0x00 ; Base 16:23
|
||||
db 0x9A ; Access byte - Code
|
||||
db 0xCF ; Flags + Limit 16:19
|
||||
db 0x00 ; Base 24:31
|
||||
|
||||
.data32: equ $ - GDT
|
||||
; 32-bit data segment
|
||||
dw 0xFFFF ; Limit 0:15
|
||||
dw 0x0000 ; Base 0:15
|
||||
db 0x00 ; Base 16:23
|
||||
db 0x92 ; Access byte - Data
|
||||
db 0xCF ; Flags + Limit 16:19
|
||||
db 0x00 ; Base 24:31
|
||||
|
||||
.pointer:
|
||||
dw $ - GDT - 1 ; Size
|
||||
dd GDT ; Address
|
||||
|
||||
extern bss_start
|
||||
extern bss_end
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "stage2.h"
|
||||
|
||||
// Constants for page tables
|
||||
#define PRESENT (1 << 0)
|
||||
#define WRITABLE (1 << 1)
|
||||
#define HUGE_PAGE (1 << 7)
|
||||
|
||||
// Page tables (must be aligned)
|
||||
__attribute__((aligned(4096))) uint64_t pml4[512];
|
||||
__attribute__((aligned(4096))) uint64_t pdpt[512];
|
||||
__attribute__((aligned(4096))) uint64_t pd[512];
|
||||
|
||||
void enable_long_mode(void) {
|
||||
// Zero out page tables
|
||||
for (int i = 0; i < 512; i++) {
|
||||
pml4[i] = 0;
|
||||
pdpt[i] = 0;
|
||||
pd[i] = 0;
|
||||
}
|
||||
|
||||
// Set up identity mapping for first 1GB
|
||||
pml4[0] = ((uint64_t)pdpt) | PRESENT | WRITABLE;
|
||||
pdpt[0] = ((uint64_t)pd) | PRESENT | WRITABLE;
|
||||
|
||||
// Map 2MB pages for first 1GB
|
||||
for (int i = 0; i < 512; i++) {
|
||||
pd[i] = (i * 0x200000) | PRESENT | WRITABLE | HUGE_PAGE;
|
||||
}
|
||||
|
||||
// Load PML4 into CR3
|
||||
__asm__ volatile (
|
||||
"mov %0, %%cr3"
|
||||
:
|
||||
: "r"(pml4)
|
||||
: "memory"
|
||||
);
|
||||
|
||||
// Enable PAE
|
||||
__asm__ volatile (
|
||||
"mov %%cr4, %%eax\n"
|
||||
"or $0x20, %%eax\n"
|
||||
"mov %%eax, %%cr4"
|
||||
:
|
||||
:
|
||||
: "eax"
|
||||
);
|
||||
|
||||
// Enable Long Mode in EFER
|
||||
__asm__ volatile (
|
||||
"mov $0xC0000080, %%ecx\n"
|
||||
"rdmsr\n"
|
||||
"or $0x100, %%eax\n"
|
||||
"wrmsr"
|
||||
:
|
||||
:
|
||||
: "eax", "ecx", "edx"
|
||||
);
|
||||
|
||||
// Enable paging
|
||||
__asm__ volatile (
|
||||
"mov %%cr0, %%eax\n"
|
||||
"or $0x80000000, %%eax\n"
|
||||
"mov %%eax, %%cr0"
|
||||
:
|
||||
:
|
||||
: "eax"
|
||||
);
|
||||
}
|
||||
|
||||
void stage2_main(void) {
|
||||
// This is where we'll start in 32-bit mode
|
||||
enable_long_mode();
|
||||
|
||||
// After long mode is enabled, we can load the kernel
|
||||
// TODO: Add FAT32 code here
|
||||
|
||||
while(1) { }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef STAGE2_H
|
||||
#define STAGE2_H
|
||||
|
||||
// Type definitions
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
// Function declarations
|
||||
void stage2_main(void);
|
||||
void enable_long_mode(void);
|
||||
|
||||
// Assembly interface functions (defined in boot.s)
|
||||
void load_gdt(void* gdt_ptr);
|
||||
void load_idt(void* idt_ptr);
|
||||
|
||||
#endif /* STAGE2_H */
|
||||
@@ -0,0 +1,37 @@
|
||||
ENTRY(_start)
|
||||
OUTPUT_FORMAT(binary)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x8000; /* Stage2 loads here */
|
||||
|
||||
.header : {
|
||||
*(.header*)
|
||||
}
|
||||
|
||||
.text : {
|
||||
*(.text.boot) /* Our assembly boot code first */
|
||||
*(.text*) /* Then other text sections */
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
*(.rodata*) /* Read-only data */
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data*) /* Writable data */
|
||||
}
|
||||
|
||||
.bss : {
|
||||
bss_start = .;
|
||||
*(.bss*) /* Zero-initialized data */
|
||||
*(COMMON)
|
||||
bss_end = .;
|
||||
}
|
||||
|
||||
._end = .; /* Mark end of binary */
|
||||
|
||||
/* Calculate total size and sectors at the end */
|
||||
STAGE2_SIZE = ABSOLUTE(._end - 0x8000);
|
||||
STAGE2_SECTORS = (STAGE2_SIZE + 511) / 512;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
extern STAGE2_SIZE
|
||||
extern STAGE2_SECTORS
|
||||
|
||||
section .header
|
||||
global _header_start
|
||||
_header_start:
|
||||
dd STAGE2_SIZE
|
||||
dw STAGE2_SECTORS
|
||||
db 0x55, 0xAA
|
||||
|
||||
section .text
|
||||
global _start
|
||||
[BITS 16]
|
||||
_start:
|
||||
mov ah, 0x00
|
||||
mov al, 0x03
|
||||
int 0x10
|
||||
|
||||
mov ah, 0x06
|
||||
mov al, 0
|
||||
mov bh, 0x07 ; normal attribute (white on black)
|
||||
mov cx, 0 ; upper left = (0,0)
|
||||
mov dx, 0x184F ; lower right = (24,79) - full screen
|
||||
int 0x10
|
||||
|
||||
jmp something
|
||||
|
||||
times 500 db 0
|
||||
|
||||
something:
|
||||
; print "loaded"
|
||||
mov ax, 0xb800
|
||||
mov ds, ax
|
||||
|
||||
mov byte [0], 'L'
|
||||
mov byte [1], 0x07
|
||||
mov byte [2], 'O'
|
||||
mov byte [3], 0x07
|
||||
mov byte [4], 'A'
|
||||
mov byte [5], 0x07
|
||||
mov byte [6], 'D'
|
||||
mov byte [7], 0x07
|
||||
mov byte [8], 'E'
|
||||
mov byte [9], 0x07
|
||||
mov byte [10], 'D'
|
||||
mov byte [11], 0x07
|
||||
hlt
|
||||
|
||||
hang:
|
||||
hang
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
# THIS IS A PLACEHOLDER FILE.
|
||||
@@ -0,0 +1 @@
|
||||
# placeholder for bios boot stage 1
|
||||
@@ -0,0 +1 @@
|
||||
# stage 2 bios boot placeholder
|
||||
@@ -0,0 +1,95 @@
|
||||
# BIOS Boot Process and Disk Layout
|
||||
|
||||
## Disk Layout
|
||||
|
||||
```
|
||||
Sector 0 (MBR):
|
||||
+-----------------------------------+ 0x000
|
||||
| |
|
||||
| Bootstrap Code |
|
||||
| (Stage 1 Bootloader) |
|
||||
| |
|
||||
| |
|
||||
+-----------------------------------+ 0x1BE (446)
|
||||
| Partition Entry 1 (16 bytes) |
|
||||
+-----------------------------------+ 0x1CE (462)
|
||||
| Partition Entry 2 (16 bytes) |
|
||||
+-----------------------------------+ 0x1DE (478)
|
||||
| Partition Entry 3 (16 bytes) |
|
||||
+-----------------------------------+ 0x1EE (494)
|
||||
| Partition Entry 4 (16 bytes) |
|
||||
+-----------------------------------+ 0x1FE (510)
|
||||
| Boot Signature (0x55AA) |
|
||||
+-----------------------------------+ 0x200 (512)
|
||||
|
||||
Sectors 1-2047:
|
||||
+-----------------------------------+ 0x200
|
||||
| |
|
||||
| Stage 2 Bootloader |
|
||||
| (Up to 1023.5 KB) |
|
||||
| |
|
||||
+-----------------------------------+ 0x100000 (Sector 2048)
|
||||
|
||||
Sector 2048 onwards:
|
||||
+-----------------------------------+ 0x100000
|
||||
| |
|
||||
| FAT32 Partition |
|
||||
| (Rest of disk) |
|
||||
| |
|
||||
+-----------------------------------+
|
||||
```
|
||||
|
||||
## Partition Entry Format (16 bytes)
|
||||
```
|
||||
Offset Size Description
|
||||
0x00 1 Boot flag (0x80 = bootable, 0x00 = non-bootable)
|
||||
0x01 3 Starting CHS address
|
||||
0x04 1 Partition type
|
||||
0x05 3 Ending CHS address
|
||||
0x08 4 Starting sector (LBA)
|
||||
0x0C 4 Number of sectors
|
||||
```
|
||||
|
||||
## Boot Process
|
||||
|
||||
1. **BIOS Power-On**
|
||||
- BIOS performs POST (Power-On Self Test)
|
||||
- Looks for bootable devices
|
||||
|
||||
2. **MBR Load**
|
||||
- BIOS loads Sector 0 (MBR) into memory at 0x7C00
|
||||
- Verifies 0x55AA signature
|
||||
- Executes Stage 1 Bootloader
|
||||
|
||||
3. **Stage 1 Bootloader**
|
||||
- Runs in 16-bit real mode
|
||||
- Limited to 446 bytes
|
||||
- Main task: Load Stage 2 Bootloader
|
||||
- Typically contains minimal disk I/O code
|
||||
|
||||
4. **Stage 2 Bootloader**
|
||||
- Located in sectors 1-2047
|
||||
- Has more space for complex operations
|
||||
- Tasks:
|
||||
- Switch to 32-bit protected mode
|
||||
- Set up basic memory management
|
||||
- Parse FAT32 filesystem
|
||||
- Switch to long mode
|
||||
- Load and execute kernel
|
||||
|
||||
5. **Kernel Load**
|
||||
- Stage 2 loads kernel from FAT32 partition
|
||||
- Sets up necessary environment
|
||||
- Transfers control to kernel
|
||||
|
||||
## Memory Layout During Boot
|
||||
```
|
||||
0x00000000 - 0x000003FF: Interrupt Vector Table
|
||||
0x00000400 - 0x000004FF: BIOS Data Area
|
||||
0x00000500 - 0x00007BFF: Free Memory
|
||||
0x00007C00 - 0x00007DFF: Stage 1 Bootloader (MBR)
|
||||
0x00007E00 - 0x0007FFFF: Free Memory (Stage 2 can be loaded here)
|
||||
0x00080000 - 0x0009FFFF: Extended BIOS Data Area
|
||||
0x000A0000 - 0x000FFFFF: BIOS ROM, Video Memory, etc.
|
||||
0x00100000 onwards: Free Memory (Kernel typically loaded here)
|
||||
```
|
||||
@@ -0,0 +1,77 @@
|
||||
# BIOS Interrupt Vector Table (IVT)
|
||||
|
||||
## Overview
|
||||
The BIOS Interrupt Vector Table (IVT) is a crucial data structure in real mode that maps interrupt numbers to their handler routines. It is located at the very beginning of memory, starting at physical address 0x0000:0x0000.
|
||||
|
||||
## Structure
|
||||
- The IVT contains 256 entries (0x00 to 0xFF)
|
||||
- Each entry is 4 bytes long:
|
||||
- 2 bytes for the offset
|
||||
- 2 bytes for the segment
|
||||
- Total size: 256 * 4 = 1024 bytes (0x400)
|
||||
|
||||
```
|
||||
Memory Layout:
|
||||
0x0000:0x0000 - Int 0x00 vector (Divide by zero)
|
||||
0x0000:0x0004 - Int 0x01 vector (Single step)
|
||||
0x0000:0x0008 - Int 0x02 vector (NMI)
|
||||
...
|
||||
0x0000:0x0040 - Int 0x10 vector (Video services)
|
||||
...
|
||||
0x0000:0x03FC - Int 0xFF vector (Last vector)
|
||||
```
|
||||
|
||||
## Common BIOS Interrupts
|
||||
- **Int 0x10**: Video Services
|
||||
- AH=0x00: Set video mode
|
||||
- AH=0x0E: Write character in TTY mode
|
||||
- AH=0x13: Write string
|
||||
|
||||
- **Int 0x13**: Disk Services
|
||||
- AH=0x00: Reset disk system
|
||||
- AH=0x02: Read sectors
|
||||
- AH=0x03: Write sectors
|
||||
- AH=0x41: Check extensions present
|
||||
- AH=0x42: Extended read sectors
|
||||
- AH=0x43: Extended write sectors
|
||||
|
||||
- **Int 0x16**: Keyboard Services
|
||||
- AH=0x00: Read keystroke
|
||||
- AH=0x01: Check for keystroke
|
||||
|
||||
## How It Works
|
||||
1. When an interrupt occurs (via hardware or `int` instruction):
|
||||
- CPU pushes FLAGS, CS, and IP onto stack
|
||||
- CPU disables further interrupts
|
||||
- CPU looks up handler address in IVT
|
||||
- CPU jumps to handler address
|
||||
|
||||
2. Example of int 0x10 call:
|
||||
```nasm
|
||||
mov ah, 0x0E ; TTY output function
|
||||
mov al, 'A' ; Character to print
|
||||
int 0x10 ; Call BIOS video interrupt
|
||||
```
|
||||
|
||||
3. The BIOS handler:
|
||||
- Receives control
|
||||
- Reads parameters from registers
|
||||
- Performs requested operation
|
||||
- Returns via IRET instruction
|
||||
|
||||
## Important Notes
|
||||
1. BIOS interrupts are only available in real mode
|
||||
2. When transitioning to protected mode:
|
||||
- BIOS interrupts become unavailable
|
||||
- Must set up new Interrupt Descriptor Table (IDT)
|
||||
- Must provide own interrupt handlers
|
||||
|
||||
3. Some BIOS operations require interrupts to be enabled:
|
||||
- Disk I/O (int 0x13)
|
||||
- Keyboard input (int 0x16)
|
||||
- Some video operations (int 0x10)
|
||||
|
||||
4. Memory Map Considerations:
|
||||
- IVT: 0x0000 - 0x03FF
|
||||
- BIOS Data Area: 0x0400 - 0x04FF
|
||||
- Your code should not overwrite these areas
|
||||
@@ -0,0 +1,164 @@
|
||||
# Boot Process and System Layout
|
||||
|
||||
## Disk Layout
|
||||
```
|
||||
FAT32 Partition
|
||||
┌─────────────────────────┐
|
||||
│ / │
|
||||
├─────────────────────────┤
|
||||
│ ├── EFI/ │
|
||||
│ │ └── BOOT/ │
|
||||
│ │ └── BOOTX64.EFI│ <- UEFI bootloader
|
||||
├─────────────────────────┤
|
||||
│ ├── boot/ │
|
||||
│ │ └── bootloader.bin │ <- BIOS bootloader
|
||||
├─────────────────────────┤
|
||||
│ └── kernel │ <- Main kernel binary
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## Kernel Binary Format
|
||||
```
|
||||
Kernel Header Structure
|
||||
┌─────────────────────────┐
|
||||
│ Magic Number (4 bytes) │ 0x00
|
||||
├─────────────────────────┤
|
||||
│ Entry Point (8 bytes) │ 0x04
|
||||
├─────────────────────────┤
|
||||
│ Stack Pointer (8 bytes) │ 0x0C
|
||||
├─────────────────────────┤
|
||||
│ Flags (4 bytes) │ 0x14
|
||||
├─────────────────────────┤
|
||||
│ Text Offset (4 bytes) │ 0x18
|
||||
├─────────────────────────┤
|
||||
│ Text Size (4 bytes) │ 0x1C
|
||||
├─────────────────────────┤
|
||||
│ Data Offset (4 bytes) │ 0x20
|
||||
├─────────────────────────┤
|
||||
│ Data Size (4 bytes) │ 0x24
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## Memory Layout (After Boot)
|
||||
```
|
||||
Virtual Memory Layout
|
||||
┌─────────────────────┐ 0xFFFFFFFF_FFFFFFFF
|
||||
│ Higher Half │
|
||||
├─────────────────────┤ 0xFFFFFFFF_FF600000
|
||||
│ Recursive │
|
||||
│ Page Mapping │
|
||||
├─────────────────────┤ 0xFFFFFFFF_C0000000
|
||||
│ Kernel Stacks │
|
||||
├─────────────────────┤
|
||||
│ Kernel Heap │
|
||||
├─────────────────────┤ 0xFFFFFFFF_80000000
|
||||
│ Kernel Code │
|
||||
├─────────────────────┤ 0x00007FFF_FFFFFFFF
|
||||
│ Guard │
|
||||
├─────────────────────┤ 0x00007FFF_00000000
|
||||
│ User Space │
|
||||
├─────────────────────┤ 0x0000000000400000
|
||||
│ Guard │
|
||||
└─────────────────────┘ 0x0000000000000000
|
||||
|
||||
Physical Memory Layout
|
||||
┌─────────────────────┐
|
||||
│ Available RAM │
|
||||
├─────────────────────┤
|
||||
│ Kernel Binary │ <- Loaded at 1MB (0x100000)
|
||||
├─────────────────────┤
|
||||
│ Reserved/BIOS │
|
||||
└─────────────────────┘ 0x00000000
|
||||
```
|
||||
|
||||
## Boot Process
|
||||
|
||||
### BIOS Boot Flow
|
||||
1. BIOS loads MBR (stage1.bin)
|
||||
2. Stage 1 bootloader:
|
||||
- Loads Stage 2 bootloader (stage2.bin) starting at sector 2048
|
||||
3. Stage 2 bootloader:
|
||||
- Switches to protected mode
|
||||
- Sets up initial page tables
|
||||
- Finds and loads kernel from FAT32
|
||||
- Enables long mode
|
||||
- Jumps to kernel entry point
|
||||
|
||||
### UEFI Boot Flow
|
||||
1. UEFI firmware loads BOOTX64.EFI
|
||||
2. UEFI bootloader:
|
||||
- Gets memory map
|
||||
- Finds and loads kernel
|
||||
- Exits boot services
|
||||
- Sets up page tables
|
||||
- Enables long mode
|
||||
- Jumps to kernel entry point
|
||||
|
||||
### Kernel Entry Point
|
||||
```rust
|
||||
extern "C" {
|
||||
fn kmain(magic: u64, boot_info: *const BootInfo) -> !;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Boot Environment
|
||||
Both bootloaders must provide:
|
||||
|
||||
### CPU State
|
||||
```
|
||||
- Long mode enabled
|
||||
- Paging enabled
|
||||
- Interrupts disabled
|
||||
- GDT set up for long mode
|
||||
- IDT not required (kernel will set up)
|
||||
```
|
||||
|
||||
### Register State
|
||||
```
|
||||
RAX = Boot magic value (e.g., 0xCAFEBABE)
|
||||
RBX = Pointer to boot info structure
|
||||
RCX = 0
|
||||
RDX = 0
|
||||
RSI = 0
|
||||
RDI = 0
|
||||
RBP = 0
|
||||
RSP = Valid stack pointer (as specified in kernel header)
|
||||
```
|
||||
|
||||
### Boot Info Structure
|
||||
```c
|
||||
struct BootInfo {
|
||||
uint64_t magic; // Boot info magic number
|
||||
uint64_t mem_map_addr; // Physical address of memory map
|
||||
uint64_t mem_map_size; // Size of memory map
|
||||
uint64_t fb_addr; // Framebuffer address (if available)
|
||||
uint32_t fb_width; // Framebuffer width
|
||||
uint32_t fb_height; // Framebuffer height
|
||||
uint32_t fb_pitch; // Framebuffer pitch
|
||||
uint8_t fb_bpp; // Bits per pixel
|
||||
uint8_t boot_type; // 0 = BIOS, 1 = UEFI
|
||||
uint8_t reserved[6]; // Padding to 64-bit align
|
||||
};
|
||||
```
|
||||
|
||||
## Required Kernel Features
|
||||
1. Position-independent code (PIC)
|
||||
2. No assumptions about physical memory layout beyond boot info
|
||||
3. Own interrupt handling
|
||||
4. Own memory management after boot
|
||||
|
||||
## Development Notes
|
||||
1. Kernel must be compiled with:
|
||||
- No red zone
|
||||
- No MMX/SSE initially
|
||||
- Position-independent code
|
||||
- No standard library dependencies
|
||||
|
||||
2. Testing can be done with:
|
||||
```bash
|
||||
# BIOS boot
|
||||
qemu-system-x86_64 disk.img
|
||||
|
||||
# UEFI boot
|
||||
qemu-system-x86_64 -bios /usr/share/OVMF/OVMF_CODE.fd disk.img
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
# x86_64 Virtual Memory Layout
|
||||
|
||||
┌─────────────────────┐ 0xFFFFFFFF_FFFFFFFF
|
||||
│ Higher Half │
|
||||
├─────────────────────┤ 0xFFFFFFFF_FF600000
|
||||
│ Recursive │ (Optional: for page table manipulation)
|
||||
│ Page Mapping │
|
||||
├─────────────────────┤ 0xFFFFFFFF_C0000000
|
||||
│ Kernel Stacks │ (Multiple 16KB or 32KB stacks, guard pages between)
|
||||
├─────────────────────┤
|
||||
│ Kernel Heap │ (Dynamic allocation for kernel)
|
||||
├─────────────────────┤ 0xFFFFFFFF_80000000
|
||||
│ Kernel Code │ (Loaded by bootloader)
|
||||
├─────────────────────┤ 0x00007FFF_FFFFFFFF
|
||||
│ Guard │ (Prevent user->kernel pointer errors)
|
||||
├─────────────────────┤ 0x00007FFF_00000000
|
||||
│ User Stacks │ (Grows down)
|
||||
├─────────────────────┤
|
||||
│ ... │ (Available for mmap, shared libraries, etc.)
|
||||
├─────────────────────┤
|
||||
│ User Heap │ (Grows up)
|
||||
├─────────────────────┤
|
||||
│ Program .bss │ (Uninitialized data)
|
||||
│ Program .data │ (Initialized data)
|
||||
│ Program .rodata │ (Read-only data)
|
||||
│ Program .text │ (Code)
|
||||
├─────────────────────┤ 0x0000000000400000
|
||||
│ Guard │ (Catch null pointer dereference)
|
||||
└─────────────────────┘ 0x0000000000000000
|
||||
|
||||
## Memory Regions Explanation
|
||||
|
||||
### Kernel Space (Higher Half)
|
||||
- **Kernel Code**: Fixed location at -2GB
|
||||
- **Kernel Heap**: Dynamic memory for kernel operations
|
||||
- **Kernel Stacks**: Multiple stacks for different CPU modes/tasks
|
||||
- **Recursive Mapping**: Optional region for page table manipulation
|
||||
|
||||
### User Space (Lower Half)
|
||||
- **Guard Pages**: Protect against null pointer dereference (0-4MB)
|
||||
- **Program Sections**: Start at 0x400000
|
||||
- .text: Program code
|
||||
- .rodata: Read-only data
|
||||
- .data: Initialized data
|
||||
- .bss: Uninitialized data
|
||||
- **User Heap**: Grows upward from end of program sections
|
||||
- **Dynamic Region**: Space for mmap allocations, shared libraries
|
||||
- **User Stacks**: Grows downward from 0x00007FFF_00000000
|
||||
|
||||
### Key Features
|
||||
- Full 48-bit addressing support (256TB virtual address space)
|
||||
- Clear separation between user and kernel space
|
||||
- Protected null pointer dereference
|
||||
- Room for stack/heap growth
|
||||
- Space for dynamic libraries and mappings
|
||||
@@ -0,0 +1,141 @@
|
||||
# UEFI Boot Process and Disk Layout
|
||||
|
||||
## Disk Layout with ESP (EFI System Partition)
|
||||
|
||||
```
|
||||
Sector 0 (MBR/GPT):
|
||||
+-----------------------------------+ 0x000
|
||||
| Protective MBR |
|
||||
| (For BIOS compatibility) |
|
||||
+-----------------------------------+ 0x200
|
||||
|
||||
EFI System Partition (FAT32):
|
||||
+-----------------------------------+ Sector 2048
|
||||
| FAT32 Filesystem |
|
||||
| /EFI/ |
|
||||
| /BOOT/ |
|
||||
| BOOTX64.EFI |
|
||||
+-----------------------------------+
|
||||
|
||||
Main Partition (FAT32):
|
||||
+-----------------------------------+
|
||||
| FAT32 Filesystem |
|
||||
| (Kernel and other files) |
|
||||
+-----------------------------------+
|
||||
```
|
||||
|
||||
## UEFI Boot Process
|
||||
|
||||
1. **System Startup**
|
||||
- Firmware initializes hardware
|
||||
- Loads UEFI runtime services
|
||||
- Sets up initial page tables and long mode
|
||||
- CPU already in 64-bit mode
|
||||
|
||||
2. **Boot Manager**
|
||||
- Scans for bootable devices
|
||||
- Looks for EFI System Partition (ESP)
|
||||
- Searches for bootloader at `/EFI/BOOT/BOOTX64.EFI`
|
||||
|
||||
3. **UEFI Bootloader**
|
||||
- Already in long mode (64-bit)
|
||||
- Has access to UEFI services:
|
||||
- File operations
|
||||
- Memory management
|
||||
- Video output
|
||||
- ACPI tables
|
||||
- Tasks:
|
||||
- Load kernel from main partition
|
||||
- Set up memory map
|
||||
- Exit boot services
|
||||
- Jump to kernel
|
||||
|
||||
4. **Kernel Load**
|
||||
- Bootloader passes:
|
||||
- Memory map
|
||||
- Framebuffer info
|
||||
- ACPI tables
|
||||
- Kernel takes control
|
||||
|
||||
## Key Differences from BIOS
|
||||
|
||||
1. **Initial State**
|
||||
- BIOS: 16-bit real mode
|
||||
- UEFI: 64-bit long mode
|
||||
|
||||
2. **Services**
|
||||
- BIOS: Limited interrupt-based services
|
||||
- UEFI: Rich API for system services
|
||||
|
||||
3. **Memory Management**
|
||||
- BIOS: Manual memory detection needed
|
||||
- UEFI: Provides memory map and allocation services
|
||||
|
||||
4. **File Access**
|
||||
- BIOS: Manual disk I/O and filesystem parsing
|
||||
- UEFI: Built-in filesystem support
|
||||
|
||||
5. **Graphics**
|
||||
- BIOS: VGA/VBE modes
|
||||
- UEFI: GOP (Graphics Output Protocol)
|
||||
|
||||
## UEFI Boot Services
|
||||
```
|
||||
Available until ExitBootServices() is called:
|
||||
- Memory allocation
|
||||
- File system access
|
||||
- Graphics output
|
||||
- Timer services
|
||||
- Environment variables
|
||||
|
||||
Runtime Services (available after boot):
|
||||
- Time services
|
||||
- Variable services
|
||||
- Reset system
|
||||
- Virtual memory services
|
||||
```
|
||||
|
||||
## Memory Map Example
|
||||
```
|
||||
Type Physical Address Range
|
||||
EfiLoaderCode 0x0000000000000000-0x0000000000100000
|
||||
EfiLoaderData 0x0000000000100000-0x0000000000200000
|
||||
EfiBootServicesCode 0x0000000000200000-0x0000000000300000
|
||||
EfiBootServicesData 0x0000000000300000-0x0000000000400000
|
||||
EfiConventionalMemory 0x0000000000400000-...
|
||||
EfiACPIReclaimMemory ...
|
||||
EfiACPIMemoryNVS ...
|
||||
EfiReservedMemoryType ...
|
||||
```
|
||||
|
||||
## Required UEFI Protocols
|
||||
```
|
||||
- EFI_LOADED_IMAGE_PROTOCOL: Access to bootloader image info
|
||||
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL: File operations
|
||||
- EFI_GRAPHICS_OUTPUT_PROTOCOL: Display output
|
||||
- EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL: Text output
|
||||
```
|
||||
|
||||
## Boot Flow Control
|
||||
```
|
||||
UEFI Firmware
|
||||
│
|
||||
▼
|
||||
Boot Manager
|
||||
│
|
||||
▼
|
||||
BOOTX64.EFI
|
||||
│
|
||||
├── Get System Info
|
||||
│ ├── Memory Map
|
||||
│ ├── ACPI Tables
|
||||
│ └── GOP Info
|
||||
│
|
||||
├── Load Kernel
|
||||
│ └── Parse FAT32
|
||||
│
|
||||
├── ExitBootServices()
|
||||
│
|
||||
└── Jump to Kernel
|
||||
└── Pass Boot Info
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
[build]
|
||||
target = "x86_64-custom.json"
|
||||
|
||||
[unstable]
|
||||
build-std = ["core", "compiler_builtins"]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
|
||||
[target.'cfg(target_os = "none")']
|
||||
runner = "bootimage runner"
|
||||
|
||||
[target.x86_64-custom]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlinker.ld",
|
||||
"-C", "code-model=kernel",
|
||||
"-C", "relocation-model=static"
|
||||
]
|
||||
Binary file not shown.
@@ -0,0 +1,55 @@
|
||||
[BITS 16]
|
||||
[ORG 0x7c00]
|
||||
start:
|
||||
mov ah,0x00 ; set mode
|
||||
mov al,0x03 ; set vga text buffer mode (80x25)
|
||||
int 0x10 ; call bios
|
||||
|
||||
cli
|
||||
|
||||
in al, 0x92
|
||||
or al, 2
|
||||
out 0x92, al
|
||||
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
|
||||
lgdt [GDT_PTR]
|
||||
|
||||
mov eax, 0x11
|
||||
mov cr0, eax
|
||||
jmp GDT_BOOT_CS-GDT:protmode
|
||||
|
||||
[BITS 32]
|
||||
protmode:
|
||||
mov ax, GDT_BOOT_DS-GDT
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
mov es, ax
|
||||
mov esp, 0x90000 ; temporary stack
|
||||
|
||||
mov edi, 0xb8000
|
||||
mov eax, "P R "
|
||||
stosd
|
||||
|
||||
hang:
|
||||
pause
|
||||
jmp hang
|
||||
|
||||
GDT_PTR:
|
||||
dw GDT_END-GDT-1
|
||||
dd GDT
|
||||
|
||||
|
||||
align 16
|
||||
GDT:
|
||||
|
||||
GDT_NULL: dq 0 ; segment zero cannot be used
|
||||
GDT_BOOT_DS: dq 0X00CF92000000FFFF
|
||||
GDT_BOOT_CS: dq 0X00CF9A000000FFFF
|
||||
|
||||
GDT_END:
|
||||
|
||||
times 510-$+start db 0;
|
||||
db 0x55;
|
||||
db 0xaa;
|
||||
@@ -0,0 +1,118 @@
|
||||
[BITS 16]
|
||||
[ORG 0x7C00]
|
||||
|
||||
; Initialize segment registers
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
mov sp, 0x7C00
|
||||
|
||||
; Reset disk system
|
||||
mov ah, 0
|
||||
int 0x13
|
||||
|
||||
; Load FAT32 BPB
|
||||
mov si, bpb_oem_name
|
||||
mov di, 0x7E00
|
||||
mov cx, bpb_size
|
||||
rep movsb
|
||||
|
||||
; Read FAT32 root directory
|
||||
mov ax, [bpb_reserved_sectors]
|
||||
mov [dapack.lba_start], ax
|
||||
|
||||
mov ah, 0x42
|
||||
mov si, dapack
|
||||
int 0x13
|
||||
jc error
|
||||
|
||||
; Find kernel file
|
||||
mov si, kernel_name
|
||||
mov di, 0x8000
|
||||
mov cx, 11 ; FAT32 8.3 filename length
|
||||
|
||||
find_kernel:
|
||||
push cx
|
||||
push si
|
||||
push di
|
||||
rep cmpsb
|
||||
pop di
|
||||
pop si
|
||||
pop cx
|
||||
je found_kernel
|
||||
|
||||
add di, 32 ; Move to next directory entry
|
||||
cmp di, 0x8200 ; Check if we've reached the end of the sector
|
||||
jl find_kernel
|
||||
|
||||
; Kernel not found
|
||||
mov si, err_no_kernel
|
||||
call print
|
||||
jmp $
|
||||
|
||||
found_kernel:
|
||||
; Get cluster number
|
||||
mov ax, [di + 20] ; High word
|
||||
shl eax, 16
|
||||
mov ax, [di + 26] ; Low word
|
||||
|
||||
; Load kernel
|
||||
mov [cluster], eax
|
||||
call load_cluster
|
||||
|
||||
; Jump to kernel
|
||||
jmp 0x8000
|
||||
|
||||
error:
|
||||
mov si, err_disk
|
||||
call print
|
||||
jmp $
|
||||
|
||||
print:
|
||||
lodsb
|
||||
or al, al
|
||||
jz .done
|
||||
mov ah, 0x0E
|
||||
int 0x10
|
||||
jmp print
|
||||
.done:
|
||||
ret
|
||||
|
||||
load_cluster:
|
||||
; Convert cluster to LBA
|
||||
sub eax, 2
|
||||
mul dword [sectors_per_cluster]
|
||||
add eax, [first_data_sector]
|
||||
|
||||
mov [dapack.lba_start], eax
|
||||
mov ah, 0x42
|
||||
mov si, dapack
|
||||
int 0x13
|
||||
ret
|
||||
|
||||
align 4
|
||||
dapack:
|
||||
db 0x10 ; Size of packet
|
||||
db 0 ; Reserved
|
||||
dw 1 ; Number of sectors
|
||||
dw 0x8000 ; Offset
|
||||
dw 0 ; Segment
|
||||
dq 0 ; LBA
|
||||
|
||||
cluster: dd 0
|
||||
sectors_per_cluster: dd 8
|
||||
first_data_sector: dd 0
|
||||
|
||||
kernel_name: db "KERNEL BIN"
|
||||
err_disk: db "Disk error", 0
|
||||
err_no_kernel: db "No kernel", 0
|
||||
|
||||
bpb_oem_name: times 8 db 0
|
||||
bpb_bytes_per_sector: dw 512
|
||||
bpb_sectors_per_cluster: db 8
|
||||
bpb_reserved_sectors: dw 32
|
||||
bpb_size: equ $ - bpb_oem_name
|
||||
|
||||
times 510-($-$$) db 0
|
||||
dw 0xAA55
|
||||
@@ -0,0 +1,83 @@
|
||||
.section .efi.text, "ax"
|
||||
.global _start
|
||||
.code64 // UEFI is already in 64-bit protected mode
|
||||
|
||||
_start:
|
||||
// Save UEFI parameters
|
||||
mov %rcx, uefi_image_handle
|
||||
mov %rdx, uefi_system_table
|
||||
|
||||
// Get memory map
|
||||
call get_memory_map
|
||||
|
||||
// Exit boot services
|
||||
mov uefi_image_handle, %rcx
|
||||
mov memory_map_key, %rdx
|
||||
call exit_boot_services
|
||||
|
||||
// Disable interrupts
|
||||
cli
|
||||
|
||||
// Load GDT for long mode
|
||||
lgdt gdt64_pointer(%rip)
|
||||
|
||||
// Setup page tables (identity map first 2MB + map kernel to higher half)
|
||||
call setup_page_tables
|
||||
|
||||
// Enable PAE
|
||||
mov %cr4, %rax
|
||||
or $(1 << 5), %rax
|
||||
mov %rax, %cr4
|
||||
|
||||
// Set long mode bit
|
||||
mov $0xC0000080, %ecx
|
||||
rdmsr
|
||||
or $(1 << 8), %eax
|
||||
wrmsr
|
||||
|
||||
// Enable paging
|
||||
mov %cr0, %rax
|
||||
or $0x80000000, %rax
|
||||
mov %rax, %cr0
|
||||
|
||||
// Long jump to higher half kernel
|
||||
lea higher_half(%rip), %rax
|
||||
jmp *%rax
|
||||
|
||||
.align 8
|
||||
higher_half:
|
||||
// Now in long mode at higher half
|
||||
// Setup stack and jump to Rust code
|
||||
mov $kernel_stack_top, %rsp
|
||||
call kmain
|
||||
|
||||
// GDT for long mode
|
||||
.align 16
|
||||
gdt64:
|
||||
.quad 0 // Null descriptor
|
||||
.quad 0x00AF9A000000FFFF // Code segment
|
||||
.quad 0x00CF92000000FFFF // Data segment
|
||||
|
||||
gdt64_pointer:
|
||||
.word gdt64_pointer - gdt64 - 1
|
||||
.quad gdt64
|
||||
|
||||
.section .efi.data, "aw"
|
||||
uefi_image_handle: .quad 0
|
||||
uefi_system_table: .quad 0
|
||||
memory_map_key: .quad 0
|
||||
|
||||
.section .bss
|
||||
.align 4096
|
||||
kernel_stack_bottom:
|
||||
.skip 16384 // 16 KB stack
|
||||
kernel_stack_top:
|
||||
|
||||
// Page tables
|
||||
.align 4096
|
||||
pml4_table:
|
||||
.skip 4096
|
||||
pdpt_table:
|
||||
.skip 4096
|
||||
pd_table:
|
||||
.skip 4096
|
||||
@@ -0,0 +1,94 @@
|
||||
.section .efi.text
|
||||
|
||||
// Function to get UEFI memory map
|
||||
get_memory_map:
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
|
||||
// Local variables for memory map
|
||||
sub $48, %rsp
|
||||
mov $0, -8(%rbp) // memory_map_size
|
||||
mov $0, -16(%rbp) // memory_map_buffer
|
||||
mov $0, -24(%rbp) // map_key
|
||||
mov $0, -32(%rbp) // descriptor_size
|
||||
mov $0, -40(%rbp) // descriptor_version
|
||||
|
||||
// First call to get size
|
||||
lea -8(%rbp), %rcx // memory_map_size
|
||||
mov $0, %rdx // memory_map
|
||||
lea -24(%rbp), %r8 // map_key
|
||||
lea -32(%rbp), %r9 // descriptor_size
|
||||
push $0 // descriptor_version
|
||||
call boot_services_get_memory_map
|
||||
|
||||
// Allocate buffer
|
||||
mov -8(%rbp), %rcx // size
|
||||
add $1000, %rcx // Add some extra space
|
||||
mov $8, %rdx // alignment
|
||||
lea -16(%rbp), %r8 // buffer pointer
|
||||
call boot_services_allocate_pool
|
||||
|
||||
// Get actual map
|
||||
mov -8(%rbp), %rcx
|
||||
mov -16(%rbp), %rdx
|
||||
lea -24(%rbp), %r8
|
||||
lea -32(%rbp), %r9
|
||||
push -40(%rbp)
|
||||
call boot_services_get_memory_map
|
||||
|
||||
// Save map key
|
||||
mov -24(%rbp), %rax
|
||||
mov %rax, memory_map_key
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
// Function to exit boot services
|
||||
exit_boot_services:
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
|
||||
// Parameters already in rcx (image_handle) and rdx (map_key)
|
||||
call boot_services_exit
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
// Function to set up page tables
|
||||
setup_page_tables:
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
|
||||
// Clear tables
|
||||
mov $pml4_table, %rdi
|
||||
mov $12288, %ecx // 3 pages (PML4, PDPT, PD)
|
||||
xor %eax, %eax
|
||||
rep stosb
|
||||
|
||||
// Set up identity mapping for first 2MB
|
||||
// PML4[0] -> PDPT
|
||||
mov $pdpt_table, %eax
|
||||
or $3, %eax // Present + Write
|
||||
mov %eax, pml4_table
|
||||
|
||||
// PDPT[0] -> PD
|
||||
mov $pd_table, %eax
|
||||
or $3, %eax
|
||||
mov %eax, pdpt_table
|
||||
|
||||
// PD[0] -> 2MB page
|
||||
mov $0x83, %eax // Present + Write + Huge
|
||||
mov %eax, pd_table
|
||||
|
||||
// Map kernel to higher half
|
||||
// PML4[511] -> PDPT
|
||||
mov $pdpt_table, %eax
|
||||
or $3, %eax
|
||||
mov %eax, pml4_table + 4088
|
||||
|
||||
// Load CR3
|
||||
mov $pml4_table, %rax
|
||||
mov %rax, %cr3
|
||||
|
||||
leave
|
||||
ret
|
||||
@@ -0,0 +1,140 @@
|
||||
/* Tell the linker that we want an x86_64 ELF64 output file */
|
||||
OUTPUT_FORMAT(elf64-x86-64)
|
||||
OUTPUT_ARCH(i386:x86-64)
|
||||
|
||||
/* Entry points */
|
||||
ENTRY(start)
|
||||
|
||||
/* Define the program headers we want in our final kernel executable */
|
||||
PHDRS
|
||||
{
|
||||
boot PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */
|
||||
text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */
|
||||
rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */
|
||||
data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */
|
||||
}
|
||||
|
||||
/* The kernel is linked to run at -2GB (KERNEL_BASE) */
|
||||
KERNEL_BASE = 0xFFFFFFFF80000000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* For BIOS boot, start at 1MB physical */
|
||||
. = 1M;
|
||||
|
||||
.boot : {
|
||||
/* BIOS boot sections */
|
||||
KEEP(*(.multiboot)) /* Multiboot header */
|
||||
*(.boottext) /* BIOS boot code */
|
||||
*(.boottext.*)
|
||||
|
||||
/* UEFI boot sections */
|
||||
KEEP(*(.pe_header)) /* PE/COFF header for UEFI */
|
||||
*(.efi.header) /* EFI header */
|
||||
. = ALIGN(4K);
|
||||
*(.efi.text) /* UEFI boot code */
|
||||
*(.efi.text.*)
|
||||
*(.efi.data) /* UEFI data */
|
||||
*(.efi.data.*)
|
||||
} :boot
|
||||
|
||||
/* Switch to higher half for kernel proper */
|
||||
. += KERNEL_BASE;
|
||||
|
||||
/* Code section */
|
||||
.text : AT(ADDR(.text) - KERNEL_BASE) {
|
||||
_text_start = .;
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
. = ALIGN(4K);
|
||||
_text_end = .;
|
||||
} :text
|
||||
|
||||
/* Read-only data */
|
||||
.rodata : AT(ADDR(.rodata) - KERNEL_BASE) {
|
||||
_rodata_start = .;
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
. = ALIGN(4K);
|
||||
_rodata_end = .;
|
||||
} :rodata
|
||||
|
||||
/* Read-write data (initialized) */
|
||||
.data : AT(ADDR(.data) - KERNEL_BASE) {
|
||||
_data_start = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.got)
|
||||
*(.got.*)
|
||||
. = ALIGN(4K);
|
||||
_data_end = .;
|
||||
} :data
|
||||
|
||||
/* Read-write data (uninitialized) */
|
||||
.bss : AT(ADDR(.bss) - KERNEL_BASE) {
|
||||
_bss_start = .;
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
. = ALIGN(4K);
|
||||
_bss_end = .;
|
||||
} :data
|
||||
|
||||
/* Page-aligned data structures */
|
||||
.padata ALIGN(4K) : AT(ADDR(.padata) - KERNEL_BASE) {
|
||||
*(.padata)
|
||||
} :data
|
||||
|
||||
/* Initial stack space */
|
||||
.stack ALIGN(4K) : AT(ADDR(.stack) - KERNEL_BASE) {
|
||||
*(.stack)
|
||||
} :data
|
||||
|
||||
/* Thread-local storage */
|
||||
.tdata ALIGN(4K) : AT(ADDR(.tdata) - KERNEL_BASE) {
|
||||
_tdata_start = .;
|
||||
*(.tdata)
|
||||
*(.tdata.*)
|
||||
. = ALIGN(4K);
|
||||
_tdata_end = .;
|
||||
} :data
|
||||
|
||||
.tbss ALIGN(4K) : AT(ADDR(.tbss) - KERNEL_BASE) {
|
||||
_tbss_start = .;
|
||||
*(.tbss)
|
||||
*(.tbss.*)
|
||||
. = ALIGN(4K);
|
||||
_tbss_end = .;
|
||||
} :data
|
||||
|
||||
/* Debugging information */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
|
||||
/* Discard unused sections */
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
*(.note.*)
|
||||
*(.comment)
|
||||
}
|
||||
}
|
||||
|
||||
/* Symbols needed by both BIOS and UEFI boot code */
|
||||
PROVIDE(_kernel_start = ADDR(.boot));
|
||||
PROVIDE(_kernel_end = ADDR(.tbss) + SIZEOF(.tbss));
|
||||
PROVIDE(_kernel_physical_end = ADDR(.tbss) - KERNEL_BASE + SIZEOF(.tbss));
|
||||
PROVIDE(_kernel_virtual_base = KERNEL_BASE);
|
||||
|
||||
/* UEFI-specific symbols */
|
||||
PROVIDE(_efi_start = ADDR(.boot));
|
||||
PROVIDE(_efi_text_start = ADDR(.efi.text));
|
||||
PROVIDE(_efi_text_end = ADDR(.efi.text) + SIZEOF(.efi.text));
|
||||
|
||||
/* BIOS-specific symbols */
|
||||
PROVIDE(_multiboot_start = ADDR(.multiboot));
|
||||
PROVIDE(_boottext_start = ADDR(.boottext));
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Build the bootloader
|
||||
nasm -f bin mbr.asm -o mbr.bin
|
||||
|
||||
# Build the kernel
|
||||
cargo build --release
|
||||
|
||||
# Create disk image
|
||||
./create_image.sh
|
||||
|
||||
# Write MBR
|
||||
dd if=mbr.bin of=disk.img conv=notrunc
|
||||
|
||||
echo "Build complete! You can now boot disk.img"
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Image configuration
|
||||
IMAGE_NAME="disk.img"
|
||||
IMAGE_SIZE_MB=64
|
||||
ESP_SIZE_MB=32 # EFI System Partition size
|
||||
|
||||
# Create a new disk image
|
||||
dd if=/dev/zero of=$IMAGE_NAME bs=1M count=$IMAGE_SIZE_MB
|
||||
|
||||
# Create partition table and ESP partition
|
||||
parted $IMAGE_NAME mklabel gpt
|
||||
parted $IMAGE_NAME mkpart ESP fat32 1MiB ${ESP_SIZE_MB}MiB
|
||||
parted $IMAGE_NAME set 1 esp on
|
||||
|
||||
# Calculate offset for mounting
|
||||
SECTOR_SIZE=512
|
||||
START_SECTOR=$(parted $IMAGE_NAME -ms unit s print | grep "^1:" | cut -d: -f2 | tr -d 's')
|
||||
OFFSET=$((START_SECTOR * SECTOR_SIZE))
|
||||
|
||||
# Create FAT32 filesystem
|
||||
LOOP_DEVICE=$(sudo losetup -f --show -o $OFFSET $IMAGE_NAME)
|
||||
sudo mkfs.fat -F 32 $LOOP_DEVICE
|
||||
|
||||
# Mount the image
|
||||
MOUNT_POINT="/tmp/esp"
|
||||
mkdir -p $MOUNT_POINT
|
||||
sudo mount $LOOP_DEVICE $MOUNT_POINT
|
||||
|
||||
# Create EFI directory structure
|
||||
sudo mkdir -p $MOUNT_POINT/EFI/BOOT
|
||||
|
||||
# Copy kernel to the ESP
|
||||
# For UEFI boot, rename it to BOOTX64.EFI
|
||||
sudo cp target/x86_64-custom/release/kernel $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
|
||||
|
||||
# For BIOS boot, also copy it to the root
|
||||
sudo cp target/x86_64-custom/release/kernel $MOUNT_POINT/
|
||||
|
||||
# Cleanup
|
||||
sudo umount $MOUNT_POINT
|
||||
sudo losetup -d $LOOP_DEVICE
|
||||
rm -rf $MOUNT_POINT
|
||||
|
||||
echo "Disk image created successfully!"
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-none",
|
||||
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
|
||||
"arch": "x86_64",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "64",
|
||||
"target-c-int-width": "32",
|
||||
"os": "none",
|
||||
"executables": true,
|
||||
"linker-flavor": "ld.lld",
|
||||
"linker": "rust-lld",
|
||||
"panic-strategy": "abort",
|
||||
"disable-redzone": true,
|
||||
"features": "-mmx,-sse,+soft-float",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [
|
||||
"--gc-sections"
|
||||
]
|
||||
}
|
||||
}
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Set root directory
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Create build directory if it doesn't exist
|
||||
mkdir -p build/bios
|
||||
|
||||
# Assemble stage1
|
||||
nasm -f elf32 boot/bios/stage1/stage1.s -o build/bios/stage1.o
|
||||
|
||||
# Link to binary
|
||||
ld -m elf_i386 -T boot/bios/stage1/stage1.ld build/bios/stage1.o -o build/bios/stage1.bin
|
||||
|
||||
# Verify size
|
||||
size=$(wc -c < build/bios/stage1.bin)
|
||||
if [ "$size" -gt 446 ]; then
|
||||
echo "Error: stage1.bin is larger than 446 bytes ($size bytes)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Successfully built stage1.bin ($size bytes)"
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Set root directory
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Create build directory if it doesn't exist
|
||||
mkdir -p build/bios
|
||||
|
||||
# Compile C code
|
||||
gcc -m32 -ffreestanding -fno-pie -fno-stack-protector -nostdlib -nodefaultlibs \
|
||||
-Wall -Wextra -O2 -c boot/bios/stage2/stage2.c -o build/bios/stage2.o
|
||||
|
||||
# Assemble boot code
|
||||
nasm -f elf32 boot/bios/stage2/boot.s -o build/bios/boot.o
|
||||
|
||||
# Link to binary
|
||||
ld -m elf_i386 -T boot/bios/stage2/stage2.ld \
|
||||
build/bios/boot.o \
|
||||
build/bios/stage2.o \
|
||||
-o build/bios/stage2.bin
|
||||
|
||||
# Verify size (stage2 can be multiple sectors, but let's warn if it's too large)
|
||||
size=$(wc -c < build/bios/stage2.bin)
|
||||
if [ "$size" -gt 32768 ]; then # 64 sectors (32KB) should be plenty
|
||||
echo "Warning: stage2.bin is larger than 32KB ($size bytes)"
|
||||
fi
|
||||
|
||||
echo "Successfully built stage2.bin ($size bytes)"
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Size in MB
|
||||
DISK_SIZE=64
|
||||
ESP_SIZE=100 # Size of EFI System Partition in sectors (100 sectors = ~50KB, enough for our bootloader)
|
||||
|
||||
# Create build directory if it doesn't exist
|
||||
mkdir -p build/bios
|
||||
|
||||
# Build stage 1/2 bootloader
|
||||
./scripts/build_stage1.sh
|
||||
./scripts/build_stage2.sh
|
||||
|
||||
# Create an empty disk image
|
||||
dd if=/dev/zero of=disk.img bs=1M count=$DISK_SIZE
|
||||
|
||||
# Create a partition table
|
||||
(
|
||||
echo o # Create a new empty DOS partition table
|
||||
echo n # Add a new partition (EFI System Partition)
|
||||
echo p # Primary partition
|
||||
echo 1 # Partition number 1
|
||||
echo 2048 # First sector (leaving room for bootloader)
|
||||
echo +${ESP_SIZE} # Size in sectors
|
||||
echo t # Change partition type
|
||||
echo ef # EFI System Partition type
|
||||
|
||||
echo n # Add a new partition (Main partition)
|
||||
echo p # Primary partition
|
||||
echo 2 # Partition number 2
|
||||
echo # Default first sector (right after ESP)
|
||||
echo # Use rest of disk
|
||||
echo t # Change partition type
|
||||
echo 2 # Select second partition
|
||||
echo 0c # FAT32 LBA type
|
||||
echo a # Make bootable
|
||||
echo 2 # Select second partition
|
||||
echo w # Write changes
|
||||
) | fdisk disk.img > /dev/null 2>&1
|
||||
|
||||
# Copy bootloader to disk image
|
||||
# First stage (MBR)
|
||||
dd if=build/bios/stage1.bin of=disk.img conv=notrunc bs=446 count=1
|
||||
|
||||
# Second stage (right after MBR)
|
||||
dd if=build/bios/stage2.bin of=disk.img conv=notrunc bs=512 seek=1
|
||||
|
||||
# Format ESP partition
|
||||
ESP_OFFSET=$((2048 * 512)) # First partition starts at sector 2048
|
||||
mformat -i disk.img@@${ESP_OFFSET} -F -h 255 -t 1 -s 63 -c 1 ::
|
||||
|
||||
# Format main partition
|
||||
MAIN_OFFSET=$(( (2048 + ESP_SIZE) * 512 )) # Second partition starts after ESP
|
||||
mformat -i disk.img@@${MAIN_OFFSET} -F -h 255 -t 1 -s 63 -c 1 ::
|
||||
|
||||
# Create EFI directory structure in ESP
|
||||
mmd -i disk.img@@${ESP_OFFSET} ::/EFI
|
||||
mmd -i disk.img@@${ESP_OFFSET} ::/EFI/BOOT
|
||||
|
||||
# When you have the UEFI bootloader ready, uncomment this:
|
||||
# mcopy -i disk.img@@${ESP_OFFSET} boot/uefi/BOOTX64.EFI ::/EFI/BOOT/
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# set root directory of project
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Build the bootloader
|
||||
./scripts/create_disk.sh
|
||||
|
||||
# Default to KVM if available
|
||||
KVM_FLAGS=""
|
||||
if [ -c /dev/kvm ]; then
|
||||
KVM_FLAGS="-enable-kvm -cpu host"
|
||||
fi
|
||||
|
||||
# Run QEMU with appropriate flags
|
||||
qemu-system-x86_64 \
|
||||
$KVM_FLAGS \
|
||||
-m 4G \
|
||||
-drive file=disk.img,format=raw \
|
||||
-monitor stdio \
|
||||
-no-reboot \
|
||||
-no-shutdown \
|
||||
-smp 4 \
|
||||
-serial file:serial.log
|
||||
Reference in New Issue
Block a user