mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-08-27 08:56:00 +00:00
kvm: Improve 8250 serial console support
Signed-off-by: Pekka Enberg <penberg@kernel.org>
This commit is contained in:
committed by
Will Deacon
parent
b74755443f
commit
46aa8d6918
+146
-39
@@ -1,66 +1,173 @@
|
||||
#include "kvm/early_printk.h"
|
||||
|
||||
#include "kvm/ioport.h"
|
||||
#include "kvm/util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static int early_serial_base = 0x3f8; /* ttyS0 */
|
||||
/* Transmitter holding register */
|
||||
#define THR 0
|
||||
|
||||
#define XMTRDY 0x20
|
||||
/* Receive buffer register */
|
||||
#define RBR 0
|
||||
|
||||
#define TXR 0 /* Transmit register (WRITE) */
|
||||
#define LSR 5 /* Line Status */
|
||||
#define MSR 6 /* Modem Status */
|
||||
/* Divisor latch low byte */
|
||||
#define DLL 0
|
||||
|
||||
static bool early_serial_txr_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
|
||||
/* Divisor latch high byte */
|
||||
#define DLM 1
|
||||
|
||||
/* Interrupt enable register */
|
||||
#define IER 1
|
||||
|
||||
/* Interrupt identification register */
|
||||
#define IIR 2
|
||||
|
||||
/* 16550 FIFO Control Register */
|
||||
#define FCR 2
|
||||
|
||||
/* Line control register */
|
||||
#define LCR 3
|
||||
enum {
|
||||
DLAB = 1 << 7, /* Divisor latch access bit (DLAB) */
|
||||
/* bit 7 - set break enable */
|
||||
PM2 = 1 << 5,
|
||||
PM1 = 1 << 4,
|
||||
PM0 = 1 << 3,
|
||||
STB = 1 << 2,
|
||||
WLS1 = 1 << 1,
|
||||
WLS0 = 1 << 0,
|
||||
};
|
||||
|
||||
/* Modem control register */
|
||||
#define MCR 4
|
||||
|
||||
/* Line status register */
|
||||
#define LSR 5
|
||||
|
||||
/* Modem status register */
|
||||
#define MSR 6
|
||||
|
||||
/* Scratch register */
|
||||
#define SCR 7
|
||||
|
||||
struct serial8250_device {
|
||||
uint16_t iobase;
|
||||
uint8_t dll;
|
||||
uint8_t dlm;
|
||||
uint8_t ier;
|
||||
uint8_t fcr;
|
||||
uint8_t lcr;
|
||||
uint8_t mcr;
|
||||
uint8_t scr;
|
||||
};
|
||||
|
||||
static struct serial8250_device device = {
|
||||
.iobase = 0x3f8, /* ttyS0 */
|
||||
};
|
||||
|
||||
static bool serial8250_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
|
||||
{
|
||||
char *p = data;
|
||||
int i;
|
||||
uint16_t offset = port - device.iobase;
|
||||
|
||||
while (count--) {
|
||||
for (i = 0; i < size; i++)
|
||||
fprintf(stdout, "%c", *p++);
|
||||
if (device.lcr & DLAB) {
|
||||
switch (offset) {
|
||||
case DLL:
|
||||
device.dll = ioport__read8(data);
|
||||
break;
|
||||
case DLM:
|
||||
device.dlm = ioport__read8(data);
|
||||
break;
|
||||
case FCR:
|
||||
device.fcr = ioport__read8(data);
|
||||
break;
|
||||
case LCR:
|
||||
device.lcr = ioport__read8(data);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
switch (offset) {
|
||||
case THR: {
|
||||
char *p = data;
|
||||
int i;
|
||||
|
||||
while (count--) {
|
||||
for (i = 0; i < size; i++)
|
||||
fprintf(stdout, "%c", *p++);
|
||||
}
|
||||
fflush(stdout);
|
||||
break;
|
||||
}
|
||||
case IER:
|
||||
device.ier = ioport__read8(data);
|
||||
break;
|
||||
case FCR:
|
||||
device.fcr = ioport__read8(data);
|
||||
break;
|
||||
case LCR:
|
||||
device.lcr = ioport__read8(data);
|
||||
break;
|
||||
case MCR:
|
||||
device.mcr = ioport__read8(data);
|
||||
break;
|
||||
case SCR:
|
||||
device.scr = ioport__read8(data);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool early_serial_rxr_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
|
||||
static bool serial8250_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
uint16_t offset = port - device.iobase;
|
||||
|
||||
static struct ioport_operations early_serial_txr_rxr_ops = {
|
||||
.io_out = early_serial_txr_out,
|
||||
.io_in = early_serial_rxr_in,
|
||||
};
|
||||
if (device.lcr & DLAB)
|
||||
return false;
|
||||
|
||||
static bool early_serial_lsr_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
|
||||
{
|
||||
uint8_t *p = data;
|
||||
|
||||
*p = XMTRDY;
|
||||
switch (offset) {
|
||||
case THR:
|
||||
ioport__write8(data, 0x00);
|
||||
break;
|
||||
case IER:
|
||||
ioport__write8(data, device.ier);
|
||||
break;
|
||||
case IIR:
|
||||
ioport__write8(data, 0x01); /* no interrupt pending */
|
||||
break;
|
||||
case LCR:
|
||||
ioport__write8(data, device.lcr);
|
||||
break;
|
||||
case MCR:
|
||||
ioport__write8(data, device.mcr);
|
||||
break;
|
||||
case LSR:
|
||||
ioport__write8(data, 0x20); /* XMTRDY */
|
||||
break;
|
||||
case MSR:
|
||||
ioport__write8(data, 0x01); /* clear to send */
|
||||
break;
|
||||
case SCR:
|
||||
ioport__write8(data, device.scr);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct ioport_operations early_serial_lsr_ops = {
|
||||
.io_in = early_serial_lsr_in,
|
||||
};
|
||||
|
||||
static bool early_serial_msr_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct ioport_operations early_serial_msr_ops = {
|
||||
.io_in = early_serial_msr_in,
|
||||
static struct ioport_operations serial8250_ops = {
|
||||
.io_in = serial8250_in,
|
||||
.io_out = serial8250_out,
|
||||
};
|
||||
|
||||
void early_printk__init(void)
|
||||
{
|
||||
ioport__register(early_serial_base + TXR, &early_serial_txr_rxr_ops, 1);
|
||||
ioport__register(early_serial_base + LSR, &early_serial_lsr_ops, 1);
|
||||
ioport__register(early_serial_base + MSR, &early_serial_msr_ops, 1);
|
||||
ioport__register(device.iobase, &serial8250_ops, 8);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user