[uart] Add first pass UART driver and logger

First attempt at a UART driver. I'm not sure it's the most stable. Now
that userspace is handling displaying logs, also removed serial and log
output support from the kernel.
This commit is contained in:
Justin C. Miller
2022-01-15 18:07:25 -08:00
parent 44d3918e4f
commit c631ec5ef5
40 changed files with 354 additions and 482 deletions

View File

@@ -6,4 +6,4 @@ programs:
flags: panic
- name: drv.uefi_fb
flags: graphical
- name: testapp
- name: drv.uart

View File

@@ -3,7 +3,7 @@
#include "clock.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
uint64_t lapic::s_ticks_per_us = 0;

View File

@@ -1,84 +0,0 @@
#include <util/no_construct.h>
#include "console.h"
#include "printf/printf.h"
#include "serial.h"
const char digits[] = "0123456789abcdef";
static util::no_construct<console> __g_console_storage;
console &g_console = __g_console_storage.value;
console::console() :
m_serial(nullptr)
{
}
console::console(serial_port *serial) :
m_serial(serial)
{
if (m_serial) {
const char *fgseq = "\x1b[2J";
while (*fgseq)
m_serial->write(*fgseq++);
}
}
void
console::echo()
{
putc(m_serial->read());
}
void
console::set_color(uint8_t fg, uint8_t bg)
{
if (m_serial) {
const char *fgseq = "\x1b[38;5;";
while (*fgseq)
m_serial->write(*fgseq++);
if (fg >= 100) m_serial->write('0' + (fg/100));
if (fg >= 10) m_serial->write('0' + (fg/10) % 10);
m_serial->write('0' + fg % 10);
m_serial->write('m');
const char *bgseq = "\x1b[48;5;";
while (*bgseq)
m_serial->write(*bgseq++);
if (bg >= 100) m_serial->write('0' + (bg/100));
if (bg >= 10) m_serial->write('0' + (bg/10) % 10);
m_serial->write('0' + bg % 10);
m_serial->write('m');
}
}
size_t
console::puts(const char *message)
{
size_t n = 0;
while (message && *message) {
n++;
putc(*message++);
}
return n;
}
void
console::putc(char c)
{
if (m_serial) {
if (c == '\n') m_serial->write('\r');
m_serial->write(c);
}
}
void console::vprintf(const char *fmt, va_list args)
{
static const unsigned buf_size = 256;
char buffer[buf_size];
vsnprintf(buffer, buf_size, fmt, args);
puts(buffer);
}

View File

@@ -1,84 +0,0 @@
#pragma once
#include <stdarg.h>
#include <stdint.h>
struct kernel_data;
class serial_port;
class console
{
public:
console();
console(serial_port *serial);
void set_color(uint8_t fg = 7, uint8_t bg = 0);
void putc(char c);
size_t puts(const char *message);
void vprintf(const char *fmt, va_list args);
inline void printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
template <typename T>
void put_hex(T x, int width = 0, char pad = ' ');
template <typename T>
void put_dec(T x, int width = 0, char pad = ' ');
void echo();
static console * get();
private:
serial_port *m_serial;
};
extern console &g_console;
inline console * console::get() { return &g_console; }
extern const char digits[];
template <typename T>
void console::put_hex(T x, int width, char pad)
{
static const int chars = sizeof(x) * 2;
char message[chars + 1];
int len = 1;
for (int i = chars - 1; i >= 0; --i) {
uint8_t nibble = (x >> (i*4)) & 0xf;
if (nibble) len = len > i + 1 ? len : i + 1;
message[chars - i - 1] = digits[nibble];
}
message[chars] = 0;
if (width > len) for(int i=0; i<(width-len); ++i) putc(pad);
puts(message + (chars - len));
if (-width > len) for(int i=0; i<(-width-len); ++i) putc(' ');
}
template <typename T>
void console::put_dec(T x, int width, char pad)
{
static const int chars = sizeof(x) * 3;
char message[chars + 1];
char *p = message + chars;
int length = 0;
*p-- = 0;
do {
*p-- = digits[x % 10];
x /= 10;
length += 1;
} while (x != 0);
if (width > length) for(int i=0; i<(width-length); ++i) putc(pad);
puts(++p);
if (-width > length) for(int i=0; i<(-width-length); ++i) putc(' ');
}

