Kevin O'Connor | d25810a | 2008-06-12 22:16:35 -0400 | [diff] [blame] | 1 | // PCI BIOS (int 1a/b1) calls |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 2 | // |
| 3 | // Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> |
| 4 | // Copyright (C) 2002 MandrakeSoft S.A. |
| 5 | // |
Kevin O'Connor | b1b7c2a | 2009-01-15 20:52:58 -0500 | [diff] [blame] | 6 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 7 | |
Kevin O'Connor | 4bc4997 | 2012-05-13 22:58:08 -0400 | [diff] [blame] | 8 | #include "biosvar.h" // GET_GLOBAL |
Kevin O'Connor | 2d2fa31 | 2013-09-14 21:55:26 -0400 | [diff] [blame] | 9 | #include "bregs.h" // struct bregs |
| 10 | #include "hw/pci.h" // pci_config_readl |
Kevin O'Connor | 4d8510c | 2016-02-03 01:28:20 -0500 | [diff] [blame] | 11 | #include "hw/pcidevice.h" // MaxPCIBus |
Kevin O'Connor | 5d369d8 | 2013-09-02 20:48:46 -0400 | [diff] [blame] | 12 | #include "hw/pci_regs.h" // PCI_VENDOR_ID |
Kevin O'Connor | 2d2fa31 | 2013-09-14 21:55:26 -0400 | [diff] [blame] | 13 | #include "output.h" // dprintf |
Kevin O'Connor | c5e06fb | 2013-09-14 22:22:28 -0400 | [diff] [blame] | 14 | #include "std/pirtable.h" // struct pir_header |
Kevin O'Connor | fa9c66a | 2013-09-14 19:10:40 -0400 | [diff] [blame] | 15 | #include "string.h" // checksum |
Kevin O'Connor | 2d2fa31 | 2013-09-14 21:55:26 -0400 | [diff] [blame] | 16 | #include "util.h" // handle_1ab1 |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 17 | |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 18 | // romlayout.S |
Kevin O'Connor | 47c8e31 | 2011-07-10 22:57:32 -0400 | [diff] [blame] | 19 | extern void entry_bios32(void); |
| 20 | extern void entry_pcibios32(void); |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 21 | |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 22 | #define RET_FUNC_NOT_SUPPORTED 0x81 |
| 23 | #define RET_BAD_VENDOR_ID 0x83 |
| 24 | #define RET_DEVICE_NOT_FOUND 0x86 |
| 25 | #define RET_BUFFER_TOO_SMALL 0x89 |
| 26 | |
| 27 | // installation check |
| 28 | static void |
| 29 | handle_1ab101(struct bregs *regs) |
| 30 | { |
Kevin O'Connor | cde7a58 | 2008-08-17 11:08:46 -0400 | [diff] [blame] | 31 | regs->al = 0x01; // Flags - "Config Mechanism #1" supported. |
| 32 | regs->bx = 0x0210; // PCI version 2.10 |
Kevin O'Connor | 0f654a9 | 2011-07-02 14:32:11 -0400 | [diff] [blame] | 33 | regs->cl = GET_GLOBAL(MaxPCIBus); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 34 | regs->edx = 0x20494350; // "PCI " |
Kevin O'Connor | 47c8e31 | 2011-07-10 22:57:32 -0400 | [diff] [blame] | 35 | regs->edi = (u32)entry_pcibios32 + BUILD_BIOS_ADDR; |
Kevin O'Connor | cde7a58 | 2008-08-17 11:08:46 -0400 | [diff] [blame] | 36 | set_code_success(regs); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 37 | } |
| 38 | |
| 39 | // find pci device |
| 40 | static void |
| 41 | handle_1ab102(struct bregs *regs) |
| 42 | { |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 43 | u32 id = (regs->cx << 16) | regs->dx; |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 44 | int count = regs->si; |
Kevin O'Connor | 0f654a9 | 2011-07-02 14:32:11 -0400 | [diff] [blame] | 45 | int bus = -1; |
| 46 | while (bus < GET_GLOBAL(MaxPCIBus)) { |
| 47 | bus++; |
Kevin O'Connor | 2b333e4 | 2011-07-02 14:49:41 -0400 | [diff] [blame] | 48 | int bdf; |
| 49 | foreachbdf(bdf, bus) { |
Kevin O'Connor | 0f654a9 | 2011-07-02 14:32:11 -0400 | [diff] [blame] | 50 | u32 v = pci_config_readl(bdf, PCI_VENDOR_ID); |
| 51 | if (v != id) |
| 52 | continue; |
| 53 | if (count--) |
| 54 | continue; |
| 55 | regs->bx = bdf; |
| 56 | set_code_success(regs); |
| 57 | return; |
| 58 | } |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 59 | } |
Kevin O'Connor | dfefeb5 | 2009-12-13 13:04:17 -0500 | [diff] [blame] | 60 | set_code_invalid(regs, RET_DEVICE_NOT_FOUND); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | // find class code |
| 64 | static void |
| 65 | handle_1ab103(struct bregs *regs) |
| 66 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 67 | int count = regs->si; |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 68 | u32 classprog = regs->ecx; |
Kevin O'Connor | 0f654a9 | 2011-07-02 14:32:11 -0400 | [diff] [blame] | 69 | int bus = -1; |
| 70 | while (bus < GET_GLOBAL(MaxPCIBus)) { |
| 71 | bus++; |
Kevin O'Connor | 2b333e4 | 2011-07-02 14:49:41 -0400 | [diff] [blame] | 72 | int bdf; |
| 73 | foreachbdf(bdf, bus) { |
Kevin O'Connor | 0f654a9 | 2011-07-02 14:32:11 -0400 | [diff] [blame] | 74 | u32 v = pci_config_readl(bdf, PCI_CLASS_REVISION); |
| 75 | if ((v>>8) != classprog) |
| 76 | continue; |
| 77 | if (count--) |
| 78 | continue; |
| 79 | regs->bx = bdf; |
| 80 | set_code_success(regs); |
| 81 | return; |
| 82 | } |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 83 | } |
Kevin O'Connor | dfefeb5 | 2009-12-13 13:04:17 -0500 | [diff] [blame] | 84 | set_code_invalid(regs, RET_DEVICE_NOT_FOUND); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 85 | } |
| 86 | |
| 87 | // read configuration byte |
| 88 | static void |
| 89 | handle_1ab108(struct bregs *regs) |
| 90 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 91 | regs->cl = pci_config_readb(regs->bx, regs->di); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 92 | set_code_success(regs); |
| 93 | } |
| 94 | |
| 95 | // read configuration word |
| 96 | static void |
| 97 | handle_1ab109(struct bregs *regs) |
| 98 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 99 | regs->cx = pci_config_readw(regs->bx, regs->di); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 100 | set_code_success(regs); |
| 101 | } |
| 102 | |
| 103 | // read configuration dword |
| 104 | static void |
| 105 | handle_1ab10a(struct bregs *regs) |
| 106 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 107 | regs->ecx = pci_config_readl(regs->bx, regs->di); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 108 | set_code_success(regs); |
| 109 | } |
| 110 | |
| 111 | // write configuration byte |
| 112 | static void |
| 113 | handle_1ab10b(struct bregs *regs) |
| 114 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 115 | pci_config_writeb(regs->bx, regs->di, regs->cl); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 116 | set_code_success(regs); |
| 117 | } |
| 118 | |
| 119 | // write configuration word |
| 120 | static void |
| 121 | handle_1ab10c(struct bregs *regs) |
| 122 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 123 | pci_config_writew(regs->bx, regs->di, regs->cx); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 124 | set_code_success(regs); |
| 125 | } |
| 126 | |
| 127 | // write configuration dword |
| 128 | static void |
| 129 | handle_1ab10d(struct bregs *regs) |
| 130 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 131 | pci_config_writel(regs->bx, regs->di, regs->ecx); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 132 | set_code_success(regs); |
| 133 | } |
| 134 | |
| 135 | // get irq routing options |
| 136 | static void |
| 137 | handle_1ab10e(struct bregs *regs) |
| 138 | { |
Kevin O'Connor | bfa02cd | 2012-06-09 13:36:45 -0400 | [diff] [blame] | 139 | struct pir_header *pirtable_gf = GET_GLOBAL(PirAddr); |
| 140 | if (! pirtable_gf) { |
Kevin O'Connor | dfefeb5 | 2009-12-13 13:04:17 -0500 | [diff] [blame] | 141 | set_code_invalid(regs, RET_FUNC_NOT_SUPPORTED); |
Kevin O'Connor | 0f803e4 | 2008-05-24 23:07:16 -0400 | [diff] [blame] | 142 | return; |
| 143 | } |
Kevin O'Connor | bfa02cd | 2012-06-09 13:36:45 -0400 | [diff] [blame] | 144 | struct pir_header *pirtable_g = GLOBALFLAT2GLOBAL(pirtable_gf); |
Kevin O'Connor | 0f803e4 | 2008-05-24 23:07:16 -0400 | [diff] [blame] | 145 | |
Kevin O'Connor | d99908e | 2009-01-21 19:14:49 -0500 | [diff] [blame] | 146 | struct param_s { |
| 147 | u16 size; |
| 148 | u16 buf_off; |
| 149 | u16 buf_seg; |
| 150 | } *param_far = (void*)(regs->di+0); |
| 151 | |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 152 | // Validate and update size. |
Kevin O'Connor | d99908e | 2009-01-21 19:14:49 -0500 | [diff] [blame] | 153 | u16 bufsize = GET_FARVAR(regs->es, param_far->size); |
| 154 | u16 pirsize = GET_GLOBAL(pirtable_g->size) - sizeof(struct pir_header); |
| 155 | SET_FARVAR(regs->es, param_far->size, pirsize); |
| 156 | if (bufsize < pirsize) { |
Kevin O'Connor | dfefeb5 | 2009-12-13 13:04:17 -0500 | [diff] [blame] | 157 | set_code_invalid(regs, RET_BUFFER_TOO_SMALL); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 158 | return; |
| 159 | } |
| 160 | |
| 161 | // Get dest buffer. |
Kevin O'Connor | d99908e | 2009-01-21 19:14:49 -0500 | [diff] [blame] | 162 | void *buf_far = (void*)(GET_FARVAR(regs->es, param_far->buf_off)+0); |
| 163 | u16 buf_seg = GET_FARVAR(regs->es, param_far->buf_seg); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 164 | |
| 165 | // Memcpy pir table slots to dest buffer. |
Kevin O'Connor | d99908e | 2009-01-21 19:14:49 -0500 | [diff] [blame] | 166 | memcpy_far(buf_seg, buf_far |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 167 | , get_global_seg() |
| 168 | , (void*)(pirtable_g->slots) + get_global_offset() |
Kevin O'Connor | 8b267cb | 2009-01-19 19:25:21 -0500 | [diff] [blame] | 169 | , pirsize); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 170 | |
| 171 | // XXX - bochs bios sets bx to (1 << 9) | (1 << 11) |
Kevin O'Connor | 51358db | 2008-12-28 21:50:29 -0500 | [diff] [blame] | 172 | regs->bx = GET_GLOBAL(pirtable_g->exclusive_irqs); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 173 | set_code_success(regs); |
| 174 | } |
| 175 | |
| 176 | static void |
| 177 | handle_1ab1XX(struct bregs *regs) |
| 178 | { |
Kevin O'Connor | dfefeb5 | 2009-12-13 13:04:17 -0500 | [diff] [blame] | 179 | set_code_unimplemented(regs, RET_FUNC_NOT_SUPPORTED); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 180 | } |
| 181 | |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 182 | void |
| 183 | handle_1ab1(struct bregs *regs) |
| 184 | { |
| 185 | //debug_stub(regs); |
| 186 | |
| 187 | if (! CONFIG_PCIBIOS) { |
Kevin O'Connor | dfefeb5 | 2009-12-13 13:04:17 -0500 | [diff] [blame] | 188 | set_invalid(regs); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 189 | return; |
| 190 | } |
| 191 | |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 192 | switch (regs->al) { |
| 193 | case 0x01: handle_1ab101(regs); break; |
| 194 | case 0x02: handle_1ab102(regs); break; |
| 195 | case 0x03: handle_1ab103(regs); break; |
| 196 | case 0x08: handle_1ab108(regs); break; |
| 197 | case 0x09: handle_1ab109(regs); break; |
| 198 | case 0x0a: handle_1ab10a(regs); break; |
| 199 | case 0x0b: handle_1ab10b(regs); break; |
| 200 | case 0x0c: handle_1ab10c(regs); break; |
| 201 | case 0x0d: handle_1ab10d(regs); break; |
| 202 | case 0x0e: handle_1ab10e(regs); break; |
| 203 | default: handle_1ab1XX(regs); break; |
| 204 | } |
| 205 | } |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 206 | |
Kevin O'Connor | 922aa1b | 2013-03-02 04:02:11 -0500 | [diff] [blame] | 207 | // Entry point for pci bios functions. |
| 208 | void VISIBLE16 VISIBLE32SEG |
| 209 | handle_pcibios(struct bregs *regs) |
| 210 | { |
| 211 | debug_enter(regs, DEBUG_HDL_pcibios); |
| 212 | handle_1ab1(regs); |
| 213 | } |
| 214 | |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 215 | |
| 216 | /**************************************************************** |
| 217 | * 32bit interface |
| 218 | ****************************************************************/ |
| 219 | |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 220 | struct bios32_s { |
| 221 | u32 signature; |
| 222 | u32 entry; |
| 223 | u8 version; |
| 224 | u8 length; |
| 225 | u8 checksum; |
| 226 | u8 reserved[5]; |
| 227 | } PACKED; |
| 228 | |
Kevin O'Connor | 89a2f96 | 2013-02-18 23:36:03 -0500 | [diff] [blame] | 229 | struct bios32_s BIOS32HEADER __aligned(16) VARFSEG = { |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 230 | .signature = 0x5f32335f, // _32_ |
| 231 | .length = sizeof(BIOS32HEADER) / 16, |
| 232 | }; |
| 233 | |
| 234 | void |
Kevin O'Connor | d83c87b | 2013-01-21 01:14:12 -0500 | [diff] [blame] | 235 | bios32_init(void) |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 236 | { |
| 237 | dprintf(3, "init bios32\n"); |
| 238 | |
Kevin O'Connor | 47c8e31 | 2011-07-10 22:57:32 -0400 | [diff] [blame] | 239 | BIOS32HEADER.entry = (u32)entry_bios32; |
Kevin O'Connor | 871e0a0 | 2009-12-30 12:14:53 -0500 | [diff] [blame] | 240 | BIOS32HEADER.checksum -= checksum(&BIOS32HEADER, sizeof(BIOS32HEADER)); |
| 241 | } |