| 1 | /* |
| 2 | * XFree86 int10 module |
| 3 | * execute BIOS int 10h calls in x86 real mode environment |
| 4 | * Copyright 1999 Egbert Eich |
| 5 | */ |
| 6 | #ifdef HAVE_XORG_CONFIG_H |
| 7 | #include <xorg-config.h> |
| 8 | #endif |
| 9 | |
| 10 | #include <string.h> |
| 11 | #include <stdlib.h> |
| 12 | |
| 13 | #include "xf86.h" |
| 14 | #include "xf86_OSproc.h" |
| 15 | #include "compiler.h" |
| 16 | #include "xf86Pci.h" |
| 17 | #define _INT10_PRIVATE |
| 18 | #if 0 |
| 19 | #include "int10Defines.h" |
| 20 | #endif |
| 21 | #include "xf86int10.h" |
| 22 | |
| 23 | #define REG pInt |
| 24 | |
| 25 | typedef enum { |
| 26 | OPT_NOINT10, |
| 27 | OPT_INIT_PRIMARY, |
| 28 | } INT10Opts; |
| 29 | |
| 30 | static const OptionInfoRec INT10Options[] = { |
| 31 | {OPT_NOINT10, "NoINT10", OPTV_BOOLEAN, {0}, FALSE}, |
| 32 | {OPT_INIT_PRIMARY, "InitPrimary", OPTV_BOOLEAN, {0}, FALSE}, |
| 33 | {-1, NULL, OPTV_NONE, {0}, FALSE}, |
| 34 | }; |
| 35 | |
| 36 | #ifdef DEBUG |
| 37 | void |
| 38 | dprint(unsigned long start, unsigned long size) |
| 39 | { |
| 40 | int i, j; |
| 41 | char *c = (char *) start; |
| 42 | |
| 43 | for (j = 0; j < (size >> 4); j++) { |
| 44 | char *d = c; |
| 45 | |
| 46 | ErrorF("\n0x%lx: ", (unsigned long) c); |
| 47 | for (i = 0; i < 16; i++) |
| 48 | ErrorF("%2.2x ", (unsigned char) (*(c++))); |
| 49 | c = d; |
| 50 | for (i = 0; i < 16; i++) { |
| 51 | ErrorF("%c", ((((CARD8) (*c)) > 32) && (((CARD8) (*c)) < 128)) ? |
| 52 | (unsigned char) (*(c)) : '.'); |
| 53 | c++; |
| 54 | } |
| 55 | } |
| 56 | ErrorF("\n"); |
| 57 | } |
| 58 | #endif |
| 59 | |
| 60 | #ifndef _PC |
| 61 | /* |
| 62 | * here we are really paranoid about faking a "real" |
| 63 | * BIOS. Most of this information was pulled from |
| 64 | * dosemu. |
| 65 | */ |
| 66 | void |
| 67 | setup_int_vect(xf86Int10InfoPtr pInt) |
| 68 | { |
| 69 | int i; |
| 70 | |
| 71 | /* let the int vects point to the SYS_BIOS seg */ |
| 72 | for (i = 0; i < 0x80; i++) { |
| 73 | MEM_WW(pInt, i << 2, 0); |
| 74 | MEM_WW(pInt, (i << 2) + 2, SYS_BIOS >> 4); |
| 75 | } |
| 76 | |
| 77 | reset_int_vect(pInt); |
| 78 | /* font tables default location (int 1F) */ |
| 79 | MEM_WW(pInt, 0x1f << 2, 0xfa6e); |
| 80 | |
| 81 | /* int 11 default location (Get Equipment Configuration) */ |
| 82 | MEM_WW(pInt, 0x11 << 2, 0xf84d); |
| 83 | /* int 12 default location (Get Conventional Memory Size) */ |
| 84 | MEM_WW(pInt, 0x12 << 2, 0xf841); |
| 85 | /* int 15 default location (I/O System Extensions) */ |
| 86 | MEM_WW(pInt, 0x15 << 2, 0xf859); |
| 87 | /* int 1A default location (RTC, PCI and others) */ |
| 88 | MEM_WW(pInt, 0x1a << 2, 0xff6e); |
| 89 | /* int 05 default location (Bound Exceeded) */ |
| 90 | MEM_WW(pInt, 0x05 << 2, 0xff54); |
| 91 | /* int 08 default location (Double Fault) */ |
| 92 | MEM_WW(pInt, 0x08 << 2, 0xfea5); |
| 93 | /* int 13 default location (Disk) */ |
| 94 | MEM_WW(pInt, 0x13 << 2, 0xec59); |
| 95 | /* int 0E default location (Page Fault) */ |
| 96 | MEM_WW(pInt, 0x0e << 2, 0xef57); |
| 97 | /* int 17 default location (Parallel Port) */ |
| 98 | MEM_WW(pInt, 0x17 << 2, 0xefd2); |
| 99 | /* fdd table default location (int 1e) */ |
| 100 | MEM_WW(pInt, 0x1e << 2, 0xefc7); |
| 101 | |
| 102 | /* Set Equipment flag to VGA */ |
| 103 | i = MEM_RB(pInt, 0x0410) & 0xCF; |
| 104 | MEM_WB(pInt, 0x0410, i); |
| 105 | /* XXX Perhaps setup more of the BDA here. See also int42(0x00). */ |
| 106 | } |
| 107 | #endif |
| 108 | |
| 109 | int |
| 110 | setup_system_bios(void *base_addr) |
| 111 | { |
| 112 | char *base = (char *) base_addr; |
| 113 | |
| 114 | /* |
| 115 | * we trap the "industry standard entry points" to the BIOS |
| 116 | * and all other locations by filling them with "hlt" |
| 117 | * TODO: implement hlt-handler for these |
| 118 | */ |
| 119 | memset(base, 0xf4, 0x10000); |
| 120 | |
| 121 | /* set bios date */ |
| 122 | strcpy(base + 0x0FFF5, "06/11/99"); |
| 123 | /* set up eisa ident string */ |
| 124 | strcpy(base + 0x0FFD9, "PCI_ISA"); |
| 125 | /* write system model id for IBM-AT */ |
| 126 | *((unsigned char *) (base + 0x0FFFE)) = 0xfc; |
| 127 | |
| 128 | return 1; |
| 129 | } |
| 130 | |
| 131 | void |
| 132 | reset_int_vect(xf86Int10InfoPtr pInt) |
| 133 | { |
| 134 | /* |
| 135 | * This table is normally located at 0xF000:0xF0A4. However, int 0x42, |
| 136 | * function 0 (Mode Set) expects it (or a copy) somewhere in the bottom |
| 137 | * 64kB. Note that because this data doesn't survive POST, int 0x42 should |
| 138 | * only be used during EGA/VGA BIOS initialisation. |
| 139 | */ |
| 140 | static const CARD8 VideoParms[] = { |
| 141 | /* Timing for modes 0x00 & 0x01 */ |
| 142 | 0x38, 0x28, 0x2d, 0x0a, 0x1f, 0x06, 0x19, 0x1c, |
| 143 | 0x02, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, |
| 144 | /* Timing for modes 0x02 & 0x03 */ |
| 145 | 0x71, 0x50, 0x5a, 0x0a, 0x1f, 0x06, 0x19, 0x1c, |
| 146 | 0x02, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, |
| 147 | /* Timing for modes 0x04, 0x05 & 0x06 */ |
| 148 | 0x38, 0x28, 0x2d, 0x0a, 0x7f, 0x06, 0x64, 0x70, |
| 149 | 0x02, 0x01, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, |
| 150 | /* Timing for mode 0x07 */ |
| 151 | 0x61, 0x50, 0x52, 0x0f, 0x19, 0x06, 0x19, 0x19, |
| 152 | 0x02, 0x0d, 0x0b, 0x0c, 0x00, 0x00, 0x00, 0x00, |
| 153 | /* Display page lengths in little endian order */ |
| 154 | 0x00, 0x08, /* Modes 0x00 and 0x01 */ |
| 155 | 0x00, 0x10, /* Modes 0x02 and 0x03 */ |
| 156 | 0x00, 0x40, /* Modes 0x04 and 0x05 */ |
| 157 | 0x00, 0x40, /* Modes 0x06 and 0x07 */ |
| 158 | /* Number of columns for each mode */ |
| 159 | 40, 40, 80, 80, 40, 40, 80, 80, |
| 160 | /* CGA Mode register value for each mode */ |
| 161 | 0x2c, 0x28, 0x2d, 0x29, 0x2a, 0x2e, 0x1e, 0x29, |
| 162 | /* Padding */ |
| 163 | 0x00, 0x00, 0x00, 0x00 |
| 164 | }; |
| 165 | int i; |
| 166 | |
| 167 | for (i = 0; i < sizeof(VideoParms); i++) |
| 168 | MEM_WB(pInt, i + (0x1000 - sizeof(VideoParms)), VideoParms[i]); |
| 169 | MEM_WW(pInt, 0x1d << 2, 0x1000 - sizeof(VideoParms)); |
| 170 | MEM_WW(pInt, (0x1d << 2) + 2, 0); |
| 171 | |
| 172 | MEM_WW(pInt, 0x10 << 2, 0xf065); |
| 173 | MEM_WW(pInt, (0x10 << 2) + 2, SYS_BIOS >> 4); |
| 174 | MEM_WW(pInt, 0x42 << 2, 0xf065); |
| 175 | MEM_WW(pInt, (0x42 << 2) + 2, SYS_BIOS >> 4); |
| 176 | MEM_WW(pInt, 0x6D << 2, 0xf065); |
| 177 | MEM_WW(pInt, (0x6D << 2) + 2, SYS_BIOS >> 4); |
| 178 | } |
| 179 | |
| 180 | void |
| 181 | set_return_trap(xf86Int10InfoPtr pInt) |
| 182 | { |
| 183 | /* |
| 184 | * Here we set the exit condition: We return when we encounter |
| 185 | * 'hlt' (=0xf4), which we locate at address 0x600 in x86 memory. |
| 186 | */ |
| 187 | MEM_WB(pInt, 0x0600, 0xf4); |
| 188 | |
| 189 | /* |
| 190 | * Allocate a segment for the stack |
| 191 | */ |
| 192 | xf86Int10AllocPages(pInt, 1, &pInt->stackseg); |
| 193 | } |
| 194 | |
| 195 | void * |
| 196 | xf86HandleInt10Options(ScrnInfoPtr pScrn, int entityIndex) |
| 197 | { |
| 198 | EntityInfoPtr pEnt = xf86GetEntityInfo(entityIndex); |
| 199 | OptionInfoPtr options = NULL; |
| 200 | |
| 201 | if (pEnt->device) { |
| 202 | pointer configOptions = NULL; |
| 203 | |
| 204 | /* Check if xf86CollectOptions() has already been called */ |
| 205 | if (((pEnt->index < 0) || |
| 206 | !pScrn || !(configOptions = pScrn->options)) && pEnt->device) |
| 207 | configOptions = pEnt->device->options; |
| 208 | |
| 209 | if (configOptions) { |
| 210 | if (!(options = (OptionInfoPtr) malloc(sizeof(INT10Options)))) |
| 211 | return NULL; |
| 212 | |
| 213 | (void) memcpy(options, INT10Options, sizeof(INT10Options)); |
| 214 | xf86ProcessOptions(pScrn->scrnIndex, configOptions, options); |
| 215 | } |
| 216 | } |
| 217 | free(pEnt); |
| 218 | |
| 219 | return options; |
| 220 | } |
| 221 | |
| 222 | Bool |
| 223 | int10skip(const void *options) |
| 224 | { |
| 225 | Bool noint10 = FALSE; |
| 226 | |
| 227 | if (!options) |
| 228 | return FALSE; |
| 229 | |
| 230 | xf86GetOptValBool(options, OPT_NOINT10, &noint10); |
| 231 | return noint10; |
| 232 | } |
| 233 | |
| 234 | Bool |
| 235 | int10_check_bios(int scrnIndex, int codeSeg, const unsigned char *vbiosMem) |
| 236 | { |
| 237 | int size; |
| 238 | |
| 239 | if ((codeSeg & 0x1f) || /* Not 512-byte aligned otherwise */ |
| 240 | ((codeSeg << 4) < V_BIOS) || ((codeSeg << 4) >= SYS_SIZE)) |
| 241 | return FALSE; |
| 242 | |
| 243 | if ((*vbiosMem != 0x55) || (*(vbiosMem + 1) != 0xAA) || !*(vbiosMem + 2)) |
| 244 | return FALSE; |
| 245 | |
| 246 | size = *(vbiosMem + 2) * 512; |
| 247 | |
| 248 | if ((size + (codeSeg << 4)) > SYS_SIZE) |
| 249 | return FALSE; |
| 250 | |
| 251 | if (bios_checksum(vbiosMem, size)) |
| 252 | xf86DrvMsg(scrnIndex, X_INFO, "Bad V_BIOS checksum\n"); |
| 253 | |
| 254 | return TRUE; |
| 255 | } |
| 256 | |
| 257 | Bool |
| 258 | initPrimary(const void *options) |
| 259 | { |
| 260 | Bool initPrimary = FALSE; |
| 261 | |
| 262 | if (!options) |
| 263 | return FALSE; |
| 264 | |
| 265 | xf86GetOptValBool(options, OPT_INIT_PRIMARY, &initPrimary); |
| 266 | return initPrimary; |
| 267 | } |
| 268 | |
| 269 | BusType |
| 270 | xf86int10GetBiosLocationType(const xf86Int10InfoPtr pInt) |
| 271 | { |
| 272 | BusType location_type; |
| 273 | |
| 274 | EntityInfoPtr pEnt = xf86GetEntityInfo(pInt->entityIndex); |
| 275 | |
| 276 | location_type = pEnt->location.type; |
| 277 | free(pEnt); |
| 278 | |
| 279 | return location_type; |
| 280 | } |
| 281 | |
| 282 | #define CHECK_V_SEGMENT_RANGE(x) \ |
| 283 | if (((x) << 4) < V_BIOS) { \ |
| 284 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, \ |
| 285 | "V_BIOS address 0x%lx out of range\n", \ |
| 286 | (unsigned long)(x) << 4); \ |
| 287 | return FALSE; \ |
| 288 | } |
| 289 | |
| 290 | Bool |
| 291 | xf86int10GetBiosSegment(xf86Int10InfoPtr pInt, void *base) |
| 292 | { |
| 293 | unsigned i; |
| 294 | int cs = ~0; |
| 295 | int segments[4]; |
| 296 | |
| 297 | segments[0] = MEM_RW(pInt, (0x10 << 2) + 2); |
| 298 | segments[1] = MEM_RW(pInt, (0x42 << 2) + 2); |
| 299 | segments[2] = V_BIOS >> 4; |
| 300 | segments[3] = ~0; |
| 301 | |
| 302 | for (i = 0; segments[i] != ~0; i++) { |
| 303 | unsigned char *vbiosMem; |
| 304 | |
| 305 | cs = segments[i]; |
| 306 | |
| 307 | CHECK_V_SEGMENT_RANGE(cs); |
| 308 | vbiosMem = (unsigned char *) base + (cs << 4); |
| 309 | if (int10_check_bios(pInt->pScrn->scrnIndex, cs, vbiosMem)) { |
| 310 | break; |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | if (segments[i] == ~0) { |
| 315 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_ERROR, "No V_BIOS found\n"); |
| 316 | return FALSE; |
| 317 | } |
| 318 | |
| 319 | xf86DrvMsg(pInt->pScrn->scrnIndex, X_INFO, "Primary V_BIOS segment is: 0x%lx\n", |
| 320 | (unsigned long) cs); |
| 321 | |
| 322 | pInt->BIOSseg = cs; |
| 323 | return TRUE; |
| 324 | } |