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