feat/kernel: Added built-in UART support
This commit is contained in:
parent
9375cc74bd
commit
6cde9b4c19
10 changed files with 295 additions and 91 deletions
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"stdnoreturn.h": "c",
|
||||||
|
"serial.h": "c"
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
MAKEFLAGS += -rR
|
MAKEFLAGS += -rR
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
|
|
||||||
QEMUFLAGS := -m 2G -debugcon stdio
|
QEMUFLAGS := -m 2G -serial stdio
|
||||||
IMAGE_NAME := release/emk
|
IMAGE_NAME := release/emk
|
||||||
|
|
||||||
HOST_CC := cc
|
HOST_CC := cc
|
||||||
|
|
19
kernel/src/arch/cpu.h
Normal file
19
kernel/src/arch/cpu.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef CPU_H
|
||||||
|
#define CPU_H
|
||||||
|
|
||||||
|
#include <stdnoreturn.h>
|
||||||
|
|
||||||
|
static inline noreturn void hlt()
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
__asm__ volatile("hlt");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline noreturn void hcf()
|
||||||
|
{
|
||||||
|
__asm__ volatile("cli");
|
||||||
|
for (;;)
|
||||||
|
__asm__ volatile("hlt");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CPU_H
|
42
kernel/src/arch/io.c
Normal file
42
kernel/src/arch/io.c
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include <arch/io.h>
|
||||||
|
|
||||||
|
void outb(uint16_t port, uint8_t value)
|
||||||
|
{
|
||||||
|
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t inb(uint16_t port)
|
||||||
|
{
|
||||||
|
uint8_t result;
|
||||||
|
__asm__ volatile("inb %1, %0" : "=a"(result) : "Nd"(port));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void outw(uint16_t port, uint16_t value)
|
||||||
|
{
|
||||||
|
__asm__ volatile("outw %0, %1" : : "a"(value), "Nd"(port) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t inw(uint16_t port)
|
||||||
|
{
|
||||||
|
uint16_t result;
|
||||||
|
__asm__ volatile("inw %1, %0" : "=a"(result) : "Nd"(port));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void outl(uint16_t port, uint32_t value)
|
||||||
|
{
|
||||||
|
__asm__ volatile("outl %0, %1" : : "a"(value), "Nd"(port) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t inl(uint16_t port)
|
||||||
|
{
|
||||||
|
uint32_t result;
|
||||||
|
__asm__ volatile("inl %1, %0" : "=a"(result) : "Nd"(port));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_wait(void)
|
||||||
|
{
|
||||||
|
__asm__ volatile("outb %%al, $0x80" : : "a"(0) : "memory");
|
||||||
|
}
|
14
kernel/src/arch/io.h
Normal file
14
kernel/src/arch/io.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef IO_H
|
||||||
|
#define IO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void outb(uint16_t port, uint8_t value);
|
||||||
|
uint8_t inb(uint16_t port);
|
||||||
|
void outw(uint16_t port, uint16_t value);
|
||||||
|
uint16_t inw(uint16_t port);
|
||||||
|
void outl(uint16_t port, uint32_t value);
|
||||||
|
uint32_t inl(uint16_t port);
|
||||||
|
void io_wait(void);
|
||||||
|
|
||||||
|
#endif // IO_H
|
82
kernel/src/dev/serial.c
Normal file
82
kernel/src/dev/serial.c
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#include <dev/serial.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
|
||||||
|
int serial_init(uint16_t port)
|
||||||
|
{
|
||||||
|
uint16_t base = (uint16_t)port;
|
||||||
|
|
||||||
|
// Disable all interrupts
|
||||||
|
outb(base + UART_IER, 0x00);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Enable DLAB to set baud rate
|
||||||
|
outb(base + UART_LCR, UART_LCR_DLAB);
|
||||||
|
io_wait();
|
||||||
|
outb(base + UART_DLL, UART_BAUD_38400 & 0xFF);
|
||||||
|
io_wait();
|
||||||
|
outb(base + UART_DLH, (UART_BAUD_38400 >> 8) & 0xFF);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Disable DLAB and set 8N1 (8 bits, no parity, 1 stop bit)
|
||||||
|
outb(base + UART_LCR, UART_LCR_8N1);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Enable FIFO, clear them, with 14-byte threshold
|
||||||
|
outb(base + UART_FCR, UART_FCR_ENABLE | UART_FCR_CLEAR_RX | UART_FCR_CLEAR_TX | UART_FCR_14BYTE);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Set DTR, RTS, and OUT2
|
||||||
|
outb(base + UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
// Test serial chip (enable loopback mode)
|
||||||
|
outb(base + UART_MCR, UART_MCR_LOOP | UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
|
||||||
|
io_wait();
|
||||||
|
outb(base + UART_DATA, 0xAE); // Send test byte
|
||||||
|
io_wait();
|
||||||
|
if (inb(base + UART_DATA) != 0xAE)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(base + UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
|
||||||
|
io_wait();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int serial_write(uint16_t port, const uint8_t *data, uint32_t length)
|
||||||
|
{
|
||||||
|
uint16_t base = port;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
// Wait for transmitter holding register to be empty
|
||||||
|
while (!(inb(base + UART_LSR) & UART_LSR_THRE))
|
||||||
|
{
|
||||||
|
io_wait();
|
||||||
|
}
|
||||||
|
outb(base + UART_DATA, data[i]);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int serial_read(uint16_t port, uint8_t *buffer, uint32_t length)
|
||||||
|
{
|
||||||
|
uint16_t base = port;
|
||||||
|
uint32_t i = 0;
|
||||||
|
|
||||||
|
while (i < length)
|
||||||
|
{
|
||||||
|
if (inb(base + UART_LSR) & UART_LSR_DR)
|
||||||
|
{
|
||||||
|
buffer[i] = inb(base + UART_DATA);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
45
kernel/src/dev/serial.h
Normal file
45
kernel/src/dev/serial.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef SERIAL_H
|
||||||
|
#define SERIAL_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define COM1 0x3f8
|
||||||
|
|
||||||
|
// UART register offsets
|
||||||
|
#define UART_DATA 0x00 // Data register (R/W)
|
||||||
|
#define UART_IER 0x01 // Interrupt Enable Register (R/W)
|
||||||
|
#define UART_DLL 0x00 // Divisor Latch Low (R/W, when DLAB=1)
|
||||||
|
#define UART_DLH 0x01 // Divisor Latch High (R/W, when DLAB=1)
|
||||||
|
#define UART_FCR 0x02 // FIFO Control Register (W)
|
||||||
|
#define UART_LCR 0x03 // Line Control Register (R/W)
|
||||||
|
#define UART_MCR 0x04 // Modem Control Register (R/W)
|
||||||
|
#define UART_LSR 0x05 // Line Status Register (R)
|
||||||
|
|
||||||
|
// Line Control Register bits
|
||||||
|
#define UART_LCR_DLAB 0x80 // Divisor Latch Access Bit
|
||||||
|
#define UART_LCR_8N1 0x03 // 8 bits, no parity, 1 stop bit
|
||||||
|
|
||||||
|
// FIFO Control Register bits
|
||||||
|
#define UART_FCR_ENABLE 0x01 // Enable FIFO
|
||||||
|
#define UART_FCR_CLEAR_RX 0x02 // Clear receive FIFO
|
||||||
|
#define UART_FCR_CLEAR_TX 0x04 // Clear transmit FIFO
|
||||||
|
#define UART_FCR_14BYTE 0xC0 // 14-byte trigger level
|
||||||
|
|
||||||
|
// Modem Control Register bits
|
||||||
|
#define UART_MCR_DTR 0x01 // Data Terminal Ready
|
||||||
|
#define UART_MCR_RTS 0x02 // Request to Send
|
||||||
|
#define UART_MCR_OUT2 0x08 // OUT2 (used for interrupts)
|
||||||
|
#define UART_MCR_LOOP 0x10 // Loopback mode
|
||||||
|
|
||||||
|
// Line Status Register bits
|
||||||
|
#define UART_LSR_DR 0x01 // Data Ready
|
||||||
|
#define UART_LSR_THRE 0x20 // Transmitter Holding Register Empty
|
||||||
|
|
||||||
|
// Baud rate divisor for 38400 baud (115200 / 3)
|
||||||
|
#define UART_BAUD_38400 3
|
||||||
|
|
||||||
|
int serial_init(uint16_t port);
|
||||||
|
int serial_write(uint16_t port, const uint8_t *data, uint32_t length);
|
||||||
|
int serial_read(uint16_t port, uint8_t *buffer, uint32_t length);
|
||||||
|
|
||||||
|
#endif // SERIAL_H
|
|
@ -1,107 +1,26 @@
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <boot/limine.h>
|
#include <boot/limine.h>
|
||||||
|
#include <arch/cpu.h>
|
||||||
|
#include <arch/io.h>
|
||||||
|
#include <dev/serial.h>
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests"))) static volatile LIMINE_BASE_REVISION(3);
|
__attribute__((used, section(".limine_requests"))) static volatile LIMINE_BASE_REVISION(3);
|
||||||
__attribute__((used, section(".limine_requests"))) static volatile struct limine_framebuffer_request framebuffer_request = {
|
|
||||||
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
|
||||||
.revision = 0};
|
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER;
|
__attribute__((used, section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER;
|
||||||
__attribute__((used, section(".limine_requests_end"))) static volatile LIMINE_REQUESTS_END_MARKER;
|
__attribute__((used, section(".limine_requests_end"))) static volatile LIMINE_REQUESTS_END_MARKER;
|
||||||
|
|
||||||
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
|
|
||||||
{
|
|
||||||
uint8_t *restrict pdest = (uint8_t *restrict)dest;
|
|
||||||
const uint8_t *restrict psrc = (const uint8_t *restrict)src;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
pdest[i] = psrc[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *memset(void *s, int c, size_t n)
|
|
||||||
{
|
|
||||||
uint8_t *p = (uint8_t *)s;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
p[i] = (uint8_t)c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *memmove(void *dest, const void *src, size_t n)
|
|
||||||
{
|
|
||||||
uint8_t *pdest = (uint8_t *)dest;
|
|
||||||
const uint8_t *psrc = (const uint8_t *)src;
|
|
||||||
|
|
||||||
if (src > dest)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
pdest[i] = psrc[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (src < dest)
|
|
||||||
{
|
|
||||||
for (size_t i = n; i > 0; i--)
|
|
||||||
{
|
|
||||||
pdest[i - 1] = psrc[i - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
int memcmp(const void *s1, const void *s2, size_t n)
|
|
||||||
{
|
|
||||||
const uint8_t *p1 = (const uint8_t *)s1;
|
|
||||||
const uint8_t *p2 = (const uint8_t *)s2;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
if (p1[i] != p2[i])
|
|
||||||
{
|
|
||||||
return p1[i] < p2[i] ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hcf(void)
|
|
||||||
{
|
|
||||||
asm("cli");
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
asm("hlt");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void emk_entry(void)
|
void emk_entry(void)
|
||||||
{
|
{
|
||||||
if (LIMINE_BASE_REVISION_SUPPORTED == false)
|
if (serial_init(COM1) != 0)
|
||||||
{
|
{
|
||||||
|
/* Just halt and say nothing */
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1)
|
if (!LIMINE_BASE_REVISION_SUPPORTED)
|
||||||
{
|
{
|
||||||
|
serial_write(COM1, (uint8_t *)"ERROR: Limine base revision is not supported\n", 45);
|
||||||
hcf();
|
hcf();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct limine_framebuffer *framebuffer = framebuffer_request.response->framebuffers[0];
|
serial_write(COM1, (uint8_t *)"Hello, World!\n", 14);
|
||||||
for (size_t i = 0; i < 100; i++)
|
hlt();
|
||||||
{
|
|
||||||
volatile uint32_t *fb_ptr = framebuffer->address;
|
|
||||||
fb_ptr[i * (framebuffer->pitch / 4) + i] = 0xffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
hcf();
|
|
||||||
}
|
}
|
65
kernel/src/lib/string.c
Normal file
65
kernel/src/lib/string.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#include <lib/string.h>
|
||||||
|
|
||||||
|
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t *restrict pdest = (uint8_t *restrict)dest;
|
||||||
|
const uint8_t *restrict psrc = (const uint8_t *restrict)src;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
pdest[i] = psrc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memset(void *s, int c, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t *p = (uint8_t *)s;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
p[i] = (uint8_t)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memmove(void *dest, const void *src, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t *pdest = (uint8_t *)dest;
|
||||||
|
const uint8_t *psrc = (const uint8_t *)src;
|
||||||
|
|
||||||
|
if (src > dest)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
pdest[i] = psrc[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (src < dest)
|
||||||
|
{
|
||||||
|
for (size_t i = n; i > 0; i--)
|
||||||
|
{
|
||||||
|
pdest[i - 1] = psrc[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memcmp(const void *s1, const void *s2, size_t n)
|
||||||
|
{
|
||||||
|
const uint8_t *p1 = (const uint8_t *)s1;
|
||||||
|
const uint8_t *p2 = (const uint8_t *)s2;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
if (p1[i] != p2[i])
|
||||||
|
{
|
||||||
|
return p1[i] < p2[i] ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
12
kernel/src/lib/string.h
Normal file
12
kernel/src/lib/string.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef STRING_H
|
||||||
|
#define STRING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void *memcpy(void *restrict dest, const void *restrict src, size_t n);
|
||||||
|
void *memset(void *s, int c, size_t n);
|
||||||
|
void *memmove(void *dest, const void *src, size_t n);
|
||||||
|
int memcmp(const void *s1, const void *s2, size_t n);
|
||||||
|
|
||||||
|
#endif // STRING_H
|
Loading…
Add table
Add a link
Reference in a new issue