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 void serial8250__interrupt(struct kvm *self) 69 { 70 /* TODO: inject 8250 interrupt for the guest */ 71 } 72 73 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 74 { 75 uint16_t offset = port - device.iobase; 76 77 if (device.lcr & UART_LCR_DLAB) { 78 switch (offset) { 79 case DLL: 80 device.dll = ioport__read8(data); 81 break; 82 case DLM: 83 device.dlm = ioport__read8(data); 84 break; 85 case FCR: 86 device.fcr = ioport__read8(data); 87 break; 88 case LCR: 89 device.lcr = ioport__read8(data); 90 break; 91 default: 92 return false; 93 } 94 } else { 95 switch (offset) { 96 case THR: { 97 char *p = data; 98 int i; 99 100 while (count--) { 101 for (i = 0; i < size; i++) 102 fprintf(stdout, "%c", *p++); 103 } 104 fflush(stdout); 105 break; 106 } 107 case IER: 108 device.ier = ioport__read8(data); 109 break; 110 case FCR: 111 device.fcr = ioport__read8(data); 112 break; 113 case LCR: 114 device.lcr = ioport__read8(data); 115 break; 116 case MCR: 117 device.mcr = ioport__read8(data); 118 break; 119 case SCR: 120 device.scr = ioport__read8(data); 121 break; 122 default: 123 return false; 124 } 125 } 126 127 return true; 128 } 129 130 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 131 { 132 uint16_t offset = port - device.iobase; 133 134 if (device.lcr & UART_LCR_DLAB) 135 return false; 136 137 switch (offset) { 138 case THR: 139 /* TODO: input support */ 140 break; 141 case IER: 142 ioport__write8(data, device.ier); 143 break; 144 case IIR: 145 ioport__write8(data, UART_IIR_NO_INT | UART_IIR_THRI); 146 break; 147 case LCR: 148 ioport__write8(data, device.lcr); 149 break; 150 case MCR: 151 ioport__write8(data, device.mcr); 152 break; 153 case LSR: 154 ioport__write8(data, UART_LSR_THRE); 155 break; 156 case MSR: 157 ioport__write8(data, UART_MSR_CTS); 158 break; 159 case SCR: 160 ioport__write8(data, device.scr); 161 break; 162 default: 163 return false; 164 } 165 166 return true; 167 } 168 169 static struct ioport_operations serial8250_ops = { 170 .io_in = serial8250_in, 171 .io_out = serial8250_out, 172 }; 173 174 void early_printk__init(void) 175 { 176 ioport__register(device.iobase, &serial8250_ops, 8); 177 } 178