add basic support of an UART device

This commit is contained in:
Stefan Lankes 2014-12-09 10:09:03 +01:00
parent 8648ca0dfe
commit df0b192e21
10 changed files with 494 additions and 7 deletions

View file

@ -29,8 +29,7 @@ GDBFLAGS = -x debug.gdb
QEMU = qemu-system-i386
QEMUFLAGS = -smp 2 -monitor stdio \
-net nic,model=rtl8139 \
-net user,hostfwd=tcp::12345-:7 \
-serial tcp::12346,server,nowait
-net user,hostfwd=tcp::12345-:7
INCLUDE = -I$(TOPDIR)/include -I$(TOPDIR)/arch/$(ARCH)/include
# Compiler options for final code
@ -73,6 +72,9 @@ veryclean: clean
qemu: $(NAME).elf
$(QEMU) $(QEMUFLAGS) -kernel $(NAME).elf
uart: $(NAME).elf
$(QEMU) -chardev null,id=tS0 -device pci-serial,chardev=tS0 -serial tcp::4555,server $(QEMUFLAGS) -kernel $(NAME).elf
debug: $(NAME).elf
$(TERM) -e $(GDB) $(GDBFLAGS) &
$(QEMU) $(QEMUFLAGS) -s -S -kernel $(NAME).elf

View file

@ -88,6 +88,9 @@ Overview of all branches
Add support of paging. See http://www.noteblok.net/2014/06/14/bachelor
for a detailed description.
6. stage6 - Add UART support
Add basic support of a serial device
Usefull Links
-------------
0. http://www.gnu.org/software/grub/manual/multiboot/

View file