View File

@@ -7,7 +7,7 @@
#include "device_manager.h"
#include "gdt.h"
#include "idt.h"
#include "log.h"
#include "logger.h"
#include "msr.h"
#include "objects/thread.h"
#include "objects/vm_area.h"

View File

@@ -1,73 +0,0 @@
#include "console.h"
#include "cpu.h"
#include "debug.h"
#include "gdt.h"
#include "objects/process.h"
#include "objects/thread.h"
size_t __counter_syscall_enter = 0;
size_t __counter_syscall_sysret = 0;
void
print_regs(const cpu_state &regs)
{
console *cons = console::get();
cpu_data &cpu = current_cpu();
uint64_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
uintptr_t cr3 = 0;
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
cons->printf(" process: %llx", cpu.process->koid());
cons->printf(" thread: %llx\n", cpu.thread->koid());
print_regL("rax", regs.rax);
print_regM("rbx", regs.rbx);
print_regR("rcx", regs.rcx);
print_regL("rdx", regs.rdx);
print_regM("rdi", regs.rdi);
print_regR("rsi", regs.rsi);
cons->puts("\n");
print_regL(" r8", regs.r8);
print_regM(" r9", regs.r9);
print_regR("r10", regs.r10);
print_regL("r11", regs.r11);
print_regM("r12", regs.r12);
print_regR("r13", regs.r13);
print_regL("r14", regs.r14);
print_regM("r15", regs.r15);
cons->puts("\n\n");
print_regL("rbp", regs.rbp);
print_regM("rsp", regs.rsp);
print_regR("sp0", cpu.rsp0);
print_regL("rip", regs.rip);
print_regM("cr3", cr3);
print_regR("cr2", cr2);
cons->puts("\n");
}
struct frame
{
frame *prev;
uintptr_t return_addr;
};
void
print_stack(const cpu_state &regs)
{
console *cons = console::get();
cons->puts("\nStack:\n");
uint64_t sp = regs.rsp;
while (sp <= regs.rbp) {
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
sp += sizeof(uint64_t);
}
}

View File

