From d0fc1ca09f0a6cc7371a3046b70f96968554a2f8 Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Sat, 3 Dec 2022 20:06:06 +0400 Subject: [PATCH] Add existing drivers --- Makefile.am | 16 ++++- include/Makefile.am | 16 ++++- include/drivers/console.h | 20 ++++++ include/drivers/intel_8253_pit.h | 21 ++++++ include/drivers/intel_8259_pic.h | 57 ++++++++++++++++ include/drivers/shutdown.h | 15 +++++ src/console.c | 36 ++++++++++ src/intel_8253_pit.c | 19 ++++++ src/intel_8259_pic.c | 112 +++++++++++++++++++++++++++++++ src/shutdown.c | 29 ++++++++ 10 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 include/drivers/console.h create mode 100644 include/drivers/intel_8253_pit.h create mode 100644 include/drivers/intel_8259_pic.h create mode 100644 include/drivers/shutdown.h create mode 100644 src/console.c create mode 100644 src/intel_8253_pit.c create mode 100644 src/intel_8259_pic.c create mode 100644 src/shutdown.c diff --git a/Makefile.am b/Makefile.am index 8092ec0..1a9b5b1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,4 +8,18 @@ SUBDIRS = include lib_LTLIBRARIES = libdrivers.la libdrivers_la_SOURCES = \ - src/foobar.c + src/foobar.c \ + src/console.c \ + src/shutdown.c + +# Intel 8253-compatible programmable interval timer + +if ASM_I386 +libdrivers_la_SOURCES += src/intel_8253_pit.c +endif + +# Intel 8259-compatible programmable interrupt controller + +if ASM_I386 +libdrivers_la_SOURCES += src/intel_8259_pic.c +endif diff --git a/include/Makefile.am b/include/Makefile.am index 316fd0f..27650d2 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,2 +1,16 @@ nobase_include_HEADERS = \ - drivers.h + drivers.h \ + drivers/console.h \ + drivers/shutdown.h + +# Intel 8253-compatible programmable interval timer + +if ASM_I386 +nobase_include_HEADERS += drivers/intel_8253_pit.h +endif + +# Intel 8259-compatible programmable interrupt controller + +if ASM_I386 +nobase_include_HEADERS += drivers/intel_8259_pic.h +endif diff --git a/include/drivers/console.h b/include/drivers/console.h new file mode 100644 index 0000000..a3b5609 --- /dev/null +++ b/include/drivers/console.h @@ -0,0 +1,20 @@ +#ifndef INCLUDED_DRIVERS_CONSOLE +#define INCLUDED_DRIVERS_CONSOLE + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void drivers_console_putc(char c); + +void drivers_console_print(const char *s); +void drivers_console_puts(const char *s); +void drivers_console_write(const char *data, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/drivers/intel_8253_pit.h b/include/drivers/intel_8253_pit.h new file mode 100644 index 0000000..92ee98b --- /dev/null +++ b/include/drivers/intel_8253_pit.h @@ -0,0 +1,21 @@ +/** + * @brief A driver for Intel 8253-compatible programmable interval timer + * + * @see https://en.wikipedia.org/wiki/Intel_8253 + * @see https://wiki.osdev.org/PIT + */ + +#ifndef DRIVERS_INCLUDED_INTEL_8253_PIT +#define DRIVERS_INCLUDED_INTEL_8253_PIT + +#ifdef __cplusplus +extern "C" { +#endif + +void drivers_intel_8253_pit_initialize(unsigned int freq); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/drivers/intel_8259_pic.h b/include/drivers/intel_8259_pic.h new file mode 100644 index 0000000..f5dd576 --- /dev/null +++ b/include/drivers/intel_8259_pic.h @@ -0,0 +1,57 @@ +/** + * @brief A driver for Intel 8259-compatible programmable interrupt controller + * + * @details + * This is not a driver for a random Intel 8259-compatible PIC, but only for a + * typical configuration of it that can be found in most PCs. + * + * @see https://en.wikipedia.org/wiki/Intel_8259 + * @see https://wiki.osdev.org/8259_PIC + * @see https://pdos.csail.mit.edu/6.828/2005/readings/hardware/8259A.pdf + */ + +#ifndef DRIVERS_INCLUDED_INTEL_8259_PIC +#define DRIVERS_INCLUDED_INTEL_8259_PIC + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enable PIC + */ +void drivers_intel_8259_pic_enable_all(); + +/** + * @brief Disable PIC + */ +void drivers_intel_8259_pic_disable_all(); + +/** + * @brief Enable single IRQ line + */ +void drivers_intel_8259_pic_enable(unsigned char number); + +/** + * @brief Disable single IRQ line + */ +void drivers_intel_8259_pic_disable(unsigned char number); + +/** + * @brief Remap PIC + */ +void drivers_intel_8259_pic_remap( + unsigned char new_master_start, + unsigned char new_slave_start +); + +/** + * @brief Signal end of interrupt + */ +void drivers_intel_8259_pic_eoi(unsigned char number); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/drivers/shutdown.h b/include/drivers/shutdown.h new file mode 100644 index 0000000..1f8d474 --- /dev/null +++ b/include/drivers/shutdown.h @@ -0,0 +1,15 @@ +#ifndef DRIVERS_INCLUDED_SHUTDOWN +#define DRIVERS_INCLUDED_SHUTDOWN + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((noreturn)) void drivers_shutdown_halt(); +__attribute__((noreturn)) void drivers_shutdown_poweroff(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/console.c b/src/console.c new file mode 100644 index 0000000..8c13f7e --- /dev/null +++ b/src/console.c @@ -0,0 +1,36 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +void drivers_console_putc(const char c) +{ +#if defined(ASM_X86) + __asm__ __volatile__("outb %1, %0" : : "dN" (0x3f8), "a" (c)); +#else + (void)c; +#endif +} + +void drivers_console_print(const char *const s) +{ + for (const char *c = s; *c; ++c) { + drivers_console_putc(*c); + } +} + +void drivers_console_puts(const char *const s) +{ + drivers_console_print(s); + drivers_console_putc('\n'); +} + +void drivers_console_write(const char *const data, const size_t size) +{ + for (size_t i = 0; i < size; i++) { + drivers_console_putc(data[i]); + } +} diff --git a/src/intel_8253_pit.c b/src/intel_8253_pit.c new file mode 100644 index 0000000..853ff37 --- /dev/null +++ b/src/intel_8253_pit.c @@ -0,0 +1,19 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +void drivers_intel_8253_pit_initialize(const unsigned int freq) +{ + const unsigned int divisor = 1193180 / freq; + + const uint8_t l = divisor & 0xff; + const uint8_t h = (divisor >> 8) & 0xff; + + __asm__ __volatile__("outb %1, %0" : : "dN" (0x43), "a" (0x36)); + __asm__ __volatile__("outb %1, %0" : : "dN" (0x40), "a" (l)); + __asm__ __volatile__("outb %1, %0" : : "dN" (0x40), "a" (h)); +} diff --git a/src/intel_8259_pic.c b/src/intel_8259_pic.c new file mode 100644 index 0000000..b91b7c0 --- /dev/null +++ b/src/intel_8259_pic.c @@ -0,0 +1,112 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +#define BITS8(n) ((uint8_t )(((uint8_t )1) << (n))) + +#define MASTER_COMMAND_PORT 0x20 +#define SLAVE_COMMAND_PORT 0xA0 + +#define MASTER_DATA_PORT 0x21 +#define SLAVE_DATA_PORT 0xA1 + +#define IRQS_COUNT 8 +#define IRQS_TOTAL 16 + +static unsigned char master_start = 0; +static unsigned char slave_start = 8; + +static uint8_t inportb(const uint16_t port) +{ + register uint8_t result; + __asm__ __volatile__("inb %1, %0" : "=a" (result) : "dN" (port)); + return result; +} + + +static void outportb(const uint16_t port, const uint8_t value) +{ + __asm__ __volatile__("outb %1, %0" : : "dN" (port), "a" (value)); +} + +void drivers_intel_8259_pic_enable_all() +{ + outportb(MASTER_DATA_PORT, 0); + outportb(SLAVE_DATA_PORT, 0); +} + +void drivers_intel_8259_pic_disable_all() +{ + outportb(MASTER_DATA_PORT, 0xFF); + outportb(SLAVE_DATA_PORT, 0xFF); +} + +void drivers_intel_8259_pic_enable(const unsigned char number) +{ + if (number < IRQS_COUNT) { + const uint8_t mask = inportb(MASTER_DATA_PORT); + outportb(MASTER_DATA_PORT, mask & ~BITS8(number)); + } else { + const uint8_t mask = inportb(SLAVE_DATA_PORT); + outportb(SLAVE_DATA_PORT, mask & ~BITS8((number - IRQS_COUNT))); + } +} + +void drivers_intel_8259_pic_disable(const unsigned char number) +{ + if (number < IRQS_COUNT) { + const uint8_t mask = inportb(MASTER_DATA_PORT); + outportb(MASTER_DATA_PORT, mask | BITS8(number)); + } else { + const uint8_t mask = inportb(SLAVE_DATA_PORT); + outportb(SLAVE_DATA_PORT, mask | BITS8((number - IRQS_COUNT))); + } +} + +void drivers_intel_8259_pic_remap( + const unsigned char new_master_start, + const unsigned char new_slave_start +) { + master_start = new_master_start; + slave_start = new_slave_start; + + // Save masks + const uint8_t master_mask = inportb(MASTER_DATA_PORT); + const uint8_t slave_mask = inportb(SLAVE_DATA_PORT); + + // Start the initialization sequence + outportb(MASTER_COMMAND_PORT, 0x11); + outportb(SLAVE_COMMAND_PORT, 0x11); + + // Set IRQ vectors + outportb(MASTER_DATA_PORT, new_master_start); + outportb(SLAVE_DATA_PORT, new_slave_start); + + // Connect master and slave with each other + outportb(MASTER_DATA_PORT, 0x04); + outportb(SLAVE_DATA_PORT, 0x02); + + // 8086/88 (MCS-80/85) mode + outportb(MASTER_DATA_PORT, 0x01); + outportb(SLAVE_DATA_PORT, 0x01); + + // Restore masks + outportb(MASTER_DATA_PORT, master_mask); + outportb(SLAVE_DATA_PORT, slave_mask); +} + +void drivers_intel_8259_pic_eoi(const unsigned char number) +{ + const bool to_slave = + number >= slave_start && number < slave_start + IRQS_COUNT; + const bool to_master = to_slave || + (number >= master_start && number < master_start + IRQS_COUNT); + + if (to_slave) outportb(SLAVE_COMMAND_PORT, 0x20); + if (to_master) outportb(MASTER_COMMAND_PORT, 0x20); +} diff --git a/src/shutdown.c b/src/shutdown.c new file mode 100644 index 0000000..194d450 --- /dev/null +++ b/src/shutdown.c @@ -0,0 +1,29 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +void drivers_shutdown_halt() +{ +#ifdef ASM_X86 + // Disable interrupts + __asm__ __volatile__("cli"); +#endif + + volatile int x = 0; + for (;;) ++x; +} + +void drivers_shutdown_poweroff() +{ +#ifdef ASM_X86 + // QEMU >= 2.0 + __asm__ __volatile__("outw %1, %0" : : "dN" (0x604), "a" (0x2000)); + // QEMU < 2.0 + __asm__ __volatile__("outw %1, %0" : : "dN" (0xB004), "a" (0x2000)); +#endif + + // If we can't poweroff then we halt + drivers_shutdown_halt(); +}