summaryrefslogtreecommitdiff
path: root/src/device/oprom/realmode/x86_interrupts.c
blob: 71fc1d52a64559eaf0e60a8516cfb4ceb0a1fa91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/* SPDX-License-Identifier: GPL-2.0-only */

#include <arch/registers.h>
#include <console/console.h>
#include <device/pci.h>
#include <device/pci_ops.h>

/* we use x86emu's register file representation */
#include <x86emu/regs.h>

#include "x86.h"

// errors go in AH. Just set these up so that word assigns
// will work. KISS.
enum {
	PCIBIOS_SUCCESSFUL = 0x0000,
	PCIBIOS_UNSUPPORTED = 0x8100,
	PCIBIOS_BADVENDOR = 0x8300,
	PCIBIOS_NODEV = 0x8600,
	PCIBIOS_BADREG = 0x8700
};

int int10_handler(void)
{
	int res=0;
	static u8 cursor_row=0, cursor_col=0;
	switch((X86_EAX & 0xff00)>>8) {
	case 0x01: // Set cursor shape
		res = 1;
		break;
	case 0x02: // Set cursor position
		if (cursor_row != ((X86_EDX >> 8) & 0xff) ||
		    cursor_col >= (X86_EDX & 0xff)) {
			printk(BIOS_INFO, "\n");
		}
		cursor_row = (X86_EDX >> 8) & 0xff;
		cursor_col = X86_EDX & 0xff;
		res = 1;
		break;
	case 0x03: // Get cursor position
		X86_EAX &= 0x00ff;
		X86_ECX = 0x0607;
		X86_EDX = (cursor_row << 8) | cursor_col;
		res = 1;
		break;
	case 0x06: // Scroll up
		printk(BIOS_INFO, "\n");
		res = 1;
		break;
	case 0x08: // Get Character and Mode at Cursor Position
		X86_EAX = 0x0f00 | 'A'; // White on black 'A'
		res = 1;
		break;
	case 0x09: // Write Character and attribute
	case 0x0e: // Write Character
		printk(BIOS_INFO, "%c", X86_EAX & 0xff);
		res = 1;
		break;
	case 0x0f: // Get video mode
		X86_EAX = 0x5002; //80x25
		X86_EBX &= 0x00ff;
		res = 1;
		break;
        default:
		printk(BIOS_WARNING, "Unknown INT10 function %04x!\n",
				X86_EAX & 0xffff);
		break;
	}
	return res;
}

int int12_handler(void)
{
	X86_EAX = 64 * 1024;
	return 1;
}

int int16_handler(void)
{
	int res=0;
	switch((X86_EAX & 0xff00)>>8) {
	case 0x00: // Check for Keystroke
		X86_EAX = 0x6120; // Space Bar, Space
		res = 1;
		break;
	case 0x01: // Check for Keystroke
		X86_EFLAGS |= 1<<6; // Zero Flag set (no key available)
		res = 1;
		break;
        default:
		printk(BIOS_WARNING, "Unknown INT16 function %04x!\n",
				X86_EAX & 0xffff);
		break;
	}
	return res;
}

#define PCI_CONFIG_SPACE_TYPE1	(1 << 0)
#define PCI_SPECIAL_CYCLE_TYPE1	(1 << 4)

int int1a_handler(void)
{
	unsigned short func = (unsigned short)X86_EAX;
	int retval = 1;
	unsigned short devid, vendorid, devfn;
	/* Use short to get rid of garbage in upper half of 32-bit register */
	short devindex;
	unsigned char bus;
	struct device *dev;
	u32 dword;
	u16 word;
	u8 byte, reg;

	switch (func) {
	case 0xb101: /* PCIBIOS Check */
		X86_EDX = 0x20494350;	/* ' ICP' */
		X86_EAX &= 0xffff0000; /* Clear AH / AL */
		X86_EAX |= PCI_CONFIG_SPACE_TYPE1 | PCI_SPECIAL_CYCLE_TYPE1;
		// last bus in the system. Hard code to 255 for now.
		// dev_enumerate() does not seem to tell us (publicly)
		X86_ECX = 0xff;
		X86_EDI = 0x00000000;	/* protected mode entry */
		retval = 1;
		break;
	case 0xb102: /* Find Device */
		devid = X86_ECX;
		vendorid = X86_EDX;
		devindex = X86_ESI;
		dev = 0;
		while ((dev = dev_find_device(vendorid, devid, dev))) {
			if (devindex <= 0)
				break;
			devindex--;
		}
		if (dev) {
			unsigned short busdevfn;
			X86_EAX &= 0xffff00ff; /* Clear AH */
			X86_EAX |= PCIBIOS_SUCCESSFUL;
			// busnum is an unsigned char;
			// devfn is an int, so we mask it off.
			busdevfn = (dev->upstream->secondary << 8)
			    | (dev->path.pci.devfn & 0xff);
			printk(BIOS_DEBUG, "0x%x: return 0x%x\n", func, busdevfn);
			X86_EBX = busdevfn;
			retval = 1;
		} else {
			X86_EAX &= 0xffff00ff; /* Clear AH */
			X86_EAX |= PCIBIOS_NODEV;
			retval = 0;
		}
		break;
	case 0xb10a: /* Read Config Dword */
	case 0xb109: /* Read Config Word */
	case 0xb108: /* Read Config Byte */
	case 0xb10d: /* Write Config Dword */
	case 0xb10c: /* Write Config Word */
	case 0xb10b: /* Write Config Byte */
		devfn = X86_EBX & 0xff;
		bus = X86_EBX >> 8;
		reg = X86_EDI;
		dev = pcidev_path_on_bus(bus, devfn);
		if (!dev) {
			printk(BIOS_DEBUG, "0x%x: BAD DEVICE bus %d devfn 0x%x\n", func, bus, devfn);
			// Or are we supposed to return PCIBIOS_NODEV?
			X86_EAX &= 0xffff00ff; /* Clear AH */
			X86_EAX |= PCIBIOS_BADREG;
			retval = 0;
			return retval;
		}
		switch (func) {
		case 0xb108: /* Read Config Byte */
			byte = pci_read_config8(dev, reg);
			X86_ECX = byte;
			break;
		case 0xb109: /* Read Config Word */
			word = pci_read_config16(dev, reg);
			X86_ECX = word;
			break;
		case 0xb10a: /* Read Config Dword */
			dword = pci_read_config32(dev, reg);
			X86_ECX = dword;
			break;
		case 0xb10b: /* Write Config Byte */
			byte = X86_ECX;
			pci_write_config8(dev, reg, byte);
			break;
		case 0xb10c: /* Write Config Word */
			word = X86_ECX;
			pci_write_config16(dev, reg, word);
			break;
		case 0xb10d: /* Write Config Dword */
			dword = X86_ECX;
			pci_write_config32(dev, reg, dword);
			break;
		}

#if CONFIG(REALMODE_DEBUG)
		printk(BIOS_DEBUG, "0x%x: bus %d devfn 0x%x reg 0x%x val 0x%x\n",
			     func, bus, devfn, reg, X86_ECX);
#endif
		X86_EAX &= 0xffff00ff; /* Clear AH */
		X86_EAX |= PCIBIOS_SUCCESSFUL;
		retval = 1;
		break;
	default:
		printk(BIOS_ERR, "UNSUPPORTED PCIBIOS FUNCTION 0x%x\n", func);
		X86_EAX &= 0xffff00ff; /* Clear AH */
		X86_EAX |= PCIBIOS_UNSUPPORTED;
		retval = 0;
		break;
	}

	return retval;
}