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 /* 16550 FIFO Control Register */ 27 #define FCR 2 28 29 /* Line control register */ 30 #define LCR 3 31 enum { 32 DLAB = 1 << 7, /* Divisor latch access bit (DLAB) */ 33 /* bit 7 - set break enable */ 34 PM2 = 1 << 5, 35 PM1 = 1 << 4, 36 PM0 = 1 << 3, 37 STB = 1 << 2, 38 WLS1 = 1 << 1, 39 WLS0 = 1 << 0, 40 }; 41 42 /* Modem control register */ 43 #define MCR 4 44 45 /* Line status register */ 46 #define LSR 5 47 48 /* Modem status register */ 49 #define MSR 6 50 51 /* Scratch register */ 52 #define SCR 7 53 54 struct serial8250_device { 55 uint16_t iobase; 56 uint8_t dll; 57 uint8_t dlm; 58 uint8_t ier; 59 uint8_t fcr; 60 uint8_t lcr; 61 uint8_t mcr; 62 uint8_t scr; 63 }; 64 65 static struct serial8250_device device = { 66 .iobase = 0x3f8, /* ttyS0 */ 67 }; 68 69 static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 70 { 71 uint16_t offset = port - device.iobase; 72 73 if (device.lcr & DLAB) { 74 switch (offset) { 75 case DLL: 76 device.dll = ioport__read8(data); 77 break; 78 case DLM: 79 device.dlm = ioport__read8(data); 80 break; 81 case FCR: 82 device.fcr = ioport__read8(data); 83 break; 84 case LCR: 85 device.lcr = ioport__read8(data); 86 break; 87 default: 88 return false; 89 } 90 } else { 91 switch (offset) { 92 case THR: { 93 char *p = data; 94 int i; 95 96 while (count--) { 97 for (i = 0; i < size; i++) 98 fprintf(stdout, "%c", *p++); 99 } 100 fflush(stdout); 101 break; 102 } 103 case IER: 104 device.ier = ioport__read8(data); 105 break; 106 case FCR: 107 device.fcr = ioport__read8(data); 108 break; 109 case LCR: 110 device.lcr = ioport__read8(data); 111 break; 112 case MCR: 113 device.mcr = ioport__read8(data); 114 break; 115 case SCR: 116 device.scr = ioport__read8(data); 117 break; 118 default: 119 return false; 120 } 121 } 122 123 return true; 124 } 125 126 static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 127 { 128 uint16_t offset = port - device.iobase; 129 130 if (device.lcr & DLAB) 131 return false; 132 133 switch (offset) { 134 case THR: 135 ioport__write8(data, 0x00); 136 break; 137 case IER: 138 ioport__write8(data, device.ier); 139 break; 140 case IIR: 141 ioport__write8(data, 0x01); /* no interrupt pending */ 142 break; 143 case LCR: 144 ioport__write8(data, device.lcr); 145 break; 146 case MCR: 147 ioport__write8(data, device.mcr); 148 break; 149 case LSR: 150 ioport__write8(data, 0x20); /* XMTRDY */ 151 break; 152 case MSR: 153 ioport__write8(data, 0x01); /* clear to send */ 154 break; 155 case SCR: 156 ioport__write8(data, device.scr); 157 break; 158 default: 159 return false; 160 } 161 162 return true; 163 } 164 165 static struct ioport_operations serial8250_ops = { 166 .io_in = serial8250_in, 167 .io_out = serial8250_out, 168 }; 169 170 void early_printk__init(void) 171 { 172 ioport__register(device.iobase, &serial8250_ops, 8); 173 } 174