aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAqua-sama <aqua@iserlohn-fortress.net>2021-02-12 17:55:31 +0200
committerAqua-sama <aqua@iserlohn-fortress.net>2021-02-12 17:55:31 +0200
commit46a5f1ee4db70a037af5038deaa1d64ad0f7862c (patch)
tree0250b88f2eb69a7a0987a78d23a06e2125df74a2
parentAdd comments to explain CGA ports better (diff)
downloadkernel.cpp-46a5f1ee4db70a037af5038deaa1d64ad0f7862c.tar.xz
Add serial0 console output
-rw-r--r--.clang-format1
-rw-r--r--drivers/makefile3
-rw-r--r--drivers/ports.h24
-rw-r--r--drivers/serial.cc88
-rw-r--r--drivers/serial.h47
-rw-r--r--libk/stdlib.h37
-rw-r--r--libk/stdlib/console.cc12
-rw-r--r--src/kernel.cc6
8 files changed, 194 insertions, 24 deletions
diff --git a/.clang-format b/.clang-format
index ee8a219..ec6cc2c 100644
--- a/.clang-format
+++ b/.clang-format
@@ -2,5 +2,6 @@
BasedOnStyle: Chromium
ColumnLimit: '120'
AccessModifierOffset: '-2'
+AllowShortBlocksOnASingleLine: true
...
diff --git a/drivers/makefile b/drivers/makefile
index b76f7c5..0d9071a 100644
--- a/drivers/makefile
+++ b/drivers/makefile
@@ -1 +1,2 @@
-CXX_OBJ += drivers/cga.o
+CXX_OBJ += drivers/cga.o \
+ drivers/serial.o
diff --git a/drivers/ports.h b/drivers/ports.h
index 338c3b8..8671f19 100644
--- a/drivers/ports.h
+++ b/drivers/ports.h
@@ -6,34 +6,36 @@ concept port_data_t = (is_same<T, uint8_t>::value || is_same<T, uint16_t>::value
template <uint16_t port_num, port_data_t T>
class Port {
public:
- [[nodiscard]] T read() {
+ [[nodiscard]] T read(uint16_t offset = 0) {
+ const uint16_t port = port_num + offset;
T result;
switch (sizeof(T)) {
case 1:
- asm volatile("inb %1, %0" : "=a"(result) : "Nd"(port_num));
+ asm volatile("inb %1, %0" : "=a"(result) : "Nd"(port));
break;
case 2:
- asm volatile("inw %1, %0" : "=a"(result) : "Nd"(port_num));
+ asm volatile("inw %1, %0" : "=a"(result) : "Nd"(port));
break;
case 4:
- asm volatile("inl %1, %0" : "=a"(result) : "Nd"(port_num));
+ asm volatile("inl %1, %0" : "=a"(result) : "Nd"(port));
break;
}
return result;
}
- void write(T data) {
+ void write(T data, uint16_t offset = 0) {
+ const uint16_t port = port_num + offset;
switch (sizeof(T)) {
case 1:
- asm volatile("outb %0, %1" : : "a"(data), "Nd"(port_num));
+ asm volatile("outb %0, %1" : : "a"(data), "Nd"(port));
break;
case 2:
- asm volatile("outw %0, %1" : : "a"(data), "Nd"(port_num));
+ asm volatile("outw %0, %1" : : "a"(data), "Nd"(port));
break;
case 4:
- asm volatile("outl %0, %1" : : "a"(data), "Nd"(port_num));
+ asm volatile("outl %0, %1" : : "a"(data), "Nd"(port));
break;
}
}
@@ -45,3 +47,9 @@ typedef Port<0x3d5, uint8_t> cga_dat_port;
// 0x3d8 : mode select register
// 0x3d8 : color control register
// 0x3da : status register
+
+/* Serial ports */
+typedef Port<0x3f8, uint8_t> com1_port_t;
+typedef Port<0x2f8, uint8_t> com2_port_t;
+typedef Port<0x3e8, uint8_t> com3_port_t;
+typedef Port<0x2e8, uint8_t> com4_port_t;
diff --git a/drivers/serial.cc b/drivers/serial.cc
new file mode 100644
index 0000000..84dd4b0
--- /dev/null
+++ b/drivers/serial.cc
@@ -0,0 +1,88 @@
+#include "serial.h"
+
+/* Line Control
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * |dla| | parity | s | data |
+ */
+enum LineControlRegister : uint8_t {
+ // data bits
+ d5bit = 0b00000000,
+ d6bit = 0b00000001,
+ d7bit = 0b00000010,
+ d8bit = 0b00000011,
+ // parity bits
+ none = 0b00000000,
+ odd = 0b00001000,
+ even = 0b00011000,
+ mark = 0b00101000,
+ space = 0b00111000,
+ // stop bits
+ s1bit = 0b00000000,
+ s2bit = 0b00000100, // 1.5 for 5bit data; 2 otherwise
+ // divisor latch access bit
+ dlab = 0b10000000,
+};
+
+/* Line Status Register */
+enum LineStatusRegister : uint8_t {
+ DR = 0b00000001, // data ready: see if there is data to read
+ OE = 0b00000010, // overrun error: see if there has been data lost
+ PE = 0b00000100, // parity error: see if there was error in transmission
+ FE = 0b00001000, // framing error: see if a stop bit was missing
+ BI = 0b00010000, // break indicator: see if there is a break in data input
+ THRE = 0b00100000, // transmitter holding register empty: see if transmission buffer is empty
+ TEMT = 0b01000000, // transmitter empty: see if transmitter is not doing anything
+ ERRO = 0b10000000, // impending error: see if there is an error with a word in the input buffer
+};
+
+SerialPort::SerialPort() {
+ /*
+ * The serial controller (UART) has a baud rate of 115200 ticks per second; to limit the speed of the port set the
+ * divisor:
+ * 1. set DLAB on LineControl
+ * 2. set BaudDiv_l
+ * 3. set BaudDiv_h
+ * 4. clear LineControl
+ */
+
+ port.write(0x00, InterruptControl); // disable all interrupts
+
+ port.write(dlab, LineControl);
+ port.write(0x03, BaudDiv_l); // div_l: 0x03 38400 baud
+ port.write(0x00, BaudDiv_h); // div_h: 0x00
+ port.write(d8bit | none | s1bit, LineControl); // 8 bits, no parity, one stop bit
+
+ port.write(0xc7, FifoControl); // enable FIFO, clear, with 14-byte threshold
+ port.write(0x0b, ModemControl); // IRQ enabled, RTS/DSR set
+}
+
+bool SerialPort::ready_read() {
+ return (port.read(LineStatus) & DR);
+}
+
+uint8_t SerialPort::read() {
+ while (!ready_read()) {
+ asm volatile("nop");
+ }
+ return port.read();
+}
+
+bool SerialPort::ready_write() {
+ return (port.read(LineStatus) & THRE);
+}
+
+void SerialPort::write(char c) {
+ while (!ready_write()) {
+ asm volatile("nop");
+ }
+
+ switch (c) {
+ case '\n':
+ port.write('\n');
+ port.write('\r');
+ break;
+ default:
+ port.write(static_cast<uint8_t>(c));
+ }
+}
+
diff --git a/drivers/serial.h b/drivers/serial.h
new file mode 100644
index 0000000..0579dd4
--- /dev/null
+++ b/drivers/serial.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <stdlib.h>
+#include "ports.h"
+
+/*
+ * Serial Port
+ *
+ * useful links:
+ * https://wiki.osdev.org/Serial_Ports
+ * https://www.lowlevel.eu/wiki/Serielle_Schnittstelle
+ */
+
+class SerialPort : public Console {
+public:
+ SerialPort();
+ ~SerialPort() = default;
+
+ bool ready_read();
+ uint8_t read();
+
+ bool ready_write();
+ void write(char c) override;
+ void write(ViewIterator& iter) override {
+ while (iter) {
+ write(iter.next());
+ }
+ }
+
+ void update_cursor() override {}
+
+private:
+ enum PortOffset : uint16_t {
+ BaudDiv_l = 0, // if dlab is set
+ BaudDiv_h = 1, // if dlab is set
+ Data = 0, // read from receive buffer / write to transmit buffer
+ InterruptControl = 1, // interrupt enable
+ FifoControl = 2, // interrupt ID and FIFO control
+ LineControl = 3, // most significant bit is the DLAB
+ ModemControl = 4,
+ LineStatus = 5,
+ ModemStatus = 6,
+ Scratch = 7,
+ };
+
+ com1_port_t port;
+};
diff --git a/libk/stdlib.h b/libk/stdlib.h
index a336586..7213384 100644
--- a/libk/stdlib.h
+++ b/libk/stdlib.h
@@ -2,15 +2,32 @@
#include <string.h>
+template <typename T, bool reverse = false>
+class Iterator {
+public:
+ T next() { return buffer[reverse ? pos-- : pos++]; }
+ operator bool() const { return reverse ? (pos >= 0) : (pos < length); }
+
+ explicit Iterator(T* b, size_t l) : buffer(b), length(l) {
+ if (reverse)
+ pos = length - 1;
+ };
+
+private:
+ ssize_t pos = 0;
+ T* const buffer;
+ const size_t length;
+};
+
class Console {
- public:
- virtual ~Console() = default;
- virtual void write(char c) = 0;
- virtual void write(ViewIterator& msg) = 0;
- virtual void update_cursor() = 0;
-
- static void set(Console* ptr);
- static Console* get();
+public:
+ virtual ~Console() = default;
+ virtual void write(char c) = 0;
+ virtual void write(ViewIterator& msg) = 0;
+ virtual void update_cursor() = 0;
+
+ static void set(Console* ptr);
+ static Iterator<Console*> begin();
};
template <typename T>
@@ -37,7 +54,9 @@ void print_c(Console* console, const T& a, const Args&... x) {
template <typename T, typename... Args>
void printk(const T& a, const Args&... x) {
- if (auto* c = Console::get()) {
+ auto iter = Console::begin();
+ while (iter) {
+ auto* c = iter.next();
print_c(c, a, x...);
c->update_cursor();
}
diff --git a/libk/stdlib/console.cc b/libk/stdlib/console.cc
index 308f055..a234e6b 100644
--- a/libk/stdlib/console.cc
+++ b/libk/stdlib/console.cc
@@ -1,15 +1,17 @@
#include "stdlib.h"
-static Console* global_console = nullptr;
+constexpr size_t max_consoles = 4;
+static Console* global_console[max_consoles] = {nullptr};
+static size_t last_console = 0;
void Console::set(Console* ptr) {
- if (ptr != nullptr) {
- global_console = ptr;
+ if (ptr != nullptr && last_console < max_consoles) {
+ global_console[last_console++] = ptr;
}
}
-Console* Console::get() {
- return global_console;
+Iterator<Console*> Console::begin() {
+ return Iterator<Console*>(global_console, last_console);
}
void abort() {
diff --git a/src/kernel.cc b/src/kernel.cc
index 4c17969..624f472 100644
--- a/src/kernel.cc
+++ b/src/kernel.cc
@@ -12,18 +12,22 @@
#include <types.h>
#include "cga.h"
#include "gdt.h"
+#include "serial.h"
extern "C" {
void dump_multiboot(uint32_t mb_magic, uint32_t mb_addr);
void dump_gdt();
void kernel_main([[maybe_unused]] uint32_t mb_magic, [[maybe_unused]] uint32_t mb_addr) {
+ SerialPort s0;
+ Console::set(&s0);
+
CGA terminal;
Console::set(&terminal);
printk("Hello, kernel World!\n");
- // dump_multiboot(mb_magic, mb_addr);
+ dump_multiboot(mb_magic, mb_addr);
dump_gdt();
printk("GDT::SegmentDescriptor tests\n");