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 |
| 12 | #include "biosvar.h" // GET_EBDA |
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 | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 15 | #define RET_FUNC_NOT_SUPPORTED 0x81 |
| 16 | #define RET_BAD_VENDOR_ID 0x83 |
| 17 | #define RET_DEVICE_NOT_FOUND 0x86 |
| 18 | #define RET_BUFFER_TOO_SMALL 0x89 |
| 19 | |
| 20 | // installation check |
| 21 | static void |
| 22 | handle_1ab101(struct bregs *regs) |
| 23 | { |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 24 | // Find max bus. |
| 25 | int bdf, max; |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 26 | foreachpci(bdf, max) { |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 27 | } |
| 28 | |
Kevin O'Connor | cde7a58 | 2008-08-17 11:08:46 -0400 | [diff] [blame] | 29 | regs->al = 0x01; // Flags - "Config Mechanism #1" supported. |
| 30 | regs->bx = 0x0210; // PCI version 2.10 |
Kevin O'Connor | e633832 | 2008-11-29 20:31:49 -0500 | [diff] [blame] | 31 | regs->cl = pci_bdf_to_bus(max - 1); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 32 | regs->edx = 0x20494350; // "PCI " |
| 33 | // XXX - bochs bios code sets edi to point to 32bit code - but no |
| 34 | // reference to this in spec. |
Kevin O'Connor | cde7a58 | 2008-08-17 11:08:46 -0400 | [diff] [blame] | 35 | set_code_success(regs); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 36 | } |
| 37 | |
| 38 | // find pci device |
| 39 | static void |
| 40 | handle_1ab102(struct bregs *regs) |
| 41 | { |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 42 | u32 id = (regs->cx << 16) | regs->dx; |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 43 | int count = regs->si; |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 44 | int bdf, max; |
| 45 | foreachpci(bdf, max) { |
| 46 | u32 v = pci_config_readl(bdf, PCI_VENDOR_ID); |
| 47 | if (v != id) |
| 48 | continue; |
| 49 | if (count--) |
| 50 | continue; |
| 51 | regs->bx = bdf; |
| 52 | set_code_success(regs); |
| 53 | return; |
| 54 | } |
| 55 | set_code_fail(regs, RET_DEVICE_NOT_FOUND); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | // find class code |
| 59 | static void |
| 60 | handle_1ab103(struct bregs *regs) |
| 61 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 62 | int count = regs->si; |
Kevin O'Connor | 4132e02 | 2008-12-04 19:39:10 -0500 | [diff] [blame] | 63 | u32 classprog = regs->ecx; |
| 64 | int bdf, max; |
| 65 | foreachpci(bdf, max) { |
| 66 | u32 v = pci_config_readl(bdf, PCI_CLASS_REVISION); |
| 67 | if ((v>>8) != classprog) |
| 68 | continue; |
| 69 | if (count--) |
| 70 | continue; |
| 71 | regs->bx = bdf; |
| 72 | set_code_success(regs); |
| 73 | return; |
| 74 | } |
| 75 | set_code_fail(regs, RET_DEVICE_NOT_FOUND); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | // read configuration byte |
| 79 | static void |
| 80 | handle_1ab108(struct bregs *regs) |
| 81 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 82 | regs->cl = pci_config_readb(regs->bx, regs->di); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 83 | set_code_success(regs); |
| 84 | } |
| 85 | |
| 86 | // read configuration word |
| 87 | static void |
| 88 | handle_1ab109(struct bregs *regs) |
| 89 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 90 | regs->cx = pci_config_readw(regs->bx, regs->di); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 91 | set_code_success(regs); |
| 92 | } |
| 93 | |
| 94 | // read configuration dword |
| 95 | static void |
| 96 | handle_1ab10a(struct bregs *regs) |
| 97 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 98 | regs->ecx = pci_config_readl(regs->bx, regs->di); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 99 | set_code_success(regs); |
| 100 | } |
| 101 | |
| 102 | // write configuration byte |
| 103 | static void |
| 104 | handle_1ab10b(struct bregs *regs) |
| 105 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 106 | pci_config_writeb(regs->bx, regs->di, regs->cl); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 107 | set_code_success(regs); |
| 108 | } |
| 109 | |
| 110 | // write configuration word |
| 111 | static void |
| 112 | handle_1ab10c(struct bregs *regs) |
| 113 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 114 | pci_config_writew(regs->bx, regs->di, regs->cx); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 115 | set_code_success(regs); |
| 116 | } |
| 117 | |
| 118 | // write configuration dword |
| 119 | static void |
| 120 | handle_1ab10d(struct bregs *regs) |
| 121 | { |
Kevin O'Connor | be19cdc | 2008-11-09 15:33:47 -0500 | [diff] [blame] | 122 | pci_config_writel(regs->bx, regs->di, regs->ecx); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 123 | set_code_success(regs); |
| 124 | } |
| 125 | |
| 126 | // get irq routing options |
| 127 | static void |
| 128 | handle_1ab10e(struct bregs *regs) |
| 129 | { |
Kevin O'Connor | 51358db | 2008-12-28 21:50:29 -0500 | [diff] [blame] | 130 | struct pir_header *pirtable_g = (void*)(GET_GLOBAL(PirOffset) + 0); |
| 131 | if (! pirtable_g) { |
Kevin O'Connor | 0f803e4 | 2008-05-24 23:07:16 -0400 | [diff] [blame] | 132 | set_code_fail(regs, RET_FUNC_NOT_SUPPORTED); |
| 133 | return; |
| 134 | } |
| 135 | |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 136 | // Validate and update size. |
| 137 | u16 size = GET_FARVAR(regs->es, *(u16*)(regs->di+0)); |
Kevin O'Connor | 51358db | 2008-12-28 21:50:29 -0500 | [diff] [blame] | 138 | u16 pirsize = (GET_GLOBAL(pirtable_g->size) |
Kevin O'Connor | d25810a | 2008-06-12 22:16:35 -0400 | [diff] [blame] | 139 | - sizeof(struct pir_header)); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 140 | SET_FARVAR(regs->es, *(u16*)(regs->di+0), pirsize); |
| 141 | if (size < pirsize) { |
| 142 | set_code_fail(regs, RET_BUFFER_TOO_SMALL); |
| 143 | return; |
| 144 | } |
| 145 | |
| 146 | // Get dest buffer. |
Kevin O'Connor | d25810a | 2008-06-12 22:16:35 -0400 | [diff] [blame] | 147 | u16 d = (GET_FARVAR(regs->es, *(u16*)(regs->di+2)) + 0); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 148 | u16 destseg = GET_FARVAR(regs->es, *(u16*)(regs->di+4)); |
| 149 | |
| 150 | // Memcpy pir table slots to dest buffer. |
Kevin O'Connor | 51358db | 2008-12-28 21:50:29 -0500 | [diff] [blame] | 151 | memcpy_far(MAKE_FARPTR(destseg, d) |
Kevin O'Connor | dfc18fc | 2009-01-19 12:53:10 -0500 | [diff] [blame^] | 152 | , MAKE_FARPTR(SEG_BIOS, pirtable_g->slots) |
Kevin O'Connor | 51358db | 2008-12-28 21:50:29 -0500 | [diff] [blame] | 153 | , pirsize); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 154 | |
| 155 | // XXX - bochs bios sets bx to (1 << 9) | (1 << 11) |
Kevin O'Connor | 51358db | 2008-12-28 21:50:29 -0500 | [diff] [blame] | 156 | regs->bx = GET_GLOBAL(pirtable_g->exclusive_irqs); |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 157 | set_code_success(regs); |
| 158 | } |
| 159 | |
| 160 | static void |
| 161 | handle_1ab1XX(struct bregs *regs) |
| 162 | { |
| 163 | set_code_fail(regs, RET_FUNC_NOT_SUPPORTED); |
| 164 | } |
| 165 | |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 166 | void |
| 167 | handle_1ab1(struct bregs *regs) |
| 168 | { |
| 169 | //debug_stub(regs); |
| 170 | |
| 171 | if (! CONFIG_PCIBIOS) { |
| 172 | set_fail(regs); |
| 173 | return; |
| 174 | } |
| 175 | |
Kevin O'Connor | a0dc296 | 2008-03-16 14:29:32 -0400 | [diff] [blame] | 176 | switch (regs->al) { |
| 177 | case 0x01: handle_1ab101(regs); break; |
| 178 | case 0x02: handle_1ab102(regs); break; |
| 179 | case 0x03: handle_1ab103(regs); break; |
| 180 | case 0x08: handle_1ab108(regs); break; |
| 181 | case 0x09: handle_1ab109(regs); break; |
| 182 | case 0x0a: handle_1ab10a(regs); break; |
| 183 | case 0x0b: handle_1ab10b(regs); break; |
| 184 | case 0x0c: handle_1ab10c(regs); break; |
| 185 | case 0x0d: handle_1ab10d(regs); break; |
| 186 | case 0x0e: handle_1ab10e(regs); break; |
| 187 | default: handle_1ab1XX(regs); break; |
| 188 | } |
| 189 | } |