@ -0,0 +1,49 @@
/**
* @author Stefan Lankes
* @file arch/x86/include/asm/pci.h
* @brief functions related to PCI initialization and information
*
* This file contains a procedure to initialize the PCI environment
* and functions to access information about specific PCI devices.
*/
#ifndef __ARCH_PCI_H__
#define __ARCH_PCI_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint32_t base[6];
uint32_t size[6];
uint32_t irq;
} pci_info_t;
/** @brief Initialize the PCI environment
*/
int pci_init(void);
/** @brief Determine the IObase address and the interrupt number of a specific device
*
* @param vendor_id The device's vendor ID
* @param device_id the device's ID
* @param info Pointer to the record pci_info_t where among other the IObase address will be stored
*
* @return
* - 0 on success
* - -EINVAL (-22) on failure
*/
int pci_get_device_info(uint32_t vendor_id, uint32_t device_id, pci_info_t* info);
/** @brief Print information of existing pci adapters
*
* @return 0 in any case
*/
int print_pci_adapters(void);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -38,6 +38,9 @@
#include <eduos/stddef.h>
#include <asm/gdt.h>
#ifdef CONFIG_PCI
#include <asm/pci.h>
#endif
#ifdef __cplusplus
extern "C" {
@ -206,6 +209,9 @@ static inline size_t lsb(size_t i)
inline static int system_init(void)
{
gdt_install();
#ifdef CONFIG_PCI
pci_init();
#endif
return 0;
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2014, Stefan Lankes, 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.
*/
/**
* @author Stefan Lankes
* @file arch/x86/include/asm/uart.h
* @brief UART related code
*/
#ifndef __ARCH_UART_H__
#define __ARCH_UART_H__
#include <eduos/stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Initialize UART output
*
* @return Returns 0 on success
*/
int uart_init(void);
/** @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

View file

@ -1,4 +1,4 @@
C_source := tasks.c vga.c gdt.c irq.c idt.c isrs.c timer.c processor.c
C_source := tasks.c vga.c gdt.c irq.c idt.c isrs.c timer.c processor.c uart.c pci.c
ASM_source := entry.asm string32.asm
MODULE := arch_x86_kernel

187
arch/x86/kernel/pci.c Normal file
View file

@ -0,0 +1,187 @@
#include <eduos/stdio.h>
#include <eduos/string.h>
#include <eduos/errno.h>
#include <asm/irqflags.h>
#include <asm/io.h>
#ifdef CONFIG_PCI
#include <asm/pci.h>
#include "pcihdr.h"
/*
* PCI configuration registers
*/
#define PCI_CFID 0x00 /* Configuration ID */
#define PCI_CFCS 0x04 /* Configurtion Command/Status */
#define PCI_CFRV 0x08 /* Configuration Revision */
#define PCI_CFLT 0x0c /* Configuration Latency Timer */
#define PCI_CBIO 0x10 /* Configuration Base IO Address */
#define PCI_CFIT 0x3c /* Configuration Interrupt */
#define PCI_CFDA 0x40 /* Configuration Driver Area */
#define PHYS_IO_MEM_START 0
#define PCI_MEM 0
#define PCI_INTA 0
#define PCI_NSLOTS 22
#define PCI_NBUS 0
#define PCI_CONF_ADDR_REG 0xcf8
#define PCI_CONF_FRWD_REG 0xcf8
#define PCI_CONF_DATA_REG 0xcfc
#define PCI_IO_CONF_START 0xc000
#define MAX_BUS 16
#define MAX_SLOTS 32
static uint32_t mechanism = 0;
static uint32_t adapters[MAX_BUS][MAX_SLOTS] = {[0 ... MAX_BUS-1][0 ... MAX_SLOTS-1] = -1};
static void pci_conf_write(uint32_t bus, uint32_t slot, uint32_t off, uint32_t val)
{
if (mechanism == 1) {
outportl(PCI_CONF_FRWD_REG, bus);
outportl(PCI_CONF_ADDR_REG, 0xf0);
outportl(PCI_IO_CONF_START | (slot << 8) | off, val);
} else {
outportl(PCI_CONF_ADDR_REG,
(0x80000000 | (bus << 16) | (slot << 11) | off));
outportl(PCI_CONF_DATA_REG, val);
}
}
static uint32_t pci_conf_read(uint32_t bus, uint32_t slot, uint32_t off)
{
uint32_t data = -1;
outportl(PCI_CONF_ADDR_REG,
(0x80000000 | (bus << 16) | (slot << 11) | off));
data = inportl(PCI_CONF_DATA_REG);
if ((data == 0xffffffff) && (slot < 0x10)) {
outportl(PCI_CONF_FRWD_REG, bus);
outportl(PCI_CONF_ADDR_REG, 0xf0);
data = inportl(PCI_IO_CONF_START | (slot << 8) | off);
if (data == 0xffffffff)
return data;
if (!mechanism)
mechanism = 1;
} else if (!mechanism)
mechanism = 2;
return data;
}
static inline uint32_t pci_what_irq(uint32_t bus, uint32_t slot)
{
return pci_conf_read(bus, slot, PCI_CFIT) & 0xFF;
}
static inline uint32_t pci_what_iobase(uint32_t bus, uint32_t slot, uint32_t nr)
{
return pci_conf_read(bus, slot, PCI_CBIO + nr*4) & 0xFFFFFFFC;
}
static inline uint32_t pci_what_size(uint32_t bus, uint32_t slot, uint32_t nr)
{
uint32_t tmp, ret;
// backup the original value
tmp = pci_conf_read(bus, slot, PCI_CBIO + nr*4);
// determine size
pci_conf_write(bus, slot, PCI_CBIO + nr*4, 0xFFFFFFFF);
ret = ~pci_conf_read(bus, slot, PCI_CBIO + nr*4) + 1;
// restore original value
pci_conf_write(bus, slot, PCI_CBIO + nr*4, tmp);
return ret;
}
int pci_init(void)
{
uint32_t slot, bus;
for (bus = 0; bus < MAX_BUS; bus++)
for (slot = 0; slot < MAX_SLOTS; slot++)
adapters[bus][slot] = pci_conf_read(bus, slot, PCI_CFID);
return 0;
}
int pci_get_device_info(uint32_t vendor_id, uint32_t device_id, pci_info_t* info)
{
uint32_t slot, bus, i;
if (!info)
return -EINVAL;
if (!mechanism)
pci_init();
for (bus = 0; bus < MAX_BUS; bus++) {
for (slot = 0; slot < MAX_SLOTS; slot++) {
if (adapters[bus][slot] != -1) {
if (((adapters[bus][slot] & 0xffff) == vendor_id) &&
(((adapters[bus][slot] & 0xffff0000) >> 16) == device_id)) {
for(i=0; i<6; i++) {
info->base[i] = pci_what_iobase(bus, slot, i);
info->size[i] = (info->base[i]) ? pci_what_size(bus, slot, i) : 0;
}
info->irq = pci_what_irq(bus, slot);
return 0;
}
}
}
}
return -EINVAL;
}
int print_pci_adapters(void)
{
uint32_t slot, bus;
uint32_t i, counter = 0;
if (!mechanism)
pci_init();
for (bus = 0; bus < MAX_BUS; bus++) {
for (slot = 0; slot < MAX_SLOTS; slot++) {
if (adapters[bus][slot] != -1) {
counter++;
kprintf("%d) Vendor ID: 0x%x Device Id: 0x%x\n",
counter, adapters[bus][slot] & 0xffff,
(adapters[bus][slot] & 0xffff0000) >> 16);
for (i=0; i<PCI_VENTABLE_LEN; i++) {
if ((adapters[bus][slot] & 0xffff) ==
(uint32_t)PciVenTable[i].VenId)
kprintf("\tVendor is %s\n",
PciVenTable[i].VenShort);
}
for (i=0; i<PCI_DEVTABLE_LEN; i++) {
if ((adapters[bus][slot] & 0xffff) ==
(uint32_t)PciDevTable[i].VenId) {
if (((adapters[bus][slot] & 0xffff0000) >> 16) ==
PciDevTable[i].DevId) {
kprintf
("\tChip: %s ChipDesc: %s\n",
PciDevTable[i].Chip,
PciDevTable[i].ChipDesc);
}
}
}
}
}
}
return 0;
}
#endif

166
arch/x86/kernel/uart.c Normal file
View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2014, Stefan Lankes, 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 <eduos/stdio.h>
#include <eduos/string.h>
#include <asm/io.h>
#include <asm/uart.h>
#include <asm/irq.h>
#include <asm/irqflags.h>
#include <asm/vga.h>
#ifdef CONFIG_PCI
#include <asm/pci.h>
#endif
#ifdef CONFIG_UART
#define UART_RX 0 /* In: Receive buffer */
#define UART_TX 0 /* Out: Transmit buffer */
#define UART_IER 1 /* Out: Interrupt Enable 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_FCR 2 /* Out: FIFO Control Register */
#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 /* Mask for trigger set at 1 */
#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_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 */
static uint32_t iobase = 0;
unsigned char uart_getchar(void)
{
return inportb(iobase + UART_RX);
}
/* Handles all UART's interrupt */
static void uart_handler(struct state *s)
{
vga_puts("UART interrupt\n");
}
/* Puts a single character on a serial device */
int uart_putchar(unsigned char c)
{
if (!iobase)
return 0;
outportb(iobase + 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 i-1;
}
static void uart_config(void)
{
/*
* enable FIFOs
* clear RX and TX FIFO
* set irq trigger to 8 bytes
*/
outportb(iobase + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_1);
/*
* 8bit word length
* 1 stop bit
* no partity
* set DLAB=1
*/
char lcr = UART_LCR_WLEN8 | UART_LCR_DLAB;
outportb(iobase + UART_LCR, lcr);
/*
* set baudrate to 115200 (on qemu)
*/
outportb(iobase + UART_DLL, 0x01);
outportb(iobase + UART_DLM, 0x00);
/* set DLAB=0 */
outportb(iobase + UART_LCR, lcr & (~UART_LCR_DLAB));
/* enable interrupt */
outportb(iobase + UART_IER, UART_IER_RDI | UART_IER_RLSI | UART_IER_THRI);
}
int uart_init(void)
{
#ifdef CONFIG_PCI
pci_info_t pci_info;
// Searching for Intel's UART device
if (pci_get_device_info(0x8086, 0x0936, &pci_info) == 0) {
iobase = pci_info.base[0];
goto Lsuccess;
}
// Searching for Qemu's UART device
if (pci_get_device_info(0x1b36, 0x0002, &pci_info) == 0) {
iobase = 0x03f8;
goto Lsuccess;
}
return -1;
Lsuccess:
irq_install_handler(32+pci_info.irq, uart_handler);
// configure uart
uart_config();
#endif
return 0;
}
#endif

View file

@ -45,6 +45,8 @@ extern "C" {
#define BYTE_ORDER LITTLE_ENDIAN
#define CONFIG_VGA
#define CONFIG_PCI
#define CONFIG_UART
#define BUILTIN_EXPECT(exp, b) __builtin_expect((exp), (b))
//#define BUILTIN_EXPECT(exp, b) (exp)

View file

@ -29,12 +29,16 @@
#include <eduos/string.h>
#include <eduos/stdarg.h>
#include <eduos/spinlock.h>
#include <asm/uart.h>
#include <asm/vga.h>
static spinlock_irqsave_t olock = SPINLOCK_IRQSAVE_INIT;
int koutput_init(void)
{
#ifdef CONFIG_UART
uart_init();
#endif
#ifdef CONFIG_VGA
vga_init();
#endif
@ -45,6 +49,9 @@ int koutput_init(void)
int kputchar(int c)
{
spinlock_irqsave_lock(&olock);
#ifdef CONFIG_UART
uart_putchar(c);
#endif
#ifdef CONFIG_VGA
vga_putchar(c);
#endif
@ -55,12 +62,10 @@ int kputchar(int c)
int kputs(const char *str)
{
#ifdef CONFIG_VGA
int i;
for(i=0; str[i] != '\0'; i++)
vga_putchar((int) str[i]);
kputchar((int) str[i]);
return i;
#else
#endif
}