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