@@ -18,11 +18,3 @@ extern "C" {
extern size_t __counter_syscall_enter;
extern size_t __counter_syscall_sysret;
void print_regs(const cpu_state &regs);
void print_stack(const cpu_state &regs);
#define print_regL(name, value) cons->printf(" %s: %016lx", name, (value));
#define print_regM(name, value) cons->printf(" %s: %016lx", name, (value));
#define print_regR(name, value) cons->printf(" %s: %016lx\n", name, (value));

View File

@@ -7,13 +7,11 @@
#include "acpi_tables.h"
#include "apic.h"
#include "clock.h"
#include "console.h"
#include "device_manager.h"
#include "interrupts.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "objects/endpoint.h"
#include "serial.h"
static endpoint * const ignore_endpoint = reinterpret_cast<endpoint*>(-1ull);
@@ -378,11 +376,6 @@ device_manager::init_drivers()
bool
device_manager::dispatch_irq(unsigned irq)
{
if (irq == 4) {
g_com1.handle_interrupt();
return true;
}
if (irq >= m_irqs.count())
return false;

View File

@@ -2,7 +2,7 @@
#include "assert.h"
#include "frame_allocator.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
using mem::frame_size;

View File

@@ -4,10 +4,9 @@
#include <util/no_construct.h>
#include "assert.h"
#include "console.h"
#include "cpu.h"
#include "gdt.h"
#include "log.h"
#include "logger.h"
#include "tss.h"
extern "C" void gdt_write(const void *gdt_ptr, uint16_t cs, uint16_t ds, uint16_t tr);
@@ -114,6 +113,7 @@ GDT::set_tss(TSS *tss)
memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
}
/*
void
GDT::dump(unsigned index) const
{
@@ -167,3 +167,4 @@ GDT::dump(unsigned index) const
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
}
}
*/

View File

@@ -2,7 +2,7 @@
#include "device_manager.h"
#include "hpet.h"
#include "io.h"
#include "log.h"
#include "logger.h"
namespace {
inline uint64_t volatile *capabilities(uint64_t volatile *base) { return base; }

View File

@@ -4,7 +4,7 @@
#include "cpu.h"
#include "idt.h"
#include "log.h"
#include "logger.h"
extern "C" {
void idt_write(const void *idt_ptr);

View File

@@ -7,7 +7,7 @@
#include "idt.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "objects/process.h"
#include "printf/printf.h"

View File

@@ -14,10 +14,8 @@ kernel = module("kernel",
"assert.cpp",
"boot.s",
"clock.cpp",
"console.cpp",
"cpprt.cpp",
"cpu.cpp",
"debug.cpp",
"debug.s",
"device_manager.cpp",
"frame_allocator.cpp",
@@ -29,7 +27,6 @@ kernel = module("kernel",
"interrupts.cpp",
"interrupts.s",
"io.cpp",
"log.cpp",
"logger.cpp",
"main.cpp",
"memory.cpp",
@@ -48,7 +45,6 @@ kernel = module("kernel",
"pci.cpp",
"printf/printf.c",
"scheduler.cpp",
"serial.cpp",
"syscall.cpp.cog",
"syscall.h.cog",
"syscall.s",

View File

@@ -1,88 +0,0 @@
#include <j6/signals.h>
#include <util/no_construct.h>
#include "assert.h"
#include "console.h"
#include "log.h"
#include "memory.h"
#include "objects/system.h"
#include "objects/thread.h"
static uint8_t log_buffer[128 * 1024];
// The logger is initialized _before_ global constructors are called,
// so that we can start log output immediately. Keep its constructor
// from being called here so as to not overwrite the previous initialization.
static util::no_construct<log::logger> __g_logger_storage;
log::logger &g_logger = __g_logger_storage.value;
static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09};
static void
output_log(logs area, log::level severity, const char *message)
{
auto *cons = console::get();
cons->set_color(level_colors[static_cast<int>(severity)]);
cons->printf("%7s %5s: %s\n",
g_logger.area_name(area),
g_logger.level_name(severity),
message);
cons->set_color();
}
// For printf.c
extern "C" void putchar_(char c) {}
void
logger_task()
{
auto *cons = console::get();
log::info(logs::task, "Starting kernel logger task");
g_logger.set_immediate(nullptr);
thread &self = thread::current();
system &sys = system::get();
size_t buffer_size = 1;
uint8_t *buffer = nullptr;
while (true) {
size_t size = g_logger.get_entry(buffer, buffer_size);
if (size > buffer_size) {
while (size > buffer_size) buffer_size *= 2;
kfree(buffer);
buffer = reinterpret_cast<uint8_t*>(kalloc(buffer_size));
kassert(buffer, "Could not allocate logger task buffer");
continue;
}
if(size) {
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
buffer[ent->bytes] = 0;
cons->set_color(level_colors[static_cast<int>(ent->severity)]);
cons->printf("%7s %5s: %s\n",
g_logger.area_name(ent->area),
g_logger.level_name(ent->severity),
ent->message);
cons->set_color();
}
if (!g_logger.has_log()) {
sys.deassert_signal(j6_signal_system_has_log);
sys.add_blocked_thread(&self);
self.wait_on_signals(j6_signal_system_has_log);
}
}
}
void logger_init()
{
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), output_log);
}
void logger_clear_immediate()
{
g_logger.set_immediate(nullptr);
}

View File

@@ -1,7 +0,0 @@
#pragma once
#include "logger.h"
void logger_init();
void logger_clear_immediate();
void logger_task();

View File

@@ -1,12 +1,25 @@
#include <string.h>
#include <util/constexpr_hash.h>
#include <util/no_construct.h>
#include "assert.h"
#include "logger.h"
#include "memory.h"
#include "objects/system.h"
#include "printf/printf.h"
static uint8_t log_buffer[128 * 1024];
// The logger is initialized _before_ global constructors are called,
// so that we can start log output immediately. Keep its constructor
// from being called here so as to not overwrite the previous initialization.
static util::no_construct<log::logger> __g_logger_storage;
log::logger &g_logger = __g_logger_storage.value;
// For printf.c
extern "C" void putchar_(char c) {}
namespace log {
logger *logger::s_log = nullptr;
@@ -131,3 +144,9 @@ void fatal(logs area, const char *fmt, ...)
}
} // namespace log
void logger_init()
{
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), nullptr);
}

