Changed to nasm
This commit is contained in:
commit
a2a4a155c2
17
include/gdt.h
Normal file
17
include/gdt.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef GDT_H
|
||||||
|
#define GDT_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
struct GDT {
|
||||||
|
uint32_t limit;
|
||||||
|
uint32_t base;
|
||||||
|
uint8_t type;
|
||||||
|
} __attribute((packed));
|
||||||
|
|
||||||
|
struct GDT_ptr {
|
||||||
|
uint16_t limit;
|
||||||
|
uint32_t base;
|
||||||
|
} __attribute((packed));
|
||||||
|
|
||||||
|
#endif //GDT_H
|
19
include/print.h
Normal file
19
include/print.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef PRINT_H
|
||||||
|
#define PRINT_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
typedef struct TextOutput {
|
||||||
|
int terminal_row;
|
||||||
|
int terminal_column;
|
||||||
|
int max_row;
|
||||||
|
int max_column;
|
||||||
|
uint16_t* vid_mem;
|
||||||
|
}__attribute__((packed)) TextOutput;
|
||||||
|
|
||||||
|
TextOutput createOutput(const int max_row, const int max_column, uint16_t* vid_mem);
|
||||||
|
void scrollText(TextOutput* textOutput);
|
||||||
|
void putChar(uint8_t character, uint8_t background, uint8_t foreground, TextOutput* textOutput);
|
||||||
|
void print(char* string, uint8_t background, uint8_t foreground, TextOutput* textOutput);
|
||||||
|
|
||||||
|
#endif // PRINT_H
|
16
include/types.h
Normal file
16
include/types.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef TYPES_H
|
||||||
|
#define TYPES_H
|
||||||
|
|
||||||
|
typedef char int8_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef int int32_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
|
||||||
|
typedef long long int int64_t;
|
||||||
|
typedef unsigned long long int uint64_t;
|
||||||
|
#endif
|
43
linker.ld
Normal file
43
linker.ld
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* The bootloader will look at this image and start execution at the symbol
|
||||||
|
designated as the entry point. */
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
/* Tell where the various sections of the object files will be put in the final
|
||||||
|
kernel image. */
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
|
||||||
|
loaded at by the bootloader. */
|
||||||
|
. = 1M;
|
||||||
|
|
||||||
|
/* First put the multiboot header, as it is required to be put very early
|
||||||
|
early in the image or the bootloader won't recognize the file format.
|
||||||
|
Next we'll put the .text section. */
|
||||||
|
.text BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.multiboot)
|
||||||
|
*(.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read-only data. */
|
||||||
|
.rodata BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.rodata)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read-write data (initialized) */
|
||||||
|
.data BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read-write data (uninitialized) and stack */
|
||||||
|
.bss BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
*(COMMON)
|
||||||
|
*(.bss)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The compiler may produce other sections, by default it will put them in
|
||||||
|
a segment with the same name. Simply add stuff here as needed. */
|
||||||
|
}
|
27
makefile
Normal file
27
makefile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# vim: noexpandtab tabstop=4 shiftwidth=4
|
||||||
|
CXX = i386-elf-gcc
|
||||||
|
CFLAGS = -std=gnu99 -ffreestanding -O2 -Wall -Wextra -nostdlib -Iinclude
|
||||||
|
ASM = nasm
|
||||||
|
|
||||||
|
OBJECTS = gdt.o boot.o print.o kernel.o
|
||||||
|
|
||||||
|
%.o : src/%.c
|
||||||
|
$(CXX) $(CFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
%.o : src/%.s
|
||||||
|
$(ASM) -felf32 $< -o $@
|
||||||
|
|
||||||
|
os.bin : $(OBJECTS)
|
||||||
|
$(CXX) -T linker.ld -o os.bin $(CFLAGS) $(OBJECTS) -lgcc
|
||||||
|
|
||||||
|
os.iso : os.bin
|
||||||
|
mkdir -p isodir/boot/grub
|
||||||
|
mv os.bin isodir/boot/os.bin
|
||||||
|
echo "menuentry 'os' {" >> grub.cfg
|
||||||
|
echo " multiboot /boot/os.bin" >> grub.cfg
|
||||||
|
echo "}" >> grub.cfg
|
||||||
|
mv grub.cfg isodir/boot/grub/grub.cfg
|
||||||
|
grub-mkrescue -o os.iso isodir
|
||||||
|
|
||||||
|
clean :
|
||||||
|
rm -rf *.o *.iso *.bin *.cfg isodir/
|
106
src/boot.s
Normal file
106
src/boot.s
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
; Declare constants for the multiboot header.
|
||||||
|
MBALIGN equ 1 << 0 ; align loaded modules on page boundaries
|
||||||
|
MEMINFO equ 1 << 1 ; provide memory map
|
||||||
|
FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
|
||||||
|
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
|
||||||
|
CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot
|
||||||
|
|
||||||
|
; Declare a multiboot header that marks the program as a kernel. These are magic
|
||||||
|
; values that are documented in the multiboot standard. The bootloader will
|
||||||
|
; search for this signature in the first 8 KiB of the kernel file, aligned at a
|
||||||
|
; 32-bit boundary. The signature is in its own section so the header can be
|
||||||
|
; forced to be within the first 8 KiB of the kernel file.
|
||||||
|
section .multiboot
|
||||||
|
align 4
|
||||||
|
dd MAGIC
|
||||||
|
dd FLAGS
|
||||||
|
dd CHECKSUM
|
||||||
|
|
||||||
|
; The multiboot standard does not define the value of the stack pointer register
|
||||||
|
; (esp) and it is up to the kernel to provide a stack. This allocates room for a
|
||||||
|
; small stack by creating a symbol at the bottom of it, then allocating 16384
|
||||||
|
; bytes for it, and finally creating a symbol at the top. The stack grows
|
||||||
|
; downwards on x86. The stack is in its own section so it can be marked nobits,
|
||||||
|
; which means the kernel file is smaller because it does not contain an
|
||||||
|
; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
|
||||||
|
; System V ABI standard and de-facto extensions. The compiler will assume the
|
||||||
|
; stack is properly aligned and failure to align the stack will result in
|
||||||
|
; undefined behavior.
|
||||||
|
section .bss
|
||||||
|
align 16
|
||||||
|
stack_bottom:
|
||||||
|
resb 16384 ; 16 KiB
|
||||||
|
stack_top:
|
||||||
|
|
||||||
|
; The linker script specifies _start as the entry point to the kernel and the
|
||||||
|
; bootloader will jump to this position once the kernel has been loaded. It
|
||||||
|
; doesn't make sense to return from this function as the bootloader is gone.
|
||||||
|
; Declare _start as a function symbol with the given symbol size.
|
||||||
|
section .text
|
||||||
|
|
||||||
|
global _gdt_flush ; Allows the C code to link to this
|
||||||
|
extern _gp ; Says that '_gp' is in another file
|
||||||
|
_gdt_flush:
|
||||||
|
lgdt [_gp] ; Load the GDT with our '_gp' which is a special pointer
|
||||||
|
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov fs, ax
|
||||||
|
mov gs, ax
|
||||||
|
mov ss, ax
|
||||||
|
jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump!
|
||||||
|
flush2:
|
||||||
|
ret ; Returns back to the C code!
|
||||||
|
|
||||||
|
global _start:function (_start.end - _start)
|
||||||
|
_start:
|
||||||
|
; The bootloader has loaded us into 32-bit protected mode on a x86
|
||||||
|
; machine. Interrupts are disabled. Paging is disabled. The processor
|
||||||
|
; state is as defined in the multiboot standard. The kernel has full
|
||||||
|
; control of the CPU. The kernel can only make use of hardware features
|
||||||
|
; and any code it provides as part of itself. There's no printf
|
||||||
|
; function, unless the kernel provides its own <stdio.h> header and a
|
||||||
|
; printf implementation. There are no security restrictions, no
|
||||||
|
; safeguards, no debugging mechanisms, only what the kernel provides
|
||||||
|
; itself. It has absolute and complete power over the
|
||||||
|
; machine.
|
||||||
|
|
||||||
|
; To set up a stack, we set the esp register to point to the top of our
|
||||||
|
; stack (as it grows downwards on x86 systems). This is necessarily done
|
||||||
|
; in assembly as languages such as C cannot function without a stack.
|
||||||
|
mov esp, stack_top
|
||||||
|
|
||||||
|
; This is a good place to initialize crucial processor state before the
|
||||||
|
; high-level kernel is entered. It's best to minimize the early
|
||||||
|
; environment where crucial features are offline. Note that the
|
||||||
|
; processor is not fully initialized yet: Features such as floating
|
||||||
|
; point instructions and instruction set extensions are not initialized
|
||||||
|
; yet. The GDT should be loaded here. Paging should be enabled here.
|
||||||
|
; C++ features such as global constructors and exceptions will require
|
||||||
|
; runtime support to work as well.
|
||||||
|
|
||||||
|
; Enter the high-level kernel. The ABI requires the stack is 16-byte
|
||||||
|
; aligned at the time of the call instruction (which afterwards pushes
|
||||||
|
; the return pointer of size 4 bytes). The stack was originally 16-byte
|
||||||
|
; aligned above and we've since pushed a multiple of 16 bytes to the
|
||||||
|
; stack since (pushed 0 bytes so far) and the alignment is thus
|
||||||
|
; preserved and the call is well defined.
|
||||||
|
; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
|
||||||
|
extern kernel_main
|
||||||
|
call kernel_main
|
||||||
|
|
||||||
|
; If the system has nothing more to do, put the computer into an
|
||||||
|
; infinite loop. To do that:
|
||||||
|
; 1) Disable interrupts with cli (clear interrupt enable in eflags).
|
||||||
|
; They are already disabled by the bootloader, so this is not needed.
|
||||||
|
; Mind that you might later enable interrupts and return from
|
||||||
|
; kernel_main (which is sort of nonsensical to do).
|
||||||
|
; 2) Wait for the next interrupt to arrive with hlt (halt instruction).
|
||||||
|
; Since they are disabled, this will lock up the computer.
|
||||||
|
; 3) Jump to the hlt instruction if it ever wakes up due to a
|
||||||
|
; non-maskable interrupt occurring or due to system management mode.
|
||||||
|
cli
|
||||||
|
|
||||||
|
.hang: hlt
|
||||||
|
jmp .hang
|
||||||
|
.end:
|
36
src/gdt.c
Normal file
36
src/gdt.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include "gdt.h"
|
||||||
|
|
||||||
|
void encodeGDT(uint8_t* gdtEntry, struct GDT source) {
|
||||||
|
if ((source.limit > 65536) && ((source.limit & 0xFFF) == 0xFFF)) {
|
||||||
|
// Set the GDT to use paging
|
||||||
|
// To do this we need to make sure the limit is aligned to 4KiB
|
||||||
|
source.limit = source.limit >> 12;
|
||||||
|
// Granularity: 1 (use paging with 4KiB segments)
|
||||||
|
// Size: 1 (32 bit protected mode)
|
||||||
|
gdtEntry[6] = 0xC0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Granularity: 0 (1 byte segments)
|
||||||
|
// Size: 1
|
||||||
|
gdtEntry[6] = 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we are encoding the limit
|
||||||
|
|
||||||
|
// Bits 0-15 encode the bottom 16 bits of the limit
|
||||||
|
gdtEntry[0] = source.limit & 0xFF;
|
||||||
|
gdtEntry[1] = (source.limit >> 8) & 0xFF;
|
||||||
|
// Bits 48-51 encode the last 4 bits of the limit
|
||||||
|
gdtEntry[6] |= (source.limit >> 16) & 0xF;
|
||||||
|
|
||||||
|
// Bits 16-39 encode the bottom 24 bits of the base
|
||||||
|
gdtEntry[2] = source.base & 0xFF;
|
||||||
|
gdtEntry[3] = (source.base >> 8) & 0xFF;
|
||||||
|
gdtEntry[4] = (source.base >> 16) & 0xFF;
|
||||||
|
// Bits 56-64 encode the last byte of the base
|
||||||
|
gdtEntry[7] = (source.base >> 24) & 0xFF;
|
||||||
|
|
||||||
|
// Bits 40-47 set the access byte, which is documented at https://wiki.osdev.org/GDT,
|
||||||
|
// where most of the ideas for this function are taken from shamelessly
|
||||||
|
gdtEntry[5] = source.type;
|
||||||
|
}
|
22
src/kernel.c
Normal file
22
src/kernel.c
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "gdt.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
#define FOREGROUND 0x0
|
||||||
|
#define BACKGROUND 0xF
|
||||||
|
|
||||||
|
void PrintWithScreenFill(char* string, TextOutput* output_stream) {
|
||||||
|
// Print a string and fill the screen
|
||||||
|
print(string, BACKGROUND, FOREGROUND, output_stream);
|
||||||
|
int row = output_stream->terminal_row;
|
||||||
|
while (output_stream->terminal_row < output_stream->max_row) {
|
||||||
|
putChar('\n', BACKGROUND, FOREGROUND, output_stream);
|
||||||
|
}
|
||||||
|
output_stream->terminal_row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernel_main(void) {
|
||||||
|
TextOutput output_stream = createOutput(25,80,(uint16_t*)0xB8000);
|
||||||
|
PrintWithScreenFill("Hello, Logan World!\n", &output_stream);
|
||||||
|
}
|
||||||
|
|
73
src/print.c
Normal file
73
src/print.c
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
TextOutput createOutput(const int max_row, const int max_column, uint16_t* vid_mem) {
|
||||||
|
// Create a new TextOutput interface
|
||||||
|
TextOutput output;
|
||||||
|
output.max_row = max_row;
|
||||||
|
output.max_column = max_column;
|
||||||
|
output.vid_mem = vid_mem;
|
||||||
|
output.terminal_row = 0;
|
||||||
|
output.terminal_column = 0;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scrollText(TextOutput* textOutput) {
|
||||||
|
// Move each character up one row
|
||||||
|
for (int y = 0; y < textOutput->max_row; y++) {
|
||||||
|
for (int x = 0; x < textOutput->max_column; x++) {
|
||||||
|
*(textOutput->vid_mem + textOutput->max_column*y + x) =
|
||||||
|
*(textOutput->vid_mem + textOutput->max_column*(y+1) + x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textOutput->terminal_row--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putChar(uint8_t character, uint8_t background, uint8_t foreground,
|
||||||
|
TextOutput* textOutput) {
|
||||||
|
foreground = foreground & 0xF; background = background & 0xF;
|
||||||
|
|
||||||
|
// Handle putting a character to the screen
|
||||||
|
if (textOutput->terminal_row == textOutput->max_row) {
|
||||||
|
scrollText(textOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == '\r') {
|
||||||
|
// Delete the character before this \r
|
||||||
|
if (textOutput->terminal_column == 0) {
|
||||||
|
textOutput->terminal_row--;
|
||||||
|
textOutput->terminal_column = 80;
|
||||||
|
}
|
||||||
|
textOutput->terminal_column--;
|
||||||
|
*(textOutput->vid_mem + textOutput->terminal_row*textOutput->max_column
|
||||||
|
+ textOutput->terminal_column) = background << 12;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (character == '\n' || textOutput->terminal_column == textOutput->max_column) {
|
||||||
|
// Make a new line
|
||||||
|
for (int i = textOutput->terminal_column; i < textOutput->max_column; i++) {
|
||||||
|
*(textOutput->vid_mem + textOutput->terminal_row*textOutput->max_column
|
||||||
|
+ textOutput->terminal_column) = background << 12;
|
||||||
|
textOutput->terminal_column++;
|
||||||
|
}
|
||||||
|
|
||||||
|
textOutput->terminal_row++;
|
||||||
|
textOutput->terminal_column = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character != '\n' && character != '\r') {
|
||||||
|
// Write character to video memory
|
||||||
|
uint16_t entry = (background << 12) | (foreground << 8) | character;
|
||||||
|
*(textOutput->vid_mem + textOutput->terminal_row*textOutput->max_column
|
||||||
|
+ textOutput->terminal_column) = entry;
|
||||||
|
textOutput->terminal_column++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(char* string, uint8_t background, uint8_t foreground, TextOutput* textOutput) {
|
||||||
|
// Print a string
|
||||||
|
for (string; *string; string++) {
|
||||||
|
putChar(*string, background, foreground, textOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user