diff --git a/hermit/Makefile b/hermit/Makefile index 77fbdd40e..2aa1efc44 100644 --- a/hermit/Makefile +++ b/hermit/Makefile @@ -140,6 +140,7 @@ include/hermit/config.inc: include/hermit/config.h @awk '/^#define MAX_CORES/{ print "%define MAX_CORES", $$3 }' include/hermit/config.h >> include/hermit/config.inc @awk '/^#define KERNEL_STACK_SIZE/{ print "%define KERNEL_STACK_SIZE", $$3 }' include/hermit/config.h >> include/hermit/config.inc @awk '/^#define VIDEO_MEM_ADDR/{ print "%define VIDEO_MEM_ADDR", $$3 }' include/hermit/config.h >> include/hermit/config.inc + @awk '/^#define CONFIG_VGA/{ print "%define CONFIG_VGA", $$3 }' include/hermit/config.h >> include/hermit/config.inc @awk '/^#define DYNAMIC_TICKS/{ print "%define DYNAMIC_TICKS", $$3 }' include/hermit/config.h >> include/hermit/config.inc @awk '/^#define SAVE_FPU/{ print "%define SAVE_FPU", $$3 }' include/hermit/config.h >> include/hermit/config.inc diff --git a/hermit/arch/x86/include/asm/uart.h b/hermit/arch/x86/include/asm/uart.h new file mode 100644 index 000000000..dfa650094 --- /dev/null +++ b/hermit/arch/x86/include/asm/uart.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014-2016, Stefan Lankes, Daniel Krebs, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ARCH_UART_H__ +#define __ARCH_UART_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Initialize UART output + * + * @return Returns 0 on success + */ +int uart_init(void); + +/** @brief Initialize UART output without a device check + * + * @return Returns 0 on success + */ +int uart_early_init(char*); + +/** @brief Simple string output on a serial device. + * + * If you want a new line you will have to "\\n". + * + * @return Length of output in bytes + */ +int uart_puts(const char *text); + +/** @brief Simple character output on a serial device. + * + * @return The original input character casted to int + */ +int uart_putchar(unsigned char c); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/kernel/Makefile b/hermit/arch/x86/kernel/Makefile index 18699e881..06654ea4c 100644 --- a/hermit/arch/x86/kernel/Makefile +++ b/hermit/arch/x86/kernel/Makefile @@ -1,4 +1,4 @@ -C_source := irq.c idt.c isrs.c gdt.c processor.c timer.c tasks.c apic.c pci.c vga.c +C_source := irq.c idt.c isrs.c gdt.c processor.c timer.c tasks.c apic.c pci.c vga.c uart.c ASM_source := entry.asm string.asm MODULE := arch_x86_kernel diff --git a/hermit/arch/x86/kernel/entry.asm b/hermit/arch/x86/kernel/entry.asm index c610cd8e4..60841b54b 100644 --- a/hermit/arch/x86/kernel/entry.asm +++ b/hermit/arch/x86/kernel/entry.asm @@ -163,6 +163,7 @@ start64: add rax, [base] mov QWORD [rdi+511*8], rax +%ifdef CONFIG_VGA ; map vga 1:1 mov rax, VIDEO_MEM_ADDR ; map vga and rax, ~0xFFF ; page align lower half @@ -171,6 +172,7 @@ start64: add rdi, boot_pgt or rax, 0x113 ; set present, global, writable and cache disable bits mov QWORD [rdi], rax +%endif ; remap kernel mov rdi, kernel_start diff --git a/hermit/arch/x86/kernel/uart.c b/hermit/arch/x86/kernel/uart.c new file mode 100644 index 000000000..13750b838 --- /dev/null +++ b/hermit/arch/x86/kernel/uart.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2014-2016, Stefan Lankes, Daniel Krebs, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PCI +#include +#endif + +#ifndef CONFIG_VGA + +/* + * This implementation based on following tutorial: + * http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming + */ + +#define UART_RX 0 /* In: Receive buffer */ +#define UART_IIR 2 /* In: Interrupt ID Register */ +#define UART_TX 0 /* Out: Transmit buffer */ +#define UART_IER 1 /* Out: Interrupt Enable Register */ +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_DLL 0 /* Out: Divisor Latch Low */ +#define UART_DLM 1 /* Out: Divisor Latch High */ +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_LSR 5 /* Line Status Register */ + +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ + +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ +#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ +#define UART_FCR_TRIGGER_MASK 0xC0 /* Mask for the FIFO trigger range */ +#define UART_FCR_TRIGGER_1 0x00 /* Trigger RDI at FIFO level 1 byte */ +#define UART_FCR_TRIGGER_4 0x40 /* Trigger RDI at FIFO level 4 byte */ +#define UART_FCR_TRIGGER_8 0x80 /* Trigger RDI at FIFO level 8 byte */ +#define UART_FCR_TRIGGER_14 0xc0 /* Trigger RDI at FIFO level 14 byte*/ + + +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 bit, 1=2 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +#define UART_MCR_CLKSEL 0x80 /* Divide clock by 4 (TI16C752, EFR[4]=1) */ +#define UART_MCR_TCRTLR 0x40 /* Access TCR/TLR (TI16C752, EFR[4]=1) */ +#define UART_MCR_XONANY 0x20 /* Enable Xon Any (TI16C752, EFR[4]=1) */ +#define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C550C/TI16C750) */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ + +static uint8_t mmio = 0; +static size_t iobase = 0; + +static inline unsigned char read_from_uart(uint32_t off) +{ + uint8_t c; + + if (mmio) + c = *((const volatile unsigned char*) (iobase + off)); + else + c = inportb(iobase + off); + + return c; +} + +static void write_to_uart(uint32_t off, unsigned char c) +{ + if (mmio) + *((volatile unsigned char*) (iobase + off)) = c; + else + outportb(iobase + off, c); +} + + +/* Get a single character on a serial device */ +static unsigned char uart_getchar(void) +{ + return read_from_uart(UART_RX); +} + +/* Puts a single character on a serial device */ +int uart_putchar(unsigned char c) +{ + if (!iobase) + return 0; + + write_to_uart(UART_TX, c); + + return (int) c; +} + +/* Uses the routine above to output a string... */ +int uart_puts(const char *text) +{ + size_t i, len = strlen(text); + + if (!iobase) + return 0; + + for (i = 0; i < len; i++) + uart_putchar(text[i]); + + return len; +} + +/* Handles all UART's interrupt */ +static void uart_handler(struct state *s) +{ + unsigned char c = read_from_uart(UART_IIR); + + while (!(c & UART_IIR_NO_INT)) { + if (c & UART_IIR_RDI) { + c = uart_getchar(); + + //TODO: handle input messages + + goto out; + } + + if(c & UART_IIR_THRI) { + // acknowledge interrupt + c = read_from_uart(UART_IIR); + + goto out; + } + + if(c & UART_IIR_RLSI) { + // acknowledge interrupt + c = read_from_uart(UART_LSR); + + goto out; + } + +out: + c = read_from_uart(UART_IIR); + } +} + +static int uart_config(void) +{ + /* + * enable FIFOs + * clear RX and TX FIFO + * set irq trigger to 8 bytes + */ + write_to_uart(UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_1); + + /* disable interrupts */ + write_to_uart(UART_IER, 0); + + /* DTR + RTS */ + write_to_uart(UART_MCR, UART_MCR_DTR|UART_MCR_RTS); + + /* + * 8bit word length + * 1 stop bit + * no partity + * set DLAB=1 + */ + char lcr = UART_LCR_WLEN8; + write_to_uart(UART_LCR, lcr); + lcr = read_from_uart(UART_LCR) | UART_LCR_DLAB; + write_to_uart(UART_LCR, lcr); + + /* + * set baudrate to 115200 + */ + uint32_t divisor = 1843200 / 115200; + write_to_uart(UART_DLL, divisor & 0xff); + write_to_uart(UART_DLM, (divisor >> 8) & 0xff); + + /* set DLAB=0 */ + write_to_uart(UART_LCR, lcr & (~UART_LCR_DLAB)); + + return 0; +} + +extern const void kernel_start; + +int uart_early_init(char* cmdline) +{ +#if 1 + // default value of our QEMU configuration + iobase = 0xc110; +#else + if (BUILTIN_EXPECT(!cmdline, 0)) + return -EINVAL; + + char* str = strstr(cmdline, "uart="); + if (!str) + return -EINVAL; + + if (strncmp(str, "uart=io:", 8) == 0) { + iobase = strtol(str+8, (char **)NULL, 16); + if (!iobase) + return -EINVAL; + mmio = 0; + } else if (strncmp(str, "uart=mmio:", 10) == 0) { + iobase = strtol(str+10, (char **)NULL, 16); + if (!iobase) + return -EINVAL; + if (iobase >= PAGE_MAP_ENTRIES*PAGE_SIZE) { + /* at this point we use the boot page table + * => IO address is not mapped + * => dirty hack, map device before the kernel + */ + int err; + size_t newaddr = ((size_t) &kernel_start - PAGE_SIZE); + + err = page_map_bootmap(newaddr & PAGE_MASK, iobase & PAGE_MASK, PG_GLOBAL | PG_ACCESSED | PG_DIRTY | PG_RW | PG_PCD); + if (BUILTIN_EXPECT(err, 0)) { + iobase = 0; + return err; + } + iobase = newaddr; + } + mmio = 1; + } +#endif + + // configure uart + return uart_config(); +} + +int uart_init(void) +{ +#ifdef CONFIG_PCI + pci_info_t pci_info; + uint32_t bar = 0; + + // Searching for Intel's UART device + if (pci_get_device_info(0x8086, 0x0936, iobase, &pci_info) == 0) + goto Lsuccess; + // Searching for Qemu's UART device + if (pci_get_device_info(0x1b36, 0x0002, iobase, &pci_info) == 0) + goto Lsuccess; + // Searching for Qemu's 2x UART device (pci-serial-2x) + if (pci_get_device_info(0x1b36, 0x0003, iobase, &pci_info) == 0) + goto Lsuccess; + // Searching for Qemu's 4x UART device (pci-serial-4x) + if (pci_get_device_info(0x1b36, 0x0004, iobase, &pci_info) == 0) + goto Lsuccess; + + return -1; + +Lsuccess: + iobase = pci_info.base[bar]; + irq_install_handler(32+pci_info.irq, uart_handler); + if (pci_info.type[0]) { + mmio = 0; + kprintf("UART uses io address 0x%x\n", iobase); + } else { + mmio = 1; + page_map(iobase & PAGE_MASK, iobase & PAGE_MASK, 1, PG_GLOBAL | PG_ACCESSED | PG_DIRTY | PG_RW | PG_PCD); + kprintf("UART uses mmio address 0x%x\n", iobase); + vma_add(iobase, iobase + PAGE_SIZE, VMA_READ|VMA_WRITE); + } + + // configure uart + return uart_config(); +#else + // per default we use COM1... + if (!iobase) + iobase = 0x3F8; + mmio = 0; + if ((iobase == 0x3F8) || (iobase == 0x3E8)) + irq_install_handler(32+4, uart_handler); + else if ((iobase == 0x2F8) || (iobase == 0x2E8)) + irq_install_handler(32+3, uart_handler); + else + return -EINVAL; + + // configure uart + return uart_config(); +#endif +} + +#endif diff --git a/hermit/arch/x86/kernel/vga.c b/hermit/arch/x86/kernel/vga.c index 0c8cc8e92..09b863e11 100644 --- a/hermit/arch/x86/kernel/vga.c +++ b/hermit/arch/x86/kernel/vga.c @@ -29,6 +29,8 @@ #include #include +#ifdef CONFIG_VGA + /* * These define our textpointer, our background and foreground * colors (attributes), and x and y cursor coordinates @@ -234,3 +236,5 @@ void vga_init(void) // our bootloader already cleared the screen vga_clear(); } + +#endif diff --git a/hermit/arch/x86/loader/Makefile b/hermit/arch/x86/loader/Makefile index 58c269554..8cab50cbd 100644 --- a/hermit/arch/x86/loader/Makefile +++ b/hermit/arch/x86/loader/Makefile @@ -12,8 +12,9 @@ STRIP_DEBUG = --strip-debug KEEP_DEBUG = --only-keep-debug OUTPUT_FORMAT = -O elf32-i386 NAME = ldhermit +TTY := $(shell tty) -C_source := main.c printf.c string.c stdio.c vga.c page.c +C_source := main.c printf.c string.c stdio.c vga.c uart.c page.c ASM_source := entry.asm OBJS := $(C_source:.c=.o) @@ -48,10 +49,12 @@ $(NAME).elf: $(OBJS) $Q$(OBJCOPY) $(STRIP_DEBUG) $(OUTPUT_FORMAT) $(NAME).elf qemu: + @echo "Do you already start netcat (nc -l 4555)?" $(QEMU) -smp 10 -m 4G -kernel $(NAME).elf -initrd ../../../usr/tests/hello \ -net nic,model=rtl8139 -net user -net dump \ - -curses -monitor telnet:127.0.0.1:1235,server,nowait \ - -s + -chardev socket,host=127.0.0.1,port=4555,id=gnc0 \ + -device pci-serial,chardev=gnc0 \ + -monitor telnet:127.0.0.1:1235,server,nowait -nographic clean: @echo Cleaning loader diff --git a/hermit/arch/x86/loader/include/uart.h b/hermit/arch/x86/loader/include/uart.h new file mode 100644 index 000000000..0567aafa5 --- /dev/null +++ b/hermit/arch/x86/loader/include/uart.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014-2016, Stefan Lankes, Daniel Krebs, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ARCH_UART_H__ +#define __ARCH_UART_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Initialize UART output + * + * @return Returns 0 on success + */ +int uart_init(void); + +/** @brief Initialize UART output without a device check + * + * @return Returns 0 on success + */ +int uart_early_init(char*); + +/** @brief Simple string output on a serial device. + * + * If you want a new line you will have to "\\n". + * + * @return Length of output in bytes + */ +int uart_puts(const char *text); + +/** @brief Simple character output on a serial device. + * + * @return The original input character casted to int + */ +int uart_putchar(unsigned char c); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hermit/arch/x86/loader/stdio.c b/hermit/arch/x86/loader/stdio.c index e37296437..585e95a9e 100644 --- a/hermit/arch/x86/loader/stdio.c +++ b/hermit/arch/x86/loader/stdio.c @@ -28,23 +28,33 @@ #include #include #include +#include int koutput_init(void) { +#ifdef CONFIG_VGA vga_init(); +#else + uart_early_init((char*) mb_info->cmdline); +#endif return 0; } int kputchar(int c) { +#ifdef CONFIG_VGA vga_putchar(c); +#else + uart_putchar(c); +#endif return 1; } int kputs(const char *str) { +#ifdef CONFIG_UART #if 0 int i, len = strlen(str); @@ -55,4 +65,7 @@ int kputs(const char *str) #else return vga_puts(str); #endif +#else // CONFIG_UART + return uart_puts(str); +#endif } diff --git a/hermit/arch/x86/loader/uart.c b/hermit/arch/x86/loader/uart.c new file mode 100644 index 000000000..545780e25 --- /dev/null +++ b/hermit/arch/x86/loader/uart.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2014-2016, Stefan Lankes, Daniel Krebs, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_VGA + +/* + * This implementation based on following tutorial: + * http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming + */ + +#define UART_RX 0 /* In: Receive buffer */ +#define UART_IIR 2 /* In: Interrupt ID Register */ +#define UART_TX 0 /* Out: Transmit buffer */ +#define UART_IER 1 /* Out: Interrupt Enable Register */ +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_DLL 0 /* Out: Divisor Latch Low */ +#define UART_DLM 1 /* Out: Divisor Latch High */ +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_LSR 5 /* Line Status Register */ + +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ + +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ +#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ +#define UART_FCR_TRIGGER_MASK 0xC0 /* Mask for the FIFO trigger range */ +#define UART_FCR_TRIGGER_1 0x00 /* Trigger RDI at FIFO level 1 byte */ +#define UART_FCR_TRIGGER_4 0x40 /* Trigger RDI at FIFO level 4 byte */ +#define UART_FCR_TRIGGER_8 0x80 /* Trigger RDI at FIFO level 8 byte */ +#define UART_FCR_TRIGGER_14 0xc0 /* Trigger RDI at FIFO level 14 byte*/ + + +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 bit, 1=2 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +#define UART_MCR_CLKSEL 0x80 /* Divide clock by 4 (TI16C752, EFR[4]=1) */ +#define UART_MCR_TCRTLR 0x40 /* Access TCR/TLR (TI16C752, EFR[4]=1) */ +#define UART_MCR_XONANY 0x20 /* Enable Xon Any (TI16C752, EFR[4]=1) */ +#define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C550C/TI16C750) */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ + +static uint8_t mmio = 0; +static size_t iobase = 0; + +static inline unsigned char read_from_uart(uint32_t off) +{ + uint8_t c; + + if (mmio) + c = *((const volatile unsigned char*) (iobase + off)); + else + c = inportb(iobase + off); + + return c; +} + +static void write_to_uart(uint32_t off, unsigned char c) +{ + if (mmio) + *((volatile unsigned char*) (iobase + off)) = c; + else + outportb(iobase + off, c); +} + +/* Puts a single character on a serial device */ +int uart_putchar(unsigned char c) +{ + if (!iobase) + return 0; + + write_to_uart(UART_TX, c); + + return (int) c; +} + +/* Uses the routine above to output a string... */ +int uart_puts(const char *text) +{ + size_t i, len = strlen(text); + + if (!iobase) + return 0; + + for (i = 0; i < len; i++) + uart_putchar(text[i]); + + return len; +} + +static int uart_config(void) +{ + /* + * enable FIFOs + * clear RX and TX FIFO + * set irq trigger to 8 bytes + */ + write_to_uart(UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_1); + + /* disable interrupts */ + write_to_uart(UART_IER, 0); + + /* DTR + RTS */ + write_to_uart(UART_MCR, UART_MCR_DTR|UART_MCR_RTS); + + /* + * 8bit word length + * 1 stop bit + * no partity + * set DLAB=1 + */ + char lcr = UART_LCR_WLEN8; + write_to_uart(UART_LCR, lcr); + lcr = read_from_uart(UART_LCR) | UART_LCR_DLAB; + write_to_uart(UART_LCR, lcr); + + /* + * set baudrate to 115200 + */ + uint32_t divisor = 1843200 / 115200; + write_to_uart(UART_DLL, divisor & 0xff); + write_to_uart(UART_DLM, (divisor >> 8) & 0xff); + + /* set DLAB=0 */ + write_to_uart(UART_LCR, lcr & (~UART_LCR_DLAB)); + + return 0; +} + +extern const void kernel_start; + +int uart_early_init(char* cmdline) +{ +#if 1 + // default value of our QEMU configuration + iobase = 0xc110; +#else + if (BUILTIN_EXPECT(!cmdline, 0)) + return -EINVAL; + + char* str = strstr(cmdline, "uart="); + if (!str) + return -EINVAL; + + if (strncmp(str, "uart=io:", 8) == 0) { + iobase = strtol(str+8, (char **)NULL, 16); + if (!iobase) + return -EINVAL; + mmio = 0; + } else if (strncmp(str, "uart=mmio:", 10) == 0) { + iobase = strtol(str+10, (char **)NULL, 16); + if (!iobase) + return -EINVAL; + if (iobase >= PAGE_MAP_ENTRIES*PAGE_SIZE) { + /* at this point we use the boot page table + * => IO address is not mapped + * => dirty hack, map device before the kernel + */ + int err; + size_t newaddr = ((size_t) &kernel_start - PAGE_SIZE); + + err = page_map_bootmap(newaddr & PAGE_MASK, iobase & PAGE_MASK, PG_GLOBAL | PG_ACCESSED | PG_DIRTY | PG_RW | PG_PCD); + if (BUILTIN_EXPECT(err, 0)) { + iobase = 0; + return err; + } + iobase = newaddr; + } + mmio = 1; + } +#endif + + // configure uart + return uart_config(); +} + +int uart_init(void) +{ +#ifdef CONFIG_PCI + pci_info_t pci_info; + uint32_t bar = 0; + + // Searching for Intel's UART device + if (pci_get_device_info(0x8086, 0x0936, iobase, &pci_info) == 0) + goto Lsuccess; + // Searching for Qemu's UART device + if (pci_get_device_info(0x1b36, 0x0002, iobase, &pci_info) == 0) + goto Lsuccess; + // Searching for Qemu's 2x UART device (pci-serial-2x) + if (pci_get_device_info(0x1b36, 0x0003, iobase, &pci_info) == 0) + goto Lsuccess; + // Searching for Qemu's 4x UART device (pci-serial-4x) + if (pci_get_device_info(0x1b36, 0x0004, iobase, &pci_info) == 0) + goto Lsuccess; + + return -1; + +Lsuccess: + iobase = pci_info.base[bar]; + //irq_install_handler(32+pci_info.irq, uart_handler); + if (pci_info.type[0]) { + mmio = 0; + kprintf("UART uses io address 0x%x\n", iobase); + } else { + mmio = 1; + page_map(iobase & PAGE_MASK, iobase & PAGE_MASK, 1, PG_GLOBAL | PG_ACCESSED | PG_DIRTY | PG_RW | PG_PCD); + kprintf("UART uses mmio address 0x%x\n", iobase); + vma_add(iobase, iobase + PAGE_SIZE, VMA_READ|VMA_WRITE); + } + + // configure uart + return uart_config(); +#else + // per default we use COM1... + if (!iobase) + iobase = 0x3F8; + mmio = 0; + + // configure uart + return uart_config(); +#endif +} + +#endif diff --git a/hermit/arch/x86/loader/vga.c b/hermit/arch/x86/loader/vga.c index 589df0a4d..b69f0e714 100644 --- a/hermit/arch/x86/loader/vga.c +++ b/hermit/arch/x86/loader/vga.c @@ -29,6 +29,8 @@ #include #include +#ifdef CONFIG_UART + #define VIDEO_MEM_ADDR 0xB8000 /* the video memory address */ /* @@ -235,3 +237,5 @@ void vga_init(void) textmemptr = (unsigned short *)VIDEO_MEM_ADDR; vga_clear(); } + +#endif diff --git a/hermit/include/hermit/config.h b/hermit/include/hermit/config.h index 09a589b06..e972e20b8 100644 --- a/hermit/include/hermit/config.h +++ b/hermit/include/hermit/config.h @@ -47,6 +47,7 @@ extern "C" { #define MAILBOX_SIZE 128 //#define WITH_PCI_IDS //#define SAVE_FPU +//#define CONFIG_VGA #define BYTE_ORDER LITTLE_ENDIAN diff --git a/hermit/kernel/main.c b/hermit/kernel/main.c index ccf1f289c..9b319fa55 100644 --- a/hermit/kernel/main.c +++ b/hermit/kernel/main.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -126,6 +127,9 @@ static int hermit_init(void) timer_init(); multitasking_init(); memory_init(); +#ifndef CONFIG_VGA + uart_init(); +#endif return 0; } diff --git a/hermit/libkern/stdio.c b/hermit/libkern/stdio.c index 7c84aef8d..dcd7ed83e 100644 --- a/hermit/libkern/stdio.c +++ b/hermit/libkern/stdio.c @@ -33,9 +33,12 @@ #include #include #include +#include static atomic_int32_t kmsg_counter = ATOMIC_INIT(-1); +#ifdef CONFIG_VGA static spinlock_irqsave_t vga_lock = SPINLOCK_IRQSAVE_INIT; +#endif /* Workaround for a compiler bug. gcc 5.1 seems to ignore this array, if we defined it as as static array. At least it is as static array not part of @@ -45,7 +48,13 @@ static spinlock_irqsave_t vga_lock = SPINLOCK_IRQSAVE_INIT; int koutput_init(void) { if (is_single_kernel()) + { +#ifdef CONFIG_VGA vga_init(); +#else + uart_early_init(NULL); +#endif + } return 0; } @@ -62,9 +71,13 @@ int kputchar(int c) kmessages[pos % KMSG_SIZE] = (unsigned char) c; if (is_single_kernel()) { +#ifdef CONFIG_VGA spinlock_irqsave_lock(&vga_lock); vga_putchar(c); spinlock_irqsave_unlock(&vga_lock); +#else + uart_putchar(c); +#endif } return 1; @@ -80,9 +93,13 @@ int kputs(const char *str) } if (is_single_kernel()) { +#ifdef CONFIG_VGA spinlock_irqsave_lock(&vga_lock); vga_puts(str); spinlock_irqsave_unlock(&vga_lock); +#else + uart_puts(str); +#endif } return len; diff --git a/hermit/mm/vma.c b/hermit/mm/vma.c index 2774fb738..c7e863dc3 100644 --- a/hermit/mm/vma.c +++ b/hermit/mm/vma.c @@ -69,10 +69,12 @@ int vma_init(void) if (BUILTIN_EXPECT(ret, 0)) goto out; +#ifdef CONFIG_VGA // add VGA video memory ret = vma_add(VIDEO_MEM_ADDR, VIDEO_MEM_ADDR + PAGE_SIZE, VMA_READ|VMA_WRITE); if (BUILTIN_EXPECT(ret, 0)) goto out; +#endif out: return ret;