diff options
author | Marvin Borner | 2020-09-12 13:54:11 +0200 |
---|---|---|
committer | Marvin Borner | 2020-09-12 13:54:11 +0200 |
commit | 9df62e1f972784d87d01baa0ad950c005f41061a (patch) | |
tree | 86f6cbef89399d56cd4165ef8a6bebd18a00d7f7 /boot | |
parent | 27337731330ec60e2f7f4abdcd4ed5ef7b8b4882 (diff) |
Independent ext2 bootloader for bigger kernels
Diffstat (limited to 'boot')
-rw-r--r-- | boot/Makefile | 18 | ||||
-rw-r--r-- | boot/entry.asm | 436 | ||||
-rw-r--r-- | boot/load.c | 392 |
3 files changed, 846 insertions, 0 deletions
diff --git a/boot/Makefile b/boot/Makefile new file mode 100644 index 0000000..44743d7 --- /dev/null +++ b/boot/Makefile @@ -0,0 +1,18 @@ +# MIT License, Copyright (c) 2020 Marvin Borner + +CC = ../cross/opt/bin/i686-elf-gcc +LD = ../cross/opt/bin/i686-elf-ld +OC = ../cross/opt/bin/i686-elf-objcopy +AS = nasm + +CFLAGS = -Wall -Wextra -nostdlib -nostdinc -ffreestanding -ffunction-sections -fno-builtin -std=c99 -m32 -pedantic-errors + +ASFLAGS = -f elf32 + +all: compile + +compile: + @mkdir -p ../build/ + @$(CC) -c $(CFLAGS) load.c -o load.o + @$(LD) -N -emain -Ttext 0x00040000 -o ../build/load.bin load.o --oformat binary + @$(AS) -f bin entry.asm -o ../build/boot.bin diff --git a/boot/entry.asm b/boot/entry.asm new file mode 100644 index 0000000..c6b36f0 --- /dev/null +++ b/boot/entry.asm @@ -0,0 +1,436 @@ +; Melvin's awesome ext2 bootloader +; I'm not really good at assembly, there are MANY ways to improve this! +; MIT License, Copyright (c) 2020 Marvin Borner + +; Definitions + +; General configurations +; TODO: Find out why 2560x1600 doesn't work +%define VIDEO_WIDTH 1920 +%define VIDEO_HEIGHT 1200 +%define VIDEO_BPP 4 + +; Boot constants +%define LOCATION 0x7c00 ; Bootloader location +%define SECTOR_END 0xaa55 ; Bootsector end signature +%define SECTOR_SIZE 510 ; 512 bytes minus signature + +; Interrupts +%define VIDEO_INT 0x10 ; Video BIOS Interrupt +%define DISK_INT 0x13 ; Disk BIOS Interrupt +%define MISC_INT 0x15 ; Miscellaneous services BIOS Interrupt + +; Characters +%define NEWLINE 0x0A ; Newline character (\n) +%define RETURN 0x0D ; Return character (\r) +%define NULL 0x00 ; NULL character (\0) + +; Video commands (VGA) +%define VIDEO_CLEAR 0x03 ; Clear screen command +%define VIDEO_OUT 0x0e ; Teletype output command + +; Disk commands +%define DISK_EXT_CHECK 0x41 ; Disk extension check command +%define DISK_EXT_CHECK_SIG1 0x55aa ; First extension check signature +%define DISK_EXT_CHECK_SIG2 0xaa55 ; Second extension check signature +%define DISK_ZERO 0x80 ; First disk - TODO: Disk detection +%define DISK_READ 0x42 ; Disk extended read command + +; EXT2 constants +%define EXT2_SB_SIZE 0x400 ; Superblock size +%define EXT2_SIG_OFFSET 0x38 ; Signature offset in superblock +%define EXT2_TABLE_OFFSET 0x08 ; Inode table offset after superblock +%define EXT2_INODE_TABLE_LOC 0x1000 ; New inode table location in memory +%define EXT2_KERNEL_INODE 0x05 ; Kernel inode +%define EXT2_INODE_SIZE 0x80 ; Single inode size +%define EXT2_GET_ADDRESS(inode) (EXT2_INODE_TABLE_LOC + (inode - 1) * EXT2_INODE_SIZE) +%define EXT2_COUNT_OFFSET 0x1c ; Inode offset of number of data blocks +%define EXT2_POINTER_OFFSET 0x28 ; Inode offset of first data pointer +%define EXT2_IND_POINTER_OFFSET 0x2c ; Inode offset of singly indirect data pointer +%define EXT2_DIRECT_POINTER_COUNT 0x0c ; Direct pointer count +%define EXT2_SIG 0xef53 ; Signature + +; Video constants (VESA) +%define VESA_START 0x2000 ; Struct starts at 0x2000 +%define VESA_END 0x3000 ; Struct ends at 0x3000 +%define VESA_GET_MODES 0x4f00 ; Get video modes (via 10h) +%define VESA_GET_INFO 0x4f01 ; Get video mode info (via 10h) +%define VESA_SET_MODE 0x4f02 ; Set video mode (via 10h) +%define VESA_SUCCESS_SIG 0x004f ; Returns if VBE call succeeded +%define VESA_MODE_OFFSET 0xe ; Offset to mode pointer +%define VESA_MODE_SEGMENT 0x10 ; Mode pointer segment +%define VESA_LIST_END 0xffff ; End of mode list +%define VESA_PITCH_OFFSET 0x10 ; Pitch offset in mode info +%define VESA_WIDTH_OFFSET 0x12 ; Width offset in mode info +%define VESA_HEIGHT_OFFSET 0x14 ; Height offset in mode info +%define VESA_BPP_OFFSET 0x19 ; Bytes Per Pixel (BPP) offset in mode info +%define VESA_FRAMEBUFFER_OFFSET 0x2a ; Framebuffer offset in mode info +%define VESA_LFB_FLAG 0x4000 ; Enable LFB flag + +; A20 constants +%define A20_GATE 0x92 ; Fast A20 gate +%define A20_ENABLED 0b10 ; Bit 1 defines whether A20 is enabled +%define A20_EXCLUDE_BIT 0xfe ; Bit 0 may be write-only, causing a crash + +; GDT constants (bitmap) +%define GDT_MAX_LIMIT 0xffff ; I just use the max limit lel +%define GDT_PRESENT 0b10000000 ; Is present +%define GDT_RING3 0b01100000 ; Privilege level 3 +%define GDT_DESCRIPTOR 0b00010000 ; Descriptor type, set for code/data +%define GDT_EXECUTABLE 0b00001000 ; Can be executed +%define GDT_READWRITE 0b00000010 ; Read/write access for code/data +%define GDT_ACCESSED 0b00000001 ; Whether segment is accessed +%define GDT_GRANULARITY (0x80 | 0x00) ; Page granularity (4KiB) +%define GDT_SIZE (0x40 | 0x00) ; Use 32 bit selectors +%define GDT_DATA_OFFSET 0x10 ; Offset to GDT data segment + +; Kernel constants +%define STACK_POINTER 0x00900000 ; The initial stack pointer in kernel mode +%define KERNEL_POSITION 0x00040000 ; Loaded kernel position in protected mode (* 0x10) + +; ENOUGH, let's go! + +bits 16 +org LOCATION + +; This is the first stage. It prints some things, checks some things +; and jumps to the second stage. Nothing special. +global _start +_start: + ; Clear screen + mov ax, VIDEO_CLEAR + int VIDEO_INT + + ; Check LBA support + mov ah, DISK_EXT_CHECK + mov bx, DISK_EXT_CHECK_SIG1 + int DISK_INT + jc lba_error + cmp bx, DISK_EXT_CHECK_SIG2 + jnz lba_error + + ; Check disk and move dl + and dl, DISK_ZERO ; Use disk 0 + jz disk_error + mov [drive], dl + + ; Load stage two + mov bx, stage_two + mov [dest], bx + call disk_read + + ; JUMP + jmp stage_two + +print: + push bx + push ax + mov ah, VIDEO_OUT + xor bh, bh + print_ch: + lodsb + test al, al + jz print_end + int VIDEO_INT + jmp print_ch + print_end: + pop ax + pop bx + ret + +disk_read: + mov si, packet ; Address of dap + mov ah, DISK_READ ; Extended read + mov dl, [drive] ; Drive number + int DISK_INT + jc disk_error + ret + +; Errors +disk_error: + mov si, disk_error_msg + call print + jmp $ +lba_error: + mov si, lba_error_msg + call print + jmp $ + +; Now put some data and routines just before the end of the boot sector because +; we've still got some space left :) + +; Video map routine +video_map: + mov bx, VESA_START ; Set load address + mov di, bx + mov ax, VESA_GET_MODES ; Get video modes + int VIDEO_INT ; Ask BIOS for data! + + cmp ax, VESA_SUCCESS_SIG ; Check VBE support in response + jne .error ; Not supported :( + + mov si, [bx + VESA_MODE_OFFSET] ; Mode pointer offset + mov ax, [bx + VESA_MODE_SEGMENT] ; Mode pointer segment + mov es, ax + + mov di, VESA_END ; End of VBE struct +.loop: + mov bx, [es:si] ; Load bx with video mode + cmp bx, VESA_LIST_END ; Is this the end? + jae .done ; Yes, there aren't any modes left + + add si, 2 + mov [.mode], bx + + mov ax, VESA_GET_INFO ; Get mode information + mov cx, [.mode] ; Save in here + int VIDEO_INT ; BIOS interrupt! + cmp ax, VESA_SUCCESS_SIG ; Check if call succeeded + jne .error ; Nope, jump to error! + + mov ax, [es:di + VESA_FRAMEBUFFER_OFFSET] ; Save framebuffer + mov [.framebuffer], ax ; Move fb address to struct + + mov ax, [es:di + VESA_PITCH_OFFSET] ; Save pitch + mov bx, [es:di + VESA_WIDTH_OFFSET] ; Save width + mov cx, [es:di + VESA_HEIGHT_OFFSET] ; Save height + mov dx, [es:di + VESA_BPP_OFFSET] ; Save BPP + + mov [.bpp], dx ; Move bpp to struct (bigger bpp is always desired) + add di, 0x100 + + cmp ax, [.pitch] ; Compare with desired pitch + jne .loop ; Not equal, continue search! + cmp bx, [.width] ; Compare with desired height + jne .loop ; Not equal, continue search! + cmp cx, [.height] ; Compare with desired height + jne .loop ; Not equal, continue search! + + lea ax, [es:di - 0x100] + mov [vid_info.array], ax +.set_mode: + mov ax, VESA_SET_MODE ; Set VBE mode + mov bx, [.mode] ; Set mode address + mov [vid_info], bx ; Move mode information to array + or bx, VESA_LFB_FLAG ; Enable LFB + int VIDEO_INT ; SET! + cmp ax, VESA_SUCCESS_SIG ; Check if set succeeded + jne .error ; Nope, jump to error! +.done: + ret ; Finished loop and set! +.error: ; Something failed - print message and loop! + mov si, video_error_msg + call print + jmp $ + +; Video default data +.mode dw 0 +.width dw VIDEO_WIDTH +.height dw VIDEO_HEIGHT +.pitch dw (VIDEO_WIDTH * VIDEO_BPP) +.bpp dw VIDEO_BPP +.framebuffer dd 0 + +; Variables +disk_error_msg db "Disk error!", NEWLINE, RETURN, NULL +lba_error_msg db "LBA error!", NEWLINE, RETURN, NULL +video_error_msg db "Video error!", NEWLINE, RETURN, NULL +drive db 0 + +; Video info struct +vid_info: +.mode dd 0 ; Mode info pointer +.array dd 0 ; Mode array pointer + +; Data +packet: + db 0x10 ; Packet size + db 0 ; Always 0 +count: + dw 4 ; Number of sectors to transfer +dest: + dw 0 ; Destination offset + dw 0 ; Destination segment +lba: + dd 1 ; LBA number + dd 0 ; More storage bytes + +; End of boot sector +times SECTOR_SIZE - ($ - $$) db 0 +dw SECTOR_END + +; This is the second stage. It tries to load the kernel (inode 5) into memory. +; To do this, it first checks the integrity of the ext2 fs. Then it has to find +; the address of the fifth inode and load its contents into memory. +; After this is finished, the stage can jump into the protected mode, enable the +; A20 line and finally jump to the kernel! ez +stage_two: + ; Verify signature + mov ax, [superblock + EXT2_SIG_OFFSET] + cmp ax, EXT2_SIG + jne disk_error + + ; Load inode table + mov ax, [superblock + EXT2_SB_SIZE + EXT2_TABLE_OFFSET] ; Inode table + shl ax, 1 ; Multiply ax by 2 + mov [lba], ax ; Sector + mov ax, 2 + mov [count], ax ; Read 1024 bytes + mov bx, EXT2_INODE_TABLE_LOC ; Copy data to 0x1000 + mov [dest], bx + call disk_read + + ; Load kernel + mov bx, EXT2_GET_ADDRESS(EXT2_KERNEL_INODE) ; First block + mov cx, [bx + EXT2_COUNT_OFFSET] ; Number of blocks for inode + lea di, [bx + EXT2_POINTER_OFFSET] ; Address of first block pointer + mov bx, 0x4000 ; Load to this address + mov [dest + 2], bx + mov bx, 0 ; Inode location = 0xF0000 + mov [dest], bx + call kernel_load + + ; Set video mode + call video_map + + jmp protected_mode_enter + +kernel_load: + mov ax, [di] ; Set ax = block pointer + shl ax, 1 ; Multiply ax by 2 + mov [lba], ax + mov [dest], bx + call disk_read + jmp .end + +.end: + add bx, 0x400 ; 1kb increase + add di, 0x4 ; Move to next block pointer + sub cx, 0x2 ; Read 2 blocks + jnz kernel_load + ret + +protected_mode_enter: + cli ; Turn off interrupts + + ; TODO: Check A20 support? + ; TODO: 0x92 method may not work on every device + in al, A20_GATE + test al, A20_ENABLED + jnz .a20_enabled + or al, A20_ENABLED + and al, A20_EXCLUDE_BIT + out A20_GATE, al + .a20_enabled: + + lgdt [gdt_desc] ; Load GDT + + ; Set protected mode via cr0 + mov eax, cr0 + or eax, 1 ; Set bit 0 + mov cr0, eax + + jmp (gdt_code - gdt):protected_mode ; JUMP! + +bits 32 ; Woah, so big! +protected_mode: + mov ax, GDT_DATA_OFFSET ; Data segment offset of GDT + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax ; Stack segment + + mov esp, STACK_POINTER ; Move stack pointer + + mov ax, (gdt_tss - gdt) | 0b11 ; Load TSS in ring 3 + ltr ax + + mov eax, vid_info ; Pass VBE struct to kernel + push eax ; Push as second kernel parameter + + mov edx, KERNEL_POSITION + lea eax, [edx] + call eax + +; GDT +align 32 +gdt: ; GDTs start +gdt_null: ; Must be null + dd 0 + dd 0 +gdt_code: ; Code segment + dw GDT_MAX_LIMIT ; Limit + dw 0 ; First base + db 0 ; Second base + db (GDT_PRESENT | GDT_DESCRIPTOR | GDT_EXECUTABLE | GDT_READWRITE) ; Configuration + db (GDT_GRANULARITY | GDT_SIZE) ; Flags + db 0 ; Third base +gdt_data: ; Data segment + dw GDT_MAX_LIMIT ; Limit + dw 0 ; First base + db 0 ; Second base + db (GDT_PRESENT | GDT_DESCRIPTOR | GDT_READWRITE) ; Configuration + db (GDT_GRANULARITY | GDT_SIZE) ; Flags + db 0 ; Third base +gdt_user_code: ; User code segment + dw GDT_MAX_LIMIT ; Limit + dw 0 ; First base + db 0 ; Second base + db (GDT_PRESENT | GDT_RING3 | GDT_DESCRIPTOR | GDT_EXECUTABLE | GDT_READWRITE) ; Configuration + db (GDT_GRANULARITY | GDT_SIZE) ; Flags + db 0 ; Third base +gdt_user_data: ; Data segment + dw GDT_MAX_LIMIT ; Limit + dw 0 ; First base + db 0 ; Second base + db (GDT_PRESENT | GDT_RING3 | GDT_DESCRIPTOR | GDT_READWRITE) ; Configuration + db (GDT_GRANULARITY | GDT_SIZE) ; Flags + db 0 ; Third base +gdt_tss: ; TSS segment + dw tss_entry + (tss_entry_end - tss_entry) ; Limit + dw tss_entry ; First base + db 0 ; Second base + db (GDT_PRESENT | GDT_RING3 | GDT_EXECUTABLE | GDT_ACCESSED) ; Configuration + db GDT_SIZE ; Flags + db 0 ; Third base +gdt_end: +gdt_desc: + dw gdt_end - gdt - 1 + dd gdt + +; TSS +tss_entry: + dd 0 ; Previous TSS + dd STACK_POINTER ; esp0 + dd gdt_data - gdt ; ss0 (data offset) + dd 0 ; esp1 + dd 0 ; ss1 + dd 0 ; esp2 + dd 0 ; ss2 + dd 0 ; cr3 + dd 0 ; eip + dd 0 ; eflags + dd 0 ; eax + dd 0 ; ecx + dd 0 ; edx + dd 0 ; ebx + dd 0 ; esp + dd 0 ; ebp + dd 0 ; esi + dd 0 ; edi + dd 0 ; es + dd 0 ; cs + dd 0 ; ss + dd 0 ; ds + dd 0 ; fs + dd 0 ; gs + dd 0 ; ldt + dw 0 ; trap + dw 0 ; iomap base +tss_entry_end: + +times 1024 - ($ - $$) db 0 + +; Start at LBA 2 +superblock: diff --git a/boot/load.c b/boot/load.c new file mode 100644 index 0000000..19ddce9 --- /dev/null +++ b/boot/load.c @@ -0,0 +1,392 @@ +// MIT License, Copyright (c) 2020 Marvin Borner +// Independent ext2 loader + +typedef signed char s8; +typedef unsigned char u8; + +typedef signed short s16; +typedef unsigned short u16; + +typedef signed long s32; +typedef unsigned long u32; + +typedef signed long long s64; +typedef unsigned long long u64; + +#define BLOCK_SIZE 1024 +#define BLOCK_COUNT 256 // BLOCK_SIZE / sizeof(u32) +#define SECTOR_SIZE 512 +#define IDE_BUSY (1 << 7) +#define IDE_READY (1 << 6) +#define IDE_DRIVE_FAULT (1 << 5) +#define IDE_ERROR (1 << 0) +#define IDE_IO 0x1F0 +#define IDE_DATA 0x0 +#define IDE_FEATURES 0x1 +#define IDE_SECTOR_COUNT 0x2 +#define IDE_LOW 0x3 +#define IDE_MID 0x4 +#define IDE_HIGH 0x5 +#define IDE_HEAD 0x6 +#define IDE_CMD 0x7 +#define IDE_ALTERNATE 0x3F6 +#define LBA_LOW(c) ((u8)(c & 0xFF)) +#define LBA_MID(c) ((u8)(c >> 8) & 0xFF) +#define LBA_HIGH(c) ((u8)(c >> 16) & 0xFF) +#define LBA_LAST(c) ((u8)(c >> 24) & 0xF) +#define IDE_CMD_READ (BLOCK_SIZE / SECTOR_SIZE == 1) ? 0x20 : 0xC4 +#define IDE_CMD_WRITE (BLOCK_SIZE / SECTOR_SIZE == 1) ? 0x30 : 0xC5 +#define IDE_CMD_READ_MUL 0xC4 +#define IDE_CMD_WRITE_MUL 0xC5 + +#define EXT2_BOOT 0 +#define EXT2_SUPER 1 +#define EXT2_ROOT 2 +#define EXT2_MAGIC 0x0000EF53 + +struct superblock { + u32 total_inodes; + u32 total_blocks; + u32 su_res_blocks; // Superuser reserved + u32 free_blocks; + u32 free_inodes; + u32 superblock_block_num; + u32 log2_block_size; + u32 log2_frag_size; + u32 blocks_per_group; + u32 frags_per_group; + u32 inodes_per_group; + u32 last_mount_time; + u32 last_write_time; + u16 mounts_since_fsck; + u16 max_mounts_since_fsck; + u16 magic; + u16 state; // 1 clean; 2 errors + u16 error_action; + u16 minor_version; + u32 last_fsck_time; + u32 max_time_since_fsck; + u32 creator_os_id; + u32 major_version; + u16 res_block_uid; + u16 res_block_gid; +}; + +struct bgd { + u32 block_bitmap; + u32 inode_bitmap; + u32 inode_table; + u16 free_blocks; + u16 free_inodes; + u16 used_dirs; + u16 pad; + u8 bg_reserved[12]; +}; + +struct inode { + u16 mode; + u16 uid; + u32 size; + + u32 last_access_time; + u32 creation_time; + u32 last_modification_time; + u32 deletion_time; + + u16 gid; + u16 link_count; + u32 blocks; + u32 flags; + u32 os_specific_val1; + u32 block[15]; + u32 generation; + + u32 reserved1; + u32 reserved2; + + u32 fragment_addr; + u8 os_specific_val2[12]; +}; + +#define INODE_SIZE (sizeof(struct inode)) + +struct dirent { + u32 inode_num; + u16 total_len; + u8 name_len; + u8 type_indicator; + u8 name[]; +}; + +struct file { + struct inode inode; + u32 pos; + u8 block_index; + u8 *buf; + u32 curr_block_pos; +}; + +static u32 heap; +void *read_inode(struct inode *in); +struct inode *get_inode(int i); +int find_inode(const char *name, int dir_inode); +void serial_install(); +void serial_print(const char *data); +int main() +{ + serial_install(); + heap = 0xf000; + void (*entry)(); + *(void **)(&entry) = read_inode(get_inode(find_inode("kernel.bin", 2))); + if (entry) { + serial_print("Loaded kernel!\n"); + entry(); + } + return 0; +} + +u8 inb(u16 port) +{ + u8 value; + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +void insl(u16 port, void *addr, int n) +{ + __asm__ volatile("cld; rep insl" + : "=D"(addr), "=c"(n) + : "d"(port), "0"(addr), "1"(n) + : "memory", "cc"); +} + +void outb(u16 port, u8 data) +{ + __asm__ volatile("outb %0, %1" ::"a"(data), "Nd"(port)); +} + +void *memcpy(void *dst, const void *src, u32 n) +{ + const char *sp = (const char *)src; + char *dp = (char *)dst; + for (; n != 0; n--) + *dp++ = *sp++; + return dst; +} + +void *memset(void *dst, char val, u32 n) +{ + char *temp = (char *)dst; + for (; n != 0; n--) + *temp++ = val; + return dst; +} + +int strncmp(const char *s1, const char *s2, u32 n) +{ + const u8 *c1 = (const u8 *)s1; + const u8 *c2 = (const u8 *)s2; + u8 ch; + int d = 0; + + while (n--) { + d = (int)(ch = *c1++) - (int)*c2++; + if (d || !ch) + break; + } + + return d; +} + +u32 strlen(const char *s) +{ + const char *ss = s; + while (*ss) + ss++; + return ss - s; +} + +void serial_install() +{ + outb(0x3f8 + 1, 0x00); + outb(0x3f8 + 3, 0x80); + outb(0x3f8 + 0, 0x03); + outb(0x3f8 + 1, 0x00); + outb(0x3f8 + 3, 0x03); + outb(0x3f8 + 2, 0xC7); + outb(0x3f8 + 4, 0x0B); +} + +int is_transmit_empty() +{ + return inb(0x3f8 + 5) & 0x20; +} + +void serial_put(char ch) +{ + while (is_transmit_empty() == 0) + ; + outb(0x3f8, (u8)ch); +} + +void serial_print(const char *data) +{ + for (u32 i = 0; i < strlen(data); i++) + serial_put(data[i]); +} + +void *malloc(u32 size) +{ + return (u32 *)(heap += size); +} + +int ide_wait(int check) +{ + char r; + + // Wait while drive is busy. Once just ready is set, exit the loop + while (((r = (char)inb(IDE_IO | IDE_CMD)) & (IDE_BUSY | IDE_READY)) != IDE_READY) + ; + + // Check for errors + if (check && (r & (IDE_DRIVE_FAULT | IDE_ERROR)) != 0) + return 0xF; + return 0; +} + +void *ide_read(void *b, u32 block) +{ + int sector_per_block = BLOCK_SIZE / SECTOR_SIZE; // 2 + int sector = block * sector_per_block; + + ide_wait(0); + outb(IDE_IO | IDE_SECTOR_COUNT, sector_per_block); // Number of sectors + outb(IDE_IO | IDE_LOW, LBA_LOW(sector)); + outb(IDE_IO | IDE_MID, LBA_MID(sector)); + outb(IDE_IO | IDE_HIGH, LBA_HIGH(sector)); + + // Slave/Master << 4 and last 4 bits + outb(IDE_IO | IDE_HEAD, 0xE0 | (1 << 4) | LBA_LAST(sector)); + outb(IDE_IO | IDE_CMD, IDE_CMD_READ); + ide_wait(0); + + // Read-only + insl(IDE_IO, b, BLOCK_SIZE / 4); + + return b; +} + +void *buffer_read(int block) +{ + return ide_read(malloc(BLOCK_SIZE), block); +} + +struct superblock *get_superblock() +{ + struct superblock *sb = buffer_read(EXT2_SUPER); + if (sb->magic != EXT2_MAGIC) + return 0; + return sb; +} + +struct bgd *get_bgd() +{ + return buffer_read(EXT2_SUPER + 1); +} + +struct inode *get_inode(int i) +{ + struct superblock *s = get_superblock(); + //assert(s); + struct bgd *b = get_bgd(); + //assert(b); + + int block_group = (i - 1) / s->inodes_per_group; + int index = (i - 1) % s->inodes_per_group; + int block = (index * INODE_SIZE) / BLOCK_SIZE; + b += block_group; + + u32 *data = buffer_read(b->inode_table + block); + struct inode *in = + (struct inode *)((u32)data + (index % (BLOCK_SIZE / INODE_SIZE)) * INODE_SIZE); + return in; +} + +u32 read_indirect(u32 indirect, u32 block_num) +{ + char *data = buffer_read(indirect); + return *(u32 *)((u32)data + block_num * sizeof(u32)); +} + +void *read_inode(struct inode *in) +{ + //assert(in); + if (!in) + return 0; + + int num_blocks = in->blocks / (BLOCK_SIZE / SECTOR_SIZE); + + //assert(num_blocks != 0); + if (!num_blocks) + return 0; + + /* u32 sz = BLOCK_SIZE * num_blocks; */ + /* void *buf = malloc(sz); */ + void *buf = (void *)0x50000; + //assert(buf != 0); + + int indirect; + + int blocknum; + char *data; + for (int i = 0; i < num_blocks; i++) { + if (i < 12) { + blocknum = in->block[i]; + data = buffer_read(blocknum); + memcpy((u32 *)((u32)buf + i * BLOCK_SIZE), data, BLOCK_SIZE); + } else if (i < BLOCK_COUNT + 12) { + indirect = in->block[12]; + blocknum = read_indirect(indirect, i - 12); + data = buffer_read(blocknum); + memcpy((u32 *)((u32)buf + (i - 1) * BLOCK_SIZE), data, BLOCK_SIZE); + } else { + indirect = in->block[13]; + blocknum = read_indirect(indirect, (i - (BLOCK_COUNT + 12)) / BLOCK_COUNT); + blocknum = read_indirect(blocknum, (i - (BLOCK_COUNT + 12)) % BLOCK_COUNT); + data = buffer_read(blocknum); + memcpy((u32 *)((u32)buf + (i - 1) * BLOCK_SIZE), data, BLOCK_SIZE); + } + } + + return buf; +} + +int find_inode(const char *name, int dir_inode) +{ + if (!dir_inode) + return -1; + + struct inode *i = get_inode(dir_inode); + + char *buf = malloc(BLOCK_SIZE * i->blocks / 2); + memset(buf, 0, BLOCK_SIZE * i->blocks / 2); + + for (u32 q = 0; q < i->blocks / 2; q++) { + char *data = buffer_read(i->block[q]); + memcpy((u32 *)((u32)buf + q * BLOCK_SIZE), data, BLOCK_SIZE); + } + + struct dirent *d = (struct dirent *)buf; + + u32 sum = 0; + do { + // Calculate the 4byte aligned size of each entry + sum += d->total_len; + if (strncmp((void *)d->name, name, d->name_len) == 0) { + return d->inode_num; + } + d = (struct dirent *)((u32)d + d->total_len); + + } while (sum < (1024 * i->blocks / 2)); + return -1; +} |