1 #include "kvm/early_printk.h" 2 3 #include "kvm/ioport.h" 4 #include "kvm/util.h" 5 6 #include <stdbool.h> 7 8 /* Transmitter holding register */ 9 #define THR 0 10 11 /* Receive buffer register */ 12 #define RBR 0 13 14 /* Divisor latch low byte */ 15 #define DLL 0 16 17 /* Divisor latch high byte */ 18 #define DLM 1 19 20 /* Interrupt enable register */ 21 #define IER 1 22 23 /* Interrupt identification register */ 24 #define IIR 2 25 26 #define UART_IIR_NO_INT 0x01 27 #define UART_IIR_THRI 0x02 28 29 /* 16550 FIFO Control Register */ 30 #define FCR 2 31 32 /* Line control register */ 33 #define LCR 3 34 35 #define UART_LCR_DLAB 0x80 36 37 /* Modem control register */ 38 #define MCR 4 39 40 /* Line status register */ 41 #define LSR 5 42 43 #define UART_LSR_THRE 0x20 44 45 /* Modem status register */ 46 #define MSR 6 47 48 #define UART_MSR_CTS 0x10 49 50 /* Scratch register */ 51 #define SCR 7 52 53 struct serial8250_device { 54 uint16_t iobase; 55 uint8_t dll; 56 uint8_t dlm; 57 uint8_t ier; 58 uint8_t fcr; 59 uint8_t lcr; 60 uint8_t mcr; 61 uint8_t scr; 62 }; 63 64 static struct serial8250_device device = { 65 .iobase = 0x3f8, /* ttyS0 */ 66 }; 67 68 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 69 { 70 uint16_t offset = port - device.iobase; 71 72 if (device.lcr & UART_LCR_DLAB) { 73 switch (offset) { 74 case DLL: 75 device.dll = ioport__read8(data); 76 break; 77 case DLM: 78 device.dlm = ioport__read8(data); 79 break; 80 case FCR: 81 device.fcr = ioport__read8(data); 82 break; 83 case LCR: 84 device.lcr = ioport__read8(data); 85 break; 86 default: 87 return false; 88 } 89 } else { 90 switch (offset) { 91 case THR: { 92 char *p = data; 93 int i; 94 95 while (count--) { 96 for (i = 0; i < size; i++) 97 fprintf(stdout, "%c", *p++); 98 } 99 fflush(stdout); 100 break; 101 } 102 case IER: 103 device.ier = ioport__read8(data); 104 break; 105 case FCR: 106 device.fcr = ioport__read8(data); 107 break; 108 case LCR: 109 device.lcr = ioport__read8(data); 110 break; 111 case MCR: 112 device.mcr = ioport__read8(data); 113 break; 114 case SCR: 115 device.scr = ioport__read8(data); 116 break; 117 default: 118 return false; 119 } 120 } 121 122 return true; 123 } 124 125 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 126 { 127 uint16_t offset = port - device.iobase; 128 129 if (device.lcr & UART_LCR_DLAB) 130 return false; 131 132 switch (offset) { 133 case THR: 134 /* TODO: input support */ 135 break; 136 case IER: 137 ioport__write8(data, device.ier); 138 break; 139 case IIR: 140 ioport__write8(data, UART_IIR_NO_INT | UART_IIR_THRI); 141 break; 142 case LCR: 143 ioport__write8(data, device.lcr); 144 break; 145 case MCR: 146 ioport__write8(data, device.mcr); 147 break; 148 case LSR: 149 ioport__write8(data, UART_LSR_THRE); 150 break; 151 case MSR: 152 ioport__write8(data, UART_MSR_CTS); 153 break; 154 case SCR: 155 ioport__write8(data, device.scr); 156 break; 157 default: 158 return false; 159 } 160 161 return true; 162 } 163 164 static struct ioport_operations serial8250_ops = { 165 .io_in = serial8250_in, 166 .io_out = serial8250_out, 167 }; 168 169 void early_printk__init(void) 170 { 171 ioport__register(device.iobase, &serial8250_ops, 8); 172 } 173