xref: /kvm-unit-tests/lib/x86/io.c (revision 5747945371b47c51cb16187a26111d06f58f06b2)
1 #include "libcflat.h"
2 #include "smp.h"
3 #include "fwcfg.h"
4 #include "asm/io.h"
5 #include "asm/page.h"
6 #include "vmalloc.h"
7 #ifndef USE_SERIAL
8 #define USE_SERIAL
9 #endif
10 
11 static struct spinlock lock;
12 static int serial_iobase = 0x3f8;
13 static int serial_inited = 0;
14 
serial_outb(char ch)15 static void serial_outb(char ch)
16 {
17         u8 lsr;
18 
19         do {
20                 lsr = inb(serial_iobase + 0x05);
21         } while (!(lsr & 0x20));
22 
23         outb(ch, serial_iobase + 0x00);
24 }
25 
serial_put(char ch)26 static void serial_put(char ch)
27 {
28         /* Force carriage return to be performed on \n */
29         if (ch == '\n')
30                 serial_outb('\r');
31         serial_outb(ch);
32 }
33 
serial_init(void)34 static void serial_init(void)
35 {
36         u8 lcr;
37 
38         /* set DLAB */
39         lcr = inb(serial_iobase + 0x03);
40         lcr |= 0x80;
41         outb(lcr, serial_iobase + 0x03);
42 
43         /* set baud rate to 115200 */
44         outb(0x01, serial_iobase + 0x00);
45         outb(0x00, serial_iobase + 0x01);
46 
47         /* clear DLAB */
48         lcr = inb(serial_iobase + 0x03);
49         lcr &= ~0x80;
50         outb(lcr, serial_iobase + 0x03);
51 
52         /* IER: disable interrupts */
53         outb(0x00, serial_iobase + 0x01);
54         /* LCR: 8 bits, no parity, one stop bit */
55         outb(0x03, serial_iobase + 0x03);
56         /* FCR: disable FIFO queues */
57         outb(0x00, serial_iobase + 0x02);
58         /* MCR: RTS, DTR on */
59         outb(0x03, serial_iobase + 0x04);
60 }
61 
print_serial(const char * buf)62 static void print_serial(const char *buf)
63 {
64 	unsigned long len = strlen(buf);
65 #ifdef USE_SERIAL
66         unsigned long i;
67         if (!serial_inited) {
68             serial_init();
69             serial_inited = 1;
70         }
71 
72         for (i = 0; i < len; i++) {
73             serial_put(buf[i]);
74         }
75 #else
76         asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
77 #endif
78 }
79 
puts(const char * s)80 void puts(const char *s)
81 {
82 	spin_lock(&lock);
83 	print_serial(s);
84 	spin_unlock(&lock);
85 }
86 
exit(int code)87 void exit(int code)
88 {
89 #ifdef USE_SERIAL
90         static const char shutdown_str[8] = "Shutdown";
91         int i;
92 
93         /* test device exit (with status) */
94         outl(code, 0xf4);
95 
96         /* if that failed, try the Bochs poweroff port */
97         for (i = 0; i < 8; i++) {
98                 outb(shutdown_str[i], 0x8900);
99         }
100 #else
101         asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4));
102 #endif
103 	if (no_test_device)
104 		printf("--- DONE: %d ---\n", code);
105 
106 	/* Fallback */
107 	while (1) {
108 		asm volatile("hlt" ::: "memory");
109 	}
110 }
111 
ioremap(phys_addr_t phys_addr,size_t size)112 void __iomem *ioremap(phys_addr_t phys_addr, size_t size)
113 {
114 	phys_addr_t base = phys_addr & PAGE_MASK;
115 	phys_addr_t offset = phys_addr - base;
116 
117 	/*
118 	 * The kernel sets PTEs for an ioremap() with page cache disabled,
119 	 * but we do not do that right now. It would make sense that I/O
120 	 * mappings would be uncached - and may help us find bugs when we
121 	 * properly map that way.
122 	 */
123 	return vmap(phys_addr, size) + offset;
124 }
125