Initial commit
This commit is contained in:
commit
6e5222e2e3
80
Makefile
Normal file
80
Makefile
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
GDB=gdb
|
||||||
|
|
||||||
|
ifeq ($(shell uname -s),Darwin)
|
||||||
|
AS=x86_64-elf-as
|
||||||
|
LD=x86_64-elf-ld
|
||||||
|
CC=x86_64-elf-gcc
|
||||||
|
GDB=x86_64-elf-gdb
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \
|
||||||
|
-Wall -ggdb -m32 -Werror -fno-omit-frame-pointer
|
||||||
|
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
|
||||||
|
|
||||||
|
run: image.bin
|
||||||
|
qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio
|
||||||
|
|
||||||
|
run-nox: image.bin
|
||||||
|
qemu-system-i386 -nographic -drive format=raw,file=$< -serial mon:stdio
|
||||||
|
|
||||||
|
debug-boot-nox: image.bin mbr.elf
|
||||||
|
qemu-system-i386 -nographic -drive format=raw,file=$< -s -S &
|
||||||
|
$(GDB) mbr.elf \
|
||||||
|
-ex "set architecture i8086" \
|
||||||
|
-ex "target remote localhost:1234" \
|
||||||
|
-ex "break *0x7c00" \
|
||||||
|
-ex "continue"
|
||||||
|
|
||||||
|
debug-boot: image.bin mbr.elf
|
||||||
|
qemu-system-i386 -drive format=raw,file=$< -s -S &
|
||||||
|
$(GDB) mbr.elf \
|
||||||
|
-ex "set architecture i8086" \
|
||||||
|
-ex "target remote localhost:1234" \
|
||||||
|
-ex "break *0x7c00" \
|
||||||
|
-ex "continue"
|
||||||
|
|
||||||
|
debug: image.bin
|
||||||
|
qemu-system-i386 -drive format=raw,file=$< -s -S &
|
||||||
|
$(GDB) kernel.bin \
|
||||||
|
-ex "target remote localhost:1234" \
|
||||||
|
-ex "break _start" \
|
||||||
|
-ex "continue"
|
||||||
|
|
||||||
|
debug-nox: image.bin
|
||||||
|
qemu-system-i386 -nographic -drive format=raw,file=$< -s -S &
|
||||||
|
$(GDB) kernel.bin \
|
||||||
|
-ex "target remote localhost:1234" \
|
||||||
|
-ex "break _start" \
|
||||||
|
-ex "continue"
|
||||||
|
|
||||||
|
fs.img: kernel.bin tools/mkfs
|
||||||
|
tools/mkfs $@ $<
|
||||||
|
|
||||||
|
LDFLAGS=-m elf_i386
|
||||||
|
|
||||||
|
user/%: user/%.o user/crt.o
|
||||||
|
$(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^
|
||||||
|
|
||||||
|
image.bin: mbr.bin fs.img
|
||||||
|
cat $^ >$@
|
||||||
|
|
||||||
|
kernel.bin: kernel.o console.o drivers/vga.o drivers/uart.o
|
||||||
|
$(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%.o: %.S
|
||||||
|
$(CC) -m32 -ffreestanding -c -g $^ -o $@
|
||||||
|
|
||||||
|
mbr.bin: mbr.o
|
||||||
|
$(LD) -m elf_i386 -Ttext=0x7c00 --oformat=binary $^ -o $@
|
||||||
|
|
||||||
|
mbr.elf: mbr.o
|
||||||
|
$(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.elf *.img *.bin *.o */*.o tools/mkfs
|
||||||
|
|
||||||
|
tools/%: tools/%.c
|
||||||
|
gcc -Wall -Werror -g $^ -o $@
|
||||||
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Yet Another BootLoader, OS Kernel and Other stuff
|
||||||
|
|
||||||
|
Quickstart:
|
||||||
|
```
|
||||||
|
$ ./setup.sh
|
||||||
|
$ make
|
||||||
|
```
|
||||||
|
|
||||||
|
Includes code from:
|
||||||
|
* https://github.com/mit-pdos/xv6-public
|
||||||
|
* https://github.com/FRosner/FrOS
|
||||||
|
* https://github.com/dhavalhirdhav/LearnOS
|
||||||
|
* https://wiki.osdev.org
|
||||||
19
console.c
Normal file
19
console.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "console.h"
|
||||||
|
#include "drivers/vga.h"
|
||||||
|
#include "drivers/uart.h"
|
||||||
|
|
||||||
|
void printk(const char* msg) {
|
||||||
|
vga_print_string_noscroll(msg);
|
||||||
|
for (; *msg; ++msg) {
|
||||||
|
uartputc(*msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void panic(const char* msg) {
|
||||||
|
printk("\nKernel panic: ");
|
||||||
|
printk(msg);
|
||||||
|
asm("cli");
|
||||||
|
while (1) {
|
||||||
|
asm("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
4
console.h
Normal file
4
console.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void printk(const char* msg);
|
||||||
|
void panic(const char* msg);
|
||||||
77
cpu/gdt.h
Normal file
77
cpu/gdt.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define STA_X 0x8 // Executable segment
|
||||||
|
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||||
|
#define STA_R 0x2 // Readable (executable segments)
|
||||||
|
|
||||||
|
// System segment type bits
|
||||||
|
#define STS_T32A 0x9 // Available 32-bit TSS
|
||||||
|
#define STS_IG32 0xE // 32-bit Interrupt Gate
|
||||||
|
#define STS_TG32 0xF // 32-bit Trap Gate
|
||||||
|
|
||||||
|
#define DPL_USER 3
|
||||||
|
|
||||||
|
#define FL_IF 0x00000200
|
||||||
|
|
||||||
|
#define SEG_KCODE 1
|
||||||
|
#define SEG_KDATA 2
|
||||||
|
#define SEG_UCODE 3
|
||||||
|
#define SEG_UDATA 4
|
||||||
|
#define SEG_TSS 5
|
||||||
|
|
||||||
|
#define SEG_ASM(type,base,lim) \
|
||||||
|
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||||
|
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||||
|
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||||
|
|
||||||
|
#define USER_BASE 0x400000 // 4 MB
|
||||||
|
#define USER_STACK_BASE 0xf00000 // 15 MB
|
||||||
|
#define KERN_STACK_BASE 0x90000
|
||||||
|
|
||||||
|
#ifndef __ASSEMBLER__
|
||||||
|
typedef unsigned uint;
|
||||||
|
typedef unsigned short ushort;
|
||||||
|
|
||||||
|
struct taskstate {
|
||||||
|
uint link; // Old ts selector
|
||||||
|
uint esp0; // Stack pointers and segment selectors
|
||||||
|
ushort ss0; // after an increase in privilege level
|
||||||
|
ushort padding1;
|
||||||
|
uint *esp1;
|
||||||
|
ushort ss1;
|
||||||
|
ushort padding2;
|
||||||
|
uint *esp2;
|
||||||
|
ushort ss2;
|
||||||
|
ushort padding3;
|
||||||
|
void *cr3; // Page directory base
|
||||||
|
uint *eip; // Saved state from last task switch
|
||||||
|
uint eflags;
|
||||||
|
uint eax; // More saved state (registers)
|
||||||
|
uint ecx;
|
||||||
|
uint edx;
|
||||||
|
uint ebx;
|
||||||
|
uint *esp;
|
||||||
|
uint *ebp;
|
||||||
|
uint esi;
|
||||||
|
uint edi;
|
||||||
|
ushort es; // Even more saved state (segment selectors)
|
||||||
|
ushort padding4;
|
||||||
|
ushort cs;
|
||||||
|
ushort padding5;
|
||||||
|
ushort ss;
|
||||||
|
ushort padding6;
|
||||||
|
ushort ds;
|
||||||
|
ushort padding7;
|
||||||
|
ushort fs;
|
||||||
|
ushort padding8;
|
||||||
|
ushort gs;
|
||||||
|
ushort padding9;
|
||||||
|
ushort ldt;
|
||||||
|
ushort padding10;
|
||||||
|
ushort t; // Trap on task switch
|
||||||
|
ushort iomb; // I/O map base address
|
||||||
|
};
|
||||||
|
|
||||||
|
void load_gdt();
|
||||||
|
void switchuvm(struct taskstate *tss, void* esp);
|
||||||
|
#endif
|
||||||
25
drivers/port.h
Normal file
25
drivers/port.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
static inline unsigned char port_byte_in(unsigned short port) {
|
||||||
|
unsigned char result;
|
||||||
|
__asm__("in %%dx, %%al" : "=a" (result) : "d" (port));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned short port_word_in(unsigned short port) {
|
||||||
|
unsigned short result;
|
||||||
|
__asm__("in %%dx, %%ax" : "=a" (result) : "d" (port));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void port_byte_out(unsigned short port, unsigned char data) {
|
||||||
|
__asm__("outb %%al, %%dx" : : "a" (data), "d" (port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void port_word_out(unsigned short port, unsigned short data) {
|
||||||
|
__asm__("outw %%ax, %%dx" : : "a" (data), "d" (port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void port_long_out(unsigned short port, unsigned int data) {
|
||||||
|
__asm__("outl %%eax, %%dx" : : "a" (data), "d" (port));
|
||||||
|
}
|
||||||
44
drivers/uart.c
Normal file
44
drivers/uart.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "uart.h"
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
static int uart;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
COM1 = 0x3f8,
|
||||||
|
};
|
||||||
|
|
||||||
|
void uartinit() {
|
||||||
|
// Turn off the FIFO
|
||||||
|
port_byte_out(COM1+2, 0);
|
||||||
|
|
||||||
|
// 9600 baud, 8 data bits, 1 stop bit, parity off.
|
||||||
|
port_byte_out(COM1+3, 0x80); // Unlock divisor
|
||||||
|
port_byte_out(COM1+0, 115200/9600);
|
||||||
|
port_byte_out(COM1+1, 0);
|
||||||
|
port_byte_out(COM1+3, 0x03); // Lock divisor, 8 data bits.
|
||||||
|
port_byte_out(COM1+4, 0);
|
||||||
|
port_byte_out(COM1+1, 0x01); // Enable receive interrupts.
|
||||||
|
|
||||||
|
// If status is 0xFF, no serial port.
|
||||||
|
if(port_byte_in(COM1+5) == 0xFF)
|
||||||
|
return;
|
||||||
|
uart = 1;
|
||||||
|
|
||||||
|
// Acknowledge pre-existing interrupt conditions;
|
||||||
|
// enable interrupts.
|
||||||
|
port_byte_in(COM1+2);
|
||||||
|
port_byte_in(COM1+0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
uartputc(char c)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!uart)
|
||||||
|
return;
|
||||||
|
for (i = 0; i < 128 && !(port_byte_in(COM1+5) & 0x20); i++) {
|
||||||
|
asm("pause");
|
||||||
|
}
|
||||||
|
port_byte_out(COM1+0, c);
|
||||||
|
}
|
||||||
4
drivers/uart.h
Normal file
4
drivers/uart.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void uartputc(char c);
|
||||||
|
void uartinit();
|
||||||
48
drivers/vga.c
Normal file
48
drivers/vga.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "port.h"
|
||||||
|
#include "vga.h"
|
||||||
|
|
||||||
|
static unsigned char get_color(unsigned char fg, unsigned char bg) {
|
||||||
|
return (bg << 4) + fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned get_offset(unsigned col, unsigned row) {
|
||||||
|
return row * COLS + col;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned get_row_from_offset(unsigned offset) {
|
||||||
|
return offset / COLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned vga_get_cursor() {
|
||||||
|
port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH);
|
||||||
|
unsigned offset = port_byte_in(VGA_DATA_REGISTER) << 8;
|
||||||
|
port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_LOW);
|
||||||
|
offset += port_byte_in(VGA_DATA_REGISTER);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vga_set_char(unsigned offset, char c) {
|
||||||
|
video_memory[2 * offset] = c;
|
||||||
|
video_memory[2 * offset + 1] = get_color(light_gray, black);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned offset;
|
||||||
|
|
||||||
|
void vga_clear_screen() {
|
||||||
|
for (unsigned i = 0; i < ROWS * COLS; ++i) {
|
||||||
|
vga_set_char(i, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vga_print_string_noscroll(const char* s) {
|
||||||
|
while (*s != 0) {
|
||||||
|
if (*s == '\n') {
|
||||||
|
offset = get_offset(0, get_row_from_offset(offset) + 1);
|
||||||
|
} else {
|
||||||
|
vga_set_char(offset, *s);
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
offset %= COLS * ROWS;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
drivers/vga.h
Normal file
38
drivers/vga.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
static char* const video_memory = (char*) 0xb8000;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ROWS = 25,
|
||||||
|
COLS = 80,
|
||||||
|
|
||||||
|
VGA_CTRL_REGISTER = 0x3d4,
|
||||||
|
VGA_DATA_REGISTER = 0x3d5,
|
||||||
|
VGA_OFFSET_LOW = 0x0f,
|
||||||
|
VGA_OFFSET_HIGH = 0x0e,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum colors16 {
|
||||||
|
black = 0,
|
||||||
|
blue,
|
||||||
|
green,
|
||||||
|
cyan,
|
||||||
|
red,
|
||||||
|
magenta,
|
||||||
|
brown,
|
||||||
|
light_gray,
|
||||||
|
dark_gray,
|
||||||
|
light_blue,
|
||||||
|
light_green,
|
||||||
|
light_cyan,
|
||||||
|
light_red,
|
||||||
|
light_magenta,
|
||||||
|
yellow,
|
||||||
|
white,
|
||||||
|
};
|
||||||
|
|
||||||
|
void vga_clear_screen();
|
||||||
|
|
||||||
|
void vga_set_char(unsigned offset, char c);
|
||||||
|
void vga_print_string(const char* s);
|
||||||
|
void vga_print_string_noscroll(const char* s);
|
||||||
51
fs/fs.h
Normal file
51
fs/fs.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Directory structure:
|
||||||
|
|
||||||
|
32-byte entries
|
||||||
|
┌───────────────────────────────┐
|
||||||
|
│Reserved │
|
||||||
|
│char[32] │
|
||||||
|
├──────┬──────┬────────┬────────┤
|
||||||
|
│Offset│Size │Reserved│Name │
|
||||||
|
│uint32│uint32│uint32 │char[20]│
|
||||||
|
├──────┼──────┼────────┼────────┤
|
||||||
|
│ ... │ │ │ │
|
||||||
|
|
||||||
|
Offset is in sectors (zero-based),
|
||||||
|
size is in bytes, name is 0-terminated.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
sector_size = 512,
|
||||||
|
ents_in_dir = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dirent {
|
||||||
|
uint32_t offset_sectors;
|
||||||
|
uint32_t size_bytes;
|
||||||
|
uint32_t reserved;
|
||||||
|
char name[20];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dir {
|
||||||
|
char reserved[32];
|
||||||
|
struct dirent entries[ents_in_dir];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stat {
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t reserved[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Find file by name and fill information in buf.
|
||||||
|
* Returns zero on success, nonzero on failure. */
|
||||||
|
int stat(const char* name, struct stat *buf);
|
||||||
|
|
||||||
|
/* Find file by name and read it into buffer with size bufsize.
|
||||||
|
* At most (bufsize & ~511) bytes will be read.
|
||||||
|
* Return number of bytes read or -1 on failure. */
|
||||||
|
int read_file(const char* name, void* buf, uint32_t bufsize);
|
||||||
12
kernel.c
Normal file
12
kernel.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "console.h"
|
||||||
|
#include "drivers/vga.h"
|
||||||
|
#include "drivers/uart.h"
|
||||||
|
|
||||||
|
|
||||||
|
void _start() {
|
||||||
|
uartinit();
|
||||||
|
|
||||||
|
vga_clear_screen();
|
||||||
|
printk("\nYABLOKO\n");
|
||||||
|
asm("hlt");
|
||||||
|
}
|
||||||
178
mbr.S
Normal file
178
mbr.S
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
#include "cpu/gdt.h"
|
||||||
|
|
||||||
|
.code16
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
mov %dl, boot_drive
|
||||||
|
mov $banner, %si
|
||||||
|
call print_string
|
||||||
|
|
||||||
|
call get_drive_geometry
|
||||||
|
call load_kernel
|
||||||
|
call switch_to_32bit
|
||||||
|
|
||||||
|
hlt
|
||||||
|
jmp . // loop forever
|
||||||
|
|
||||||
|
get_drive_geometry:
|
||||||
|
mov $8, %ah
|
||||||
|
mov boot_drive, %dl
|
||||||
|
int $0x13
|
||||||
|
inc %dh // number of heads
|
||||||
|
mov %dh, disk_heads
|
||||||
|
and 0x3f, %cl
|
||||||
|
mov %cl, sectors_per_track
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
.equ ELF32_ENTRY_OFFSET, 0x18
|
||||||
|
.equ ELF32_PHDR_OFFSET, 0x1c
|
||||||
|
.equ ELF32_PHENTSIZE_OFFSET, ELF32_PHDR_OFFSET + 14
|
||||||
|
.equ ELF32_PHNUM_OFFSET, ELF32_PHENTSIZE_OFFSET + 2
|
||||||
|
.equ ELF32_PHDR_P_OFFSET, 4
|
||||||
|
.equ ELF32_PHDR_PTYPE_OFFSET, 0
|
||||||
|
.equ ELF32_PHDR_FILESZ_OFFSET, 4*4
|
||||||
|
.equ KERNEL_OFFSET, 0x1000
|
||||||
|
|
||||||
|
.equ PT_LOAD, 1
|
||||||
|
|
||||||
|
.equ MBR_SECTORS, 2
|
||||||
|
.equ SECTOR_BASE, 1
|
||||||
|
.equ ELFHDR_SECTORS, 8
|
||||||
|
|
||||||
|
.equ SECTOR_SIZE, 512
|
||||||
|
.equ SECTOR_SHIFT, 9
|
||||||
|
|
||||||
|
load_kernel:
|
||||||
|
mov $1, %al // sectors to read
|
||||||
|
mov $SECTOR_BASE + MBR_SECTORS, %cl // start after MBR
|
||||||
|
call bios_disk_read
|
||||||
|
|
||||||
|
mov KERNEL_OFFSET + ELF32_ENTRY_OFFSET, %si
|
||||||
|
mov %si, entry // store entry point
|
||||||
|
|
||||||
|
mov KERNEL_OFFSET + ELF32_PHNUM_OFFSET, %si
|
||||||
|
read_segment:
|
||||||
|
dec %si // no offset to the first entry
|
||||||
|
mov %si, %ax
|
||||||
|
mulb KERNEL_OFFSET + ELF32_PHENTSIZE_OFFSET
|
||||||
|
mov %ax, %di
|
||||||
|
add KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di
|
||||||
|
// now di holds offset to the phentry
|
||||||
|
mov KERNEL_OFFSET + ELF32_PHDR_PTYPE_OFFSET(%di), %ax
|
||||||
|
cmp $PT_LOAD, %ax
|
||||||
|
jnz read_segment // not a PT_LOAD segment
|
||||||
|
mov KERNEL_OFFSET + ELF32_PHDR_FILESZ_OFFSET(%di), %ax
|
||||||
|
test %ax, %ax
|
||||||
|
jz read_segment // empty segment
|
||||||
|
|
||||||
|
// now di holds offset to the last phentry loaded from file, ax its filesz
|
||||||
|
|
||||||
|
add KERNEL_OFFSET + ELF32_PHDR_P_OFFSET(%di), %ax
|
||||||
|
sub $0x1000, %ax // we won't load the header
|
||||||
|
add $SECTOR_SIZE - 1, %ax
|
||||||
|
shr $SECTOR_SHIFT, %ax // round up to sector count
|
||||||
|
|
||||||
|
mov $SECTOR_BASE + MBR_SECTORS + ELFHDR_SECTORS, %cl //start after ELF header
|
||||||
|
call bios_disk_read
|
||||||
|
ret
|
||||||
|
|
||||||
|
bios_disk_read:
|
||||||
|
// expects %al to specify number of sectors, %cl the initial sector
|
||||||
|
xor %ah, %ah
|
||||||
|
mov %ax, %si
|
||||||
|
mov $0, %ch // cylinder 0
|
||||||
|
mov $0, %dh // head 0
|
||||||
|
mov $KERNEL_OFFSET, %bx // bx -> destination
|
||||||
|
mov boot_drive, %dl // dl -> disk
|
||||||
|
mov $1, %al
|
||||||
|
|
||||||
|
1:
|
||||||
|
mov $2, %ah // read mode
|
||||||
|
int $0x13
|
||||||
|
jc fail
|
||||||
|
add $SECTOR_SIZE, %bx
|
||||||
|
inc %cl
|
||||||
|
dec %si
|
||||||
|
jnz 1b
|
||||||
|
ret
|
||||||
|
|
||||||
|
fail:
|
||||||
|
mov $read_error, %si
|
||||||
|
call print_string
|
||||||
|
hlt
|
||||||
|
jmp .
|
||||||
|
|
||||||
|
switch_to_32bit:
|
||||||
|
mov $2, %al
|
||||||
|
out %al, $0x92 // enable A20
|
||||||
|
|
||||||
|
cli // 1. disable interrupts
|
||||||
|
lgdt gdt_descriptor // 2. load GDT descriptor
|
||||||
|
mov %cr0, %eax
|
||||||
|
or $1, %eax // 3. enable protected mode
|
||||||
|
mov %eax, %cr0
|
||||||
|
ljmp $SEG_KCODE << 3, $init_32bit // 4. far jump
|
||||||
|
|
||||||
|
|
||||||
|
.code32
|
||||||
|
init_32bit:
|
||||||
|
mov $SEG_KDATA << 3, %ax // 5. update segment registers
|
||||||
|
mov %ax, %ds
|
||||||
|
mov %ax, %ss
|
||||||
|
mov %ax, %es
|
||||||
|
mov %ax, %fs
|
||||||
|
mov %ax, %gs
|
||||||
|
|
||||||
|
mov $KERN_STACK_BASE, %ebp // 6. setup stack
|
||||||
|
mov %ebp, %esp
|
||||||
|
|
||||||
|
movzwl entry, %esi
|
||||||
|
call *%esi // 7. jump to the kernel
|
||||||
|
jmp . // 8. loop forever
|
||||||
|
|
||||||
|
|
||||||
|
.code16
|
||||||
|
print_string:
|
||||||
|
mov $0x0e, %ah // "teletype output"
|
||||||
|
repeat:
|
||||||
|
lodsb // equivalent to mov (%si), %al; inc %si
|
||||||
|
|
||||||
|
test %al, %al
|
||||||
|
je done
|
||||||
|
|
||||||
|
int $0x10 // bios interrupt
|
||||||
|
jmp repeat
|
||||||
|
done:
|
||||||
|
ret
|
||||||
|
|
||||||
|
. = _start + 256 # pad to 256 bytes
|
||||||
|
boot_drive:
|
||||||
|
.byte 0
|
||||||
|
banner:
|
||||||
|
.asciz "YABLOKO bootloader started\n\r"
|
||||||
|
read_error:
|
||||||
|
.asciz "Read error\n\r"
|
||||||
|
|
||||||
|
.balign 2
|
||||||
|
entry:
|
||||||
|
.word 0
|
||||||
|
disk_heads:
|
||||||
|
.byte 0
|
||||||
|
sectors_per_track:
|
||||||
|
.byte 0
|
||||||
|
|
||||||
|
.balign 4
|
||||||
|
gdt_start:
|
||||||
|
.quad 0x0 // null descriptor
|
||||||
|
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
||||||
|
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
|
||||||
|
gdt_end:
|
||||||
|
|
||||||
|
// GDT descriptor
|
||||||
|
gdt_descriptor:
|
||||||
|
.word gdt_end - gdt_start - 1 // size (16 bit)
|
||||||
|
.int gdt_start // address (32 bit)
|
||||||
|
|
||||||
|
. = _start + 510 # pad to 510 bytes
|
||||||
|
.byte 0x55, 0xaa # boot sector magic value
|
||||||
10
setup.sh
Executable file
10
setup.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh -x
|
||||||
|
|
||||||
|
if [ `uname` = Darwin ]; then
|
||||||
|
brew install \
|
||||||
|
x86_64-elf-binutils x86_64-elf-gcc \
|
||||||
|
x86_64-elf-gdb qemu
|
||||||
|
elif [ `uname` = Linux ]; then
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install qemu-system-x86
|
||||||
|
fi
|
||||||
79
tools/mkfs.c
Normal file
79
tools/mkfs.c
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "../fs/fs.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
char* basename(char* path) {
|
||||||
|
char* c = strrchr(path, '/');
|
||||||
|
if (c && *c) {
|
||||||
|
return c + 1;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
char sector[sector_size];
|
||||||
|
struct dir dir = {{0}};
|
||||||
|
|
||||||
|
if (argc < 3) {
|
||||||
|
fprintf(stderr, "Usage: %s OUT.FS KERNEL.BIN [FILES...]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* image = fopen(argv[1], "wb");
|
||||||
|
if (!image) {
|
||||||
|
perror(argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(&dir, sizeof(dir), 1, image) < 1) {
|
||||||
|
perror("fwrite");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
uint32_t sector_offset = 1;
|
||||||
|
|
||||||
|
for (int i = 2; i < argc; ++i) {
|
||||||
|
char* name = argv[i];
|
||||||
|
struct dirent *dirent = &dir.entries[i-2];
|
||||||
|
dirent->offset_sectors = sector_offset;
|
||||||
|
dirent->size_bytes = 0;
|
||||||
|
|
||||||
|
FILE* file = fopen(name, "rb");
|
||||||
|
if (!file) {
|
||||||
|
perror(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read_size;
|
||||||
|
while ((read_size = fread(sector, 1, sizeof(sector), file))) {
|
||||||
|
if (fwrite(sector, 1, sizeof(sector), image) != sizeof(sector)) {
|
||||||
|
perror(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
sector_offset++;
|
||||||
|
dirent->size_bytes += read_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fclose(file)) {
|
||||||
|
perror(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirent->reserved = 0;
|
||||||
|
dirent->name[sizeof(dirent->name) - 1] = '\0';
|
||||||
|
strncpy(dirent->name, basename(name), sizeof(dirent->name) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(image, 0, SEEK_SET);
|
||||||
|
if (fwrite(&dir, sizeof(dir), 1, image) < 1) {
|
||||||
|
perror("fwrite");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fclose(image)) {
|
||||||
|
perror(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user