diff --git a/.gitignore b/.gitignore index ad104d4..d40674c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ iso_root *.o .vscode docs +**/compile_commands.json ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. diff --git a/kernel/include/kernel/fs/chardev.h b/kernel/include/kernel/fs/chardev.h index 3d0c60e..d251dd6 100644 --- a/kernel/include/kernel/fs/chardev.h +++ b/kernel/include/kernel/fs/chardev.h @@ -1,5 +1,10 @@ #pragma once #include +#include + +struct chardev_info { + process_queue rsrc_wait_queue; +}; void init_tty1 (inode* absolute_root); diff --git a/kernel/include/kernel/fs/vfs.h b/kernel/include/kernel/fs/vfs.h index 28e1795..91cce69 100644 --- a/kernel/include/kernel/fs/vfs.h +++ b/kernel/include/kernel/fs/vfs.h @@ -19,6 +19,9 @@ typedef enum { UNDEF, EFILE, DIRECTORY, LINK, CHAR_DEV } file_type_t; typedef struct inode inode; typedef struct file file; +typedef struct chardev_info chardev_info_t; +typedef struct ramfs_info ramfs_info_t; + typedef struct { int (*lookup) (char*, inode**, inode*); int (*create) (char*, inode**, inode*); @@ -40,6 +43,10 @@ struct inode { inode_operations* i_iops; file_operations* i_fops; file_type_t i_type; + union { + chardev_info_t* chardev_info; + ramfs_info_t* ramfs_info; + } i_info; }; struct file { diff --git a/kernel/include/kernel/hw/keyboard.h b/kernel/include/kernel/hw/keyboard.h new file mode 100644 index 0000000..3335029 --- /dev/null +++ b/kernel/include/kernel/hw/keyboard.h @@ -0,0 +1,68 @@ +#pragma once + +#include + +constexpr unsigned short kb_ps2_data_port = 0x60; +constexpr unsigned short kb_ps2_status_port = 0x64; +constexpr unsigned short kb_ps2_cmd_port = 0x64; + +constexpr unsigned char kb_ps2_cmd_get_ccb = 0x20; +constexpr unsigned char kb_ps2_cmd_set_ccb = 0x60; +constexpr unsigned char kb_ps2_disable_port_2 = 0xA7; +constexpr unsigned char kb_ps2_enable_port_2 = 0xA8; +constexpr unsigned char kb_ps2_test_port_2 = 0xA9; +constexpr unsigned char kb_ps2_test_controller = 0xAA; +constexpr unsigned char kb_ps2_test_port_1 = 0xAB; +constexpr unsigned char kb_ps2_disable_port_1 = 0xAD; +constexpr unsigned char kb_ps2_enable_port_1 = 0xAE; +constexpr unsigned char kb_ps2_read_controller_ip = 0xC0; +constexpr unsigned char kb_ps2_read_controller_op = 0xD0; +constexpr unsigned char kb_ps2_write_controller_op = 0xD0; +constexpr unsigned char kb_ps2_reset = 0xFF; + +typedef union { + uint8_t raw; + struct { + uint8_t irq1_enable : 1; + uint8_t irq12_enable : 1; + uint8_t system_flag : 1; + uint8_t reserved1 : 1; + uint8_t port_1_clock_disable : 1; + uint8_t port_2_clock_disable : 1; + uint8_t port_1_tl_enable : 1; + uint8_t reserved2 : 1; + } __attribute__ ((packed)); +} kb_ps2_cfg_byte_t; + +typedef union { + uint8_t raw; + struct { + uint8_t system_reset : 1; + uint8_t a20_gate : 1; + uint8_t port_2_clock : 1; + uint8_t port_2_data : 1; + uint8_t op_buffer_is_port_1 : 1; + uint8_t op_buffer_is_port_2 : 1; + uint8_t port_1_clock : 1; + uint8_t port_1_data : 1; + } __attribute__ ((packed)); +} kb_ps2_controller_output_port_t; + +typedef union { + uint8_t raw; + struct { + uint8_t out_buffer_full : 1; + uint8_t in_buffer_full : 1; + uint8_t system_flag : 1; + uint8_t is_controller_command : 1; + uint8_t reserved : 2; + uint8_t is_timeout_error : 1; + uint8_t is_parity_error : 1; + } __attribute__ ((packed)); +} kb_ps2_status_register_t; + +typedef void (*kb_tty_handler_t) (unsigned char); + +void init_kb (void); +void register_kb_tty_handler (kb_tty_handler_t handler); +unsigned char pop_next_char (void); diff --git a/kernel/include/kernel/hw/keypress_map.h b/kernel/include/kernel/hw/keypress_map.h new file mode 100644 index 0000000..3f600c9 --- /dev/null +++ b/kernel/include/kernel/hw/keypress_map.h @@ -0,0 +1,320 @@ +#pragma once + +constexpr unsigned char kb_action_escape = 0x80; +constexpr unsigned char kb_action_lctl = 0x81; +constexpr unsigned char kb_action_lshift = 0x82; +constexpr unsigned char kb_action_rshift = 0x83; +constexpr unsigned char kb_action_lalt = 0x84; +constexpr unsigned char kb_action_caps = 0x85; +constexpr unsigned char kb_action_f1 = 0x86; +constexpr unsigned char kb_action_f2 = 0x87; +constexpr unsigned char kb_action_f3 = 0x88; +constexpr unsigned char kb_action_f4 = 0x89; +constexpr unsigned char kb_action_f5 = 0x8A; +constexpr unsigned char kb_action_f6 = 0x8B; +constexpr unsigned char kb_action_f7 = 0x8C; +constexpr unsigned char kb_action_f8 = 0x8D; +constexpr unsigned char kb_action_f9 = 0x8E; +constexpr unsigned char kb_action_f10 = 0x8F; +constexpr unsigned char kb_action_numlk = 0x90; +constexpr unsigned char kb_action_scrllk = 0x91; +constexpr unsigned char kb_action_f11 = 0x92; +constexpr unsigned char kb_action_f12 = 0x93; +constexpr unsigned char kb_action_rctl = 0x94; +constexpr unsigned char kb_action_ralt = 0x95; +constexpr unsigned char kb_action_home = 0x96; +constexpr unsigned char kb_action_cup = 0x97; +constexpr unsigned char kb_action_pgup = 0x98; +constexpr unsigned char kb_action_cleft = 0x99; +constexpr unsigned char kb_action_cright = 0x9A; +constexpr unsigned char kb_action_end = 0x9B; +constexpr unsigned char kb_action_cdown = 0x9C; +constexpr unsigned char kb_action_pgdn = 0x9D; +constexpr unsigned char kb_action_insert = 0x9E; +constexpr unsigned char kb_action_delete = 0x9F; +constexpr unsigned char kb_action_ext = 0xE0; + +constexpr unsigned char kb_ps2_sc1_released_offset = 0x80; + +constexpr unsigned char keypress_to_char_or_action[256] = {0, + kb_action_escape, + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '0', + '-', + '=', + '\b', + '\t', + 'q', + 'w', + 'e', + 'r', + 't', + 'y', + 'u', + 'i', + 'o', + 'p', + '[', + ']', + '\n', + kb_action_lctl, + 'a', + 's', + 'd', + 'f', + 'g', + 'h', + 'j', + 'k', + 'l', + ';', + '\'', + '`', + kb_action_lshift, + '\\', + 'z', + 'x', + 'c', + 'v', + 'b', + 'n', + 'm', + ',', + '.', + '/', + kb_action_rshift, + '*', + kb_action_lalt, + ' ', + kb_action_caps, + kb_action_f1, + kb_action_f2, + kb_action_f3, + kb_action_f4, + kb_action_f5, + kb_action_f6, + kb_action_f7, + kb_action_f8, + kb_action_f9, + kb_action_f10, + kb_action_numlk, + kb_action_scrllk, + '7', + '8', + '9', + '-', + '4', + '5', + '6', + '+', + '1', + '2', + '3', + '0', + '.', + 0, + 0, + 0, + kb_action_f11, + kb_action_f12, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + kb_action_escape, + '!', + '@', + '#', + '$', + '%', + '^', + '&', + '*', + '(', + ')', + '_', + '+', + '\b', + '\t', + 'Q', + 'W', + 'E', + 'R', + 'T', + 'Y', + 'U', + 'I', + 'O', + 'P', + '{', + '}', + '\n', + kb_action_lctl, + 'A', + 'S', + 'D', + 'F', + 'G', + 'H', + 'J', + 'K', + 'L', + ':', + '\"', + '~', + kb_action_lshift, + '|', + 'Z', + 'X', + 'C', + 'V', + 'B', + 'N', + 'M', + '<', + '>', + '?', + kb_action_rshift, + '*', + kb_action_lalt, + ' ', + kb_action_caps, + kb_action_f1, + kb_action_f2, + kb_action_f3, + kb_action_f4, + kb_action_f5, + kb_action_f6, + kb_action_f7, + kb_action_f8, + kb_action_f9, + kb_action_f10, + kb_action_numlk, + kb_action_scrllk, + '7', + '8', + '9', + '-', + '4', + '5', + '6', + '+', + '1', + '2', + '3', + '0', + '.', + 0, + 0, + 0, + kb_action_f11, + kb_action_f12, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + kb_action_ext, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0}; + +constexpr unsigned char ext_keypress_to_action[128] = {[0x1C] = '\n', + [0x1D] = kb_action_rctl, + [0x35] = '/', + [0x38] = kb_action_ralt, + [0x47] = kb_action_home, + [0x48] = kb_action_cup, + [0x49] = kb_action_pgup, + [0x4B] = kb_action_cleft, + [0x4D] = kb_action_cright, + [0x4F] = kb_action_end, + [0x50] = kb_action_cdown, + [0x51] = kb_action_pgdn, + [0x52] = kb_action_insert, + [0x53] = kb_action_delete}; + +typedef struct { + bool lctl, rctl, lshift, rshift, lalt, ralt, caps, numlk, scrllk, ext; +} statemachine_t; + +int kp_isaction (unsigned char kp); +int kp_ischar (unsigned char kp); +int kp_isext (unsigned char kp); +int kp_islk (unsigned char kp); + +unsigned char map_keypress (statemachine_t* sm, unsigned char kp); diff --git a/kernel/include/kernel/process.h b/kernel/include/kernel/process.h index 42b417a..d71eacf 100644 --- a/kernel/include/kernel/process.h +++ b/kernel/include/kernel/process.h @@ -35,6 +35,9 @@ process* get_current_process (void); int dequeue_process (process_queue* queue, process** result); int enqueue_process (process_queue* queue, process* new_process); +void process_block (process_queue* wait_queue); +void process_unblock (process* p); + void schedule (registers_t* registers); int process_fork (process* source_process, process** dest_ptr); diff --git a/kernel/src/kernel/console.c b/kernel/src/kernel/console.c index e1dd8f2..72b79e2 100644 --- a/kernel/src/kernel/console.c +++ b/kernel/src/kernel/console.c @@ -123,6 +123,10 @@ void putchar (unsigned char rc) { idx = ((idx / xc) + 1) * xc; if (idx >= xc * yc) idx -= xc * yc; return; + case '\b': + idx--; + registerChar (0, idx); + return; } registerChar (rc, idx); idx++; diff --git a/kernel/src/kernel/entry.c b/kernel/src/kernel/entry.c index b682271..0849ff6 100644 --- a/kernel/src/kernel/entry.c +++ b/kernel/src/kernel/entry.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,7 @@ static void print_info (void) { __attribute__ ((noreturn)) void _start_stage2 (void) { init_graphics (framebuffer); init_console (framebuffer->width, framebuffer->height, 40, 40, 1, 1, 2); + init_kb (); init_acpi (rsdp); for (int i = 0; i < 3; i++) // open stdin, stdout and stderr @@ -146,8 +148,11 @@ __attribute__ ((noreturn)) void _start_stage2 (void) { jump_to_usermode (entry_point, user_stack_base, ¤t->p_user); } - for (;;) - do_syscall (SYSCALL_SCHED_YIELD, 0, 0, 0); + for (;;) { + unsigned char c = 0; + do_syscall (SYSCALL_SYS_READ, 0, (uint64_t)&c, 1); + kprintf ("%c", c); + } } static void get_limine_requests (void) { diff --git a/kernel/src/kernel/fs/chardev.c b/kernel/src/kernel/fs/chardev.c index 771c7b4..715b34f 100644 --- a/kernel/src/kernel/fs/chardev.c +++ b/kernel/src/kernel/fs/chardev.c @@ -1,10 +1,15 @@ +#include #include #include #include #include #include +#include +#include #include +inode* tty1_ptr = nullptr; + static int stdout_write (inode* node, file* f, void* buf, size_t len) { (void)node, (void)f; // args not used bool stdio_buf = get_update_on_putch (); @@ -18,6 +23,30 @@ static int stdout_write (inode* node, file* f, void* buf, size_t len) { return len; } +static void stdin_kb_handler (unsigned char nextchar) { + (void)nextchar; // factually we don't really need this + process* next_process = nullptr; + while (dequeue_process (&tty1_ptr->i_info.chardev_info->rsrc_wait_queue, &next_process) == 0) { + if (!next_process) break; + process_unblock (next_process); + } +} + +static int stdin_read (inode* node, file* f, void* buffer, size_t size) { + (void)node, (void)f; // args not used + char* cbuffer = (char*)buffer; + for (size_t i = 0; i < size; i++) { + unsigned char c = 255; + while ((c = pop_next_char ()) == 255) + process_block (&tty1_ptr->i_info.chardev_info->rsrc_wait_queue); + if (c < 0x80) + cbuffer[i] = c; + else + i--; + } + return (int)size; +} + void init_tty1 (inode* absolute_root) { inode* dev_dir = nullptr; int error = do_mkdir ("dev", &dev_dir, absolute_root); @@ -32,9 +61,16 @@ void init_tty1 (inode* absolute_root) { kmemset (tty1_fops, 0, sizeof (file_operations)); tty1_fops->write = stdout_write; - // tty1_fops->read = // connect to keyboard get_next_char when available + tty1_fops->read = stdin_read; + + chardev_info_t* tty1_info = kmalloc (sizeof (chardev_info_t)); + kmemset (tty1_info, 0, sizeof (chardev_info_t)); tty1_file->i_iops = nullptr; tty1_file->i_fops = tty1_fops; tty1_file->i_type = CHAR_DEV; + tty1_file->i_info.chardev_info = tty1_info; + + tty1_ptr = tty1_file; + register_kb_tty_handler (stdin_kb_handler); } \ No newline at end of file diff --git a/kernel/src/kernel/hw/keyboard.c b/kernel/src/kernel/hw/keyboard.c new file mode 100644 index 0000000..67d4536 --- /dev/null +++ b/kernel/src/kernel/hw/keyboard.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool is_kb_setup = false; +static charqueue* kb_keypress_charqueue; +static statemachine_t* kb_statemachine; +static kb_tty_handler_t kb_tty_handler = nullptr; + +static inline kb_ps2_status_register_t kb_read_status_register (void) { + return (kb_ps2_status_register_t){.raw = inb (kb_ps2_status_port)}; +} + +static inline unsigned char kb_send_command (unsigned char command, bool will_return) { + while (kb_read_status_register ().in_buffer_full) + ; + outb (kb_ps2_cmd_port, command); + if (!will_return) return 0; + while (!(kb_read_status_register ().out_buffer_full)) + ; + return inb (kb_ps2_data_port); +} + +static inline void kb_send_data (unsigned char data) { + while (kb_read_status_register ().in_buffer_full) + ; + outb (kb_ps2_data_port, data); +} + +static inline unsigned char kb_read_data (void) { + while (!(kb_read_status_register ().out_buffer_full)) + ; + return inb (kb_ps2_data_port); +} + +static inline kb_ps2_cfg_byte_t kb_read_cfg_byte (void) { + return (kb_ps2_cfg_byte_t){.raw = kb_send_command (kb_ps2_cmd_get_ccb, true)}; +} + +static inline void kb_write_cfg_byte (kb_ps2_cfg_byte_t cfg_byte) { + kb_send_command (kb_ps2_cmd_set_ccb, false); + kb_send_data (cfg_byte.raw); +} + +static inline bool kb_reset (void) { + kb_send_data (kb_ps2_reset); + if (kb_read_data () != 0xFA) return false; + if (kb_read_data () != 0XAA) return false; + return true; +} + +static void kb_handler (registers_t* registers) { + (void)registers; + if (kb_read_status_register ().out_buffer_full) { + unsigned char scancode = inb (kb_ps2_data_port); + unsigned char processed = map_keypress (kb_statemachine, scancode); + if (processed != 0) { + push_charqueue (kb_keypress_charqueue, processed); + if (kb_tty_handler) kb_tty_handler (processed); + } + } + pic_send_eoi (1); +} + +unsigned char pop_next_char (void) { + unsigned char ret = 255; + if (peek_charqueue (kb_keypress_charqueue, &ret) == 0) + pop_charqueue (kb_keypress_charqueue, &ret); + return ret; +} + +void register_kb_tty_handler (kb_tty_handler_t handler) { kb_tty_handler = handler; } + +void init_kb (void) { + kb_keypress_charqueue = create_charqueue (); + kb_statemachine = kmalloc (sizeof (statemachine_t)); + kmemset ((void*)kb_statemachine, 0, sizeof (statemachine_t)); + + // TODO: actually verify the PS2 controller exists + + kb_send_command (kb_ps2_disable_port_1, false); + kb_send_command (kb_ps2_disable_port_2, false); + + while (kb_read_status_register ().out_buffer_full) + inb (kb_ps2_data_port); + + kb_ps2_cfg_byte_t cfg_byte = kb_read_cfg_byte (); + cfg_byte.irq1_enable = cfg_byte.irq12_enable = 0; + cfg_byte.port_1_tl_enable = 1; + cfg_byte.port_1_clock_disable = 0; + kb_write_cfg_byte (cfg_byte); + + if (kb_send_command (kb_ps2_test_controller, true) != 0x55) return; + + kb_write_cfg_byte (cfg_byte); + kb_send_command (kb_ps2_enable_port_1, false); + + is_kb_setup = kb_reset (); + + cfg_byte.irq1_enable = 1; + kb_write_cfg_byte (cfg_byte); + + if (kb_keypress_charqueue != nullptr) { + idt_register_handler (0x21, kb_handler); + pic_clr_mask (1); + } +} diff --git a/kernel/src/kernel/hw/keypress.c b/kernel/src/kernel/hw/keypress.c new file mode 100644 index 0000000..ca58120 --- /dev/null +++ b/kernel/src/kernel/hw/keypress.c @@ -0,0 +1,81 @@ +#include +#include + +int kp_isaction (unsigned char kp) { + return kb_action_escape <= keypress_to_char_or_action[kp] && + keypress_to_char_or_action[kp] <= kb_action_delete; +} +int kp_isext (unsigned char kp) { return kp == kb_action_ext; } +int kp_ischar (unsigned char kp) { return kp != 0 && !kp_isaction (kp) && !kp_isext (kp); } +int kp_islk (unsigned char kp) { + return keypress_to_char_or_action[kp] == kb_action_caps || + keypress_to_char_or_action[kp] == kb_action_numlk || + keypress_to_char_or_action[kp] == kb_action_scrllk; +} + +unsigned char map_keypress (statemachine_t* sm, unsigned char kp) { + if (kp == 0) return 0; + if (sm->ext) { + sm->ext = false; + if (kp >= kb_ps2_sc1_released_offset) { + unsigned char released = kp - kb_ps2_sc1_released_offset; + if (ext_keypress_to_action[released] == kb_action_ralt) + sm->ralt = false; + else if (ext_keypress_to_action[released] == kb_action_rctl) + sm->rctl = false; + return 0; + } + unsigned char mapped = ext_keypress_to_action[kp]; + if (ext_keypress_to_action[mapped] == kb_action_ralt) + sm->ralt = true; + else if (ext_keypress_to_action[mapped] == kb_action_rctl) + sm->rctl = true; + return mapped; + } + + if (kp_isext (kp)) { + sm->ext = true; + return 0; + } + + if (kp >= kb_ps2_sc1_released_offset) { + unsigned char released = keypress_to_char_or_action[kp - kb_ps2_sc1_released_offset]; + if (released == kb_action_lshift) + sm->lshift = false; + else if (released == kb_action_rshift) + sm->rshift = false; + else if (released == kb_action_lalt) + sm->lalt = false; + else if (released == kb_action_lctl) + sm->lctl = false; + return 0; + } + + if (kp_isaction (kp)) { + unsigned char mapped = keypress_to_char_or_action[kp]; + if (mapped == kb_action_lshift) + sm->lshift = true; + else if (mapped == kb_action_rshift) + sm->rshift = true; + else if (mapped == kb_action_lalt) + sm->lalt = true; + else if (mapped == kb_action_lctl) + sm->lctl = true; + else if (mapped == kb_action_caps) + sm->caps = !sm->caps; + else if (mapped == kb_action_numlk) + sm->numlk = !sm->numlk; + else if (mapped == kb_action_scrllk) + sm->scrllk = !sm->scrllk; + return mapped; + } + + if (kp_ischar (kp)) { + int offset = 128 * (sm->lshift || sm->rshift); + if (isalpha (keypress_to_char_or_action[kp])) + offset = 128 * (sm->caps ^ (sm->lshift || sm->rshift)); + return keypress_to_char_or_action[offset + kp]; + } + + return 0; +} diff --git a/kernel/src/kernel/process.c b/kernel/src/kernel/process.c index 8804524..5f1ad80 100644 --- a/kernel/src/kernel/process.c +++ b/kernel/src/kernel/process.c @@ -181,6 +181,17 @@ int process_fork (process* source_process, process** dest_ptr) { return new_process->p_id; } +void process_block (process_queue* wait_queue) { + current_process->p_state = TASK_BLOCKED; + enqueue_process (wait_queue, current_process); + do_sched_yield (); +} + +void process_unblock (process* p) { + p->p_state = TASK_READY; + enqueue_process (&ready_queue, p); +} + static uint64_t sys_fork (uint64_t arg1, uint64_t arg2, uint64_t arg3) { (void)arg1, (void)arg2, (void)arg3; // fork does not use any args process* child = nullptr; diff --git a/user/hello/hello.c b/user/hello/hello.c index bfec9b6..6ed9923 100644 --- a/user/hello/hello.c +++ b/user/hello/hello.c @@ -14,6 +14,10 @@ void _start (void) { const char* msg = "Hello from USERLAND!!!\n"; syscall3 (4, 1, (long)msg, 24); + + for (;;) + ; + syscall3 (1, 0, 0, 0); while (1)