Refactoring assembly entry point

This commit is contained in:
Marco Cetica
2021-02-03 15:59:42 +01:00
parent dc3803491e
commit 1b207add8c
61 changed files with 4782 additions and 4779 deletions

View File

@@ -1,11 +1,11 @@
OBJS = tty.o gdt.o idt.o isr.o timer.o keyboard.o \
fs.o cpuid.o
CC = i686-elf-gcc # cross-compiler
CFLAGS = -m32 -fno-stack-protector -DDEFAULT_USER=root -DDEFAULT_HOSTNAME=vulcan -ffreestanding -Wall -Wextra -Werror -g -c
all:${OBJS}
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
OBJS = tty.o gdt.o idt.o isr.o timer.o keyboard.o \
fs.o cpuid.o
CC = i686-elf-gcc # cross-compiler
CFLAGS = -m32 -fno-stack-protector -DDEFAULT_USER=root -DDEFAULT_HOSTNAME=vulcan -ffreestanding -Wall -Wextra -Werror -g -c
all:${OBJS}
%.o: %.c
$(CC) $(CFLAGS) $< -o $@

View File

@@ -1,195 +1,195 @@
#include "cpuid.h"
#include "../libc/string.h"
#define INTEL_MAGIC_NUMBER 0x756e6547
#define AMD_MAGIC_NUMBER 0x68747541
#define UNRECOGNIZED_CPU 0xBADFF
static cpuid_t get_cpuid(icpuid_t cpu);
static icpuid_t detect_cpu(void);
static icpuid_t intel_cpu(void);
static icpuid_t amd_cpu(void);
static icpuid_t generic_cpu(void);
icpuid_t detect_cpu(void) {
uint32_t ebx, null;
icpuid_t i_cpu;
cpuid(0, null, ebx, null, null);
// Select CPU brand
switch(ebx) {
case INTEL_MAGIC_NUMBER:
i_cpu = intel_cpu();
break;
case AMD_MAGIC_NUMBER:
i_cpu = amd_cpu();
break;
default:
i_cpu = generic_cpu();
break;
}
return i_cpu;
}
icpuid_t intel_cpu(void) {
uint32_t eax, ebx, null;
icpuid_t icpu;
// Fill the structure
cpuid(1, eax, ebx, null, null);
icpu.model = (eax >> 4) & 0xF;
icpu.family = (eax >> 8) & 0xF;
icpu.type = (eax >> 12) & 0xF;
icpu.brand = INTEL_MAGIC_NUMBER;
icpu.stepping = eax & 0xF;
icpu.reserved = eax >> 14;
return icpu;
}
icpuid_t amd_cpu(void) {
uint32_t eax, null;
icpuid_t icpu;
// Fill the structure
cpuid(1, eax, null, null, null);
icpu.model = (eax >> 4) & 0xF;
icpu.family = (eax >> 8) & 0xF;
icpu.stepping = eax & 0xF;
icpu.reserved = eax >> 12;
icpu.brand = AMD_MAGIC_NUMBER;
return icpu;
}
icpuid_t generic_cpu(void) {
icpuid_t icpu;
icpu.brand = UNRECOGNIZED_CPU; // Magic number for unknown CPUs
return icpu;
}
cpuid_t get_cpuid(icpuid_t cpu) {
cpuid_t cpuid;
uint8_t model[64];
// Recognize CPU brand
if(cpu.brand == AMD_MAGIC_NUMBER) {
switch(cpu.family) {
case 4:
strcpy(model, (uint8_t*)"486 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
case 5:
switch(cpu.model) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
strcpy(model, (uint8_t*)"K6 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
case 8:
strcpy(model, (uint8_t*)"K6-2 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
case 9:
strcpy(model, (uint8_t*)"K6-III model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
default:
strcpy(model, (uint8_t*)"K5/K6 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
}
break;
case 6:
switch(cpu.model) {
case 1:
case 2:
case 3:
cpuid.model = (uint8_t*)"Duron model 3";
break;
case 4:
strcpy(model, (uint8_t*)"Athlon model ");
strcat(model, (void*)cpu.model);
cpuid.model = model;
break;
case 6:
cpuid.model = (uint8_t*)"Athlon MP/Mobile Athlon Model 6";
break;
case 7:
cpuid.model = (uint8_t*)"Mobile Duron Model 7";
break;
default:
strcpy(model, (uint8_t*)"Duron/Athlon model ");
strcat(model, (void*)cpu.model);
cpuid.model = model;
break;
}
break;
}
} else if(cpu.brand == INTEL_MAGIC_NUMBER) {
switch(cpu.type) {
case 0:
cpuid.type =(uint8_t*)"Original OEM";
break;
case 1:
cpuid.type = (uint8_t*)"Overdrive";
break;
case 2:
cpuid.type = (uint8_t*)"Dual-capable";
break;
case 3:
cpuid.type = (uint8_t*)"Reserved";
break;
}
switch(cpu.family) {
case 3:
cpuid.family = (uint8_t*)"i386";
break;
case 4:
cpuid.family = (uint8_t*)"i486";
break;
case 5:
cpuid.family = (uint8_t*)"Pentium II Model 5/Xeon/Celeron";
break;
case 6:
cpuid.family = (uint8_t*)"Pentium Pro";
break;
case 15:
cpuid.family = (uint8_t*)"Pentium 4";
break;
}
} else if(cpu.brand == UNRECOGNIZED_CPU)
cpuid.family = (uint8_t*)"Generic (x86) CPU";
return cpuid;
}
uint8_t *get_cpu_type() {
icpuid_t icpu = detect_cpu(); // Detect CPU brand(Intel, AMD or generic x86)
cpuid_t cpu_type = get_cpuid(icpu);
return (uint8_t*)cpu_type.type;
}
uint8_t *get_cpu_family() {
icpuid_t icpu = detect_cpu(); // Detect CPU brand(Intel, AMD or generic x86)
cpuid_t cpu_family = get_cpuid(icpu);
return (uint8_t*)cpu_family.family;
}
#include "cpuid.h"
#include "../libc/string.h"
#define INTEL_MAGIC_NUMBER 0x756e6547
#define AMD_MAGIC_NUMBER 0x68747541
#define UNRECOGNIZED_CPU 0xBADFF
static cpuid_t get_cpuid(icpuid_t cpu);
static icpuid_t detect_cpu(void);
static icpuid_t intel_cpu(void);
static icpuid_t amd_cpu(void);
static icpuid_t generic_cpu(void);
icpuid_t detect_cpu(void) {
uint32_t ebx, null;
icpuid_t i_cpu;
cpuid(0, null, ebx, null, null);
// Select CPU brand
switch(ebx) {
case INTEL_MAGIC_NUMBER:
i_cpu = intel_cpu();
break;
case AMD_MAGIC_NUMBER:
i_cpu = amd_cpu();
break;
default:
i_cpu = generic_cpu();
break;
}
return i_cpu;
}
icpuid_t intel_cpu(void) {
uint32_t eax, ebx, null;
icpuid_t icpu;
// Fill the structure
cpuid(1, eax, ebx, null, null);
icpu.model = (eax >> 4) & 0xF;
icpu.family = (eax >> 8) & 0xF;
icpu.type = (eax >> 12) & 0xF;
icpu.brand = INTEL_MAGIC_NUMBER;
icpu.stepping = eax & 0xF;
icpu.reserved = eax >> 14;
return icpu;
}
icpuid_t amd_cpu(void) {
uint32_t eax, null;
icpuid_t icpu;
// Fill the structure
cpuid(1, eax, null, null, null);
icpu.model = (eax >> 4) & 0xF;
icpu.family = (eax >> 8) & 0xF;
icpu.stepping = eax & 0xF;
icpu.reserved = eax >> 12;
icpu.brand = AMD_MAGIC_NUMBER;
return icpu;
}
icpuid_t generic_cpu(void) {
icpuid_t icpu;
icpu.brand = UNRECOGNIZED_CPU; // Magic number for unknown CPUs
return icpu;
}
cpuid_t get_cpuid(icpuid_t cpu) {
cpuid_t cpuid;
uint8_t model[64];
// Recognize CPU brand
if(cpu.brand == AMD_MAGIC_NUMBER) {
switch(cpu.family) {
case 4:
strcpy(model, (uint8_t*)"486 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
case 5:
switch(cpu.model) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
strcpy(model, (uint8_t*)"K6 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
case 8:
strcpy(model, (uint8_t*)"K6-2 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
case 9:
strcpy(model, (uint8_t*)"K6-III model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
default:
strcpy(model, (uint8_t*)"K5/K6 model "); // Set model name
strcat(model, (void*)cpu.model); // Set model version
cpuid.model = model;
break;
}
break;
case 6:
switch(cpu.model) {
case 1:
case 2:
case 3:
cpuid.model = (uint8_t*)"Duron model 3";
break;
case 4:
strcpy(model, (uint8_t*)"Athlon model ");
strcat(model, (void*)cpu.model);
cpuid.model = model;
break;
case 6:
cpuid.model = (uint8_t*)"Athlon MP/Mobile Athlon Model 6";
break;
case 7:
cpuid.model = (uint8_t*)"Mobile Duron Model 7";
break;
default:
strcpy(model, (uint8_t*)"Duron/Athlon model ");
strcat(model, (void*)cpu.model);
cpuid.model = model;
break;
}
break;
}
} else if(cpu.brand == INTEL_MAGIC_NUMBER) {
switch(cpu.type) {
case 0:
cpuid.type =(uint8_t*)"Original OEM";
break;
case 1:
cpuid.type = (uint8_t*)"Overdrive";
break;
case 2:
cpuid.type = (uint8_t*)"Dual-capable";
break;
case 3:
cpuid.type = (uint8_t*)"Reserved";
break;
}
switch(cpu.family) {
case 3:
cpuid.family = (uint8_t*)"i386";
break;
case 4:
cpuid.family = (uint8_t*)"i486";
break;
case 5:
cpuid.family = (uint8_t*)"Pentium II Model 5/Xeon/Celeron";
break;
case 6:
cpuid.family = (uint8_t*)"Pentium Pro";
break;
case 15:
cpuid.family = (uint8_t*)"Pentium 4";
break;
}
} else if(cpu.brand == UNRECOGNIZED_CPU)
cpuid.family = (uint8_t*)"Generic (x86) CPU";
return cpuid;
}
uint8_t *get_cpu_type() {
icpuid_t icpu = detect_cpu(); // Detect CPU brand(Intel, AMD or generic x86)
cpuid_t cpu_type = get_cpuid(icpu);
return (uint8_t*)cpu_type.type;
}
uint8_t *get_cpu_family() {
icpuid_t icpu = detect_cpu(); // Detect CPU brand(Intel, AMD or generic x86)
cpuid_t cpu_family = get_cpuid(icpu);
return (uint8_t*)cpu_family.family;
}

View File

@@ -1,36 +1,36 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef CPUID_H
#define CPUID_H
#include <stdint.h>
#define cpuid(in, a, b, c, d) __asm__("cpuid": "=a" (a), "=b"(b), "=d" (d) : "a" (in));
typedef struct {
uint32_t model;
uint32_t family;
uint32_t type;
uint32_t brand;
uint32_t stepping;
uint32_t reserved;
} icpuid_t;
typedef struct {
uint8_t *model;
uint8_t *family;
uint8_t *type;
uint8_t *brand;
uint8_t *stepping;
uint8_t *reserved;
} cpuid_t;
// return type and family processor
uint8_t *get_cpu_type();
uint8_t *get_cpu_family();
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef CPUID_H
#define CPUID_H
#include <stdint.h>
#define cpuid(in, a, b, c, d) __asm__("cpuid": "=a" (a), "=b"(b), "=d" (d) : "a" (in));
typedef struct {
uint32_t model;
uint32_t family;
uint32_t type;
uint32_t brand;
uint32_t stepping;
uint32_t reserved;
} icpuid_t;
typedef struct {
uint8_t *model;
uint8_t *family;
uint8_t *type;
uint8_t *brand;
uint8_t *stepping;
uint8_t *reserved;
} cpuid_t;
// return type and family processor
uint8_t *get_cpu_type();
uint8_t *get_cpu_family();
#endif

View File

@@ -1,47 +1,47 @@
#include "fs.h"
fs_node_t *fs_root = 0; // Initialize the root of the filesystem
uint32_t read_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
// Check if inode got a read callback from the kernel
if(node->read != 0)
return node->read(node, offset, size, buffer);
else
return 0;
}
uint32_t write_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
// Check if inode got a write callback from the kernel
if(node->write != 0)
return node->write(node, offset, size, buffer);
else
return 0;
}
void open_fs(fs_node_t *node) {
// Check if inode got a open callback from the kernel
if(node->open != 0)
return node->open(node);
}
void close_fs(fs_node_t *node) {
// Check if inode got a close callback from the kernel
if(node->close != 0)
return node->close(node);
}
struct dirent *readdir_fs(fs_node_t *node, uint32_t index) {
// Read dir content(only if file descriptor is FS_DIRECTORY)
if((node->flags&0x7) == FS_DIRECTORY && node->readdir != 0)
return node->readdir(node, index);
else
return 0;
}
fs_node_t *finddir_fs(fs_node_t *node, char *name) {
// Check if an inode is a directory
if((node->flags&0x7) == FS_DIRECTORY && node->finddir != 0)
return node->finddir(node, name);
else
return 0;
#include "fs.h"
fs_node_t *fs_root = 0; // Initialize the root of the filesystem
uint32_t read_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
// Check if inode got a read callback from the kernel
if(node->read != 0)
return node->read(node, offset, size, buffer);
else
return 0;
}
uint32_t write_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
// Check if inode got a write callback from the kernel
if(node->write != 0)
return node->write(node, offset, size, buffer);
else
return 0;
}
void open_fs(fs_node_t *node) {
// Check if inode got a open callback from the kernel
if(node->open != 0)
return node->open(node);
}
void close_fs(fs_node_t *node) {
// Check if inode got a close callback from the kernel
if(node->close != 0)
return node->close(node);
}
struct dirent *readdir_fs(fs_node_t *node, uint32_t index) {
// Read dir content(only if file descriptor is FS_DIRECTORY)
if((node->flags&0x7) == FS_DIRECTORY && node->readdir != 0)
return node->readdir(node, index);
else
return 0;
}
fs_node_t *finddir_fs(fs_node_t *node, char *name) {
// Check if an inode is a directory
if((node->flags&0x7) == FS_DIRECTORY && node->finddir != 0)
return node->finddir(node, name);
else
return 0;
}

View File

@@ -1,84 +1,84 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef FS_H
#define FS_H
#include <stdint.h>
/* This Virtual File System(VFS) is a simplified version of
* standard UNIX VFS where all files comes organized in a graph
* of nodes. Keeping in mind the concept of "everything is a file" we can
* store a file, a directory, a serial device or anything else just by adding
* a new node to the data structure.
* And this is a list of common operations:
* - Open: Initialize a new node as a file descriptor
* - Close: CLose a node
* - Read: return the content of a node
* - Write: set content to node
* - Readdir: Return directory content
* - Finddir: Given a specific name find the corresponding child node.*/
// Define some standard node types
#define FS_FILE 0x01
#define FS_DIRECTORY 0x02
#define FS_CHARDEVICE 0x03
#define FS_BLOCKDEVICE 0x04
#define FS_PIPE 0x05
#define FS_SYMLINK 0x06
#define FS_MOUNTPOINT 0x08
struct fs_node;
/* Define some callbacks to be called when read/write/open/close
* operations are called */
typedef uint32_t (*read_type_t)(struct fs_node*, uint32_t, uint32_t, uint8_t*);
typedef uint32_t (*write_type_t)(struct fs_node*, uint32_t, uint32_t, uint8_t*);
typedef void (*open_type_t)(struct fs_node*);
typedef void (*close_type_t)(struct fs_node*);
typedef struct dirent*(*readdir_type_t)(struct fs_node*, uint32_t);
typedef struct fs_node*(*finddir_type_t)(struct fs_node*, char *name);
// This define the structure of a node
typedef struct fs_node {
uint8_t name[128]; // File name
uint32_t mask; // Permission mask
uint32_t uid; // Owning user
uint32_t gid; // Owning group
uint32_t flags; // Node type
uint32_t inode; // used by file systems to identify files
uint32_t length; // Length of the file, in bytes.
uint32_t impl;
// Callback section
read_type_t read;
write_type_t write;
open_type_t open;
close_type_t close;
readdir_type_t readdir;
finddir_type_t finddir;
struct fs_node *ptr; // Used by mountpoints and symlinks
} fs_node_t;
struct dirent {
uint8_t name[120]; // File name
uint32_t ino; // POSIX standard requires inode number;
};
// Filesystem root
extern fs_node_t *fs_root;
/* Write/Read/Open/Close operations
* NOTE: those functions are NOT like the Callback
* functions; the first one deals with inodes while
* the second one deals with file descriptors. */
uint32_t read_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
uint32_t write_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
void open_fs(fs_node_t *node);
void close_fs(fs_node_t *node);
struct dirent *readdir_fs(fs_node_t *node, uint32_t index);
fs_node_t *finddir_fs(fs_node_t *node, char *name);
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef FS_H
#define FS_H
#include <stdint.h>
/* This Virtual File System(VFS) is a simplified version of
* standard UNIX VFS where all files comes organized in a graph
* of nodes. Keeping in mind the concept of "everything is a file" we can
* store a file, a directory, a serial device or anything else just by adding
* a new node to the data structure.
* And this is a list of common operations:
* - Open: Initialize a new node as a file descriptor
* - Close: CLose a node
* - Read: return the content of a node
* - Write: set content to node
* - Readdir: Return directory content
* - Finddir: Given a specific name find the corresponding child node.*/
// Define some standard node types
#define FS_FILE 0x01
#define FS_DIRECTORY 0x02
#define FS_CHARDEVICE 0x03
#define FS_BLOCKDEVICE 0x04
#define FS_PIPE 0x05
#define FS_SYMLINK 0x06
#define FS_MOUNTPOINT 0x08
struct fs_node;
/* Define some callbacks to be called when read/write/open/close
* operations are called */
typedef uint32_t (*read_type_t)(struct fs_node*, uint32_t, uint32_t, uint8_t*);
typedef uint32_t (*write_type_t)(struct fs_node*, uint32_t, uint32_t, uint8_t*);
typedef void (*open_type_t)(struct fs_node*);
typedef void (*close_type_t)(struct fs_node*);
typedef struct dirent*(*readdir_type_t)(struct fs_node*, uint32_t);
typedef struct fs_node*(*finddir_type_t)(struct fs_node*, char *name);
// This define the structure of a node
typedef struct fs_node {
uint8_t name[128]; // File name
uint32_t mask; // Permission mask
uint32_t uid; // Owning user
uint32_t gid; // Owning group
uint32_t flags; // Node type
uint32_t inode; // used by file systems to identify files
uint32_t length; // Length of the file, in bytes.
uint32_t impl;
// Callback section
read_type_t read;
write_type_t write;
open_type_t open;
close_type_t close;
readdir_type_t readdir;
finddir_type_t finddir;
struct fs_node *ptr; // Used by mountpoints and symlinks
} fs_node_t;
struct dirent {
uint8_t name[120]; // File name
uint32_t ino; // POSIX standard requires inode number;
};
// Filesystem root
extern fs_node_t *fs_root;
/* Write/Read/Open/Close operations
* NOTE: those functions are NOT like the Callback
* functions; the first one deals with inodes while
* the second one deals with file descriptors. */
uint32_t read_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
uint32_t write_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
void open_fs(fs_node_t *node);
void close_fs(fs_node_t *node);
struct dirent *readdir_fs(fs_node_t *node, uint32_t index);
fs_node_t *finddir_fs(fs_node_t *node, char *name);
#endif

View File

@@ -1,103 +1,103 @@
#include "gdt.h"
// Internal method
extern void gdt_flush(uint32_t); // Defined on cpu/gdt.asm
static gdt_entry_t construct_null_entry();
static gdt_entry_t construct_entry(gdt_access_t access);
static void init_gdt();
gdt_entry_t gdt_entries[5];
gdt_ptr_t gdt_ptr;
// This method will be called by the kernel
void gdt_setup() {
init_gdt();
}
static void init_gdt() {
gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
gdt_ptr.base = (uint32_t)&gdt_entries;
gdt_entry_t null_segment = construct_null_entry();
gdt_entry_t kernel_mode_code_segment = construct_entry(
(struct gdt_access){
.type = GDT_CODE_TYPE_EXEC_READ,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING0,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t kernel_mode_data_segment = construct_entry(
(struct gdt_access){
.type = GDT_DATA_TYPE_READ_WRITE,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING0,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t user_mode_code_segment = construct_entry(
(struct gdt_access){
.type = GDT_CODE_TYPE_EXEC_READ,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING3,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t user_mode_data_segment = construct_entry(
(struct gdt_access){
.type = GDT_DATA_TYPE_READ_WRITE,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING3,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entries[0] = null_segment;
gdt_entries[1] = kernel_mode_code_segment;
gdt_entries[2] = kernel_mode_data_segment;
gdt_entries[3] = user_mode_code_segment;
gdt_entries[4] = user_mode_data_segment;
gdt_flush((uint32_t)&gdt_ptr);
}
static gdt_entry_t construct_entry(gdt_access_t access) {
gdt_entry_t entry = (struct gdt_entry_struct) {
.base_low = GDT_BASE & 0xFFFF,
.base_middle = (GDT_BASE >> 16) & 0xFF,
.base_high = (GDT_BASE >> 24) & 0xFF,
.limit_low = (GDT_LIMIT & 0xFFFF),
.access = access,
.granularity = (struct gdt_granularity) {
.g = GDT_GRANULARITY_4K,
.d = GDT_OPERAND_SIZE_32,
.zero = 0,
.seglen = GDT_SEGMENT_LENGTH
}
};
return entry;
}
// The only difference is in the access
static gdt_entry_t construct_null_entry() {
gdt_entry_t null_entry = (struct gdt_entry_struct) {
.base_low = 0,
.base_middle = 0,
.base_high = 0,
.limit_low = 0,
.access = (struct gdt_access) {
.p = 0,
.dpl = 0,
.dt = 0,
.type = 0
},
.granularity = (struct gdt_granularity) {
.g = 0,
.d = 0,
.zero = 0,
.seglen = 0
}
};
return null_entry;
#include "gdt.h"
// Internal method
extern void gdt_flush(uint32_t); // Defined on cpu/gdt.asm
static gdt_entry_t construct_null_entry();
static gdt_entry_t construct_entry(gdt_access_t access);
static void init_gdt();
gdt_entry_t gdt_entries[5];
gdt_ptr_t gdt_ptr;
// This method will be called by the kernel
void gdt_setup() {
init_gdt();
}
static void init_gdt() {
gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
gdt_ptr.base = (uint32_t)&gdt_entries;
gdt_entry_t null_segment = construct_null_entry();
gdt_entry_t kernel_mode_code_segment = construct_entry(
(struct gdt_access){
.type = GDT_CODE_TYPE_EXEC_READ,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING0,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t kernel_mode_data_segment = construct_entry(
(struct gdt_access){
.type = GDT_DATA_TYPE_READ_WRITE,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING0,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t user_mode_code_segment = construct_entry(
(struct gdt_access){
.type = GDT_CODE_TYPE_EXEC_READ,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING3,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t user_mode_data_segment = construct_entry(
(struct gdt_access){
.type = GDT_DATA_TYPE_READ_WRITE,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING3,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entries[0] = null_segment;
gdt_entries[1] = kernel_mode_code_segment;
gdt_entries[2] = kernel_mode_data_segment;
gdt_entries[3] = user_mode_code_segment;
gdt_entries[4] = user_mode_data_segment;
gdt_flush((uint32_t)&gdt_ptr);
}
static gdt_entry_t construct_entry(gdt_access_t access) {
gdt_entry_t entry = (struct gdt_entry_struct) {
.base_low = GDT_BASE & 0xFFFF,
.base_middle = (GDT_BASE >> 16) & 0xFF,
.base_high = (GDT_BASE >> 24) & 0xFF,
.limit_low = (GDT_LIMIT & 0xFFFF),
.access = access,
.granularity = (struct gdt_granularity) {
.g = GDT_GRANULARITY_4K,
.d = GDT_OPERAND_SIZE_32,
.zero = 0,
.seglen = GDT_SEGMENT_LENGTH
}
};
return entry;
}
// The only difference is in the access
static gdt_entry_t construct_null_entry() {
gdt_entry_t null_entry = (struct gdt_entry_struct) {
.base_low = 0,
.base_middle = 0,
.base_high = 0,
.limit_low = 0,
.access = (struct gdt_access) {
.p = 0,
.dpl = 0,
.dt = 0,
.type = 0
},
.granularity = (struct gdt_granularity) {
.g = 0,
.d = 0,
.zero = 0,
.seglen = 0
}
};
return null_entry;
}

View File

@@ -1,139 +1,139 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _GDT_H_
#define _GDT_H_
#include <stdint.h>
/*
* First a bit of theory:
* GDT(Global Descriptor Table) is a complex data structure used in x86 systems
* to define memory areas.
* Technically speaking GDT is formed by an array of 8-bytes segment descriptors,
* the first descriptor of the GDT is always a NULL one and CANNOT be used to allocate
* memory; so we need at least two descriptors(plus the null descriptor) to successfully allocate
* data on our memory.
* In x86 architecture there're two methods to provide virtual memory: Segmentation and Paging:
* With the first one every memory access is done within his own segment, so each address(of a process)
* is added to the segment's base address and checked against the segment's length.
* With paging, however, the address space is split into blocks(usually of 4KiB) called pages.
* Each page can be mapped to physical memory or it can be unmapped(to create virtual memory).
* Segmentation is a built-in functionality of x86 architecture, so to get around this we need to
* define our own GDT. A cool thing segmentation can do for us is to set Ring Level:
* a privilege level to allow our process to run in a 'unprivileged' mode(called user mode, ring 3)
* and to allow drivers(or kernel related stuff) to run in a 'supervisor-mode'(called kernel mode, ring 0).
* Usually bootloader(such as GRUB) sets up GDT for us, the problem is that we cannot known where it is or
* if it is has been overwritten during some other tasks. So it's a good idea to implement
* a new GDT ourself.
*/
/* Those values were taken from Intel's developer manual */
// GDT fields
#define GDT_BASE 0x00000000
#define GDT_LIMIT 0xFFFFFFFF
// GDT granularity
#define GDT_SEGMENT_LENGTH 0xF
#define GDT_OPERAND_SIZE_16 0
#define GDT_OPERAND_SIZE_32 1
#define GDT_GRANULARITY_1K 0
#define GDT_GRANULARITY_4K 1
// GDT access type fields
#define GDT_DATA_TYPE_READ_ONLY 0x0
#define GDT_DATA_TYPE_READ_ONLY_ACCESSED 0x1
#define GDT_DATA_TYPE_READ_WRITE 0x2
#define GDT_DATA_TYPE_READ_WRITE_ACCESSED 0x3
#define GDT_DATA_TYPE_READ_ONLY_EXPAND_DOWN 0x4
#define GDT_DATA_TYPE_READ_ONLY_EXPAND_DOWN_ACCESSED 0x5
#define GDT_DATA_TYPE_READ_WRITE_EXPAND_DOWN 0x6
#define GDT_DATA_TYPE_READ_WRITE_EXPAND_DOWN_ACCESSED 0x7
#define GDT_DATA_TYPE_EXEC_ONLY 0x8
#define GDT_CODE_TYPE_EXEC_ONLY_ACCESSED 0x9
#define GDT_CODE_TYPE_EXEC_READ 0xA
#define GDT_CODE_TYPE_EXEC_READ_ACCESSED 0xB
#define GDT_CODE_TYPE_EXEC_CONFORMING 0xC
#define GDT_CODE_TYPE_EXEC_CONFORMING_ACCESSED 0xD
#define GDT_CODE_TYPE_EXEC_READ_CONFORMING 0xE
#define GDT_CODE_TYPE_EXEC_READ_CONFORMING_ACCESSED 0xF
// Descriptor type fields
#define GDT_SYSTEM_DESCRIPTOR 0
#define GDT_CODE_AND_DATA_DESCRIPTOR 1
// GDT Ring number
#define GDT_RING0 0
#define GDT_RING1 1
#define GDT_RING2 2
#define GDT_RING3 3
// 'Present' field
#define GDT_SEGMENT_NOT_PRESENT 0
#define GDT_SEGMENT_PRESENT 1
/* Global Descriptor Table (GDT) implementation */
/* gdt_access is used to access portion of the GDT
* | 0 - 3 | 4 | 3 - 6 | 7 |
* | Type | DT | DPL | P |
* Type: Which type
* DT: descriptor type
* DPL: Kernel ring(0-3)
* P: is segment present? (bool)
*/
struct gdt_access {
uint8_t type: 4; // 4 Bits
uint8_t dt: 1; // 1 Bit
uint8_t dpl: 2; // 2 Bits
uint8_t p: 1; // 1 Bits
}__attribute__((packed));
typedef struct gdt_access gdt_access_t;
/* gdt_granularity is used to get portion of GDT entry
* | 0 - 3 | 4 | 5 | 6 | 7 |
* | seglen | 0 | D | G |
* seglen: segment length
* 0: Always zero
* D: Operand size (0 = 16 bit, 1 = 32 bit)
* G: granularity (0 = 1 Byte, 1 = 4KiB)
*/
struct gdt_granularity {
uint8_t seglen: 4;
uint8_t zero: 2;
uint8_t d: 1;
uint8_t g: 1;
}__attribute__((packed));
typedef struct gdt_granularity gdt_gran_t;
/* gdt_entry_struct contains the value of a single GDT entry
* Each slice is 64 bits.
* | 0 - 15 | 16 - 31 | 32 - 39 | 40 - 47 | 48 - 55 | 56 - 63 |
* | lim low| base low|base mid | access | gran | base hg |
* lim low: Lower 16 bits of the limit
* base low: Lower 16 bits of the base
* base mid: Next 8 bits of the base
* access: access flag, e.g. which ring this segment can be used in.
* gran.
*/
struct gdt_entry_struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
gdt_access_t access;
gdt_gran_t granularity;
uint8_t base_high;
}__attribute__((packed));
typedef struct gdt_entry_struct gdt_entry_t;
/* Also we have to define a pointer to the data structure
* This is needed to locate it later */
struct gdt_ptr {
uint16_t limit;
uint32_t base;
}__attribute__((packed));
typedef struct gdt_ptr gdt_ptr_t;
/* GDT Kernel API */
void gdt_setup();
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef _GDT_H_
#define _GDT_H_
#include <stdint.h>
/*
* First a bit of theory:
* GDT(Global Descriptor Table) is a complex data structure used in x86 systems
* to define memory areas.
* Technically speaking GDT is formed by an array of 8-bytes segment descriptors,
* the first descriptor of the GDT is always a NULL one and CANNOT be used to allocate
* memory; so we need at least two descriptors(plus the null descriptor) to successfully allocate
* data on our memory.
* In x86 architecture there're two methods to provide virtual memory: Segmentation and Paging:
* With the first one every memory access is done within his own segment, so each address(of a process)
* is added to the segment's base address and checked against the segment's length.
* With paging, however, the address space is split into blocks(usually of 4KiB) called pages.
* Each page can be mapped to physical memory or it can be unmapped(to create virtual memory).
* Segmentation is a built-in functionality of x86 architecture, so to get around this we need to
* define our own GDT. A cool thing segmentation can do for us is to set Ring Level:
* a privilege level to allow our process to run in a 'unprivileged' mode(called user mode, ring 3)
* and to allow drivers(or kernel related stuff) to run in a 'supervisor-mode'(called kernel mode, ring 0).
* Usually bootloader(such as GRUB) sets up GDT for us, the problem is that we cannot known where it is or
* if it is has been overwritten during some other tasks. So it's a good idea to implement
* a new GDT ourself.
*/
/* Those values were taken from Intel's developer manual */
// GDT fields
#define GDT_BASE 0x00000000
#define GDT_LIMIT 0xFFFFFFFF
// GDT granularity
#define GDT_SEGMENT_LENGTH 0xF
#define GDT_OPERAND_SIZE_16 0
#define GDT_OPERAND_SIZE_32 1
#define GDT_GRANULARITY_1K 0
#define GDT_GRANULARITY_4K 1
// GDT access type fields
#define GDT_DATA_TYPE_READ_ONLY 0x0
#define GDT_DATA_TYPE_READ_ONLY_ACCESSED 0x1
#define GDT_DATA_TYPE_READ_WRITE 0x2
#define GDT_DATA_TYPE_READ_WRITE_ACCESSED 0x3
#define GDT_DATA_TYPE_READ_ONLY_EXPAND_DOWN 0x4
#define GDT_DATA_TYPE_READ_ONLY_EXPAND_DOWN_ACCESSED 0x5
#define GDT_DATA_TYPE_READ_WRITE_EXPAND_DOWN 0x6
#define GDT_DATA_TYPE_READ_WRITE_EXPAND_DOWN_ACCESSED 0x7
#define GDT_DATA_TYPE_EXEC_ONLY 0x8
#define GDT_CODE_TYPE_EXEC_ONLY_ACCESSED 0x9
#define GDT_CODE_TYPE_EXEC_READ 0xA
#define GDT_CODE_TYPE_EXEC_READ_ACCESSED 0xB
#define GDT_CODE_TYPE_EXEC_CONFORMING 0xC
#define GDT_CODE_TYPE_EXEC_CONFORMING_ACCESSED 0xD
#define GDT_CODE_TYPE_EXEC_READ_CONFORMING 0xE
#define GDT_CODE_TYPE_EXEC_READ_CONFORMING_ACCESSED 0xF
// Descriptor type fields
#define GDT_SYSTEM_DESCRIPTOR 0
#define GDT_CODE_AND_DATA_DESCRIPTOR 1
// GDT Ring number
#define GDT_RING0 0
#define GDT_RING1 1
#define GDT_RING2 2
#define GDT_RING3 3
// 'Present' field
#define GDT_SEGMENT_NOT_PRESENT 0
#define GDT_SEGMENT_PRESENT 1
/* Global Descriptor Table (GDT) implementation */
/* gdt_access is used to access portion of the GDT
* | 0 - 3 | 4 | 3 - 6 | 7 |
* | Type | DT | DPL | P |
* Type: Which type
* DT: descriptor type
* DPL: Kernel ring(0-3)
* P: is segment present? (bool)
*/
struct gdt_access {
uint8_t type: 4; // 4 Bits
uint8_t dt: 1; // 1 Bit
uint8_t dpl: 2; // 2 Bits
uint8_t p: 1; // 1 Bits
}__attribute__((packed));
typedef struct gdt_access gdt_access_t;
/* gdt_granularity is used to get portion of GDT entry
* | 0 - 3 | 4 | 5 | 6 | 7 |
* | seglen | 0 | D | G |
* seglen: segment length
* 0: Always zero
* D: Operand size (0 = 16 bit, 1 = 32 bit)
* G: granularity (0 = 1 Byte, 1 = 4KiB)
*/
struct gdt_granularity {
uint8_t seglen: 4;
uint8_t zero: 2;
uint8_t d: 1;
uint8_t g: 1;
}__attribute__((packed));
typedef struct gdt_granularity gdt_gran_t;
/* gdt_entry_struct contains the value of a single GDT entry
* Each slice is 64 bits.
* | 0 - 15 | 16 - 31 | 32 - 39 | 40 - 47 | 48 - 55 | 56 - 63 |
* | lim low| base low|base mid | access | gran | base hg |
* lim low: Lower 16 bits of the limit
* base low: Lower 16 bits of the base
* base mid: Next 8 bits of the base
* access: access flag, e.g. which ring this segment can be used in.
* gran.
*/
struct gdt_entry_struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
gdt_access_t access;
gdt_gran_t granularity;
uint8_t base_high;
}__attribute__((packed));
typedef struct gdt_entry_struct gdt_entry_t;
/* Also we have to define a pointer to the data structure
* This is needed to locate it later */
struct gdt_ptr {
uint16_t limit;
uint32_t base;
}__attribute__((packed));
typedef struct gdt_ptr gdt_ptr_t;
/* GDT Kernel API */
void gdt_setup();
#endif

View File

@@ -1,122 +1,122 @@
#include "idt.h"
#include "../libc/string.h"
#include "ports.h"
// Internal method
extern void idt_flush(idt_ptr_t*); // defined on cpu/idt.asm
static void init_idt();
static void idt_set_gate(uint8_t idx, void(*base), uint16_t selector, idt_flags_t flags);
static void pic_remap(uint8_t offset1, uint8_t offset2);
idt_entry_t idt_entries[256]; // 256 interrupts
idt_ptr_t idt_ptr;
// This method will be called by the kernel
void idt_setup() {
init_idt();
}
static void idt_set_gate(uint8_t idx, void(*base), uint16_t selector, idt_flags_t flags) {
idt_entries[idx] = (struct idt_entry) {
.base_low = (uint32_t)base & 0xFFFF,
.base_high = ((uint32_t)base >> 16) & 0xFFFF,
.seg_sel = selector,
.flags = flags
};
}
static void init_idt() {
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
idt_ptr.base = idt_entries;
memset(&idt_entries, 0, sizeof(idt_entry_t) * 256);
idt_flags_t flags = {
.reserved = IDT_FLAG_RESERVED, // Always 0x0E
.dpl = 0,
.p = 1
};
// Remap IDT with ISRs
idt_set_gate(0, isr0, 0x08, flags);
idt_set_gate(1, isr1, 0x08, flags);
idt_set_gate(2, isr2, 0x08, flags);
idt_set_gate(3, isr3, 0x08, flags);
idt_set_gate(4, isr4, 0x08, flags);
idt_set_gate(5, isr5, 0x08, flags);
idt_set_gate(6, isr6, 0x08, flags);
idt_set_gate(7, isr7, 0x08, flags);
idt_set_gate(8, isr8, 0x08, flags);
idt_set_gate(9, isr9, 0x08, flags);
idt_set_gate(10, isr10, 0x08, flags);
idt_set_gate(11, isr11, 0x08, flags);
idt_set_gate(12, isr12, 0x08, flags);
idt_set_gate(13, isr13, 0x08, flags);
idt_set_gate(14, isr14, 0x08, flags);
idt_set_gate(15, isr15, 0x08, flags);
idt_set_gate(16, isr16, 0x08, flags);
idt_set_gate(17, isr17, 0x08, flags);
idt_set_gate(18, isr18, 0x08, flags);
idt_set_gate(19, isr19, 0x08, flags);
idt_set_gate(20, isr20, 0x08, flags);
idt_set_gate(21, isr21, 0x08, flags);
idt_set_gate(22, isr22, 0x08, flags);
idt_set_gate(23, isr23, 0x08, flags);
idt_set_gate(24, isr24, 0x08, flags);
idt_set_gate(25, isr25, 0x08, flags);
idt_set_gate(26, isr26, 0x08, flags);
idt_set_gate(27, isr27, 0x08, flags);
idt_set_gate(28, isr28, 0x08, flags);
idt_set_gate(29, isr29, 0x08, flags);
idt_set_gate(30, isr30, 0x08, flags);
idt_set_gate(31, isr31, 0x08, flags);
// Remap PIC
pic_remap(PIC1_START_INTERRUPT, PIC2_START_INTERRUPT);
// Also remap 15 entries for IRQs
idt_set_gate(32, irq0, 0x08, flags);
idt_set_gate(33, irq1, 0x08, flags);
idt_set_gate(34, irq2, 0x08, flags);
idt_set_gate(35, irq3, 0x08, flags);
idt_set_gate(36, irq4, 0x08, flags);
idt_set_gate(37, irq5, 0x08, flags);
idt_set_gate(38, irq6, 0x08, flags);
idt_set_gate(39, irq7, 0x08, flags);
idt_set_gate(40, irq8, 0x08, flags);
idt_set_gate(41, irq9, 0x08, flags);
idt_set_gate(42, irq10, 0x08, flags);
idt_set_gate(43, irq11, 0x08, flags);
idt_set_gate(44, irq12, 0x08, flags);
idt_set_gate(45, irq13, 0x08, flags);
idt_set_gate(46, irq14, 0x08, flags);
idt_set_gate(47, irq15, 0x08, flags);
idt_flush(&idt_ptr);
// Finally enable hardware interrupts with an assembly instruction
__asm__ __volatile__ ("sti");
}
// Taken from: http://wiki.osdev.org/8259_PIC
static void pic_remap(uint8_t offset1, uint8_t offset2) {
uint8_t a1, a2;
a1 = inb(PIC1_DATA); // Save masks
a2 = inb(PIC2_DATA);
outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); // Start init sequence
outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
outb(PIC1_DATA, offset1);
outb(PIC2_DATA, offset2);
outb(PIC1_DATA, 4); // Tell master PIC that there is a slave PIC at IRQ2
outb(PIC1_DATA, 2); // Tell salve PIC it's cascade identity
outb(PIC1_DATA, ICW4_8086);
outb(PIC2_DATA, ICW4_8086);
// Restore saved masks
outb(PIC1_DATA, a1);
outb(PIC2_DATA, a2);
#include "idt.h"
#include "../libc/string.h"
#include "ports.h"
// Internal method
extern void idt_flush(idt_ptr_t*); // defined on cpu/idt.asm
static void init_idt();
static void idt_set_gate(uint8_t idx, void(*base), uint16_t selector, idt_flags_t flags);
static void pic_remap(uint8_t offset1, uint8_t offset2);
idt_entry_t idt_entries[256]; // 256 interrupts
idt_ptr_t idt_ptr;
// This method will be called by the kernel
void idt_setup() {
init_idt();
}
static void idt_set_gate(uint8_t idx, void(*base), uint16_t selector, idt_flags_t flags) {
idt_entries[idx] = (struct idt_entry) {
.base_low = (uint32_t)base & 0xFFFF,
.base_high = ((uint32_t)base >> 16) & 0xFFFF,
.seg_sel = selector,
.flags = flags
};
}
static void init_idt() {
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
idt_ptr.base = idt_entries;
memset(&idt_entries, 0, sizeof(idt_entry_t) * 256);
idt_flags_t flags = {
.reserved = IDT_FLAG_RESERVED, // Always 0x0E
.dpl = 0,
.p = 1
};
// Remap IDT with ISRs
idt_set_gate(0, isr0, 0x08, flags);
idt_set_gate(1, isr1, 0x08, flags);
idt_set_gate(2, isr2, 0x08, flags);
idt_set_gate(3, isr3, 0x08, flags);
idt_set_gate(4, isr4, 0x08, flags);
idt_set_gate(5, isr5, 0x08, flags);
idt_set_gate(6, isr6, 0x08, flags);
idt_set_gate(7, isr7, 0x08, flags);
idt_set_gate(8, isr8, 0x08, flags);
idt_set_gate(9, isr9, 0x08, flags);
idt_set_gate(10, isr10, 0x08, flags);
idt_set_gate(11, isr11, 0x08, flags);
idt_set_gate(12, isr12, 0x08, flags);
idt_set_gate(13, isr13, 0x08, flags);
idt_set_gate(14, isr14, 0x08, flags);
idt_set_gate(15, isr15, 0x08, flags);
idt_set_gate(16, isr16, 0x08, flags);
idt_set_gate(17, isr17, 0x08, flags);
idt_set_gate(18, isr18, 0x08, flags);
idt_set_gate(19, isr19, 0x08, flags);
idt_set_gate(20, isr20, 0x08, flags);
idt_set_gate(21, isr21, 0x08, flags);
idt_set_gate(22, isr22, 0x08, flags);
idt_set_gate(23, isr23, 0x08, flags);
idt_set_gate(24, isr24, 0x08, flags);
idt_set_gate(25, isr25, 0x08, flags);
idt_set_gate(26, isr26, 0x08, flags);
idt_set_gate(27, isr27, 0x08, flags);
idt_set_gate(28, isr28, 0x08, flags);
idt_set_gate(29, isr29, 0x08, flags);
idt_set_gate(30, isr30, 0x08, flags);
idt_set_gate(31, isr31, 0x08, flags);
// Remap PIC
pic_remap(PIC1_START_INTERRUPT, PIC2_START_INTERRUPT);
// Also remap 15 entries for IRQs
idt_set_gate(32, irq0, 0x08, flags);
idt_set_gate(33, irq1, 0x08, flags);
idt_set_gate(34, irq2, 0x08, flags);
idt_set_gate(35, irq3, 0x08, flags);
idt_set_gate(36, irq4, 0x08, flags);
idt_set_gate(37, irq5, 0x08, flags);
idt_set_gate(38, irq6, 0x08, flags);
idt_set_gate(39, irq7, 0x08, flags);
idt_set_gate(40, irq8, 0x08, flags);
idt_set_gate(41, irq9, 0x08, flags);
idt_set_gate(42, irq10, 0x08, flags);
idt_set_gate(43, irq11, 0x08, flags);
idt_set_gate(44, irq12, 0x08, flags);
idt_set_gate(45, irq13, 0x08, flags);
idt_set_gate(46, irq14, 0x08, flags);
idt_set_gate(47, irq15, 0x08, flags);
idt_flush(&idt_ptr);
// Finally enable hardware interrupts with an assembly instruction
__asm__ __volatile__ ("sti");
}
// Taken from: http://wiki.osdev.org/8259_PIC
static void pic_remap(uint8_t offset1, uint8_t offset2) {
uint8_t a1, a2;
a1 = inb(PIC1_DATA); // Save masks
a2 = inb(PIC2_DATA);
outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4); // Start init sequence
outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
outb(PIC1_DATA, offset1);
outb(PIC2_DATA, offset2);
outb(PIC1_DATA, 4); // Tell master PIC that there is a slave PIC at IRQ2
outb(PIC1_DATA, 2); // Tell salve PIC it's cascade identity
outb(PIC1_DATA, ICW4_8086);
outb(PIC2_DATA, ICW4_8086);
// Restore saved masks
outb(PIC1_DATA, a1);
outb(PIC2_DATA, a2);
}

View File

@@ -1,144 +1,144 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _IDT_H_
#define _IDT_H_
#include <stdint.h>
/*
* First a bit of theory:
* Sometimes you want to interrupt the processor from what it is currently doing
* and force it to do something more "critical", such as a timer update or a keyboard interrupt
* request(IRQ) fires. The processor, then, will handle those IRQs activating an
* interrupt handler(ISR, Interrupt Service Routines). In order to know where to find those
* ISRs, the CPU will use the IDT. So we can say that the Interrupt Description Table
* is another data structure(organized the same way as the GDT) that will provide a list
* of interrupts handlers(ISRs) to the CPU.
*/
// Reserved bits in IDT entries
#define IDT_FLAG_RESERVED 0x0E
// PIC
#define PIC1 0x20 // I/O address for master PIC
#define PIC2 0xA0 // I/O address for slave PIC
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define PIC1_START_INTERRUPT 0x20 // Master PIC after remapping
#define PIC2_START_INTERRUPT 0x28 // Slave PIC after remapping
#define PIC_EOI 0x20 // End of interrupt
#define ICW1_ICW4 0x01
#define ICW1_SINGLE 0x02
#define ICW1_INTERVAL4 0x04
#define ICW1_LEVEL 0x08
#define ICW1_INIT 0x10
#define ICW4_8086 0x01 // 8086/88 (MCS-80/85) mode
#define ICW4_AUTO 0x02
#define ICW4_BUF_SLAVE 0x08
#define ICW4_BUF_MASTER 0x0C
#define ICW4_SFNM 0x10
/* Interrupt Descriptor Table */
/* idt_flags contains access flag of a single IDT entry
* | 0 - 4 | 5 - 6 | 7 |
* | res | dpl | p |
* res: always 0x0E
* dpl: ring num (0 - 3)
* p: segment present (bool)
*/
struct idt_flags {
uint8_t reserved: 5;
uint8_t dpl: 2;
uint8_t p: 1;
}__attribute__((packed));
typedef struct idt_flags idt_flags_t;
/* idt_entry contains the value of an IDT entry
* Each entry is 64 bits(like GDT entry)
* | 0 - 15 | 16 - 31 | 32 - 39 | 40 - 47 | 48 - 63 |
* | b low | seg sel | res | flags | b high |
* b low: Lower 16 bits of the base
* seg sel: Segment selector for code segment
* res: always 0
* flags: idt_flags struct
* b high: Higher 16 bits of the base
*/
struct idt_entry {
uint16_t base_low;
uint16_t seg_sel;
uint8_t reserved;
idt_flags_t flags;
uint16_t base_high;
}__attribute__((packed));
typedef struct idt_entry idt_entry_t;
/* Also we have to define a pointer to the data structure
* This is needed to locate it later */
struct idt_ptr {
uint16_t limit;
idt_entry_t *base;
}__attribute__((packed));
typedef struct idt_ptr idt_ptr_t;
/* IDT Kernel API */
void idt_setup();
// ISRs method declaration
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
extern void irq0();
extern void irq1();
extern void irq2();
extern void irq3();
extern void irq4();
extern void irq5();
extern void irq6();
extern void irq7();
extern void irq8();
extern void irq9();
extern void irq10();
extern void irq11();
extern void irq12();
extern void irq13();
extern void irq14();
extern void irq15();
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef _IDT_H_
#define _IDT_H_
#include <stdint.h>
/*
* First a bit of theory:
* Sometimes you want to interrupt the processor from what it is currently doing
* and force it to do something more "critical", such as a timer update or a keyboard interrupt
* request(IRQ) fires. The processor, then, will handle those IRQs activating an
* interrupt handler(ISR, Interrupt Service Routines). In order to know where to find those
* ISRs, the CPU will use the IDT. So we can say that the Interrupt Description Table
* is another data structure(organized the same way as the GDT) that will provide a list
* of interrupts handlers(ISRs) to the CPU.
*/
// Reserved bits in IDT entries
#define IDT_FLAG_RESERVED 0x0E
// PIC
#define PIC1 0x20 // I/O address for master PIC
#define PIC2 0xA0 // I/O address for slave PIC
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define PIC1_START_INTERRUPT 0x20 // Master PIC after remapping
#define PIC2_START_INTERRUPT 0x28 // Slave PIC after remapping
#define PIC_EOI 0x20 // End of interrupt
#define ICW1_ICW4 0x01
#define ICW1_SINGLE 0x02
#define ICW1_INTERVAL4 0x04
#define ICW1_LEVEL 0x08
#define ICW1_INIT 0x10
#define ICW4_8086 0x01 // 8086/88 (MCS-80/85) mode
#define ICW4_AUTO 0x02
#define ICW4_BUF_SLAVE 0x08
#define ICW4_BUF_MASTER 0x0C
#define ICW4_SFNM 0x10
/* Interrupt Descriptor Table */
/* idt_flags contains access flag of a single IDT entry
* | 0 - 4 | 5 - 6 | 7 |
* | res | dpl | p |
* res: always 0x0E
* dpl: ring num (0 - 3)
* p: segment present (bool)
*/
struct idt_flags {
uint8_t reserved: 5;
uint8_t dpl: 2;
uint8_t p: 1;
}__attribute__((packed));
typedef struct idt_flags idt_flags_t;
/* idt_entry contains the value of an IDT entry
* Each entry is 64 bits(like GDT entry)
* | 0 - 15 | 16 - 31 | 32 - 39 | 40 - 47 | 48 - 63 |
* | b low | seg sel | res | flags | b high |
* b low: Lower 16 bits of the base
* seg sel: Segment selector for code segment
* res: always 0
* flags: idt_flags struct
* b high: Higher 16 bits of the base
*/
struct idt_entry {
uint16_t base_low;
uint16_t seg_sel;
uint8_t reserved;
idt_flags_t flags;
uint16_t base_high;
}__attribute__((packed));
typedef struct idt_entry idt_entry_t;
/* Also we have to define a pointer to the data structure
* This is needed to locate it later */
struct idt_ptr {
uint16_t limit;
idt_entry_t *base;
}__attribute__((packed));
typedef struct idt_ptr idt_ptr_t;
/* IDT Kernel API */
void idt_setup();
// ISRs method declaration
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
extern void irq0();
extern void irq1();
extern void irq2();
extern void irq3();
extern void irq4();
extern void irq5();
extern void irq6();
extern void irq7();
extern void irq8();
extern void irq9();
extern void irq10();
extern void irq11();
extern void irq12();
extern void irq13();
extern void irq14();
extern void irq15();
#endif

View File

@@ -1,56 +1,56 @@
#include "initrd.h"
// Declare various things
initrd_header_t *initrd_header; // Header
initrd_file_header_t *file_header; // List of headers
fs_node_t *initrd_root; // Directory root node
fs_node_t *initrd_dev; // Directory dev node(/dev)
fs_node_t *root_nodes; // List of file nodes
uint32_t nroot_nodes;
struct dirent dirent;
fs_node_t *init_ramdisk(uint32_t multiboot_location) {
// Initialize main and file headers
initrd_header = (initrd_header_t*)multiboot_location;
file_header = (initrd_file_header_t*)(multiboot_location+sizeof(initrd_header_t));
// Initialize root directory
initrd_root = (fs_node_t*)
}
static uint32_t initrd_read(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
initrd_file_header_t header = file_header[node->inode];
if(offset > header.length)
return 0;
if(offset+size > header.length)
size = header.length-offset;
memcpy(buffer, (uint8_t*) (header.offset+offset), size);
return size;
}
static struct dirent *initrd_readdir(fs_node_t *node, uint32_t index) {
if(node == initrd_root && index == 0) {
strcpy(dirent.name, "dev");
dirent.name[3] = 0; // Add null terminator to the string
dirent.ino = 0;
return &dirent;
}
if(index-1 >= &root_nodes)
return 0;
strcpy(dirent.name, root_nodes[index-1].name);
dirent.name[strlen(root_nodes[index-1].name)] = 0; // Add null terminator
dirent.ino = root_nodes[index-1].inode;
return &dirent;
}
static fs_node_t *initrd_finddir(fs_node_t *node, uint8_t *name) {
if(node == initrd_root && !strcmp(name, "dev"))
return initrd_dev;
for(uint32_t i = 0; i < nroot_nodes; i++)
if(!strcmp(name, root_nodes[i].name))
return &root_nodes[i];
return 0;
#include "initrd.h"
// Declare various things
initrd_header_t *initrd_header; // Header
initrd_file_header_t *file_header; // List of headers
fs_node_t *initrd_root; // Directory root node
fs_node_t *initrd_dev; // Directory dev node(/dev)
fs_node_t *root_nodes; // List of file nodes
uint32_t nroot_nodes;
struct dirent dirent;
fs_node_t *init_ramdisk(uint32_t multiboot_location) {
// Initialize main and file headers
initrd_header = (initrd_header_t*)multiboot_location;
file_header = (initrd_file_header_t*)(multiboot_location+sizeof(initrd_header_t));
// Initialize root directory
initrd_root = (fs_node_t*)
}
static uint32_t initrd_read(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) {
initrd_file_header_t header = file_header[node->inode];
if(offset > header.length)
return 0;
if(offset+size > header.length)
size = header.length-offset;
memcpy(buffer, (uint8_t*) (header.offset+offset), size);
return size;
}
static struct dirent *initrd_readdir(fs_node_t *node, uint32_t index) {
if(node == initrd_root && index == 0) {
strcpy(dirent.name, "dev");
dirent.name[3] = 0; // Add null terminator to the string
dirent.ino = 0;
return &dirent;
}
if(index-1 >= &root_nodes)
return 0;
strcpy(dirent.name, root_nodes[index-1].name);
dirent.name[strlen(root_nodes[index-1].name)] = 0; // Add null terminator
dirent.ino = root_nodes[index-1].inode;
return &dirent;
}
static fs_node_t *initrd_finddir(fs_node_t *node, uint8_t *name) {
if(node == initrd_root && !strcmp(name, "dev"))
return initrd_dev;
for(uint32_t i = 0; i < nroot_nodes; i++)
if(!strcmp(name, root_nodes[i].name))
return &root_nodes[i];
return 0;
}

View File

@@ -1,36 +1,36 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef INITRD_H
#define INITRD_H
/* Ramdisk is a file system that is loaded along with the kernel
* and it usually contains configuration files or file system drivers.
* It is used BEFORE partitions are being mounted*/
#include <stdint.h>
#include "../libc/string.h"
#include "fs.h"
typedef struct {
uint32_t nfiles; // Number of files in the ramdisk
} initrd_header_t;
typedef struct {
uint8_t magic; // Magic number for error checking
int8_t name[64]; // Filename
uint32_t offset;
uint32_t length; // Length of the file
} initrd_file_header_t;
// Function to initialize initrd, we'll pass the multiboot
// module as parameter
fs_node_t *init_ramdisk(uint32_t multiboot_location);
static uint32_t initrd_read(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
static struct dirent *initrd_readdir(fs_node_t *node, uint32_t index);
static fs_node_t *initrd_finddir(fs_node_t *node, uint8_t *name);
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef INITRD_H
#define INITRD_H
/* Ramdisk is a file system that is loaded along with the kernel
* and it usually contains configuration files or file system drivers.
* It is used BEFORE partitions are being mounted*/
#include <stdint.h>
#include "../libc/string.h"
#include "fs.h"
typedef struct {
uint32_t nfiles; // Number of files in the ramdisk
} initrd_header_t;
typedef struct {
uint8_t magic; // Magic number for error checking
int8_t name[64]; // Filename
uint32_t offset;
uint32_t length; // Length of the file
} initrd_file_header_t;
// Function to initialize initrd, we'll pass the multiboot
// module as parameter
fs_node_t *init_ramdisk(uint32_t multiboot_location);
static uint32_t initrd_read(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer);
static struct dirent *initrd_readdir(fs_node_t *node, uint32_t index);
static fs_node_t *initrd_finddir(fs_node_t *node, uint8_t *name);
#endif

View File

@@ -1,87 +1,87 @@
#include <stdint.h>
#include "isr.h"
#include "tty.h"
#include "../libc/string.h"
#include "ports.h"
#define PIC1 0x20 // I/O address for master PIC
#define PIC2 0xA0 // I/O address for slave PIC
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define PIC_EOI 0x20 // End Of Interrupt command
// List of messages for known interrupts
uint8_t *interrupts_messages[] = {
(uint8_t*)"Division by Zero", // 0
(uint8_t*)"Debug",
(uint8_t*)"Non-maskable interrupt",
(uint8_t*)"Breakpoint",
(uint8_t*)"Detected overflow",
(uint8_t*)"Out-of-bounds", // 5
(uint8_t*)"Invalid opcode",
(uint8_t*)"No coprocessor",
(uint8_t*)"Double fault",
(uint8_t*)"Coprocessor segment overrun",
(uint8_t*)"Bad TSS", // 10
(uint8_t*)"Segment not present",
(uint8_t*)"Stack fault",
(uint8_t*)"General protection fault",
(uint8_t*)"Page fault",
(uint8_t*)"Unknown interrupt", // 15
(uint8_t*)"Coprocessor fault",
(uint8_t*)"Alignment check",
(uint8_t*)"Machine check",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved"
};
isr_t interrupt_handler[256];
void isr_handler(registers_t regs) {
if(interrupt_handler[regs.int_num] != 0) {
isr_t handler = interrupt_handler[regs.int_num];
handler(regs);
} else {
uint8_t *buf = (uint8_t*)"\nReceived interrupt: ";
kprint_c((uint8_t*)buf,strlen(buf), LIGHT_BROWN, BLACK);
kprint_c(interrupts_messages[(uint8_t)regs.int_num],
strlen(interrupts_messages[(uint8_t)regs.int_num]),
WHITE,
BLACK
);
}
}
void ack_irq(uint32_t int_num) {
// Send and End Of Interrupt(EOF) at the PICs.
if(int_num >= 40)
outb(PIC2_COMMAND, PIC_EOI); // Send reset signal to slave
outb(PIC1_COMMAND, PIC_EOI); // In any case, reset the master
}
void irq_handler(registers_t regs) {
ack_irq(regs.int_num);
if(interrupt_handler[regs.int_num] != 0) {
isr_t handler = interrupt_handler[regs.int_num];
handler(regs);
}
}
void register_interrupt_handler(uint8_t n, isr_t handler) {
interrupt_handler[n] = handler;
#include <stdint.h>
#include "isr.h"
#include "tty.h"
#include "../libc/string.h"
#include "ports.h"
#define PIC1 0x20 // I/O address for master PIC
#define PIC2 0xA0 // I/O address for slave PIC
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define PIC_EOI 0x20 // End Of Interrupt command
// List of messages for known interrupts
uint8_t *interrupts_messages[] = {
(uint8_t*)"Division by Zero", // 0
(uint8_t*)"Debug",
(uint8_t*)"Non-maskable interrupt",
(uint8_t*)"Breakpoint",
(uint8_t*)"Detected overflow",
(uint8_t*)"Out-of-bounds", // 5
(uint8_t*)"Invalid opcode",
(uint8_t*)"No coprocessor",
(uint8_t*)"Double fault",
(uint8_t*)"Coprocessor segment overrun",
(uint8_t*)"Bad TSS", // 10
(uint8_t*)"Segment not present",
(uint8_t*)"Stack fault",
(uint8_t*)"General protection fault",
(uint8_t*)"Page fault",
(uint8_t*)"Unknown interrupt", // 15
(uint8_t*)"Coprocessor fault",
(uint8_t*)"Alignment check",
(uint8_t*)"Machine check",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved",
(uint8_t*)"Reserved"
};
isr_t interrupt_handler[256];
void isr_handler(registers_t regs) {
if(interrupt_handler[regs.int_num] != 0) {
isr_t handler = interrupt_handler[regs.int_num];
handler(regs);
} else {
uint8_t *buf = (uint8_t*)"\nReceived interrupt: ";
kprint_c((uint8_t*)buf,strlen(buf), LIGHT_BROWN, BLACK);
kprint_c(interrupts_messages[(uint8_t)regs.int_num],
strlen(interrupts_messages[(uint8_t)regs.int_num]),
WHITE,
BLACK
);
}
}
void ack_irq(uint32_t int_num) {
// Send and End Of Interrupt(EOF) at the PICs.
if(int_num >= 40)
outb(PIC2_COMMAND, PIC_EOI); // Send reset signal to slave
outb(PIC1_COMMAND, PIC_EOI); // In any case, reset the master
}
void irq_handler(registers_t regs) {
ack_irq(regs.int_num);
if(interrupt_handler[regs.int_num] != 0) {
isr_t handler = interrupt_handler[regs.int_num];
handler(regs);
}
}
void register_interrupt_handler(uint8_t n, isr_t handler) {
interrupt_handler[n] = handler;
}

View File

@@ -1,72 +1,72 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _ISR_H_
#define _ISR_H_
#include <stdint.h>
/*
* When we implement ISRs we have to keep in mind that the first 32 interrupts(and so the
* first 32 ISRs) are reserved by the CPU to signal the kernel about critical actions,
* such as divide-by-zero or a stack overflow/buffer overflow.
*
* Below ther's a list of the first, reserved, interrupts...and yeah, we have to implement
* all of them by ourself(btw in Assembly) :D
*
* 0 - Division by zero exception
* 1 - Debug exception
* 2 - Non maskable interrupt
* 3 - Breakpoint exception
* 4 - Into detected overflow
* 5 - Out of bounds exception
* 6 - Invalid opcode exception
* 7 - No coprocessor exception
* 8 - Double fault (pushes an error code)
* 9 - Coprocessor segment overrun
* 10 - Bad TSS (pushes an error code)
* 11 - Segment not present (pushes an error code)
* 12 - Stack fault (pushes an error code)
* 13 - General protection fault (pushes an error code)
* 14 - Page fault (pushes an error code)
* 15 - Unknown interrupt exception
* 16 - Coprocessor fault
* 17 - Alignment check exception
* 18 - Machine check exception
* 19-31 - Reserved */
#define IRQ0 32
#define IRQ1 33
#define IRQ2 34
#define IRQ3 35
#define IRQ4 36
#define IRQ5 37
#define IRQ6 38
#define IRQ7 39
#define IRQ8 40
#define IRQ9 41
#define IRQ10 42
#define IRQ11 43
#define IRQ12 44
#define IRQ13 45
#define IRQ14 46
#define IRQ15 47
typedef struct registers {
uint32_t ds; // Data segment
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed with pusha
uint32_t int_num, err_code; // Interrupt number and error code
uint32_t eip, cs, eflags, usereap, ss; // Pushed by CPU
} registers_t;
typedef void (*isr_t)(registers_t);
void ack_irq(uint32_t int_num);
void register_interrupt_handler(uint8_t n, isr_t handler);
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef _ISR_H_
#define _ISR_H_
#include <stdint.h>
/*
* When we implement ISRs we have to keep in mind that the first 32 interrupts(and so the
* first 32 ISRs) are reserved by the CPU to signal the kernel about critical actions,
* such as divide-by-zero or a stack overflow/buffer overflow.
*
* Below ther's a list of the first, reserved, interrupts...and yeah, we have to implement
* all of them by ourself(btw in Assembly) :D
*
* 0 - Division by zero exception
* 1 - Debug exception
* 2 - Non maskable interrupt
* 3 - Breakpoint exception
* 4 - Into detected overflow
* 5 - Out of bounds exception
* 6 - Invalid opcode exception
* 7 - No coprocessor exception
* 8 - Double fault (pushes an error code)
* 9 - Coprocessor segment overrun
* 10 - Bad TSS (pushes an error code)
* 11 - Segment not present (pushes an error code)
* 12 - Stack fault (pushes an error code)
* 13 - General protection fault (pushes an error code)
* 14 - Page fault (pushes an error code)
* 15 - Unknown interrupt exception
* 16 - Coprocessor fault
* 17 - Alignment check exception
* 18 - Machine check exception
* 19-31 - Reserved */
#define IRQ0 32
#define IRQ1 33
#define IRQ2 34
#define IRQ3 35
#define IRQ4 36
#define IRQ5 37
#define IRQ6 38
#define IRQ7 39
#define IRQ8 40
#define IRQ9 41
#define IRQ10 42
#define IRQ11 43
#define IRQ12 44
#define IRQ13 45
#define IRQ14 46
#define IRQ15 47
typedef struct registers {
uint32_t ds; // Data segment
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed with pusha
uint32_t int_num, err_code; // Interrupt number and error code
uint32_t eip, cs, eflags, usereap, ss; // Pushed by CPU
} registers_t;
typedef void (*isr_t)(registers_t);
void ack_irq(uint32_t int_num);
void register_interrupt_handler(uint8_t n, isr_t handler);
#endif

View File

@@ -1,118 +1,118 @@
/* The keyboard driver works with a device called PS/2: to talk with this
* controller we can use serial communication(e.g. ports.h). The actual flow of
* data is made with "commands", each command is one byte and the keyboard's
* controller can send two type of response:
* ACK(Acknowledge): to acknowledge the previous command
* Resend: to resend the previous command due to an error.
* We have also to wait between the command, the data and the response of
* the PS/2 controller.
* This device should not exists anymore in any modern computer
* motherboard; however the CPU(or the motherboard?) should be able to
* simulate it even if we're using some USB keyboard.
* Apart of that the keyboard will be listen on IRQ1(33),
* so we have to register an ISR for that.
*/
#include "keyboard.h"
#include "isr.h"
#include "ports.h"
#include "tty.h"
#include "../userspace/shell.h"
#include "../libc/stdio.h"
static void keyboard_callback();
/* Keyboard scan codes map , layout is: standard US keyboard */
uint8_t keyboard_scan_codes[] = {
0, (uint8_t)27, (uint8_t)'1', (uint8_t)'2', (uint8_t)'3', (uint8_t)'4', (uint8_t)'5', (uint8_t)'6', (uint8_t)'7', (uint8_t)'8',
(uint8_t)'0', (uint8_t)'0', (uint8_t)'-', (uint8_t)'=', (uint8_t)'\b',
(uint8_t)'\t',
(uint8_t)'q', (uint8_t)'w', (uint8_t)'e', (uint8_t)'r',
(uint8_t)'t', (uint8_t)'y', (uint8_t)'u', (uint8_t)'i', (uint8_t)'o', (uint8_t)'p', (uint8_t)'[', (uint8_t)']', (uint8_t)'\n',
0,
(uint8_t)'a', (uint8_t)'s', (uint8_t)'d', (uint8_t)'f', (uint8_t)'g', (uint8_t)'h', (uint8_t)'j', (uint8_t)'k', (uint8_t)'l', (uint8_t)';',
(uint8_t)'\'', (uint8_t)'`', 0,
(uint8_t)'\\', (uint8_t)'z', (uint8_t)'x', (uint8_t)'c', (uint8_t)'v', (uint8_t)'b', (uint8_t)'n',
(uint8_t)'m', (uint8_t)',', (uint8_t)'.', (uint8_t)'/', 0,
(uint8_t)'*',
0, // Alt
(uint8_t)' ', // Spacebar
0, // Caps lock
0, // 59 - F1 key
0, // 59 - F1 key
0, 0, 0, 0, 0, 0, 0, 0,
0, // F10
0, // 69 - Num lock
0, // Scroll lock
0, // Home key
0, // Up arrow
0, // Page up
(uint8_t)'-',
0, // Left arrow
0,
0, // Right arrow
(uint8_t)'+',
0, // 79 End key
0, // Down arrow
0, // Page down
0, // Insert key
0, // Delete key
0, 0, 0,
0, // F11 key
0, // F12 key
0 // Others key are undefined
};
uint8_t command[512]; // Max length of a single command
uint8_t temp[512];
uint32_t cmd_index = 0;
uint32_t shiftdown = 0;
void clear() {
for(uint16_t i = 0; i < 512; i++)
command[i] = 0;
}
void clear_tmp(void) {
for(uint16_t i = 0; i < 512; i++)
command[i] = 0;
}
static void keyboard_callback() {
uint8_t scan_code = inb(KB_DATA_PORT); // Read from keyboard
uint8_t keycode = keyboard_scan_codes[scan_code]; // Get ASCII value
if(scan_code & 0x80) {
if(scan_code == 0xAA || scan_code == 0x86)
shiftdown = 0;
} else {
// Handle backspace
if(keycode == 0x08) {
if(cmd_index <= 0) // If at start of the prompt
return; // Do not delete it.
cmd_index--; // Otherwise go back
for(uint32_t i = 0; i < cmd_index; ++i)
temp[i] = command[i];
clear();
for(uint32_t i = 0; i < cmd_index; ++i)
command[i] = temp[i];
clear_tmp();
backspace();
} else if(keycode == 0x0A) { // Handle Enter
processCommand(command);
cmd_index = 0;
clear();
init_prompt();
} else {
printf("%c", keycode);
command[cmd_index] = keycode;
cmd_index++;
}
}
}
void init_keyboard() {
register_interrupt_handler(IRQ1, &keyboard_callback);
}
/* The keyboard driver works with a device called PS/2: to talk with this
* controller we can use serial communication(e.g. ports.h). The actual flow of
* data is made with "commands", each command is one byte and the keyboard's
* controller can send two type of response:
* ACK(Acknowledge): to acknowledge the previous command
* Resend: to resend the previous command due to an error.
* We have also to wait between the command, the data and the response of
* the PS/2 controller.
* This device should not exists anymore in any modern computer
* motherboard; however the CPU(or the motherboard?) should be able to
* simulate it even if we're using some USB keyboard.
* Apart of that the keyboard will be listen on IRQ1(33),
* so we have to register an ISR for that.
*/
#include "keyboard.h"
#include "isr.h"
#include "ports.h"
#include "tty.h"
#include "../userspace/shell.h"
#include "../libc/stdio.h"
static void keyboard_callback();
/* Keyboard scan codes map , layout is: standard US keyboard */
uint8_t keyboard_scan_codes[] = {
0, (uint8_t)27, (uint8_t)'1', (uint8_t)'2', (uint8_t)'3', (uint8_t)'4', (uint8_t)'5', (uint8_t)'6', (uint8_t)'7', (uint8_t)'8',
(uint8_t)'0', (uint8_t)'0', (uint8_t)'-', (uint8_t)'=', (uint8_t)'\b',
(uint8_t)'\t',
(uint8_t)'q', (uint8_t)'w', (uint8_t)'e', (uint8_t)'r',
(uint8_t)'t', (uint8_t)'y', (uint8_t)'u', (uint8_t)'i', (uint8_t)'o', (uint8_t)'p', (uint8_t)'[', (uint8_t)']', (uint8_t)'\n',
0,
(uint8_t)'a', (uint8_t)'s', (uint8_t)'d', (uint8_t)'f', (uint8_t)'g', (uint8_t)'h', (uint8_t)'j', (uint8_t)'k', (uint8_t)'l', (uint8_t)';',
(uint8_t)'\'', (uint8_t)'`', 0,
(uint8_t)'\\', (uint8_t)'z', (uint8_t)'x', (uint8_t)'c', (uint8_t)'v', (uint8_t)'b', (uint8_t)'n',
(uint8_t)'m', (uint8_t)',', (uint8_t)'.', (uint8_t)'/', 0,
(uint8_t)'*',
0, // Alt
(uint8_t)' ', // Spacebar
0, // Caps lock
0, // 59 - F1 key
0, // 59 - F1 key
0, 0, 0, 0, 0, 0, 0, 0,
0, // F10
0, // 69 - Num lock
0, // Scroll lock
0, // Home key
0, // Up arrow
0, // Page up
(uint8_t)'-',
0, // Left arrow
0,
0, // Right arrow
(uint8_t)'+',
0, // 79 End key
0, // Down arrow
0, // Page down
0, // Insert key
0, // Delete key
0, 0, 0,
0, // F11 key
0, // F12 key
0 // Others key are undefined
};
uint8_t command[512]; // Max length of a single command
uint8_t temp[512];
uint32_t cmd_index = 0;
uint32_t shiftdown = 0;
void clear() {
for(uint16_t i = 0; i < 512; i++)
command[i] = 0;
}
void clear_tmp(void) {
for(uint16_t i = 0; i < 512; i++)
command[i] = 0;
}
static void keyboard_callback() {
uint8_t scan_code = inb(KB_DATA_PORT); // Read from keyboard
uint8_t keycode = keyboard_scan_codes[scan_code]; // Get ASCII value
if(scan_code & 0x80) {
if(scan_code == 0xAA || scan_code == 0x86)
shiftdown = 0;
} else {
// Handle backspace
if(keycode == 0x08) {
if(cmd_index <= 0) // If at start of the prompt
return; // Do not delete it.
cmd_index--; // Otherwise go back
for(uint32_t i = 0; i < cmd_index; ++i)
temp[i] = command[i];
clear();
for(uint32_t i = 0; i < cmd_index; ++i)
command[i] = temp[i];
clear_tmp();
backspace();
} else if(keycode == 0x0A) { // Handle Enter
processCommand(command);
cmd_index = 0;
clear();
init_prompt();
} else {
printf("%c", keycode);
command[cmd_index] = keycode;
cmd_index++;
}
}
}
void init_keyboard() {
register_interrupt_handler(IRQ1, &keyboard_callback);
}

View File

@@ -1,17 +1,17 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
#include <stdint.h>
#define KB_DATA_PORT 0x60 // Keyboard serial port
void init_keyboard();
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
#include <stdint.h>
#define KB_DATA_PORT 0x60 // Keyboard serial port
void init_keyboard();
#endif

View File

@@ -1,27 +1,27 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _PORTS_H
#define _PORTS_H
#include <stdint.h>
/* outb:
* Redirect data to port(high level interface for ports.asm)
* @param port: Output port to send data to.
* @param data: The actual data to send to port
*/
void outb(uint16_t port, uint16_t data);
/* inb:
* Fetch data from a port, return a char
* @param port: Input port to read data from.
*/
uint8_t inb(uint16_t port);
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef _PORTS_H
#define _PORTS_H
#include <stdint.h>
/* outb:
* Redirect data to port(high level interface for ports.asm)
* @param port: Output port to send data to.
* @param data: The actual data to send to port
*/
void outb(uint16_t port, uint16_t data);
/* inb:
* Fetch data from a port, return a char
* @param port: Input port to read data from.
*/
uint8_t inb(uint16_t port);
#endif

View File

@@ -1,39 +1,39 @@
#include "timer.h"
#include "isr.h"
#include "tty.h"
#include "../libc/string.h"
#include "ports.h"
// Start tick at zero
uint32_t tick = 0;
static void timer_callback(registers_t regs) {
tick++;
/* uint8_t buf[8];
itoa(tick, buf, 10);
kprint((uint8_t*)"Time: ");
uitoa(tick, buf, 10);
kprint((uint8_t*)buf);
kprint((uint8_t*)"\n");*/
// Cast to void unused parameter
UNUSED_PAR(regs);
}
void init_timer(uint32_t frequency) {
// Register a new ISR for IRQ0
register_interrupt_handler(IRQ0, &timer_callback);
/* As mentioned before, we'll send to PIT a value to divide for
* his system clock(1.1931 MHz). We have to keep in mind that
* this value must fit into a 16 bits variable */
uint32_t divisor = 1193180 / frequency;
// Send command to the right port
outb(0x43, 0x36);
// The two divisor has to be sent byte-wise, to do this we split them in two parts
uint8_t low = (uint8_t)(divisor & 0xFF);
uint8_t high = (uint8_t)((divisor >> 8) & 0xFF);
// Send the frequency divisor
outb(0x40, low);
outb(0x40, high);
}
#include "timer.h"
#include "isr.h"
#include "tty.h"
#include "../libc/string.h"
#include "ports.h"
// Start tick at zero
uint32_t tick = 0;
static void timer_callback(registers_t regs) {
tick++;
/* uint8_t buf[8];
itoa(tick, buf, 10);
kprint((uint8_t*)"Time: ");
uitoa(tick, buf, 10);
kprint((uint8_t*)buf);
kprint((uint8_t*)"\n");*/
// Cast to void unused parameter
UNUSED_PAR(regs);
}
void init_timer(uint32_t frequency) {
// Register a new ISR for IRQ0
register_interrupt_handler(IRQ0, &timer_callback);
/* As mentioned before, we'll send to PIT a value to divide for
* his system clock(1.1931 MHz). We have to keep in mind that
* this value must fit into a 16 bits variable */
uint32_t divisor = 1193180 / frequency;
// Send command to the right port
outb(0x43, 0x36);
// The two divisor has to be sent byte-wise, to do this we split them in two parts
uint8_t low = (uint8_t)(divisor & 0xFF);
uint8_t high = (uint8_t)((divisor >> 8) & 0xFF);
// Send the frequency divisor
outb(0x40, low);
outb(0x40, high);
}

View File

@@ -1,41 +1,41 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _TIMER_H_
#define _TIMER_H_
#include <stdint.h>
/*
* The PIT(Programmable Interval Timer) is a chip that consist of an oscillator
* It's connected to IRQ0 and it can be configure at a user-defined rate
* between 10.Hz to 1.1931 MHz. The PIT is the primary method used to implement
* a system clock and for implement multitasking.
* The PIT's internal clock(~1.1931 MHz) is fed through a frequency divider that
* modulate the final signal. This chip has 3 channel, each with his own
* frequency divider:
* Channel 0: The most useful, it's output is connected to IRQ0
* Channel 1: It were used to control refresh rates on DRAM(RAMs with capacitors)
* Channel 2: Controls the PC speakers.
*
* In our case, we will use only channel 0.
* So we have to set up PIT at a frequency 'f', so it interrupts us at regular
* intervals. We'll set the frequency to 100Hz(once every 10 ms); to do this
* we'll send the PIT a divisor that will be divided for it's input frequency.
* E.g. -> divisor = 1193180 Hz(1.1931MHz) / 100 Hz
*
* Apart of that, the PIT has 4 registers: 0x40-0x42(data ports) and 0x43(command port)
*/
void init_timer(uint32_t frequency);
extern uint32_t tick;
/* Since regs parameter(from timer_callback) will be unused
* GCC(with -Werror flag) will throw an error, so we can avoid this
* using the following macro
*/
#define UNUSED_PAR(x) (void)(x)
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef _TIMER_H_
#define _TIMER_H_
#include <stdint.h>
/*
* The PIT(Programmable Interval Timer) is a chip that consist of an oscillator
* It's connected to IRQ0 and it can be configure at a user-defined rate
* between 10.Hz to 1.1931 MHz. The PIT is the primary method used to implement
* a system clock and for implement multitasking.
* The PIT's internal clock(~1.1931 MHz) is fed through a frequency divider that
* modulate the final signal. This chip has 3 channel, each with his own
* frequency divider:
* Channel 0: The most useful, it's output is connected to IRQ0
* Channel 1: It were used to control refresh rates on DRAM(RAMs with capacitors)
* Channel 2: Controls the PC speakers.
*
* In our case, we will use only channel 0.
* So we have to set up PIT at a frequency 'f', so it interrupts us at regular
* intervals. We'll set the frequency to 100Hz(once every 10 ms); to do this
* we'll send the PIT a divisor that will be divided for it's input frequency.
* E.g. -> divisor = 1193180 Hz(1.1931MHz) / 100 Hz
*
* Apart of that, the PIT has 4 registers: 0x40-0x42(data ports) and 0x43(command port)
*/
void init_timer(uint32_t frequency);
extern uint32_t tick;
/* Since regs parameter(from timer_callback) will be unused
* GCC(with -Werror flag) will throw an error, so we can avoid this
* using the following macro
*/
#define UNUSED_PAR(x) (void)(x)
#endif

View File

@@ -1,153 +1,153 @@
#include "tty.h"
#include "../libc/string.h"
#include "ports.h"
#define VGA_PTR ((uint8_t*) VIDEO_MEM_ADDR) // Pointer to frame buffer
// Also define a 2 byte pointer because cells are 16 bits wide
#define UVGA_PTR ((uint16_t *)VIDEO_MEM_ADDR)
#define STRINGIZE(x) #x
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
static uint32_t fb_col = 1; // X
static uint32_t fb_row = 0; // Y
void write_cell(int16_t i, uint8_t c, uint8_t fg, uint8_t bg) {
uint8_t *fb = VGA_PTR;
fb[i*2] = c;
fb[i*2 + 1] = ((bg & 0x0F) << 4) | (fg & 0x0F);
}
void move_cursor(uint16_t pos) {
outb(VGA_CMD_PORT, VGA_HIGH_BYTE);
outb(VGA_DATA_PORT, ((pos >> 8) & 0x00FF));
outb(VGA_CMD_PORT, VGA_LOW_BYTE);
outb(VGA_DATA_PORT, pos & 0x00FF);
}
void cursor_adv() { // TODO: specify number of adv. with a parameter
if(fb_col < VGA_WIDTH - 1)
fb_col++;
else
newline();
move_cursor(fb_col + (fb_row * VGA_WIDTH));
}
void cursor_prev() {
if(fb_col == 0) {
if(fb_row == 0)
return; // If first row do not do anything
fb_col = VGA_WIDTH - 1;
fb_row--;
} else
fb_col--;
move_cursor(fb_col + (fb_row * VGA_WIDTH));
}
void backspace() {
uint16_t pos;
uint8_t c = ' ';
fb_col--;
pos = fb_col + (fb_row * VGA_WIDTH);
write_cell(pos, c, WHITE, BLACK);
}
void kprint_c(uint8_t *buf, uint32_t len, uint8_t fg, uint8_t bg) {
uint16_t pos;
for(uint32_t i = 0; i < len; i++) {
uint8_t c = buf[i];
if(c == '\n' || c == '\r')
newline();
else if(c == '\t')
tab();
else {
pos = fb_col + (fb_row * VGA_WIDTH);
write_cell(pos, (uint8_t)c, fg, bg);
cursor_adv();
}
}
}
void kprint(uint8_t *buf) {
kprint_c(buf, strlen(buf), WHITE, BLACK);
}
void kprint_dec(uint32_t num) {
if(num == 0) {
uint8_t *buf = (uint8_t*)'0';
kprint_c(buf, strlen(buf), WHITE, BLACK);;
return;
}
int32_t acc = num;
uint8_t c[32];
int32_t i = 0;
while(acc > 0) {
c[i] = '0' + acc%10;
acc /= 10;
i++;
}
c[i] = 0;
uint8_t c2[32];
c2[i--] = 0;
uint32_t j = 0;
while(i >= 0)
c2[i--] = c[j++];
kprint(c2);
}
void init_prompt() {
uint8_t user[64], hostname[64];
#ifdef DEFAULT_USER
strcpy(user, (uint8_t*)STRINGIZE_VALUE_OF(DEFAULT_USER));
#else
#error "-DDEFAULT_USER flag not set"
#endif
#ifdef DEFAULT_HOSTNAME
strcpy(hostname, (uint8_t*)STRINGIZE_VALUE_OF(DEFAULT_HOSTNAME));
#else
#error "-DDEFAULT_HOSTNAME flag not set"
#endif
newline();
kprint_c(user, strlen(user), LIGHT_CYAN, BLACK);
kprint_c((uint8_t*)"@", 1, LIGHT_RED, BLACK);
kprint_c(hostname, strlen(hostname), LIGHT_MAGENTA, BLACK);
kprint_c((uint8_t*)" #> ", 4, LIGHT_BLUE, BLACK);
}
void clear_prompt() {
fb_col = 1;
fb_row = 0;
for(uint32_t i = 0; i < (VGA_WIDTH * VGA_HEIGHT); i++)
write_cell(i, ' ', WHITE, BLACK);
move_cursor(0);
}
void clear_row(uint8_t row) {
for(size_t i = 0; i < VGA_WIDTH; i++)
write_cell((row*VGA_WIDTH)+i, ' ', WHITE, BLACK);
}
void scroll() {
uint16_t *fb = UVGA_PTR;
memmove(fb, fb+VGA_WIDTH, VGA_WIDTH*2*(VGA_HEIGHT*2-1));
clear_row(VGA_HEIGHT - 1);
}
void newline() {
if(fb_row < VGA_HEIGHT - 1) // If there's at least one cell add it
fb_row++;
else // Otherwise scroll framebuffer
scroll();
fb_col = 1;
move_cursor(fb_col + (fb_row * VGA_WIDTH));
}
void tab() {
for(uint8_t i = 0; i < 4; i++)
cursor_adv(); // Increment cursor 4 times
}
#include "tty.h"
#include "../libc/string.h"
#include "ports.h"
#define VGA_PTR ((uint8_t*) VIDEO_MEM_ADDR) // Pointer to frame buffer
// Also define a 2 byte pointer because cells are 16 bits wide
#define UVGA_PTR ((uint16_t *)VIDEO_MEM_ADDR)
#define STRINGIZE(x) #x
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
static uint32_t fb_col = 1; // X
static uint32_t fb_row = 0; // Y
void write_cell(int16_t i, uint8_t c, uint8_t fg, uint8_t bg) {
uint8_t *fb = VGA_PTR;
fb[i*2] = c;
fb[i*2 + 1] = ((bg & 0x0F) << 4) | (fg & 0x0F);
}
void move_cursor(uint16_t pos) {
outb(VGA_CMD_PORT, VGA_HIGH_BYTE);
outb(VGA_DATA_PORT, ((pos >> 8) & 0x00FF));
outb(VGA_CMD_PORT, VGA_LOW_BYTE);
outb(VGA_DATA_PORT, pos & 0x00FF);
}
void cursor_adv() { // TODO: specify number of adv. with a parameter
if(fb_col < VGA_WIDTH - 1)
fb_col++;
else
newline();
move_cursor(fb_col + (fb_row * VGA_WIDTH));
}
void cursor_prev() {
if(fb_col == 0) {
if(fb_row == 0)
return; // If first row do not do anything
fb_col = VGA_WIDTH - 1;
fb_row--;
} else
fb_col--;
move_cursor(fb_col + (fb_row * VGA_WIDTH));
}
void backspace() {
uint16_t pos;
uint8_t c = ' ';
fb_col--;
pos = fb_col + (fb_row * VGA_WIDTH);
write_cell(pos, c, WHITE, BLACK);
}
void kprint_c(uint8_t *buf, uint32_t len, uint8_t fg, uint8_t bg) {
uint16_t pos;
for(uint32_t i = 0; i < len; i++) {
uint8_t c = buf[i];
if(c == '\n' || c == '\r')
newline();
else if(c == '\t')
tab();
else {
pos = fb_col + (fb_row * VGA_WIDTH);
write_cell(pos, (uint8_t)c, fg, bg);
cursor_adv();
}
}
}
void kprint(uint8_t *buf) {
kprint_c(buf, strlen(buf), WHITE, BLACK);
}
void kprint_dec(uint32_t num) {
if(num == 0) {
uint8_t *buf = (uint8_t*)'0';
kprint_c(buf, strlen(buf), WHITE, BLACK);;
return;
}
int32_t acc = num;
uint8_t c[32];
int32_t i = 0;
while(acc > 0) {
c[i] = '0' + acc%10;
acc /= 10;
i++;
}
c[i] = 0;
uint8_t c2[32];
c2[i--] = 0;
uint32_t j = 0;
while(i >= 0)
c2[i--] = c[j++];
kprint(c2);
}
void init_prompt() {
uint8_t user[64], hostname[64];
#ifdef DEFAULT_USER
strcpy(user, (uint8_t*)STRINGIZE_VALUE_OF(DEFAULT_USER));
#else
#error "-DDEFAULT_USER flag not set"
#endif
#ifdef DEFAULT_HOSTNAME
strcpy(hostname, (uint8_t*)STRINGIZE_VALUE_OF(DEFAULT_HOSTNAME));
#else
#error "-DDEFAULT_HOSTNAME flag not set"
#endif
newline();
kprint_c(user, strlen(user), LIGHT_CYAN, BLACK);
kprint_c((uint8_t*)"@", 1, LIGHT_RED, BLACK);
kprint_c(hostname, strlen(hostname), LIGHT_MAGENTA, BLACK);
kprint_c((uint8_t*)" #> ", 4, LIGHT_BLUE, BLACK);
}
void clear_prompt() {
fb_col = 1;
fb_row = 0;
for(uint32_t i = 0; i < (VGA_WIDTH * VGA_HEIGHT); i++)
write_cell(i, ' ', WHITE, BLACK);
move_cursor(0);
}
void clear_row(uint8_t row) {
for(size_t i = 0; i < VGA_WIDTH; i++)
write_cell((row*VGA_WIDTH)+i, ' ', WHITE, BLACK);
}
void scroll() {
uint16_t *fb = UVGA_PTR;
memmove(fb, fb+VGA_WIDTH, VGA_WIDTH*2*(VGA_HEIGHT*2-1));
clear_row(VGA_HEIGHT - 1);
}
void newline() {
if(fb_row < VGA_HEIGHT - 1) // If there's at least one cell add it
fb_row++;
else // Otherwise scroll framebuffer
scroll();
fb_col = 1;
move_cursor(fb_col + (fb_row * VGA_WIDTH));
}
void tab() {
for(uint8_t i = 0; i < 4; i++)
cursor_adv(); // Increment cursor 4 times
}

View File

@@ -1,63 +1,63 @@
/**************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _TTY_H_
#define _TTY_H_
#include <stdint.h>
// TODO: write something about Frame Buffer
// VGA colors
enum TTY_COLORS {
BLACK, // 0
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHT_GREY,
DARK_GREY,
LIGHT_BLUE,
LIGHT_GREEN,
LIGHT_CYAN,
LIGHT_RED,
LIGHT_MAGENTA,
LIGHT_BROWN,
WHITE // 15
};
/* Framebuffer properties */
#define VIDEO_MEM_ADDR 0x000B8000 // frame buffer address
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
/* VGA I/O ports */
#define VGA_CMD_PORT 0x3D4
#define VGA_DATA_PORT 0x3D5
/* VGA I/O ports commands */
#define VGA_HIGH_BYTE 14
#define VGA_LOW_BYTE 15
/* Kernel's VGA API */
void write_cell(int16_t i, uint8_t c, uint8_t fg, uint8_t bg);
void move_cursor(uint16_t pos);
void cursor_adv();
void backspace();
void kprint_c(uint8_t *buf, uint32_t len, uint8_t fg, uint8_t bg);
void kprint(uint8_t *buf);
void kprint_dec(uint32_t num);
void init_prompt();
void clear_prompt();
void clear_row(uint8_t row);
void scroll(); // Scroll one row
void newline();
void tab();
#endif
/*****************************************
* VulcanOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019-2021 *
* Released under GPLv3 *
* https://github.com/ice-bit/vulcanos *
*****************************************/
#ifndef _TTY_H_
#define _TTY_H_
#include <stdint.h>
// TODO: write something about Frame Buffer
// VGA colors
enum TTY_COLORS {
BLACK, // 0
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHT_GREY,
DARK_GREY,
LIGHT_BLUE,
LIGHT_GREEN,
LIGHT_CYAN,
LIGHT_RED,
LIGHT_MAGENTA,
LIGHT_BROWN,
WHITE // 15
};
/* Framebuffer properties */
#define VIDEO_MEM_ADDR 0x000B8000 // frame buffer address
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
/* VGA I/O ports */
#define VGA_CMD_PORT 0x3D4
#define VGA_DATA_PORT 0x3D5
/* VGA I/O ports commands */
#define VGA_HIGH_BYTE 14
#define VGA_LOW_BYTE 15
/* Kernel's VGA API */
void write_cell(int16_t i, uint8_t c, uint8_t fg, uint8_t bg);
void move_cursor(uint16_t pos);
void cursor_adv();
void backspace();
void kprint_c(uint8_t *buf, uint32_t len, uint8_t fg, uint8_t bg);
void kprint(uint8_t *buf);
void kprint_dec(uint32_t num);
void init_prompt();
void clear_prompt();
void clear_row(uint8_t row);
void scroll(); // Scroll one row
void newline();
void tab();
#endif