From e4fcbb20b4c0a0d86ef9a59a20acc5e65431ff95 Mon Sep 17 00:00:00 2001 From: Jenny Curle Date: Sun, 7 Apr 2019 15:34:15 +0100 Subject: [PATCH] Implement Serial Console messaging. Intended to be used for debugging, but it's useful. --- include/kernel/serial.h | 19 +++++ include/kernel/tty.h | 3 - include/kernel/utils.h | 8 +- kernel/gdt.c | 4 +- kernel/kernel.c | 19 +++-- kernel/serial.c | 184 ++++++++++++++++++++++++++++++++++++++++ kernel/utils.c | 71 +++++++++++++++- makefile | 1 + 8 files changed, 295 insertions(+), 14 deletions(-) create mode 100755 include/kernel/serial.h create mode 100755 kernel/serial.c diff --git a/include/kernel/serial.h b/include/kernel/serial.h new file mode 100755 index 0000000..2522681 --- /dev/null +++ b/include/kernel/serial.h @@ -0,0 +1,19 @@ +#pragma once + +extern void init_serial(); + +extern void serial_set_baud_rate(uint16_t, uint16_t); + +extern void serial_configure_line(uint16_t); + +extern void serial_configure_buffers(uint16_t); + +extern void serial_write(uint16_t, const char); + +extern void serial_configure_modem(uint16_t); + +extern int serial_check_tqueue(uint16_t); + +extern void serial_print(uint16_t, const char*); + +extern void serial_printf(const char*, ...); diff --git a/include/kernel/tty.h b/include/kernel/tty.h index 46f62c2..f9aab15 100644 --- a/include/kernel/tty.h +++ b/include/kernel/tty.h @@ -25,8 +25,5 @@ void puts(const char*); void handleControlSequence(struct csi_sequence); void set_cursor(int, int); void term_scroll(bool); -void int_to_ascii(int, char*); -void int_to_hex(int, char*); -void empty_string(char*); bool isDigit(char c); diff --git a/include/kernel/utils.h b/include/kernel/utils.h index ba8debb..91bc58c 100755 --- a/include/kernel/utils.h +++ b/include/kernel/utils.h @@ -12,4 +12,10 @@ void outb(unsigned short, unsigned char); void memcpy(void*, void*, size_t); -void memset(void*, int, size_t); \ No newline at end of file +void memset(void*, int, size_t); + +void int_to_ascii(int, char*); + +void int_to_hex(int, char*); + +void empty_string(char*); diff --git a/kernel/gdt.c b/kernel/gdt.c index 9f97fe5..c872a23 100755 --- a/kernel/gdt.c +++ b/kernel/gdt.c @@ -30,13 +30,13 @@ void gdt_set_gate(int num, unsigned long base, gdt[num].low_limit = (limit & 0xFFFF); gdt[num].granular = ((limit >> 16) & 0x0F); - gdt[num].granular != (gran & 0xF0); + gdt[num].granular = (gran & 0xF0); gdt[num].access = access; } void gdt_install() { gp.limit = (sizeof(struct gdt_item) * 3) - 1; - gp.base = &gdt; + gp.base = (unsigned int)&gdt; gdt_set_gate(0, 0, 0, 0, 0); //NULL item diff --git a/kernel/kernel.c b/kernel/kernel.c index 5b67615..7d123de 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -2,15 +2,20 @@ //#include #include -#include +#include +#include -int kernel_main(void) { - //Prepare the GDT - gdt_install(); - //Prepare interrupts +int kernel_main(void) { + // Prepare the screen, and blank it out. + screen_initialize(); + + // Prepare the serial console. + init_serial(); + serial_print(0x3F8, "[INFO] Serial ready."); + // Prepare the GDT + //gdt_install(); + // Prepare interrupts //idt_install(); - //Prepare the screen, and blank it out. - screen_initialize(); //Print a copyright message. term_writes("(c)"); diff --git a/kernel/serial.c b/kernel/serial.c new file mode 100755 index 0000000..01f808f --- /dev/null +++ b/kernel/serial.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include + +#define SERIAL_DATA_PORT(base) (base) +#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2) +#define SERIAL_LINE_COMMAND_PORT(base) (base + 3) +#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4) +#define SERIAL_LINE_STATUS_PORT(base) (base + 5) +#define SERIAL_COM1_BASE 0x3F8 + +#define SERIAL_LINE_ENABLE_DLAB 0x80 + +/** serial_set_baud_rate: + * Sets the speed that data is sent via the serial port. + * The default speed is 115200 bits/s. + * The argument given is the divisor of the number. + * Hence, the new speed becomes (115200/divisor) bits/s. + * + * @param com The COM port to configure. + * @param divisor The new divisor for the baud rate. + */ + +void serial_set_baud_rate(uint16_t com, uint16_t divisor) { + outb(SERIAL_LINE_COMMAND_PORT(com), + SERIAL_LINE_ENABLE_DLAB); + + outb(SERIAL_DATA_PORT(com), + (divisor >> 8) & 0x00FF); + + outb(SERIAL_DATA_PORT(com), + divisor & 0x00FF); + +} + +/** serial_configure_line: + * Sets a specific data byteset in the Serial hardware. + * Required for expected operation. + * + * @param com The COM port to configure. + */ + +void serial_configure_line(uint16_t com) { + /* Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 | + * Content: | d | b | parity| s | dl | + * Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03 + */ + + outb(SERIAL_LINE_COMMAND_PORT(com), 0x0B); +} + +/** serial_configure_buffers: + * Enables FIFO (First In First Out) on the Serial line. + * It clears both the receive and transmit queues. + * It uses a 14 byte wide queue. + * + * @param com The COM port to configure. + */ + +void serial_configure_buffers(uint16_t com) { + /* Bit: | 7 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * Content: | 1v1 | bs | r | dma | clt | clr | e | + * Value: | 1 1 | 0 | 0 | 0 | 1 | 1 | 1 | = 0xC7 + */ + + outb(SERIAL_FIFO_COMMAND_PORT(com), 0xC7); +} + +/** serial_configure_modem + * @param com The COM port to configure. + */ + +void serial_configure_modem(uint16_t com) { + /* Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * Content: | r | r | af | lb | ao2 | ao1 | rts | dtr | + * Value: | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | = 0x03 + */ + + outb(SERIAL_MODEM_COMMAND_PORT(com), 0x3); +} + +/** serial_check_tqueue: + * Checks whether the Transmit FIFO Queue is empty on the given port. + * + * @param com The COM port to check. + * @return 0 If the queue is not empty. + * 1 If the queue is empty. + */ + +int serial_check_tqueue(uint16_t com) { + return inb(SERIAL_LINE_STATUS_PORT(com)) & 0x20; +} + +/** serial_write: + * Prints a single character to the serial port. + * Does nothing fancy. + * @param com The COM port to write to. + * @param data The character to write. + */ + +void serial_write(uint16_t com, const char chr) { + //Hang until we have access to the COM port. + while(serial_check_tqueue(com) == 0); + outb(com, chr); +} + +/** serial_print: + * Prints a string to the serial port. + * Does not support ANSI codes, or color. + * + * @param com The COM port to write to. + * @param data The string to write. + */ + +void serial_print(uint16_t com, const char* data) { + for(size_t i = 0; i < strlen(data); i++) { + serial_write(com, data[i]); + } +} + +/** serial_printf: + * A printf-style function for the serial port. + * + * @param format The base string - used to substitute the succeding values. + * @param ... The substitutions. + */ + +void serial_printf(uint16_t com, const char* format, ...) { + uint32_t storage; //To hold temporary variables + char stringstore[10] = {0}; //To convert ints to strings. + va_list list; + va_start(list, format); + + for(size_t i = 0; i < strlen(format); i++) { + //Check for the format specifier + if(format[i] == '%') { + //Check for the other specs + if(format[i+1] == 'd') { + + storage = va_arg(list, int); + int_to_ascii(storage, stringstore); + serial_print(com, stringstore); + empty_string(stringstore); + i += 2; + + } else if(format[i+1] == 'x') { + + storage = va_arg(list, int); + int_to_hex(storage, stringstore); + serial_print(com, stringstore); + empty_string(stringstore); + i += 2; + + } else if(format[i+1] == 's') { + + serial_print(com, va_arg(list, char*)); + i += 2; + + } else { + serial_print(com, "ERROR: Attempting to parse unknown format string."); + return; + } + } else { + serial_write(com, format[i]); + i++; + } + } + va_end(list); //Free the list +} + +void init_serial() { + // Disable interrupts + outb(SERIAL_COM1_BASE + 1, 0x00); + + // Set baud rate divisor. + serial_set_baud_rate(SERIAL_COM1_BASE, 3); + // 8 bits, no parity, one stop bit. + serial_configure_line(SERIAL_COM1_BASE); + // Enable FIFO and clear buffers. + serial_configure_buffers(SERIAL_COM1_BASE); + // Enable IRQs, RTS/DSR set. + serial_configure_modem(SERIAL_COM1_BASE); +} diff --git a/kernel/utils.c b/kernel/utils.c index 279cb89..cb70f3d 100644 --- a/kernel/utils.c +++ b/kernel/utils.c @@ -1,4 +1,7 @@ - +#include +#include +#include +#include #include size_t strlen(const char* string) { @@ -33,3 +36,69 @@ void memset(void* src, int chr, size_t n) { *ptr++ = (unsigned char) chr; } } + +void int_to_ascii(int num, char* string) { + size_t i = 0; //Counter. + //TODO: Convert this to a for-loop? + int32_t calc = 0; + bool negative = false; + + if(num == 0) { + string[0] = '0'; + string[1] = '\0'; + return; + } + + if(num < 0) { + negative = true; + num = -num; + } + + while(num != 0) { + calc = num % 10; + num = num / 10; + calc += 48; + + string[i] = calc; + i++; + } + + if(negative) { + string[i] = '-'; + i++; + } + + for(size_t j = 0; j < i/2; j++) { + calc = string[j]; + string[j] = string[i-j-1]; + string[j-i-1] = calc; + } +} + +void int_to_hex(int num, char* string) { + empty_string(string); + + uint8_t i = 8; + uint8_t temp = 0; + + while(i != 0) { + i--; + temp = num & 0xF; + num = num >> 4; + + if(temp >= 10) { + temp += 7; + } + + temp += 48; + string[i] = temp; + } +} + +void empty_string(char* string) { + size_t len = strlen(string); + + for(size_t i = 0; i < len + 1; i++) { + string[i] = '\0'; + } +} \ No newline at end of file diff --git a/makefile b/makefile index ffe3c36..e77edb4 100755 --- a/makefile +++ b/makefile @@ -30,6 +30,7 @@ LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS) KERNEL_OBJS= \ $(KERNEL_ARCH_OBJS) \ kernel/utils.o \ +kernel/serial.o \ kernel/gdt.o \ kernel/idt.o \ kernel/kernel.o