View File

@@ -129,3 +129,5 @@ void fatal(logs area, const char *fmt, ...);
extern log::logger &g_logger;
} // namespace log
void logger_init();

View File

@@ -10,22 +10,19 @@
#include "assert.h"
#include "block_device.h"
#include "clock.h"
#include "console.h"
#include "cpu.h"
#include "device_manager.h"
#include "gdt.h"
#include "idt.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "msr.h"
#include "objects/channel.h"
#include "objects/event.h"
#include "objects/thread.h"
#include "objects/vm_area.h"
#include "scheduler.h"
#include "serial.h"
#include "syscall.h"
#include "sysconf.h"
#include "tss.h"
@@ -56,18 +53,6 @@ void load_init_server(bootproto::program &program, uintptr_t modules_address);
unsigned start_aps(lapic &apic, const util::vector<uint8_t> &ids, void *kpml4);
void
init_console()
{
serial_port *com1 = new (&g_com1) serial_port(COM1);
console *cons = new (&g_console) console(com1);
cons->set_color(0x21, 0x00);
cons->puts("jsix OS ");
cons->set_color(0x08, 0x00);
cons->puts(GIT_VERSION " booting...\n");
}
void
run_constructors()
{
@@ -86,9 +71,7 @@ kernel_main(bootproto::args *args)
panic::symbol_table = util::offset_pointer(args->symbol_table, mem::linear_offset);
}
init_console();
logger_init();
cpu_validate();
extern IDT &g_bsp_idt;
@@ -154,31 +137,7 @@ kernel_main(bootproto::args *args)
sysconf_create();
interrupts_enable();
g_com1.handle_interrupt();
/*
block_device *disk = devices->get_block_device(0);
if (disk) {
for (int i=0; i<1; ++i) {
uint8_t buf[512];
memset(buf, 0, 512);
kassert(disk->read(0x200, sizeof(buf), buf),
"Disk read returned 0");
console *cons = console::get();
uint8_t *p = &buf[0];
for (int i = 0; i < 8; ++i) {
for (int j = 0; j < 16; ++j) {
cons->printf(" %02x", *p++);
}
cons->putc('\n');
}
}
} else {
log::warn(logs::boot, "No block devices present.");
}
*/
//g_com1.handle_interrupt();
scheduler *sched = new scheduler {g_num_cpus};
scheduler_ready = true;
@@ -186,7 +145,6 @@ kernel_main(bootproto::args *args)
// Load the init server
load_init_server(*args->init, args->modules);
sched->create_kernel_task(logger_task, scheduler::max_priority/4, true);
sched->start();
}

View File

@@ -10,7 +10,7 @@
#include "gdt.h"
#include "heap_allocator.h"
#include "io.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "msr.h"
#include "objects/process.h"

View File

@@ -2,7 +2,7 @@
#include <util/pointers.h>
#include "cpu.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "objects/thread.h"
#include "objects/process.h"

View File

