Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 1 | /****************************************************************************** |
| 2 | * Copyright (c) 2004, 2008 IBM Corporation |
| 3 | * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net> |
| 4 | * All rights reserved. |
| 5 | * This program and the accompanying materials |
| 6 | * are made available under the terms of the BSD License |
| 7 | * which accompanies this distribution, and is available at |
| 8 | * http://www.opensource.org/licenses/bsd-license.php |
| 9 | * |
| 10 | * Contributors: |
| 11 | * IBM Corporation - initial implementation |
| 12 | *****************************************************************************/ |
| 13 | |
Uwe Hermann | 01ce601 | 2010-03-05 10:03:50 +0000 | [diff] [blame] | 14 | #include <types.h> |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 15 | #include "compat/rtas.h" |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 16 | |
| 17 | #include "biosemu.h" |
| 18 | #include "mem.h" |
| 19 | #include "device.h" |
| 20 | #include "debug.h" |
| 21 | #include "pmm.h" |
Uwe Hermann | 01ce601 | 2010-03-05 10:03:50 +0000 | [diff] [blame] | 22 | #include "interrupt.h" |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 23 | |
| 24 | #include <x86emu/x86emu.h> |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 25 | #include "../x86emu/prim_ops.h" |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 26 | |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 27 | #include <device/pci.h> |
| 28 | #include <device/pci_ops.h> |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 29 | |
| 30 | |
| 31 | //setup to run the code at the address, that the Interrupt Vector points to... |
Myles Watson | b025911 | 2010-03-05 18:27:19 +0000 | [diff] [blame] | 32 | static void |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 33 | setupInt(int intNum) |
| 34 | { |
| 35 | DEBUG_PRINTF_INTR("%s(%x): executing interrupt handler @%08x\n", |
| 36 | __func__, intNum, my_rdl(intNum * 4)); |
| 37 | // push current R_FLG... will be popped by IRET |
| 38 | push_word((u16) M.x86.R_FLG); |
| 39 | CLEAR_FLAG(F_IF); |
| 40 | CLEAR_FLAG(F_TF); |
| 41 | // push current CS:IP to the stack, will be popped by IRET |
| 42 | push_word(M.x86.R_CS); |
| 43 | push_word(M.x86.R_IP); |
| 44 | // set CS:IP to the interrupt handler address... so the next executed instruction will |
| 45 | // be the interrupt handler |
| 46 | M.x86.R_CS = my_rdw(intNum * 4 + 2); |
| 47 | M.x86.R_IP = my_rdw(intNum * 4); |
| 48 | } |
| 49 | |
| 50 | // handle int10 (VGA BIOS Interrupt) |
Myles Watson | b025911 | 2010-03-05 18:27:19 +0000 | [diff] [blame] | 51 | static void |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 52 | handleInt10(void) |
| 53 | { |
| 54 | // the data for INT10 is stored in BDA (0000:0400h) offset 49h-66h |
| 55 | // function number in AH |
| 56 | //DEBUG_PRINTF_CS_IP("%s:\n", __func__); |
| 57 | //x86emu_dump_xregs(); |
| 58 | //if ((M.x86.R_IP == 0x32c2) && (M.x86.R_SI == 0x1ce2)){ |
| 59 | //X86EMU_trace_on(); |
| 60 | //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; |
| 61 | //} |
| 62 | switch (M.x86.R_AH) { |
| 63 | case 0x00: |
| 64 | // set video mode |
| 65 | // BDA offset 49h is current video mode |
| 66 | my_wrb(0x449, M.x86.R_AL); |
| 67 | if (M.x86.R_AL > 7) |
| 68 | M.x86.R_AL = 0x20; |
| 69 | else if (M.x86.R_AL == 6) |
| 70 | M.x86.R_AL = 0x3f; |
| 71 | else |
| 72 | M.x86.R_AL = 0x30; |
| 73 | break; |
| 74 | case 0x01: |
| 75 | // set cursor shape |
| 76 | // ignore |
| 77 | break; |
| 78 | case 0x02: |
| 79 | // set cursor position |
| 80 | // BH: pagenumber, DX: cursor_pos (DH:row, DL:col) |
| 81 | // BDA offset 50h-60h are 8 cursor position words for |
| 82 | // eight possible video pages |
| 83 | my_wrw(0x450 + (M.x86.R_BH * 2), M.x86.R_DX); |
| 84 | break; |
| 85 | case 0x03: |
| 86 | //get cursor position |
| 87 | // BH: pagenumber |
| 88 | // BDA offset 50h-60h are 8 cursor position words for |
| 89 | // eight possible video pages |
| 90 | M.x86.R_AX = 0; |
| 91 | M.x86.R_CH = 0; // start scan line ??? |
| 92 | M.x86.R_CL = 0; // end scan line ??? |
| 93 | M.x86.R_DX = my_rdw(0x450 + (M.x86.R_BH * 2)); |
| 94 | break; |
| 95 | case 0x05: |
| 96 | // set active page |
| 97 | // BDA offset 62h is current page number |
| 98 | my_wrb(0x462, M.x86.R_AL); |
| 99 | break; |
| 100 | case 0x06: |
| 101 | //scroll up windows |
| 102 | break; |
| 103 | case 0x07: |
| 104 | //scroll down windows |
| 105 | break; |
| 106 | case 0x08: |
| 107 | //read character and attribute at position |
| 108 | M.x86.R_AH = 0x07; // white-on-black |
| 109 | M.x86.R_AL = 0x20; // a space... |
| 110 | break; |
| 111 | case 0x09: |
| 112 | // write character and attribute |
| 113 | //AL: char, BH: page number, BL: attribute, CX: number of times to write |
| 114 | //BDA offset 62h is current page number |
| 115 | CHECK_DBG(DEBUG_PRINT_INT10) { |
| 116 | u32 i = 0; |
| 117 | if (M.x86.R_BH == my_rdb(0x462)) { |
| 118 | for (i = 0; i < M.x86.R_CX; i++) |
| 119 | printf("%c", M.x86.R_AL); |
| 120 | } |
| 121 | } |
| 122 | break; |
| 123 | case 0x0a: |
| 124 | // write character |
| 125 | //AL: char, BH: page number, BL: attribute, CX: number of times to write |
| 126 | //BDA offset 62h is current page number |
| 127 | CHECK_DBG(DEBUG_PRINT_INT10) { |
| 128 | u32 i = 0; |
| 129 | if (M.x86.R_BH == my_rdb(0x462)) { |
| 130 | for (i = 0; i < M.x86.R_CX; i++) |
| 131 | printf("%c", M.x86.R_AL); |
| 132 | } |
| 133 | } |
| 134 | break; |
| 135 | case 0x0e: |
| 136 | // teletype output: write character and advance cursor... |
| 137 | //AL: char, BH: page number, BL: attribute |
| 138 | //BDA offset 62h is current page number |
| 139 | CHECK_DBG(DEBUG_PRINT_INT10) { |
| 140 | // we ignore the pagenumber on this call... |
| 141 | //if (M.x86.R_BH == my_rdb(0x462)) |
| 142 | { |
| 143 | printf("%c", M.x86.R_AL); |
| 144 | // for debugging, to read all lines |
| 145 | //if (M.x86.R_AL == 0xd) // carriage return |
| 146 | // printf("\n"); |
| 147 | } |
| 148 | } |
| 149 | break; |
| 150 | case 0x0f: |
| 151 | // get video mode |
| 152 | // BDA offset 49h is current video mode |
| 153 | // BDA offset 62h is current page number |
| 154 | // BDA offset 4ah is columns on screen |
| 155 | M.x86.R_AH = 80; //number of character columns... we hardcode it to 80 |
| 156 | M.x86.R_AL = my_rdb(0x449); |
| 157 | M.x86.R_BH = my_rdb(0x462); |
| 158 | break; |
| 159 | default: |
| 160 | printf("%s(): unknown function (%x) for int10 handler.\n", |
| 161 | __func__, M.x86.R_AH); |
| 162 | DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", |
| 163 | M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, |
| 164 | M.x86.R_DX); |
| 165 | HALT_SYS(); |
| 166 | break; |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | // this table translates ASCII chars into their XT scan codes: |
| 171 | static u8 keycode_table[256] = { |
| 172 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 - 7 |
| 173 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 - 15 |
| 174 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 - 23 |
| 175 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24 - 31 |
| 176 | 0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, // 32 - 39 |
| 177 | 0x0a, 0x0b, 0x09, 0x2b, 0x33, 0x0d, 0x34, 0x35, // 40 - 47 |
| 178 | 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 48 - 55 |
| 179 | 0x09, 0x0a, 0x27, 0x27, 0x33, 0x2b, 0x34, 0x35, // 56 - 63 |
| 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 64 - 71 |
| 181 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 72 - 79 |
| 182 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 - 87 |
| 183 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 88 - 95 |
| 184 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 - 103 |
| 185 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 104 - 111 |
| 186 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 112 - 119 |
| 187 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 120 - 127 |
| 188 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ... |
| 189 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 190 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 191 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 192 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 193 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 194 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 195 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 196 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 197 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 198 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 199 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 200 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 201 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 202 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 203 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 204 | } |
| 205 | |
| 206 | ; |
| 207 | |
Myles Watson | b025911 | 2010-03-05 18:27:19 +0000 | [diff] [blame] | 208 | static void |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 209 | translate_keycode(u64 * keycode) |
| 210 | { |
| 211 | u8 scan_code = 0; |
| 212 | u8 char_code = 0; |
| 213 | if (*keycode < 256) { |
| 214 | scan_code = keycode_table[*keycode]; |
| 215 | char_code = (u8) * keycode & 0xff; |
| 216 | } else { |
| 217 | switch (*keycode) { |
| 218 | case 0x1b50: |
| 219 | // F1 |
| 220 | scan_code = 0x3b; |
| 221 | char_code = 0x0; |
| 222 | break; |
| 223 | default: |
| 224 | printf("%s(): unknown multibyte keycode: %llx\n", |
| 225 | __func__, *keycode); |
| 226 | break; |
| 227 | } |
| 228 | } |
| 229 | //assemble scan/char code in keycode |
| 230 | *keycode = (u64) ((((u16) scan_code) << 8) | char_code); |
| 231 | } |
| 232 | |
| 233 | // handle int16 (Keyboard BIOS Interrupt) |
Myles Watson | b025911 | 2010-03-05 18:27:19 +0000 | [diff] [blame] | 234 | static void |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 235 | handleInt16(void) |
| 236 | { |
| 237 | // keyboard buffer is in BIOS Memory Area: |
| 238 | // offset 0x1a (WORD) pointer to next char in keybuffer |
| 239 | // offset 0x1c (WORD) pointer to next insert slot in keybuffer |
| 240 | // offset 0x1e-0x3e: 16 WORD Ring Buffer |
| 241 | // since we currently always read the char from the FW buffer, |
| 242 | // we misuse the ring buffer, we use it as pointer to a u64 that stores |
| 243 | // multi-byte keys (e.g. special keys in VT100 terminal) |
Martin Roth | 63373ed | 2013-07-08 16:24:19 -0600 | [diff] [blame] | 244 | // and as long as a key is available (not 0) we don't read further keys |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 245 | u64 *keycode = (u64 *) (M.mem_base + 0x41e); |
| 246 | s8 c; |
| 247 | // function number in AH |
| 248 | DEBUG_PRINTF_INTR("%s(): Keyboard Interrupt: function: %x.\n", |
| 249 | __func__, M.x86.R_AH); |
| 250 | DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", M.x86.R_AX, |
| 251 | M.x86.R_BX, M.x86.R_CX, M.x86.R_DX); |
| 252 | switch (M.x86.R_AH) { |
| 253 | case 0x00: |
| 254 | // get keystroke |
| 255 | if (*keycode) { |
| 256 | M.x86.R_AX = (u16) * keycode; |
| 257 | // clear keycode |
| 258 | *keycode = 0; |
| 259 | } else { |
| 260 | M.x86.R_AH = 0x61; // scancode for space key |
| 261 | M.x86.R_AL = 0x20; // a space |
| 262 | } |
| 263 | break; |
| 264 | case 0x01: |
| 265 | // check keystroke |
| 266 | // ZF set = no keystroke |
| 267 | // read first byte of key code |
| 268 | if (*keycode) { |
| 269 | // already read, but not yet taken |
| 270 | CLEAR_FLAG(F_ZF); |
| 271 | M.x86.R_AX = (u16) * keycode; |
| 272 | } else { |
| 273 | /* TODO: we need getchar... */ |
| 274 | c = -1; //getchar(); |
| 275 | if (c == -1) { |
| 276 | // no key available |
| 277 | SET_FLAG(F_ZF); |
| 278 | } else { |
| 279 | *keycode = c; |
| 280 | |
| 281 | // since after an ESC it may take a while to receive the next char, |
| 282 | // we send something that is not shown on the screen, and then try to get |
| 283 | // the next char |
| 284 | // TODO: only after ESC?? what about other multibyte keys |
| 285 | printf("tt%c%c", 0x08, 0x08); // 0x08 == Backspace |
| 286 | |
| 287 | /* TODO: we need getchar... */ |
| 288 | while ((c = -1 /*getchar()*/) != -1) { |
| 289 | *keycode = (*keycode << 8) | c; |
| 290 | DEBUG_PRINTF(" key read: %0llx\n", |
| 291 | *keycode); |
| 292 | } |
| 293 | translate_keycode(keycode); |
| 294 | DEBUG_PRINTF(" translated key: %0llx\n", |
| 295 | *keycode); |
| 296 | if (*keycode == 0) { |
| 297 | //not found |
| 298 | SET_FLAG(F_ZF); |
| 299 | } else { |
| 300 | CLEAR_FLAG(F_ZF); |
| 301 | M.x86.R_AX = (u16) * keycode; |
| 302 | //X86EMU_trace_on(); |
| 303 | //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | break; |
| 308 | default: |
| 309 | printf("%s(): unknown function (%x) for int16 handler.\n", |
| 310 | __func__, M.x86.R_AH); |
| 311 | DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", |
| 312 | M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, |
| 313 | M.x86.R_DX); |
| 314 | HALT_SYS(); |
| 315 | break; |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | // handle int1a (PCI BIOS Interrupt) |
Myles Watson | b025911 | 2010-03-05 18:27:19 +0000 | [diff] [blame] | 320 | static void |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 321 | handleInt1a(void) |
| 322 | { |
| 323 | // function number in AX |
| 324 | u8 bus, devfn, offs; |
| 325 | struct device* dev; |
| 326 | switch (M.x86.R_AX) { |
| 327 | case 0xb101: |
| 328 | // Installation check |
| 329 | CLEAR_FLAG(F_CF); // clear CF |
| 330 | M.x86.R_EDX = 0x20494350; // " ICP" endian swapped "PCI " |
| 331 | M.x86.R_AL = 0x1; // Config Space Mechanism 1 supported |
| 332 | M.x86.R_BX = 0x0210; // PCI Interface Level Version 2.10 |
| 333 | M.x86.R_CL = 0xff; // number of last PCI Bus in system TODO: check! |
| 334 | break; |
| 335 | case 0xb102: |
| 336 | // Find PCI Device |
| 337 | // device_id in CX, vendor_id in DX |
| 338 | // device index in SI (i.e. if multiple devices with same vendor/device id |
| 339 | // are connected). We currently only support device index 0 |
| 340 | // |
| 341 | DEBUG_PRINTF_INTR("%s(): function: %x: PCI Find Device\n", |
| 342 | __func__, M.x86.R_AX); |
| 343 | /* FixME: support SI != 0 */ |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 344 | #if CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 345 | dev = dev_find_device(M.x86.R_DX, M.x86.R_CX, 0); |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 346 | if (dev != 0) { |
| 347 | DEBUG_PRINTF_INTR |
| 348 | ("%s(): function %x: PCI Find Device --> 0x%04x\n", |
| 349 | __func__, M.x86.R_AX, M.x86.R_BX); |
| 350 | |
| 351 | M.x86.R_BH = dev->bus->secondary; |
| 352 | M.x86.R_BL = dev->path.pci.devfn; |
| 353 | M.x86.R_AH = 0x00; // return code: success |
| 354 | CLEAR_FLAG(F_CF); |
| 355 | #else |
| 356 | // only allow the device to find itself... |
| 357 | if ((M.x86.R_CX == bios_device.pci_device_id) |
| 358 | && (M.x86.R_DX == bios_device.pci_vendor_id) |
| 359 | // device index must be 0 |
| 360 | && (M.x86.R_SI == 0)) { |
| 361 | CLEAR_FLAG(F_CF); |
| 362 | M.x86.R_AH = 0x00; // return code: success |
| 363 | M.x86.R_BH = bios_device.bus; |
| 364 | M.x86.R_BL = bios_device.devfn; |
| 365 | #endif |
| 366 | } else { |
| 367 | DEBUG_PRINTF_INTR |
| 368 | ("%s(): function %x: invalid device/vendor/device index! (%04x/%04x/%02x expected: %04x/%04x/00) \n", |
| 369 | __func__, M.x86.R_AX, M.x86.R_CX, M.x86.R_DX, |
| 370 | M.x86.R_SI, bios_device.pci_device_id, |
| 371 | bios_device.pci_vendor_id); |
| 372 | |
| 373 | SET_FLAG(F_CF); |
| 374 | M.x86.R_AH = 0x86; // return code: device not found |
| 375 | } |
| 376 | break; |
| 377 | case 0xb108: //read configuration byte |
| 378 | case 0xb109: //read configuration word |
| 379 | case 0xb10a: //read configuration dword |
| 380 | bus = M.x86.R_BH; |
| 381 | devfn = M.x86.R_BL; |
| 382 | offs = M.x86.R_DI; |
| 383 | DEBUG_PRINTF_INTR("%s(): function: %x: PCI Config Read from device: bus: %02x, devfn: %02x, offset: %02x\n", |
| 384 | __func__, M.x86.R_AX, bus, devfn, offs); |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 385 | #if CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 386 | dev = dev_find_slot(bus, devfn); |
| 387 | DEBUG_PRINTF_INTR("%s(): function: %x: dev_find_slot() returned: %s\n", |
| 388 | __func__, M.x86.R_AX, dev_path(dev)); |
| 389 | if (dev == 0) { |
| 390 | // fail accesses to non-existent devices... |
| 391 | #else |
| 392 | dev = bios_device.dev; |
| 393 | if ((bus != bios_device.bus) |
| 394 | || (devfn != bios_device.devfn)) { |
| 395 | // fail accesses to any device but ours... |
| 396 | #endif |
| 397 | printf |
| 398 | ("%s(): Config read access invalid device! bus: %02x (%02x), devfn: %02x (%02x), offs: %02x\n", |
| 399 | __func__, bus, bios_device.bus, devfn, |
| 400 | bios_device.devfn, offs); |
| 401 | SET_FLAG(F_CF); |
| 402 | M.x86.R_AH = 0x87; //return code: bad pci register |
| 403 | HALT_SYS(); |
| 404 | return; |
| 405 | } else { |
| 406 | switch (M.x86.R_AX) { |
| 407 | case 0xb108: |
| 408 | M.x86.R_CL = |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 409 | #if CONFIG_PCI_OPTION_ROM_RUN_YABEL |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 410 | pci_read_config8(dev, offs); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 411 | #else |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 412 | (u8) rtas_pci_config_read(bios_device. |
| 413 | puid, 1, |
| 414 | bus, devfn, |
| 415 | offs); |
| 416 | #endif |
| 417 | DEBUG_PRINTF_INTR |
| 418 | ("%s(): function %x: PCI Config Read @%02x --> 0x%02x\n", |
| 419 | __func__, M.x86.R_AX, offs, |
| 420 | M.x86.R_CL); |
| 421 | break; |
| 422 | case 0xb109: |
| 423 | M.x86.R_CX = |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 424 | #if CONFIG_PCI_OPTION_ROM_RUN_YABEL |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 425 | pci_read_config16(dev, offs); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 426 | #else |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 427 | (u16) rtas_pci_config_read(bios_device. |
| 428 | puid, 2, |
| 429 | bus, devfn, |
| 430 | offs); |
| 431 | #endif |
| 432 | DEBUG_PRINTF_INTR |
| 433 | ("%s(): function %x: PCI Config Read @%02x --> 0x%04x\n", |
| 434 | __func__, M.x86.R_AX, offs, |
| 435 | M.x86.R_CX); |
| 436 | break; |
| 437 | case 0xb10a: |
| 438 | M.x86.R_ECX = |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 439 | #if CONFIG_PCI_OPTION_ROM_RUN_YABEL |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 440 | pci_read_config32(dev, offs); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 441 | #else |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 442 | (u32) rtas_pci_config_read(bios_device. |
| 443 | puid, 4, |
| 444 | bus, devfn, |
| 445 | offs); |
| 446 | #endif |
| 447 | DEBUG_PRINTF_INTR |
| 448 | ("%s(): function %x: PCI Config Read @%02x --> 0x%08x\n", |
| 449 | __func__, M.x86.R_AX, offs, |
| 450 | M.x86.R_ECX); |
| 451 | break; |
| 452 | } |
| 453 | CLEAR_FLAG(F_CF); |
| 454 | M.x86.R_AH = 0x0; // return code: success |
| 455 | } |
| 456 | break; |
| 457 | case 0xb10b: //write configuration byte |
| 458 | case 0xb10c: //write configuration word |
| 459 | case 0xb10d: //write configuration dword |
| 460 | bus = M.x86.R_BH; |
| 461 | devfn = M.x86.R_BL; |
| 462 | offs = M.x86.R_DI; |
| 463 | if ((bus != bios_device.bus) |
| 464 | || (devfn != bios_device.devfn)) { |
| 465 | // fail accesses to any device but ours... |
| 466 | printf |
| 467 | ("%s(): Config read access invalid! bus: %x (%x), devfn: %x (%x), offs: %x\n", |
| 468 | __func__, bus, bios_device.bus, devfn, |
| 469 | bios_device.devfn, offs); |
| 470 | SET_FLAG(F_CF); |
| 471 | M.x86.R_AH = 0x87; //return code: bad pci register |
| 472 | HALT_SYS(); |
| 473 | return; |
| 474 | } else { |
| 475 | switch (M.x86.R_AX) { |
| 476 | case 0xb10b: |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 477 | #if CONFIG_PCI_OPTION_ROM_RUN_YABEL |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 478 | pci_write_config8(bios_device.dev, offs, M.x86.R_CL); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 479 | #else |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 480 | rtas_pci_config_write(bios_device.puid, 1, bus, |
| 481 | devfn, offs, M.x86.R_CL); |
| 482 | #endif |
| 483 | DEBUG_PRINTF_INTR |
| 484 | ("%s(): function %x: PCI Config Write @%02x <-- 0x%02x\n", |
| 485 | __func__, M.x86.R_AX, offs, |
| 486 | M.x86.R_CL); |
| 487 | break; |
| 488 | case 0xb10c: |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 489 | #if CONFIG_PCI_OPTION_ROM_RUN_YABEL |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 490 | pci_write_config16(bios_device.dev, offs, M.x86.R_CX); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 491 | #else |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 492 | rtas_pci_config_write(bios_device.puid, 2, bus, |
| 493 | devfn, offs, M.x86.R_CX); |
| 494 | #endif |
| 495 | DEBUG_PRINTF_INTR |
| 496 | ("%s(): function %x: PCI Config Write @%02x <-- 0x%04x\n", |
| 497 | __func__, M.x86.R_AX, offs, |
| 498 | M.x86.R_CX); |
| 499 | break; |
| 500 | case 0xb10d: |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 501 | #if CONFIG_PCI_OPTION_ROM_RUN_YABEL |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 502 | pci_write_config32(bios_device.dev, offs, M.x86.R_ECX); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 503 | #else |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 504 | rtas_pci_config_write(bios_device.puid, 4, bus, |
| 505 | devfn, offs, M.x86.R_ECX); |
| 506 | #endif |
| 507 | DEBUG_PRINTF_INTR |
| 508 | ("%s(): function %x: PCI Config Write @%02x <-- 0x%08x\n", |
| 509 | __func__, M.x86.R_AX, offs, |
| 510 | M.x86.R_ECX); |
| 511 | break; |
| 512 | } |
| 513 | CLEAR_FLAG(F_CF); |
| 514 | M.x86.R_AH = 0x0; // return code: success |
| 515 | } |
| 516 | break; |
| 517 | default: |
| 518 | printf("%s(): unknown function (%x) for int1a handler.\n", |
| 519 | __func__, M.x86.R_AX); |
| 520 | DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", |
| 521 | M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, |
| 522 | M.x86.R_DX); |
| 523 | HALT_SYS(); |
| 524 | break; |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | // main Interrupt Handler routine, should be registered as x86emu interrupt handler |
| 529 | void |
| 530 | handleInterrupt(int intNum) |
| 531 | { |
| 532 | u8 int_handled = 0; |
| 533 | #ifndef DEBUG_PRINT_INT10 |
| 534 | // this printf makes output by int 10 unreadable... |
| 535 | // so we only enable it, if int10 print is disabled |
| 536 | DEBUG_PRINTF_INTR("%s(%x)\n", __func__, intNum); |
| 537 | #endif |
| 538 | |
Martin Roth | 63373ed | 2013-07-08 16:24:19 -0600 | [diff] [blame] | 539 | /* check whether this interrupt has a function pointer set in yabel_intFuncArray and run that */ |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 540 | if (yabel_intFuncArray[intNum]) { |
| 541 | DEBUG_PRINTF_INTR("%s(%x) intHandler overridden, calling it...\n", __func__, intNum); |
| 542 | int_handled = (*yabel_intFuncArray[intNum])(); |
| 543 | } else { |
| 544 | switch (intNum) { |
| 545 | case 0x10: //BIOS video interrupt |
| 546 | case 0x42: // INT 10h relocated by EGA/VGA BIOS |
| 547 | case 0x6d: // INT 10h relocated by VGA BIOS |
| 548 | // get interrupt vector from IDT (4 bytes per Interrupt starting at address 0 |
| 549 | if ((my_rdl(intNum * 4) == 0xF000F065) || //F000:F065 is default BIOS interrupt handler address |
| 550 | (my_rdl(intNum * 4) == 0xF4F4F4F4)) //invalid |
| 551 | { |
| 552 | #if 0 |
| 553 | // ignore interrupt... |
| 554 | DEBUG_PRINTF_INTR |
| 555 | ("%s(%x): invalid interrupt Vector (%08x) found, interrupt ignored...\n", |
| 556 | __func__, intNum, my_rdl(intNum * 4)); |
| 557 | DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", |
| 558 | M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, |
| 559 | M.x86.R_DX); |
| 560 | //HALT_SYS(); |
| 561 | #endif |
| 562 | handleInt10(); |
| 563 | int_handled = 1; |
| 564 | } |
| 565 | break; |
| 566 | case 0x16: |
| 567 | // Keyboard BIOS Interrupt |
| 568 | handleInt16(); |
| 569 | int_handled = 1; |
| 570 | break; |
| 571 | case 0x1a: |
| 572 | // PCI BIOS Interrupt |
| 573 | handleInt1a(); |
| 574 | int_handled = 1; |
| 575 | break; |
| 576 | case PMM_INT_NUM: |
Stefan Reinauer | 1d888a9 | 2011-04-21 20:24:43 +0000 | [diff] [blame] | 577 | /* The self-defined PMM INT number, this is called by |
| 578 | * the code in PMM struct, and it is handled by |
| 579 | * pmm_handleInt() |
Stefan Reinauer | 38cd29e | 2009-08-11 21:28:25 +0000 | [diff] [blame] | 580 | */ |
| 581 | pmm_handleInt(); |
| 582 | int_handled = 1; |
| 583 | break; |
| 584 | default: |
| 585 | printf("Interrupt %#x (Vector: %x) not implemented\n", intNum, |
| 586 | my_rdl(intNum * 4)); |
| 587 | DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", |
| 588 | M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, |
| 589 | M.x86.R_DX); |
| 590 | int_handled = 1; |
| 591 | HALT_SYS(); |
| 592 | break; |
| 593 | } |
| 594 | } |
| 595 | // if we did not handle the interrupt, jump to the interrupt vector... |
| 596 | if (!int_handled) { |
| 597 | setupInt(intNum); |
| 598 | } |
| 599 | } |
| 600 | |
| 601 | // prepare and execute Interrupt 10 (VGA Interrupt) |
| 602 | void |
| 603 | runInt10(void) |
| 604 | { |
| 605 | // Initialize stack and data segment |
| 606 | M.x86.R_SS = STACK_SEGMENT; |
| 607 | M.x86.R_DS = DATA_SEGMENT; |
| 608 | M.x86.R_SP = STACK_START_OFFSET; |
| 609 | |
| 610 | // push a HLT instruction and a pointer to it onto the stack |
| 611 | // any return will pop the pointer and jump to the HLT, thus |
| 612 | // exiting (more or less) cleanly |
| 613 | push_word(0xf4f4); //F4=HLT |
| 614 | //push_word(M.x86.R_SS); |
| 615 | //push_word(M.x86.R_SP + 2); |
| 616 | |
| 617 | // setupInt will push the current CS and IP to the stack to return to it, |
| 618 | // but we want to halt, so set CS:IP to the HLT instruction we just pushed |
| 619 | // to the stack |
| 620 | M.x86.R_CS = M.x86.R_SS; |
| 621 | M.x86.R_IP = M.x86.R_SP; // + 4; |
| 622 | |
| 623 | CHECK_DBG(DEBUG_TRACE_X86EMU) { |
| 624 | X86EMU_trace_on(); |
| 625 | } |
| 626 | CHECK_DBG(DEBUG_JMP) { |
| 627 | M.x86.debug |= DEBUG_TRACEJMP_REGS_F; |
| 628 | M.x86.debug |= DEBUG_TRACEJMP_REGS_F; |
| 629 | M.x86.debug |= DEBUG_TRACECALL_F; |
| 630 | M.x86.debug |= DEBUG_TRACECALL_REGS_F; |
| 631 | } |
| 632 | setupInt(0x10); |
| 633 | DEBUG_PRINTF_INTR("%s(): starting execution of INT10...\n", |
| 634 | __func__); |
| 635 | X86EMU_exec(); |
| 636 | DEBUG_PRINTF_INTR("%s(): execution finished\n", __func__); |
| 637 | } |
| 638 | |
| 639 | // prepare and execute Interrupt 13 (Disk Interrupt) |
| 640 | void |
| 641 | runInt13(void) |
| 642 | { |
| 643 | // Initialize stack and data segment |
| 644 | M.x86.R_SS = STACK_SEGMENT; |
| 645 | M.x86.R_DS = DATA_SEGMENT; |
| 646 | M.x86.R_SP = STACK_START_OFFSET; |
| 647 | |
| 648 | // push a HLT instruction and a pointer to it onto the stack |
| 649 | // any return will pop the pointer and jump to the HLT, thus |
| 650 | // exiting (more or less) cleanly |
| 651 | push_word(0xf4f4); //F4=HLT |
| 652 | //push_word(M.x86.R_SS); |
| 653 | //push_word(M.x86.R_SP + 2); |
| 654 | |
| 655 | // setupInt will push the current CS and IP to the stack to return to it, |
| 656 | // but we want to halt, so set CS:IP to the HLT instruction we just pushed |
| 657 | // to the stack |
| 658 | M.x86.R_CS = M.x86.R_SS; |
| 659 | M.x86.R_IP = M.x86.R_SP; |
| 660 | |
| 661 | CHECK_DBG(DEBUG_TRACE_X86EMU) { |
| 662 | X86EMU_trace_on(); |
| 663 | } |
| 664 | CHECK_DBG(DEBUG_JMP) { |
| 665 | M.x86.debug |= DEBUG_TRACEJMP_REGS_F; |
| 666 | M.x86.debug |= DEBUG_TRACEJMP_REGS_F; |
| 667 | M.x86.debug |= DEBUG_TRACECALL_F; |
| 668 | M.x86.debug |= DEBUG_TRACECALL_REGS_F; |
| 669 | } |
| 670 | |
| 671 | setupInt(0x13); |
| 672 | DEBUG_PRINTF_INTR("%s(): starting execution of INT13...\n", |
| 673 | __func__); |
| 674 | X86EMU_exec(); |
| 675 | DEBUG_PRINTF_INTR("%s(): execution finished\n", __func__); |
| 676 | } |