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