@@ -2,7 +2,6 @@
#include <util/pointers.h>
#include "assert.h"
#include "console.h"
#include "memory.h"
#include "frame_allocator.h"
#include "page_table.h"
@@ -259,6 +258,7 @@ page_table::free(page_table::level l)
void
page_table::dump(page_table::level lvl, bool recurse)
{
/*
console *cons = console::get();
cons->printf("\nLevel %d page table @ %lx:\n", lvl, this);
@@ -289,4 +289,5 @@ page_table::dump(page_table::level lvl, bool recurse)
next->dump(deeper(lvl), true);
}
}
*/
}

View File

@@ -1,6 +1,5 @@
#include "assert.h"
#include "console.h"
#include "log.h"
#include "logger.h"
#include "interrupts.h"
#include "pci.h"
@@ -36,6 +35,7 @@ struct pci_cap_msi64
} __attribute__ ((packed));
/*
void dump_msi(pci_cap_msi *cap)
{
auto cons = console::get();
@@ -62,6 +62,7 @@ void dump_msi(pci_cap_msi *cap)
}
cons->putc('\n');
};
*/
pci_device::pci_device() :
m_base(nullptr),

View File

@@ -5,14 +5,13 @@
#include "apic.h"
#include "assert.h"
#include "clock.h"
#include "console.h"
#include "cpu.h"
#include "debug.h"
#include "device_manager.h"
#include "gdt.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
#include "logger.h"
#include "msr.h"
#include "objects/channel.h"
#include "objects/process.h"

View File

