diff --git a/Makefile b/Makefile index cb30283..863aa8b 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ BUILD_DIR = build ISO_DIR = iso_root OS_NAME = KeblaOS -OS_VERSION = 0.6 +OS_VERSION = 0.7 HOST_HOME = /home/baponkar @@ -56,6 +56,16 @@ $(BUILD_DIR)/kernel.o: $(SRC_DIR)/kernel/kernel.c $(GCC) $(GCC_FLAG) -c $(SRC_DIR)/x86_64/gdt.c -o $(BUILD_DIR)/gdt.o $(NASM) $(NASM_FLAG) $(SRC_DIR)/x86_64/gdt_load.asm -o $(BUILD_DIR)/gdt_load.o + $(GCC) $(GCC_FLAG) -c $(SRC_DIR)/x86_64/idt.c -o $(BUILD_DIR)/idt.o + $(NASM) $(NASM_FLAG) $(SRC_DIR)/x86_64/idt_load.asm -o $(BUILD_DIR)/idt_load.o + + $(GCC) $(GCC_FLAG) -c $(SRC_DIR)/x86_64/pit_timer.c -o $(BUILD_DIR)/pit_timer.o + + $(GCC) $(GCC_FLAG) -c $(SRC_DIR)/driver/keyboard.c -o $(BUILD_DIR)/keyboard.o + $(GCC) $(GCC_FLAG) -c $(SRC_DIR)/driver/speaker.c -o $(BUILD_DIR)/speaker.o + $(GCC) $(GCC_FLAG) -c $(SRC_DIR)/usr/shell.c -o $(BUILD_DIR)/shell.o + $(NASM) $(NASM_FLAG) $(SRC_DIR)/usr/print_reg_values.asm -o $(BUILD_DIR)/print_reg_values.o + # Linking object files into kernel binary $(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel.o \ @@ -66,7 +76,14 @@ $(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel.o \ $(BUILD_DIR)/stdlib.o \ $(BUILD_DIR)/vga.o \ $(BUILD_DIR)/gdt.o \ - $(BUILD_DIR)/gdt_load.o + $(BUILD_DIR)/gdt_load.o \ + $(BUILD_DIR)/idt.o \ + $(BUILD_DIR)/idt_load.o \ + $(BUILD_DIR)/pit_timer.o \ + $(BUILD_DIR)/keyboard.o \ + $(BUILD_DIR)/speaker.o \ + $(BUILD_DIR)/shell.o \ + $(BUILD_DIR)/print_reg_values.o $(LD) $(LD_FLAG) -T $(SRC_DIR)/linker.ld -o $(BUILD_DIR)/kernel.bin \ $(BUILD_DIR)/kernel.o \ @@ -77,7 +94,14 @@ $(BUILD_DIR)/kernel.bin: $(BUILD_DIR)/kernel.o \ $(BUILD_DIR)/stdlib.o \ $(BUILD_DIR)/vga.o \ $(BUILD_DIR)/gdt.o \ - $(BUILD_DIR)/gdt_load.o + $(BUILD_DIR)/gdt_load.o \ + $(BUILD_DIR)/idt.o \ + $(BUILD_DIR)/idt_load.o \ + $(BUILD_DIR)/pit_timer.o \ + $(BUILD_DIR)/keyboard.o \ + $(BUILD_DIR)/speaker.o \ + $(BUILD_DIR)/shell.o \ + $(BUILD_DIR)/print_reg_values.o # Creating ISO image diff --git a/iso_root/boot/limine/limine.conf b/iso_root/boot/limine/limine.conf index c731ac2..5673a5a 100644 --- a/iso_root/boot/limine/limine.conf +++ b/iso_root/boot/limine/limine.conf @@ -6,7 +6,7 @@ # Build Date : 05/12/2024 # Timeout in seconds that Limine will use before automatically booting. -timeout: 1 +timeout: 5 verbose: yes diff --git a/src/driver/keyboard.c b/src/driver/keyboard.c new file mode 100644 index 0000000..91ac85f --- /dev/null +++ b/src/driver/keyboard.c @@ -0,0 +1,244 @@ +/* +https://wiki.osdev.org/PS/2_Keyboard + +*/ + + +#include "keyboard.h" + +#define BUFFER_SIZE 128 // Max Size of Command Buffer +char COMMAND_BUFFER[BUFFER_SIZE]; // Command string Container Buffer +int BUFFER_INDEX = 0; // Current Length of Command String Buffer + +uint32_t scanCode; // What key is pressed +bool press; // Press down, or released + +bool shift = false; // Shift Key pressed or not +bool capsLock = false; // Caps Lock Key pressed or not + + + + +const uint32_t lowercase[128] = { + UNKNOWN,ESC,'1','2','3','4','5','6','7','8', + '9','0','-','=','\b','\t','q','w','e','r', + 't','y','u','i','o','p','[',']','\n',CTRL, + 'a','s','d','f','g','h','j','k','l',';', + '\'','`',LSHIFT,'\\','z','x','c','v','b','n', + 'm',',','.','/',RSHIFT,'*',ALT,' ',CAPS_LOCK,F1, + F2,F3,F4,F5,F6,F7,F8,F9,F10,NUM_LOCK,SCROLL_LOCK, + HOME,UP,PAGE_UP,'-',LEFT,UNKNOWN,RIGHT,'+',END, + DOWN,PAGE_DOWN,INSERT,DELETE,UNKNOWN,UNKNOWN,UNKNOWN,F11,F12,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN +}; + + +const uint32_t uppercase[128] = { + UNKNOWN,ESC,'!','@','#','$','%','^','&','*', + '(',')','_','+','\b','\t','Q','W','E','R', + 'T','Y','U','I','O','P','{','}','\n',CTRL, + 'A','S','D','F','G','H','J','K','L',':', + '"','~',LSHIFT,'|','Z','X','C','V','B','N', + 'M','<','>','?',RSHIFT,'*',ALT,' ',CAPS_LOCK,F1, + F2,F3,F4,F5,F6,F7,F8,F9,F10,NUM_LOCK,SCROLL_LOCK, + HOME,UP,PAGE_UP,'-',LEFT,UNKNOWN,RIGHT,'+',END, + DOWN,PAGE_DOWN,INSERT,DELETE,UNKNOWN,UNKNOWN,UNKNOWN,F11,F12,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, + UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN +}; + + +int getScanCode(){ + return inb(0x60) & 0x7F; +} + + +bool getKeyState(){ + char key_state = (char) inb(0x60) & 0x80; // Press down return 0x0000000, or released return 0xfffff80 + + // Key Released + if(key_state == 0xFFFFFF80){ + return false; + } + // Key Pressed + else{ + return true; + } +} + + +char scanCodeToChar(uint32_t scanCode) { + if(scanCode == 0xE0){ + scanCode = (scanCode | 0x100); // Example: Combine with a base to indicate extended + } + + if (scanCode >= NUM_1 && scanCode <= NUM_0) { + // Handles NUM_0 to NUM_9, using the shift flag + return shift ? uppercase[scanCode] : lowercase[scanCode]; + } else { + // For other keys, check capsLock or other flags + return capsLock ? uppercase[scanCode] : lowercase[scanCode]; + } +} + +void key_ctrl(uint32_t scanCode, bool keyPress){ + switch(scanCode){ + case 0x00000060: // Extra scan code returning + break; + case 0x00000608: // Extra scan code returning + break; + case UNKNOWN: + case ESC: // ESC Key + // beep(); + break; + case ENTER: // Enter Key Manage + handel_enter_key(keyPress); + break; + case CTRL: // CTRL + break; + case ALT: // ALT + break; + case F1: // F1 + break; + case F2: // F2 + break; + case F3: // F3 + break; + case F4: // F4 + break; + case F5: // F5 + break; + case F6: // F6 + break; + case F7: // F7 + break; + case F8: // F8 + break; + case F9: // F9 + break; + case F10: // F10 + break; + case DELETE: // DELETEete key + handel_del_key(keyPress); + case F11: // F11 + break; + case F12: // F12 + break; + case LSHIFT: // Left shift key + handel_shift_key(keyPress); + break; + case RSHIFT: // Right shift key + handel_shift_key(keyPress); + break; + case CAPS_LOCK: // Caps Lock Key + handel_caps_lock_key(keyPress); + break; + case UP: + // if(press == true){ + // move_cursor_up(); + // } + break; + case LEFT: + if(keyPress == true){ + move_cur_left(); + } + break; + case RIGHT: + if(keyPress == true){ + move_cur_right(BUFFER_INDEX + 2); + } + break; + case DOWN: + // if(keyPress == true){ + // move_cursor_down(); + // } + break; + case BACKSPACE: + handel_backspace_key(keyPress); + break; + default: + if(keyPress == true){ + putchar(scanCodeToChar(scanCode)); + COMMAND_BUFFER[BUFFER_INDEX] = scanCodeToChar(scanCode); + BUFFER_INDEX++; + } + break; + } +} + +void handel_enter_key(bool keyPressed){ + if(keyPressed == false){ + create_newline(); + execute_command(COMMAND_BUFFER); + clear_buffer(COMMAND_BUFFER, BUFFER_SIZE); + BUFFER_INDEX = 0; + } +} + +void handel_shift_key(bool keyPressed){ + shift = keyPressed; + capsLock = !capsLock; +} + +void handel_caps_lock_key(bool keyPressed){ + if(keyPressed == true){ + capsLock = !capsLock; + } +} + +void handel_backspace_key(bool keyPressed){ + if(keyPressed == true && BUFFER_INDEX > 0){ + backspace_manage(); + BUFFER_INDEX--; + COMMAND_BUFFER[BUFFER_INDEX] = '\0'; + } +} + +void handel_del_key(bool keyPressed){ + int cur_pos_col; + if(keyPressed == true){ + //del_manage(); // updating screen + cur_pos_col = get_cursor_pos_x() - 2; // 2 for cursor size + for(int i=cur_pos_col; i +#include +#include + + +#include "../x86_64/idt.h" +#include "../util/util.h" +#include "../lib/stdlib.h" +#include "../lib/string.h" +#include "../usr/shell.h" +#include "../driver/speaker.h" +#include "../driver/vga.h" + + +#define KEYBOARD_COMMAND_PORT 0x64 // Keyboard Command Port +#define KEYBOARD_DATA_PORT 0x60 // Keyboard Data Port + +// Scan Code values +#define UNKNOWN 0xFFFFFFFF + +#define ESC 0x00000001 +#define NUM_1 0x00000002 +#define NUM_2 0x00000003 +#define NUM_3 0x00000004 +#define NUM_4 0x00000005 +#define NUM_5 0x00000006 +#define NUM_6 0x00000007 +#define NUM_7 0x00000008 +#define NUM_8 0x00000009 +#define NUM_9 0x0000000A +#define NUM_0 0x0000000B +#define MINUS 0x0000000C // '-' +#define EQUAL 0x0000000D // '=' +#define BACKSPACE 0x0000000E +#define TAB 0x0000000F +#define Q 0x00000010 +#define W 0x00000011 +#define E 0x00000012 +#define R 0x00000013 +#define T 0x00000014 +#define Y 0x00000015 +#define U 0x00000016 +#define I 0x00000017 +#define O 0x00000018 +#define P 0x00000019 +#define LBRACKET 0x0000001A // '[' +#define RBRACKET 0x0000001B // ']' +#define ENTER 0x0000001C +#define CTRL 0x0000001D +#define A 0x0000001E +#define S 0x0000001F +#define D 0x00000020 +#define F 0x00000021 +#define G 0x00000022 +#define H 0x00000023 +#define J 0x00000024 +#define K 0x00000025 +#define L 0x00000026 +#define SEMICOLON 0x00000027 // '' +#define APOSTROPHE 0x00000028 // '\'' +#define BACKTICK 0x00000029 // '`' +#define LSHIFT 0x0000002A +#define BACKSLASH 0x0000002B // '\' +#define Z 0x0000002C +#define X 0x0000002D +#define C 0x0000002E +#define V 0x0000002F +#define B 0x00000030 +#define N 0x00000031 +#define M 0x00000032 +#define COMMA 0x00000033 // ',' +#define PERIOD 0x00000034 // '.' +#define SLASH 0x00000035 // '/' +#define RSHIFT 0x00000036 +#define NUMPAD_ASTERISK 0x00000037 // '*' +#define ALT 0x00000038 +#define SPACE 0x00000039 +#define CAPS_LOCK 0x0000003A + +#define F1 0x0000003B +#define F2 0x0000003C +#define F3 0x0000003D +#define F4 0x0000003E +#define F5 0x0000003F +#define F6 0x00000040 +#define F7 0x00000041 +#define F8 0x00000042 +#define F9 0x00000043 +#define F10 0x00000044 +#define NUM_LOCK 0x00000045 +#define SCROLL_LOCK 0x00000046 + +#define NUMPAD_7 0x00000047 +#define NUMPAD_8 0x00000048 +#define NUMPAD_9 0x00000049 +#define NUMPAD_MINUS 0x0000004A +#define NUMPAD_4 0x0000004B +#define NUMPAD_5 0x0000004C +#define NUMPAD_6 0x0000004D +#define NUMPAD_PLUS 0x0000004E +#define NUMPAD_1 0x0000004F +#define NUMPAD_2 0x00000050 +#define NUMPAD_3 0x00000051 +#define NUMPAD_0 0x00000052 +#define NUMPAD_PERIOD 0x00000053 // '.' + +#define F11 0x00000057 +#define F12 0x00000058 + +#define HOME 0x00000047 // Same as NUMPAD_7 without Num Lock +#define UP 0x00000048 // Same as NUMPAD_8 without Num Lock +#define PAGE_UP 0x00000049 // Same as NUMPAD_9 without Num Lock +#define LEFT 0x0000004B // Same as NUMPAD_4 without Num Lock +#define RIGHT 0x0000004D // Same as NUMPAD_6 without Num Lock +#define END 0x0000004F // Same as NUMPAD_1 without Num Lock +#define DOWN 0x00000050 // Same as NUMPAD_2 without Num Lock +#define PAGE_DOWN 0x00000051 // Same as NUMPAD_3 without Num Lock +#define INSERT 0x00000052 // Same as NUMPAD_0 without Num Lock +#define DELETE 0x00000053 // Same as NUMPAD_PERIOD without Num Lock + +int getScanCode(); +bool getKeyState(); +char scanCodeToChar(uint32_t scanCode); +void key_ctrl(uint32_t scanCode, bool keyPress); +void keyboardHandler(registers_t *regs); +void initKeyboard(); +void disableKeyboard(); + +void handel_enter_key(bool keyPressed); +void handel_shift_key(bool keyPressed); +void handel_caps_lock_key(bool keyPressed); +void handel_backspace_key(bool keyPressed); +void handel_del_key(bool keyPressed); + +void read_command(char* input); +bool is_printable(char ch); + + + diff --git a/src/driver/speaker.c b/src/driver/speaker.c new file mode 100644 index 0000000..30ff3b5 --- /dev/null +++ b/src/driver/speaker.c @@ -0,0 +1,48 @@ +#include "speaker.h" + +// PIT control port and speaker port +#define PIT_CONTROL_PORT 0x43 +#define PIT_CHANNEL_2_DATA_PORT 0x42 +#define SPEAKER_CONTROL_PORT 0x61 + +// PIT frequency for channel 2 +#define PIT_FREQUENCY 1193180 + +void play_sound(uint32_t frequency) { + // Calculate the PIT divisor + uint16_t divisor = (uint16_t)(PIT_FREQUENCY / frequency); + + // Set the PIT to square wave mode (Mode 3) for channel 2 + outb(PIT_CONTROL_PORT, 0xB6); // 10110110 (Channel 2, Access Mode LSB then MSB, Mode 3, Binary) + + // Send the divisor to channel 2's data port (low byte, then high byte) + outb(PIT_CHANNEL_2_DATA_PORT, (uint8_t)(divisor & 0xFF)); // Send LSB + outb(PIT_CHANNEL_2_DATA_PORT, (uint8_t)((divisor >> 8) & 0xFF)); // Send MSB + + // Enable the speaker by setting bit 2 of port 0x61 + uint8_t tmp = inb(SPEAKER_CONTROL_PORT); + if (tmp != (tmp | 3)) { + outb(SPEAKER_CONTROL_PORT, tmp | 3); // Enable both speaker and channel 2 + } +} + +void stop_sound() { + // Disable the speaker by clearing bit 0 and bit 1 of port 0x61 + uint8_t tmp = inb(SPEAKER_CONTROL_PORT); + outb(SPEAKER_CONTROL_PORT, tmp & 0xFC); // Clear bits 0 and 1 +} + +void beep() { + // Play a bell sound (usually around 1000 Hz) + play_sound(1000); + + // Wait for a short duration (this delay is about 500ms) + // for (int i = 0; i < 100000; i++) { + // asm volatile("nop"); + // } + print("before\n"); + delay(1); + print("after"); + // Stop the sound + stop_sound(); +} diff --git a/src/driver/speaker.h b/src/driver/speaker.h new file mode 100644 index 0000000..ca548ce --- /dev/null +++ b/src/driver/speaker.h @@ -0,0 +1,12 @@ +#ifndef SPEAKER_H +#define SPEAKER_H + +#include +#include "ports.h" +#include "../x86_64/pit_timer.h" + +void play_sound(uint32_t frequency); +void stop_sound(void); +void beep(void); + +#endif diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 7c04db5..96a1803 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -180,6 +180,12 @@ void kmain(void){ init_gdt(); check_gdt(); + init_idt(); + // check_idt(); + + init_timer(); + initKeyboard(); + hcf(); } diff --git a/src/kernel/kernel.h b/src/kernel/kernel.h index ee9b7a2..37b97ef 100644 --- a/src/kernel/kernel.h +++ b/src/kernel/kernel.h @@ -8,6 +8,9 @@ #include "../driver/vga.h" #include "../x86_64/gdt.h" +#include "../x86_64/idt.h" +#include "../x86_64/pit_timer.h" +#include "../driver/keyboard.h" void kmain(void); diff --git a/src/limine.conf b/src/limine.conf index c731ac2..5673a5a 100644 --- a/src/limine.conf +++ b/src/limine.conf @@ -6,7 +6,7 @@ # Build Date : 05/12/2024 # Timeout in seconds that Limine will use before automatically booting. -timeout: 1 +timeout: 5 verbose: yes diff --git a/src/screenshot/keblaOS_0.7.gif b/src/screenshot/keblaOS_0.7.gif new file mode 100644 index 0000000..4283df9 Binary files /dev/null and b/src/screenshot/keblaOS_0.7.gif differ diff --git a/src/usr/print_reg_values.asm b/src/usr/print_reg_values.asm new file mode 100644 index 0000000..90706f5 --- /dev/null +++ b/src/usr/print_reg_values.asm @@ -0,0 +1,45 @@ +[EXTERN print_registers_c] + +[GLOBAL print_registers] + + + +print_registers: + ; Push all general-purpose registers to the stack + ; Storing all general purpose registers state + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + call print_registers_c ; Call the C function to print them + + ; Restore all general-purpose registers + ; Clear all general purpose registers state + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + iretq diff --git a/src/usr/shell.c b/src/usr/shell.c new file mode 100644 index 0000000..bb52abb --- /dev/null +++ b/src/usr/shell.c @@ -0,0 +1,159 @@ + +#include "shell.h" + +bool shell_running = false; // Global flag to manage shell state + +void shell_prompt() { + print("KeblaOS>> "); +} + + +void execute_command(char* command) { + if (strcmp(command, "help") == 0) { + print("Available commands: help, clear, reboot, poweroff, time, uptime, regvalue, features, exit.\n"); + } else if (strcmp(command, "clear") == 0) { + clear_screen(); // Clear the screen using your VGA driver + } else if (strcmp(command, "reboot") == 0) { + print("Rebooting...\n"); + // You will need to implement the reboot function depending on your OS + reboot(); + } else if (strcmp(command, "poweroff") == 0){ + print("Shutting Down!\n"); + print("Please Wait..."); + poweroff(); + } else if (strcmp(command, "time") == 0){ + print("Current Time :\n"); + // printing current time + } else if (strcmp(command, "uptime") == 0){ + print("Up time : \n"); + // printing the time run this os + }else if (strcmp(command, "regvalue") == 0){ + print_registers(); + }else if (strcmp(command, "check gdt") == 0){ + check_gdt(); + }else if (strcmp(command, "features") == 0){ + print("Following features have implemented:\n"); + print_features(); + }else if (strcmp(command, "test interrupt") == 0){ + print("Test Interrupts\n"); + void test_interrupt(); + } else if (strcmp(command, "exit") == 0) { + print("Exiting shell...\n"); + shell_running = false; + }else { + print("Unknown command: "); + print(command); + print("\n"); + print("type 'help'\n"); + } + + if(!strcmp(command, "poweroff") == 0){ + print(">>"); + } +} + + +void run_shell(bool is_shell_running) { + char input[BUFFER_SIZE]; + + while (is_shell_running) { + shell_prompt(); // Display shell prompt + //read_command(input); // Function to read user input (can be implemented based on your keyboard handler) + //execute_command(input); // Process the input command + } +} + + +void poweroff() { + outw(0x604, 0x2000); // Write to ACPI power-off port (used by Bochs/QEMU) +} + + +void reboot(){ + outb(0x64, 0xFE); // Send reset command to the keyboard controller +} + + + +// This C function will print the register values passed via the stack. +void print_registers_c(uint64_t rdi, uint64_t rsi, uint64_t rbp, uint64_t rsp, + uint64_t rbx, uint64_t rdx, uint64_t rcx, uint64_t rax, + uint64_t r8, uint64_t r9, uint64_t r10, uint64_t r11, uint64_t r12, uint64_t r13, uint64_t r14, uint64_t r15 ) { + print("Register Values:\n"); + + print("RAX: "); + print_hex(rax); + print("\n"); + + print("RBX: "); + print_hex(rbx); + print("\n"); + + print("RCX: "); + print_hex(rcx); + print("\n"); + + print("RDX: "); + print_hex(rdx); + print("\n"); + + print("RSI: "); + print_hex(rsi); + print("\n"); + + print("RDI: "); + print_hex(rdi); + print("\n"); + + print("RBP: "); + print_hex(rbp); + print("\n"); + + print("R8: "); + print_hex(r8); + print("\n"); + + print("R9: "); + print_hex(r9); + print("\n"); + + print("R10: "); + print_hex(r10); + print("\n"); + + print("R11: "); + print_hex(r11); + print("\n"); + + print("R12: "); + print_hex(r12); + print("\n"); + + print("R13: "); + print_hex(r13); + print("\n"); + + print("R14: "); + print_hex(r14); + print("\n"); + + print("R15: "); + print_hex(r15); + print("\n"); +} + + +void print_features(){ + print("Features:\n"); + print("1. GRUB2 Bootloading\n"); + print("2. Keyboard Driver\n"); + print("3. VGA Text Mode Display Driver\n"); + print("4. GDT initialization\n"); + print("5. IDT initialization\n"); + print("6. PIT initialization\n"); + print("7. Basic User Shell"); + print("7. Standard Libraries : match.h, stddef.h, stdint.h, stdio.h, stdlib.h, string.h\n"); + print("_________________________________________________________________________________\n"); +} + + diff --git a/src/usr/shell.h b/src/usr/shell.h new file mode 100644 index 0000000..48cfa9e --- /dev/null +++ b/src/usr/shell.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../driver/vga.h" // Assuming you have a VGA text driver. +#include "../driver/ports.h" +#include "../lib/string.h" // Assuming you have string manipulation functions. +#include "../driver/keyboard.h" // Assuming you have keyboard input handling. +#include "../x86_64/gdt.h" +#include "../util/util.h" + +#define BUFFER_SIZE 256 + +void shell_prompt(); +void execute_command(char* command); +void shell(); +void run_shell(bool is_shell_running); + +void poweroff(); +void reboot(); +void print_registers_c(uint64_t rdi, uint64_t rsi, uint64_t rbp, uint64_t rsp, + uint64_t rbx, uint64_t rdx, uint64_t rcx, uint64_t rax, + uint64_t r8, uint64_t r9, uint64_t r10, uint64_t r11, uint64_t r12, uint64_t r13, uint64_t r14, uint64_t r15 ); +extern void print_registers(); +void print_features(); diff --git a/src/versiion_info.md b/src/versiion_info.md index dd34bd2..b6c57be 100644 --- a/src/versiion_info.md +++ b/src/versiion_info.md @@ -14,7 +14,7 @@ Screenshot 1 ![screenshot](./screenshot/screenshot_01.png) Screenshot 2 -![screenshot](./screenshot/screenshot_02.png) +![screenshot](./screenshot/keblaOS_0.7.gif) ## Used Tools Version : - [x] [Limine Bootloader](https://github.com/limine-bootloader/limine) - 8.6.0 @@ -30,10 +30,18 @@ Screenshot 2 - [x] stdlib.c included in lib directory - [x] string.c includeed in lib directory - [x] Global Descriptor Table(GDT) +- [x] Interrupt Descriptor Table(IDT) +- [x] PIT Timer +- [x] Shell +- [x] Speaker Driver +- [x] Keyboard Driver + `src` directory is containing source code. `build` directory is containing generated object file, binary file and iso file. `iso_root` is required for building `image.iso` file. To build and run by QEmu iso `make -B`. -Downloaded from [here](https://github.com/baponkar/KeblaOS). \ No newline at end of file +Downloaded from [here](https://github.com/baponkar/KeblaOS). + + diff --git a/src/x86_64/idt.c b/src/x86_64/idt.c new file mode 100644 index 0000000..2e946b6 --- /dev/null +++ b/src/x86_64/idt.c @@ -0,0 +1,261 @@ +/* +Interrupt Descriptor Table +https://wiki.osdev.org/Interrupt_Descriptor_Table +https://stackoverflow.com/questions/52214531/x86-64-order-of-passing-parameters-in-registers +https://github.com/dreamportdev/Osdev-Notes/blob/master/02_Architecture/05_InterruptHandling.md + +*/ + +#include "idt.h" + +idt_entry_t idt_entries[256]; +idt_ptr_t idt_ptr; + +char* exception_messages[] = { + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + "Double fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment not present", + "Stack fault", + "General protection fault", + "Page fault", + "Unknown Interrupt", + "Coprocessor Fault", + "Alignment Fault", + "Machine Check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + + + +void idt_set_gate(uint8_t index, uint64_t offset, uint16_t selector, uint8_t attr){ + + idt_entries[index].offset_1 = (uint16_t) offset & 0xFFFF; // set lower 16 bit + idt_entries[index].selector = selector; // set 16 bit of selector + idt_entries[index].ist = 0; // disabled ist i.e clear 3 bit of ist and 5 bit of reserved field + idt_entries[index].type_attributes = attr; // set 8 bit of P(1 bit) + DPL(2 bit) + gate type(4 bit) + 0(1 bit) + idt_entries[index].offset_2 = (uint16_t) (offset >> 16) & 0xFFFF; // set 16 bit + + idt_entries[index].offset_3 = (uint32_t) (offset >> 32) & 0xFFFFFFFF; // set upper 32 bit + idt_entries[index].zero = 0; // set top 32 bit to zero +} + + +void isr_install(){ + idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1; + idt_ptr.base = (uint64_t) &idt_entries; + + memset(&idt_entries, 0, sizeof(idt_entry_t) * 256); + + // Setting Interrupts Service Routine Gate(ISR Gate) + // https://stackoverflow.com/questions/9113310/segment-selector-in-ia-32 + idt_set_gate( 0, (uint64_t)isr0 , 0x08, 0x8E); // selector = 0x08 = 0b1000, 32-bit Interrupt Gate => attr = 0x8E = 1000 1110, (p=0b1,0b0, dpl=0b00, gate type=0b1110) + idt_set_gate( 1, (uint64_t)isr1 , 0x08, 0x8E); // Keyboard + idt_set_gate( 2, (uint64_t)isr2 , 0x08, 0x8E); // selector value is 1000 because GDT code segment index is 1 + idt_set_gate( 3, (uint64_t)isr3 , 0x08, 0x8E); // selector = index + table_to_use + privilege + idt_set_gate( 4, (uint64_t)isr4 , 0x08, 0x8E); // selector = 1<<3(index 1) + 0<<2(TI for GDT 0) + 0<<1(for ring 0) => 1000 + 000 + 00 = 1000 = 0x08 + idt_set_gate( 5, (uint64_t)isr5 , 0x08, 0x8E); + idt_set_gate( 6, (uint64_t)isr6 , 0x08, 0x8E); + idt_set_gate( 7, (uint64_t)isr7 , 0x08, 0x8E); + idt_set_gate( 8, (uint64_t)isr8 , 0x08, 0x8E); + idt_set_gate( 9, (uint64_t)isr9 , 0x08, 0x8E); + idt_set_gate( 10, (uint64_t)isr10 , 0x08, 0x8E); + idt_set_gate( 11, (uint64_t)isr11 , 0x08, 0x8E); + idt_set_gate( 12, (uint64_t)isr12 , 0x08, 0x8E); + idt_set_gate( 13, (uint64_t)isr13 , 0x08, 0x8E); + idt_set_gate( 14, (uint64_t)isr14 , 0x08, 0x8E); // paging + idt_set_gate( 15, (uint64_t)isr15 , 0x08, 0x8E); + idt_set_gate( 16, (uint64_t)isr16 , 0x08, 0x8E); + idt_set_gate( 17, (uint64_t)isr17 , 0x08, 0x8E); + idt_set_gate( 18, (uint64_t)isr18 , 0x08, 0x8E); + idt_set_gate( 19, (uint64_t)isr19 , 0x08, 0x8E); + idt_set_gate( 20, (uint64_t)isr20 , 0x08, 0x8E); + idt_set_gate( 21, (uint64_t)isr21 , 0x08, 0x8E); + idt_set_gate( 22, (uint64_t)isr22 , 0x08, 0x8E); + idt_set_gate( 23, (uint64_t)isr23 , 0x08, 0x8E); + idt_set_gate( 24, (uint64_t)isr24 , 0x08, 0x8E); + idt_set_gate( 25, (uint64_t)isr25 , 0x08, 0x8E); + idt_set_gate( 26, (uint64_t)isr26 , 0x08, 0x8E); + idt_set_gate( 27, (uint64_t)isr27 , 0x08, 0x8E); + idt_set_gate( 28, (uint64_t)isr28 , 0x08, 0x8E); + idt_set_gate( 29, (uint64_t)isr29 , 0x08, 0x8E); + idt_set_gate( 30, (uint64_t)isr30 , 0x08, 0x8E); + idt_set_gate( 31, (uint64_t)isr31 , 0x08, 0x8E); + + idt_set_gate(128, (uint64_t)isr128, 0x08, 0x8E); //System call Write + idt_set_gate(177, (uint64_t)isr177, 0x08, 0x8E); //System call Read + +} + + +// This gets called from our ASM interrupt handler stub. +void isr_handler(registers_t regs) +{ + if(regs.int_no == 128){ + // syscall_handler(®s); + //return; + } + else if(regs.int_no == 177){ + // syscall_handler(®s); + //return; + } + else if (regs.int_no == 14) { // Check if it is a page fault + // page_fault(®s); // Call your page fault handler directly + //return; + } + else if(regs.int_no < 32){ + print("recieved interrupt: "); + print_dec(regs.int_no); + putchar('\n'); + print(exception_messages[regs.int_no]); + putchar('\n'); + print("Error Code: "); + print_dec(regs.err_code); + print("\n"); + print("System Halted!\n"); + for (;;){ + asm ("hlt"); + } + } +} + + +/* This array is actually an array of function pointers. We use +* this to handle custom Interrupt handlers for a given Interrupt */ +void *interrupt_routines[16] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +/* This installs a custom Interrupt handler for the given Interrupt */ +void interrupt_install_handler(int int_no, void (*handler)(registers_t *r)) +{ + interrupt_routines[int_no] = handler; +} + + +/* This clears the handler for a given Interrupt */ +void interrupt_uninstall_handler(int int_no) +{ + interrupt_routines[int_no] = 0; +} + + +void init_idt(){ + isr_install(); + irq_remap(); + irq_install(); + idt_flush((uint64_t) &idt_ptr); +} + + +void test_interrupt() { + // print("Testing Interrupts\n"); + // print_dec( 104 / 0 ); // Int no 0 + // asm volatile ("int $0x3"); // Breakpoint int no : 3 + // asm volatile ("int $0x0"); // Division By Zero, int no : 0 + // asm volatile ("int $0xE"); // Page Fault Request, int no: 14 + // asm volatile("int $0xF"); // int no 15 + // asm volatile("int $0x10"); // int no 16 + // asm volatile("int $0x11"); // int no 17 + // asm volatile ("int $0x20"); // Interrupt Request, int no: 32 + // asm volatile ("int $0x21"); // Interrupt Request, int no : 33 + // asm volatile ("int $0x22"); // Interrupt Request, int no: 34 +} + +#define PIC1_COMMAND_PORT 0x20 //Primary PIC(programmable interrupt controller) Command Port: +#define PIC1_DATA_PORT 0x21 //Primary PIC Data Port +#define PIC2_COMMAND_PORT 0xA0 //Secondary PIC Command Port: +#define PIC2_DATA_PORT 0xA1 //Secondary PIC Data Port + +void irq_remap(void) +{ + outb(PIC1_COMMAND_PORT, 0x11); // 0x11 is the command to initialize the PICs in cascade mode, + outb(PIC2_COMMAND_PORT, 0x11); // which means the two PICs will be working together + + outb(PIC1_DATA_PORT, 0x20); // For PIC1 (master), the vector base is set to 0x20 (interrupts 32 to 39). + outb(PIC2_DATA_PORT, 0x28); // For PIC2 (slave), the vector base is set to 0x28 (interrupts 40 to 47). + + outb(PIC1_DATA_PORT, 0x04); // For the master PIC, 0x04 means that the slave PIC is connected to IRQ2. + outb(PIC2_DATA_PORT, 0x02); // For the slave PIC, 0x02 indicates that it's connected to the master PIC's IRQ2. + + outb(PIC1_DATA_PORT, 0x01); // 0x01 enables the 8086 mode (which is the typical mode for x86 systems). + outb(PIC2_DATA_PORT, 0x01); + + outb(PIC1_DATA_PORT, 0x0); // These commands unmask all interrupts on the master and slave PICs by + outb(PIC2_DATA_PORT, 0x0); // setting the interrupt mask to 0x0, meaning all IRQ lines are enabled. +} + + +void irq_install() +{ + // Setup for Interrupts Request Gate (IRQ Gate) + idt_set_gate(32, (uint64_t)irq0, 0x08, 0x8E); // Timer Interrupt + idt_set_gate(33, (uint64_t)irq1, 0x08, 0x8E); + idt_set_gate(34, (uint64_t)irq2, 0x08, 0x8E); + idt_set_gate(35, (uint64_t)irq3, 0x08, 0x8E); + idt_set_gate(36, (uint64_t)irq4, 0x08, 0x8E); + idt_set_gate(37, (uint64_t)irq5, 0x08, 0x8E); + idt_set_gate(38, (uint64_t)irq6, 0x08, 0x8E); + idt_set_gate(39, (uint64_t)irq7, 0x08, 0x8E); + idt_set_gate(40, (uint64_t)irq8, 0x08, 0x8E); + idt_set_gate(41, (uint64_t)irq9, 0x08, 0x8E); + idt_set_gate(42, (uint64_t)irq10, 0x08, 0x8E); + idt_set_gate(43, (uint64_t)irq11, 0x08, 0x8E); + idt_set_gate(44, (uint64_t)irq12, 0x08, 0x8E); + idt_set_gate(45, (uint64_t)irq13, 0x08, 0x8E); + idt_set_gate(46, (uint64_t)irq14, 0x08, 0x8E); + idt_set_gate(47, (uint64_t)irq15, 0x08, 0x8E); +} + +#define PIC_EOI 0x20 // End of Interrupt + +void irq_handler(registers_t *r) +{ + /* This is a blank function pointer */ + void (*handler)(registers_t *r); + + /* Find out if we have a custom handler to run for this + * IRQ, and then finally, run it */ + handler = interrupt_routines[r->int_no - 32]; + if (handler) + { + handler(r); + } + + /* If the IDT entry that was invoked was greater than 40 + * (meaning IRQ8 - 15), then we need to send an EOI to + * the slave controller */ + if (r->int_no >= 40) + { + outb(PIC2_COMMAND_PORT, PIC_EOI); /* slave */ + } + + /* In either case, we need to send an EOI to the master + * interrupt controller too */ + outb(PIC1_COMMAND_PORT, PIC_EOI); /* master */ +} + diff --git a/src/x86_64/idt.h b/src/x86_64/idt.h new file mode 100644 index 0000000..7f81853 --- /dev/null +++ b/src/x86_64/idt.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include +#include + +#include "../driver/vga.h" +#include "../driver/ports.h" +#include "../util/util.h" + + +struct idt_entry_struct +{//128 bit + uint16_t offset_1; // offset bits 0..15, 16 bit + uint16_t selector; // a code segment selector in GDT or LDT, 16 bit + uint8_t ist; // bits 0..2 holds Interrupt Stack Table offset, rest of bits zero.2 bit + uint8_t type_attributes; // gate type, dpl, and p fields , 8 bit + uint16_t offset_2; // offset bits 16..31 , 16 bit + + uint32_t offset_3; // offset bits 32..63 , 32 bit + uint32_t zero; // reserved, +} __attribute__((packed)); +typedef struct idt_entry_struct idt_entry_t; + + +// A struct describing a pointer to an array of interrupt handlers. +// This is in a format suitable for giving to 'lidt'. +struct idt_ptr_struct +{ + uint16_t limit; + uint64_t base; // The address of the first element in our idt_entry_t array. +} __attribute__((packed)); +typedef struct idt_ptr_struct idt_ptr_t; + +void idt_set_gate(uint8_t index, uint64_t offset, uint16_t selector, uint8_t attr); +void isr_install(); +void isr_handler(registers_t regs); + +void interrupt_install_handler(int int_no, void (*handler)(registers_t *r)); +void interrupt_uninstall_handler(int int_no); + +void init_idt(); +void test_interrupt(); + +void irq_remap(void); +void irq_install(); +void irq_handler(registers_t *r); + +// Externel functions from ASM +extern void idt_flush(uint64_t); + +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 isr128(); +extern void isr177(); + +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(); + + diff --git a/src/x86_64/idt_load.asm b/src/x86_64/idt_load.asm new file mode 100644 index 0000000..8a78417 --- /dev/null +++ b/src/x86_64/idt_load.asm @@ -0,0 +1,183 @@ + +section .text +[GLOBAL idt_flush] +idt_flush: + lidt [rdi] ; Load the IDT pointer + sti ; Enable interrupts + ret + + +; Setup Interrupt Service Routine(ISR) + +%macro ISR_NOERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli + push 0 ; Dummy error code + push %1 ; Interrupt number + jmp isr_common_stub +%endmacro + +%macro ISR_ERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli + push %1 ; Interrupt number only + jmp isr_common_stub +%endmacro + +[EXTERN isr_handler] +isr_common_stub: + pushaq ; Push all general-purpose registers, this works only inside of macro + + mov ax, 0x10 ; Load the kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov rdi, rsp ; Pass the current stack pointer to `isr_handler` + call isr_handler + + pophaq ; Pop all general-purpose registers + + ; Adjust stack pointer depending on whether error code was pushed + cmp qword [rsp + 8], 0 ; Check if dummy error code was pushed + jnz .skip_cleanup ; If real error code, jump to .skip_cleanup + add rsp, 16 ; If dummy error code, adjust rsp by 8 + sti + iretq + +.skip_cleanup: + add rsp, 8 ; Clean up interrupt number 8 for dummy error code and another 8 for interrupt no + sti + iretq + + + +ISR_NOERRCODE 0 +ISR_NOERRCODE 1 +ISR_NOERRCODE 2 +ISR_NOERRCODE 3 +ISR_NOERRCODE 4 +ISR_NOERRCODE 5 +ISR_NOERRCODE 6 ; Return from interrupt 6 +ISR_NOERRCODE 7 + +ISR_ERRCODE 8 +ISR_NOERRCODE 9 +ISR_ERRCODE 10 +ISR_ERRCODE 11 +ISR_ERRCODE 12 +ISR_ERRCODE 13 +ISR_ERRCODE 14 +ISR_NOERRCODE 15 +ISR_NOERRCODE 16 +ISR_NOERRCODE 17 +ISR_NOERRCODE 18 +ISR_NOERRCODE 19 +ISR_NOERRCODE 20 +ISR_NOERRCODE 21 +ISR_NOERRCODE 22 +ISR_NOERRCODE 23 +ISR_NOERRCODE 24 +ISR_NOERRCODE 25 +ISR_NOERRCODE 26 +ISR_NOERRCODE 27 +ISR_NOERRCODE 28 +ISR_NOERRCODE 29 +ISR_NOERRCODE 30 +ISR_NOERRCODE 31 + +ISR_NOERRCODE 128 ; System Call +ISR_NOERRCODE 177 ; System Call + + +; Setup Interrupt Request(IRQ) + +%macro IRQ 2 + global irq%1 + irq%1: + cli ; Clear Interrupts + push 0 ; Push a dummy error code + mov rax, %2 + push rax ; Push irq code + jmp irq_common_stub +%endmacro + + +IRQ 0, 32 +IRQ 1, 33 +IRQ 2, 34 +IRQ 3, 35 +IRQ 4, 36 +IRQ 5, 37 +IRQ 6, 38 +IRQ 7, 39 +IRQ 8, 40 +IRQ 9, 41 +IRQ 10, 42 +IRQ 11, 43 +IRQ 12, 44 +IRQ 13, 45 +IRQ 14, 46 +IRQ 15, 47 + + +; This is a stub that we have created for IRQ based ISRs. This calls +[extern irq_handler] +irq_common_stub: + ; Storing all general purpose registers state + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + ; Save the CR2 register (holds page fault linear address) + mov rax, cr2 + push rax + + mov ax, 0x10 ; Load the Kernel Data Segment descriptor! + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov rdi, rsp ; Pass stack pointer to `irq_handler` + call irq_handler + + add rsp, 16 ; Clean up pushed error code and IRQ number + pop rax + + ; Clear all general purpose registers state + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + ;add rsp, 16 ; Adjust stack to clean up any pushed error code or ISR number (if present) + sti ; Store Interrupts + iretq ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP! + diff --git a/src/x86_64/pit_timer.c b/src/x86_64/pit_timer.c new file mode 100644 index 0000000..8ced9ac --- /dev/null +++ b/src/x86_64/pit_timer.c @@ -0,0 +1,59 @@ +/* +https://wiki.osdev.org/Programmable_Interval_Timer +*/ + +#include "pit_timer.h" + +uint64_t max_value = 18446744073709551615ULL; // Unsigned long long literal + +const uint32_t freq = 1000; // ticks per second +// PIT Frequency = 119318.16666 Mhz +// uint64_t divisor = 1193182 / freq; // 1.1931816666 ~ MHz 1.193182 MHz +uint64_t divisor = 1193; + +uint64_t ticks = 0; + +uint32_t sec = 0; + +void timerHadler(registers_t *regs){ + if(ticks >= max_value){ + ticks = 0; + } + ticks++; + + if (ticks % freq == 0) { + sec++; + + // Calculate hours, minutes, and seconds + uint32_t hours = sec / 3600; + uint32_t minutes = (sec % 3600) / 60; + uint32_t seconds = sec % 60; + + // Display time in HH:MM:SS format at position (0, 0) + // print_dec(sec); + } +} + + +void init_timer(){ + ticks = 0; + interrupt_install_handler(0, &timerHadler); + + // 0011 0110 + outb(PIT_COMMAND_PORT, 0x36); + outb(PIT_CHANNEL_0, (uint8_t)(divisor & 0xFF)); // FF = 1111 1111 => last eight digit + outb(PIT_CHANNEL_0, (uint8_t)((divisor >> 8) & 0xFF)); +} + + +void delay(uint32_t ms) { + uint64_t endTicks = ticks + (ms * freq / 1000); // Convert milliseconds to ticks + while (ticks < endTicks) { + // Wait until the desired number of ticks has passed + print_dec(ticks); + print("\n"); + } +} + + + diff --git a/src/x86_64/pit_timer.h b/src/x86_64/pit_timer.h new file mode 100644 index 0000000..4b28869 --- /dev/null +++ b/src/x86_64/pit_timer.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +#include "../util/util.h" +#include "idt.h" +#include "../driver/vga.h" +#include "../driver/ports.h" + + +void init_timer(); +void timerHandler(registers_t *regs); +void delay(uint32_t ms); + +