LIBS += -lrt
LIBS += -lpthread
+LIBS += -lutil
# Additional ARCH settings for x86
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
return 0;
}
+static int tty_parser(const struct option *opt, const char *arg, int unset)
+{
+ int tty = atoi(arg);
+
+ term_set_tty(tty);
+
+ return 0;
+}
+
static int shmem_parser(const struct option *opt, const char *arg, int unset)
{
const u64 default_size = SHMEM_DEFAULT_SIZE;
OPT_STRING('\0', "console", &console, "serial or virtio",
"Console to use"),
OPT_STRING('\0', "dev", &dev, "device_file", "KVM device file"),
+ OPT_CALLBACK('\0', "tty", NULL, "tty id",
+ "Remap guest TTY into a pty on the host",
+ tty_parser),
OPT_GROUP("Kernel options:"),
OPT_STRING('k', "kernel", &kernel_filename, "kernel",
struct serial8250_device {
pthread_mutex_t mutex;
+ u8 id;
u16 iobase;
u8 irq;
[0] = {
.mutex = PTHREAD_MUTEX_INITIALIZER,
+ .id = 0,
.iobase = 0x3f8,
.irq = 4,
[1] = {
.mutex = PTHREAD_MUTEX_INITIALIZER,
+ .id = 1,
.iobase = 0x2f8,
.irq = 3,
[2] = {
.mutex = PTHREAD_MUTEX_INITIALIZER,
+ .id = 2,
.iobase = 0x3e8,
.irq = 4,
[3] = {
.mutex = PTHREAD_MUTEX_INITIALIZER,
+ .id = 3,
.iobase = 0x2e8,
.irq = 3,
return;
}
- if (!term_readable(CONSOLE_8250))
+ if (!term_readable(CONSOLE_8250, dev->id))
return;
- c = term_getc(CONSOLE_8250);
+ c = term_getc(CONSOLE_8250, dev->id);
if (c < 0)
return;
dev->lsr |= UART_LSR_DR;
}
-/*
- * Interrupts are injected for ttyS0 only.
- */
void serial8250__inject_interrupt(struct kvm *kvm)
{
- struct serial8250_device *dev = &devices[0];
+ int i;
- mutex_lock(&dev->mutex);
+ for (i = 0; i < 4; i++) {
+ struct serial8250_device *dev = &devices[i];
- serial8250__receive(kvm, dev);
+ mutex_lock(&dev->mutex);
- if (dev->ier & UART_IER_RDI && dev->lsr & UART_LSR_DR)
- dev->iir = UART_IIR_RDI;
- else if (dev->ier & UART_IER_THRI)
- dev->iir = UART_IIR_THRI;
- else
- dev->iir = UART_IIR_NO_INT;
+ serial8250__receive(kvm, dev);
- if (dev->iir != UART_IIR_NO_INT) {
- kvm__irq_line(kvm, dev->irq, 0);
- kvm__irq_line(kvm, dev->irq, 1);
- }
+ if (dev->ier & UART_IER_RDI && dev->lsr & UART_LSR_DR)
+ dev->iir = UART_IIR_RDI;
+ else if (dev->ier & UART_IER_THRI)
+ dev->iir = UART_IIR_THRI;
+ else
+ dev->iir = UART_IIR_NO_INT;
- mutex_unlock(&dev->mutex);
+ if (dev->iir != UART_IIR_NO_INT) {
+ kvm__irq_line(kvm, dev->irq, 0);
+ kvm__irq_line(kvm, dev->irq, 1);
+ }
+
+ mutex_unlock(&dev->mutex);
+ }
}
void serial8250__inject_sysrq(struct kvm *kvm)
char *addr = data;
if (!(dev->mcr & UART_MCR_LOOP))
- term_putc(CONSOLE_8250, addr, size);
+ term_putc(CONSOLE_8250, addr, size, dev->id);
dev->iir = UART_IIR_NO_INT;
break;
#define CONSOLE_8250 1
#define CONSOLE_VIRTIO 2
-int term_putc_iov(int who, struct iovec *iov, int iovcnt);
-int term_getc_iov(int who, struct iovec *iov, int iovcnt);
-int term_putc(int who, char *addr, int cnt);
-int term_getc(int who);
+int term_putc_iov(int who, struct iovec *iov, int iovcnt, int term);
+int term_getc_iov(int who, struct iovec *iov, int iovcnt, int term);
+int term_putc(int who, char *addr, int cnt, int term);
+int term_getc(int who, int term);
-bool term_readable(int who);
+bool term_readable(int who, int term);
+void term_set_tty(int term);
void term_init(void);
#endif /* KVM__TERM_H */
#include <unistd.h>
#include <sys/uio.h>
#include <signal.h>
+#include <pty.h>
+#include <utmp.h>
#include "kvm/read-write.h"
#include "kvm/term.h"
#include "kvm/kvm.h"
#include "kvm/kvm-cpu.h"
+
+#define TERM_FD_IN 0
+#define TERM_FD_OUT 1
+
extern struct kvm *kvm;
static struct termios orig_term;
int active_console;
-int term_getc(int who)
+int term_fds[4][2];
+
+int term_getc(int who, int term)
{
int c;
if (who != active_console)
return -1;
- if (read_in_full(STDIN_FILENO, &c, 1) < 0)
+ if (read_in_full(term_fds[term][TERM_FD_IN], &c, 1) < 0)
return -1;
c &= 0xff;
return c;
}
-int term_putc(int who, char *addr, int cnt)
+int term_putc(int who, char *addr, int cnt, int term)
{
+ int ret;
+
if (who != active_console)
return -1;
- while (cnt--)
- fprintf(stdout, "%c", *addr++);
+ while (cnt--) {
+ ret = write(term_fds[term][TERM_FD_OUT], addr++, 1);
+ if (ret < 0)
+ return 0;
+ }
- fflush(stdout);
return cnt;
}
-int term_getc_iov(int who, struct iovec *iov, int iovcnt)
+int term_getc_iov(int who, struct iovec *iov, int iovcnt, int term)
{
int c;
if (who != active_console)
return 0;
- c = term_getc(who);
+ c = term_getc(who, term);
if (c < 0)
return 0;
- *((int *)iov[0].iov_base) = c;
+ *((int *)iov[TERM_FD_IN].iov_base) = c;
return sizeof(char);
}
-int term_putc_iov(int who, struct iovec *iov, int iovcnt)
+int term_putc_iov(int who, struct iovec *iov, int iovcnt, int term)
{
if (who != active_console)
return 0;
- return writev(STDOUT_FILENO, iov, iovcnt);
+ return writev(term_fds[term][TERM_FD_OUT], iov, iovcnt);
}
-bool term_readable(int who)
+bool term_readable(int who, int term)
{
struct pollfd pollfd = (struct pollfd) {
- .fd = STDIN_FILENO,
+ .fd = term_fds[term][TERM_FD_IN],
.events = POLLIN,
.revents = 0,
};
static void term_cleanup(void)
{
- tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
+ int i;
+
+ for (i = 0; i < 4; i++)
+ tcsetattr(term_fds[i][TERM_FD_IN], TCSANOW, &orig_term);
}
static void term_sig_cleanup(int sig)
raise(sig);
}
+void term_set_tty(int term)
+{
+ struct termios orig_term;
+ int master, slave;
+ char new_pty[PATH_MAX];
+
+ if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
+ die("unable to save initial standard input settings");
+
+ orig_term.c_lflag &= ~(ICANON | ECHO | ISIG);
+
+ if (openpty(&master, &slave, new_pty, &orig_term, NULL) < 0)
+ return;
+
+ close(slave);
+
+ pr_info("Assigned terminal %d to pty %s\n", term, new_pty);
+
+ term_fds[term][TERM_FD_IN] = term_fds[term][TERM_FD_OUT] = master;
+}
+
void term_init(void)
{
struct termios term;
+ int i;
if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
die("unable to save initial standard input settings");
term.c_lflag &= ~(ICANON | ECHO | ISIG);
tcsetattr(STDIN_FILENO, TCSANOW, &term);
+ for (i = 0; i < 4; i++)
+ if (term_fds[i][TERM_FD_IN] == 0) {
+ term_fds[i][TERM_FD_IN] = STDIN_FILENO;
+ term_fds[i][TERM_FD_OUT] = STDOUT_FILENO;
+ }
+
signal(SIGTERM, term_sig_cleanup);
atexit(term_cleanup);
}
vq = param;
- if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) {
+ if (term_readable(CONSOLE_VIRTIO, 0) && virt_queue__available(vq)) {
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
- len = term_getc_iov(CONSOLE_VIRTIO, iov, in);
+ len = term_getc_iov(CONSOLE_VIRTIO, iov, in, 0);
virt_queue__set_used_elem(vq, head, len);
virtio_pci__signal_vq(kvm, &cdev.vpci, vq - cdev.vqs);
}
while (virt_queue__available(vq)) {
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
- len = term_putc_iov(CONSOLE_VIRTIO, iov, out);
+ len = term_putc_iov(CONSOLE_VIRTIO, iov, out, 0);
virt_queue__set_used_elem(vq, head, len);
}