Refactoring assembly entry point
This commit is contained in:
@@ -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 $@
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user