@@ -3,15 +3,17 @@
#include <string.h>
#include "console.h"
#include "debug.h"
#include "log.h"
#include "logger.h"
#include "syscall.h"
extern "C" {
void syscall_invalid(uint64_t call);
}
size_t __counter_syscall_enter = 0;
size_t __counter_syscall_sysret = 0;
/*[[[cog code generation
from definitions.context import Context
@@ -27,12 +29,7 @@ uintptr_t syscall_registry[num_syscalls] __attribute__((section(".syscall_regist
void
syscall_invalid(uint64_t call)
{
console *cons = console::get();
cons->set_color(9);
cons->printf("\nReceived unknown syscall: %02x\n", call);
cons->set_color();
_halt();
log::warn(logs::syscall, "Received unknown syscall: %02x\n", call);
}
void

View File

@@ -1,7 +1,7 @@
#include <j6/errors.h>
#include <j6/types.h>
#include "log.h"
#include "logger.h"
#include "objects/endpoint.h"
#include "syscalls/helpers.h"

View File

@@ -4,7 +4,7 @@
#include <util/vector.h>
#include "assert.h"
#include "log.h"
#include "logger.h"
#include "objects/thread.h"
#include "syscalls/helpers.h"

View File

@@ -1,7 +1,7 @@
#include <j6/errors.h>
#include <j6/types.h>
#include "log.h"
#include "logger.h"
#include "objects/process.h"
#include "syscalls/helpers.h"

View File

@@ -4,7 +4,7 @@
#include "cpu.h"
#include "device_manager.h"
#include "frame_allocator.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "objects/endpoint.h"
#include "objects/thread.h"

View File

@@ -1,7 +1,7 @@
#include <j6/errors.h>
#include <j6/types.h>
#include "log.h"
#include "logger.h"
#include "objects/process.h"
#include "objects/thread.h"
#include "syscalls/helpers.h"

View File

@@ -2,7 +2,7 @@
#include <j6/signals.h>
#include <j6/types.h>
#include "log.h"
#include "logger.h"
#include "objects/process.h"
#include "objects/vm_area.h"
#include "syscalls/helpers.h"

View File

@@ -3,7 +3,7 @@
#include "assert.h"
#include "cpu.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "objects/vm_area.h"
#include "tss.h"

View File

@@ -4,7 +4,7 @@
#include "assert.h"
#include "frame_allocator.h"
#include "log.h"
#include "logger.h"
#include "memory.h"
#include "objects/process.h"
#include "objects/thread.h"

22
src/user/drv.uart/io.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include "io.h"
uint8_t
inb(uint16_t port)
{
uint8_t val;
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
return val;
}
void
outb(uint16_t port, uint8_t val)
{
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
}
void
io_wait(unsigned times)
{
for (unsigned i = 0; i < times; ++i)
outb(0x80, 0);
}

24
src/user/drv.uart/io.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
extern "C" {
/// Read a byte from an IO port.
/// \arg port The address of the IO port
/// \returns One byte read from the port
uint8_t inb(uint16_t port);
/// Write a byte to an IO port.
/// \arg port The addres of the IO port
/// \arg val The byte to write
void outb(uint16_t port, uint8_t val);
/// Pause briefly by doing IO to port 0x80
/// \arg times Number of times to delay by writing
void io_wait(unsigned times = 1);
}
constexpr uint16_t COM1 = 0x03f8;
constexpr uint16_t COM2 = 0x02f8;

196
src/user/drv.uart/main.cpp Normal file
View File

@@ -0,0 +1,196 @@
#include <new>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <j6/errors.h>
#include <j6/flags.h>
#include <j6/signals.h>
#include <j6/syscalls.h>
#include <j6/sysconf.h>
#include <j6/types.h>
#include <util/constexpr_hash.h>
#include "io.h"
#include "serial.h"
extern "C" {
int main(int, const char **);
}
constexpr j6_handle_t handle_self = 1;
constexpr j6_handle_t handle_sys = 2;
struct entry
{
uint8_t bytes;
uint8_t area;
uint8_t severity;
uint8_t sequence;
char message[0];
};
static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09};
const char *level_names[] = {"", "debug", "info", "warn", "error", "fatal"};
const char *area_names[] = {
#define LOG(name, lvl) #name ,
#include <j6/tables/log_areas.inc>
#undef LOG
nullptr
};
constexpr size_t in_buf_size = 512;
constexpr size_t out_buf_size = 128 * 1024;
uint8_t com1_in[in_buf_size];
uint8_t com2_in[in_buf_size];
uint8_t com1_out[out_buf_size];
uint8_t com2_out[out_buf_size];
serial_port *g_com1;
serial_port *g_com2;
constexpr size_t stack_size = 0x10000;
constexpr uintptr_t stack_top = 0xf80000000;
void
print_header()
{
char stringbuf[150];
unsigned version_major = j6_sysconf(j6sc_version_major);
unsigned version_minor = j6_sysconf(j6sc_version_minor);
unsigned version_patch = j6_sysconf(j6sc_version_patch);
unsigned version_git = j6_sysconf(j6sc_version_gitsha);
size_t len = snprintf(stringbuf, sizeof(stringbuf),
"\e[38;5;21mjsix OS\e[38;5;8m %d.%d.%d (%07x) booting...\e[0m\r\n",
version_major, version_minor, version_patch, version_git);
g_com1->write(stringbuf, len);
}
void
log_pump_proc()
{
size_t buffer_size = 0;
void *message_buffer = nullptr;
char stringbuf[300];
j6_status_t result = j6_system_request_iopl(handle_sys, 3);
if (result != j6_status_ok)
return;
while (true) {
size_t size = buffer_size;
j6_status_t s = j6_system_get_log(handle_sys, message_buffer, &size);
if (s == j6_err_insufficient) {
free(message_buffer);
buffer_size = size * 2;
message_buffer = malloc(buffer_size);
continue;
} else if (s != j6_status_ok) {
j6_log("uart driver got error from get_log");
continue;
}
if (size == 0) {
j6_signal_t sigs = 0;
j6_kobject_wait(handle_sys, j6_signal_system_has_log, &sigs);
continue;
}
const entry *e = reinterpret_cast<entry*>(message_buffer);
const char *area_name = area_names[e->area];
const char *level_name = level_names[e->severity];
uint8_t level_color = level_colors[e->severity];
size_t len = snprintf(stringbuf, sizeof(stringbuf),
"\e[38;5;%dm%7s %5s: %s\e[38;5;0m\r\n",
level_color, area_name, level_name, e->message);
g_com1->write(stringbuf, len);
}
}
int
main(int argc, const char **argv)
{
j6_log("uart driver starting");
j6_handle_t endp = j6_handle_invalid;
j6_status_t result = j6_status_ok;
result = j6_system_request_iopl(handle_sys, 3);
if (result != j6_status_ok)
return result;
result = j6_endpoint_create(&endp);
if (result != j6_status_ok)
return result;
result = j6_system_bind_irq(handle_sys, endp, 3);
if (result != j6_status_ok)
return result;
result = j6_system_bind_irq(handle_sys, endp, 4);
if (result != j6_status_ok)
return result;
serial_port com1 {COM1, in_buf_size, com1_in, out_buf_size, com1_out};
serial_port com2 {COM2, in_buf_size, com2_in, out_buf_size, com2_out};
g_com1 = &com1;
g_com2 = &com2;
print_header();
j6_handle_t child_stack_vma = j6_handle_invalid;
result = j6_vma_create_map(&child_stack_vma, stack_size, stack_top-stack_size, j6_vm_flag_write);
if (result != j6_status_ok)
return result;
uint64_t *sp = reinterpret_cast<uint64_t*>(stack_top - 0x10);
sp[0] = sp[1] = 0;
j6_handle_t child = j6_handle_invalid;
result = j6_thread_create(&child, handle_self, stack_top - 0x10, reinterpret_cast<uintptr_t>(&log_pump_proc));
if (result != j6_status_ok)
return result;
size_t len = 0;
while (true) {
uint64_t tag = 0;
result = j6_endpoint_receive(endp, &tag, nullptr, &len, 10);
if (result == j6_err_timed_out) {
com1.handle_interrupt();
com2.handle_interrupt();
continue;
}
if (result != j6_status_ok) {
j6_log("uart driver got error waiting for irq");
continue;
}
if (!j6_tag_is_irq(tag)) {
j6_log("uart driver got non-irq waiting for irq");
continue;
}
unsigned irq = j6_tag_to_irq(tag);
switch (irq) {
case 3:
com2.handle_interrupt();
break;
case 4:
com1.handle_interrupt();
break;
default:
j6_log("uart driver got unknown irq waiting for irq");
}
}
j6_log("uart driver somehow got to the end of main");
return 0;
}

View File

@@ -1,17 +1,7 @@
#include <string.h>
#include <util/no_construct.h>
#include "assert.h"
#include "interrupts.h"
#include "io.h"
#include "serial.h"
// This object is initialized _before_ global constructors are called,
// so we don't want it to have global constructors at all, lest it
// overwrite the previous initialization.
static util::no_construct<serial_port> __g_com1_storage;
serial_port &g_com1 = __g_com1_storage.value;
constexpr size_t fifo_size = 64;
// register offsets
@@ -28,20 +18,13 @@ constexpr uint16_t MSR = 6;
constexpr uint16_t DLL = 0; // DLAB == 1
constexpr uint16_t DLH = 1; // DLAB == 1
uint8_t com1_out_buffer[4096*4];
uint8_t com1_in_buffer[512];
serial_port::serial_port() :
m_writing(false),
m_port(0)
{
}
serial_port::serial_port(uint16_t port) :
serial_port::serial_port(uint16_t port,
size_t in_buffer_len, uint8_t *in_buffer,
size_t out_buffer_len, uint8_t *out_buffer) :
m_writing(false),
m_port(port),
m_out_buffer(com1_out_buffer, sizeof(com1_out_buffer)),
m_in_buffer(com1_in_buffer, sizeof(com1_in_buffer))
m_out_buffer(out_buffer, out_buffer_len),
m_in_buffer(in_buffer, in_buffer_len)
{
outb(port + IER, 0x00); // Disable all interrupts
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
@@ -62,7 +45,6 @@ inline bool write_ready(uint16_t port) { return (inb(port + LSR) & 0x20) != 0; }
void
serial_port::handle_interrupt()
{
interrupts_disable();
uint8_t iir = inb(m_port + IIR);
while ((iir & 1) == 0) {
@@ -74,11 +56,9 @@ serial_port::handle_interrupt()
break;
case 1: // Transmit buffer empty
do_write();
break;
case 2: // Received data available
do_read();
if (read_ready(m_port)) do_read();
if (write_ready(m_port)) do_write();
break;
case 3: // Line status change
@@ -89,7 +69,6 @@ serial_port::handle_interrupt()
iir = inb(m_port + IIR);
}
interrupts_enable();
}
void
@@ -110,30 +89,39 @@ serial_port::do_read()
void
serial_port::do_write()
{
uint8_t *data = nullptr;
uint8_t tmp[fifo_size];
// If another thread is writing data, just give up and
// try again later
util::scoped_trylock lock {m_lock};
if (!lock.locked())
return;
uint8_t *data = nullptr;
size_t n = m_out_buffer.get_block(reinterpret_cast<void**>(&data));
m_writing = (n > 0);
if (!m_writing)
if (!m_writing) {
m_out_buffer.consume(0);
return;
}
if (n > fifo_size)
n = fifo_size;
memcpy(tmp, data, n);
m_out_buffer.consume(n);
for (size_t i = 0; i < n; ++i)
for (size_t i = 0; i < n; ++i) {
if (!write_ready(m_port)) {
n = i;
break;
}
outb(m_port, data[i]);
}
m_out_buffer.consume(n);
}
void
serial_port::handle_error(uint16_t reg, uint8_t value)
{
kassert(false, "serial line error");
while (1) asm ("hlt");
}
char
@@ -147,17 +135,18 @@ serial_port::read()
return c;
}
void
serial_port::write(char c)
size_t
serial_port::write(const char *c, size_t len)
{
uint8_t *data = nullptr;
size_t avail = m_out_buffer.reserve(1, reinterpret_cast<void**>(&data));
if (!avail)
return;
*data = c;
m_out_buffer.commit(1);
size_t avail = m_out_buffer.reserve(len, reinterpret_cast<void**>(&data));
memcpy(data, c, avail);
m_out_buffer.commit(avail);
if (!m_writing)
do_write();
return avail;
}

View File

@@ -4,17 +4,18 @@
#include <stdint.h>
#include <util/bip_buffer.h>
#include <util/spinlock.h>
class serial_port
{
public:
/// Constructor.
/// \arg port The IO address of the serial port
serial_port(uint16_t port);
serial_port(uint16_t port,
size_t in_buffer_len, uint8_t *in_buffer,
size_t out_buffer_len, uint8_t *out_buffer);
serial_port();
void write(char c);
size_t write(const char *str, size_t len);
char read();
void handle_interrupt();
@@ -24,10 +25,9 @@ private:
uint16_t m_port;
util::bip_buffer m_out_buffer;
util::bip_buffer m_in_buffer;
util::spinlock m_lock;
void do_read();
void do_write();
void handle_error(uint16_t reg, uint8_t value);
};
extern serial_port &g_com1;

View File

@@ -0,0 +1,12 @@
# vim: ft=python
module("drv.uart",
targets = [ "user" ],
deps = [ "libc", "util" ],
description = "UART driver",
sources = [
"io.cpp",
"main.cpp",
"serial.cpp",
])

View File

@@ -55,6 +55,12 @@ load_program(const module_program &prog, char *err_msg)
return false;
}
res = j6_process_give_handle(proc, handle_system, nullptr);
if (res != j6_status_ok) {
sprintf(err_msg, " ** error loading program '%s': giving system handle: %lx", prog.filename, res);
return false;
}
uintptr_t load_addr = load_addr_base;
for (auto &seg : progelf.programs()) {