Commit 7e84c2498f0ff3999937d18d1e9abaa030400000
1 parent
e670b89e
full TSS support - IO map check support - conforming segment check fixes - iret in vm86 mode fix
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@450 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
465 additions
and
60 deletions
target-i386/helper.c
| @@ -126,6 +126,56 @@ void cpu_loop_exit(void) | @@ -126,6 +126,56 @@ void cpu_loop_exit(void) | ||
| 126 | longjmp(env->jmp_env, 1); | 126 | longjmp(env->jmp_env, 1); |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | +/* return non zero if error */ | ||
| 130 | +static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr, | ||
| 131 | + int selector) | ||
| 132 | +{ | ||
| 133 | + SegmentCache *dt; | ||
| 134 | + int index; | ||
| 135 | + uint8_t *ptr; | ||
| 136 | + | ||
| 137 | + if (selector & 0x4) | ||
| 138 | + dt = &env->ldt; | ||
| 139 | + else | ||
| 140 | + dt = &env->gdt; | ||
| 141 | + index = selector & ~7; | ||
| 142 | + if ((index + 7) > dt->limit) | ||
| 143 | + return -1; | ||
| 144 | + ptr = dt->base + index; | ||
| 145 | + *e1_ptr = ldl_kernel(ptr); | ||
| 146 | + *e2_ptr = ldl_kernel(ptr + 4); | ||
| 147 | + return 0; | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) | ||
| 151 | +{ | ||
| 152 | + unsigned int limit; | ||
| 153 | + limit = (e1 & 0xffff) | (e2 & 0x000f0000); | ||
| 154 | + if (e2 & DESC_G_MASK) | ||
| 155 | + limit = (limit << 12) | 0xfff; | ||
| 156 | + return limit; | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +static inline uint8_t *get_seg_base(uint32_t e1, uint32_t e2) | ||
| 160 | +{ | ||
| 161 | + return (uint8_t *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000)); | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2) | ||
| 165 | +{ | ||
| 166 | + sc->base = get_seg_base(e1, e2); | ||
| 167 | + sc->limit = get_seg_limit(e1, e2); | ||
| 168 | + sc->flags = e2; | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +/* init the segment cache in vm86 mode. */ | ||
| 172 | +static inline void load_seg_vm(int seg, int selector) | ||
| 173 | +{ | ||
| 174 | + selector &= 0xffff; | ||
| 175 | + cpu_x86_load_seg_cache(env, seg, selector, | ||
| 176 | + (uint8_t *)(selector << 4), 0xffff, 0); | ||
| 177 | +} | ||
| 178 | + | ||
| 129 | static inline void get_ss_esp_from_tss(uint32_t *ss_ptr, | 179 | static inline void get_ss_esp_from_tss(uint32_t *ss_ptr, |
| 130 | uint32_t *esp_ptr, int dpl) | 180 | uint32_t *esp_ptr, int dpl) |
| 131 | { | 181 | { |
| @@ -161,54 +211,322 @@ static inline void get_ss_esp_from_tss(uint32_t *ss_ptr, | @@ -161,54 +211,322 @@ static inline void get_ss_esp_from_tss(uint32_t *ss_ptr, | ||
| 161 | } | 211 | } |
| 162 | } | 212 | } |
| 163 | 213 | ||
| 164 | -/* return non zero if error */ | ||
| 165 | -static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr, | ||
| 166 | - int selector) | 214 | +/* XXX: merge with load_seg() */ |
| 215 | +static void tss_load_seg(int seg_reg, int selector) | ||
| 167 | { | 216 | { |
| 217 | + uint32_t e1, e2; | ||
| 218 | + int rpl, dpl, cpl; | ||
| 219 | + | ||
| 220 | + if ((selector & 0xfffc) != 0) { | ||
| 221 | + if (load_segment(&e1, &e2, selector) != 0) | ||
| 222 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 223 | + if (!(e2 & DESC_S_MASK)) | ||
| 224 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 225 | + rpl = selector & 3; | ||
| 226 | + dpl = (e2 >> DESC_DPL_SHIFT) & 3; | ||
| 227 | + cpl = env->hflags & HF_CPL_MASK; | ||
| 228 | + if (seg_reg == R_CS) { | ||
| 229 | + if (!(e2 & DESC_CS_MASK)) | ||
| 230 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 231 | + if (dpl != rpl) | ||
| 232 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 233 | + if ((e2 & DESC_C_MASK) && dpl > rpl) | ||
| 234 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 235 | + | ||
| 236 | + } else if (seg_reg == R_SS) { | ||
| 237 | + /* SS must be writable data */ | ||
| 238 | + if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) | ||
| 239 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 240 | + if (dpl != cpl || dpl != rpl) | ||
| 241 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 242 | + } else { | ||
| 243 | + /* not readable code */ | ||
| 244 | + if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) | ||
| 245 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 246 | + /* if data or non conforming code, checks the rights */ | ||
| 247 | + if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { | ||
| 248 | + if (dpl < cpl || dpl < rpl) | ||
| 249 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 250 | + } | ||
| 251 | + } | ||
| 252 | + if (!(e2 & DESC_P_MASK)) | ||
| 253 | + raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); | ||
| 254 | + cpu_x86_load_seg_cache(env, seg_reg, selector, | ||
| 255 | + get_seg_base(e1, e2), | ||
| 256 | + get_seg_limit(e1, e2), | ||
| 257 | + e2); | ||
| 258 | + } else { | ||
| 259 | + if (seg_reg == R_SS || seg_reg == R_CS) | ||
| 260 | + raise_exception_err(EXCP0A_TSS, selector & 0xfffc); | ||
| 261 | + } | ||
| 262 | +} | ||
| 263 | + | ||
| 264 | +#define SWITCH_TSS_JMP 0 | ||
| 265 | +#define SWITCH_TSS_IRET 1 | ||
| 266 | +#define SWITCH_TSS_CALL 2 | ||
| 267 | + | ||
| 268 | +/* XXX: restore CPU state in registers (PowerPC case) */ | ||
| 269 | +static void switch_tss(int tss_selector, | ||
| 270 | + uint32_t e1, uint32_t e2, int source) | ||
| 271 | +{ | ||
| 272 | + int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; | ||
| 273 | + uint8_t *tss_base; | ||
| 274 | + uint32_t new_regs[8], new_segs[6]; | ||
| 275 | + uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap; | ||
| 276 | + uint32_t old_eflags, eflags_mask; | ||
| 168 | SegmentCache *dt; | 277 | SegmentCache *dt; |
| 169 | int index; | 278 | int index; |
| 170 | uint8_t *ptr; | 279 | uint8_t *ptr; |
| 171 | 280 | ||
| 172 | - if (selector & 0x4) | ||
| 173 | - dt = &env->ldt; | 281 | + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; |
| 282 | + | ||
| 283 | + /* if task gate, we read the TSS segment and we load it */ | ||
| 284 | + if (type == 5) { | ||
| 285 | + if (!(e2 & DESC_P_MASK)) | ||
| 286 | + raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc); | ||
| 287 | + tss_selector = e1 >> 16; | ||
| 288 | + if (tss_selector & 4) | ||
| 289 | + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); | ||
| 290 | + if (load_segment(&e1, &e2, tss_selector) != 0) | ||
| 291 | + raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); | ||
| 292 | + if (e2 & DESC_S_MASK) | ||
| 293 | + raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); | ||
| 294 | + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; | ||
| 295 | + if ((type & 7) != 1) | ||
| 296 | + raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc); | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + if (!(e2 & DESC_P_MASK)) | ||
| 300 | + raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc); | ||
| 301 | + | ||
| 302 | + if (type & 8) | ||
| 303 | + tss_limit_max = 103; | ||
| 174 | else | 304 | else |
| 175 | - dt = &env->gdt; | ||
| 176 | - index = selector & ~7; | 305 | + tss_limit_max = 43; |
| 306 | + tss_limit = get_seg_limit(e1, e2); | ||
| 307 | + tss_base = get_seg_base(e1, e2); | ||
| 308 | + if ((tss_selector & 4) != 0 || | ||
| 309 | + tss_limit < tss_limit_max) | ||
| 310 | + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); | ||
| 311 | + old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; | ||
| 312 | + if (old_type & 8) | ||
| 313 | + old_tss_limit_max = 103; | ||
| 314 | + else | ||
| 315 | + old_tss_limit_max = 43; | ||
| 316 | + | ||
| 317 | + /* read all the registers from the new TSS */ | ||
| 318 | + if (type & 8) { | ||
| 319 | + /* 32 bit */ | ||
| 320 | + new_cr3 = ldl_kernel(tss_base + 0x1c); | ||
| 321 | + new_eip = ldl_kernel(tss_base + 0x20); | ||
| 322 | + new_eflags = ldl_kernel(tss_base + 0x24); | ||
| 323 | + for(i = 0; i < 8; i++) | ||
| 324 | + new_regs[i] = ldl_kernel(tss_base + (0x28 + i * 4)); | ||
| 325 | + for(i = 0; i < 6; i++) | ||
| 326 | + new_segs[i] = lduw_kernel(tss_base + (0x48 + i * 4)); | ||
| 327 | + new_ldt = lduw_kernel(tss_base + 0x60); | ||
| 328 | + new_trap = ldl_kernel(tss_base + 0x64); | ||
| 329 | + } else { | ||
| 330 | + /* 16 bit */ | ||
| 331 | + new_cr3 = 0; | ||
| 332 | + new_eip = lduw_kernel(tss_base + 0x0e); | ||
| 333 | + new_eflags = lduw_kernel(tss_base + 0x10); | ||
| 334 | + for(i = 0; i < 8; i++) | ||
| 335 | + new_regs[i] = lduw_kernel(tss_base + (0x12 + i * 2)) | 0xffff0000; | ||
| 336 | + for(i = 0; i < 4; i++) | ||
| 337 | + new_segs[i] = lduw_kernel(tss_base + (0x22 + i * 4)); | ||
| 338 | + new_ldt = lduw_kernel(tss_base + 0x2a); | ||
| 339 | + new_segs[R_FS] = 0; | ||
| 340 | + new_segs[R_GS] = 0; | ||
| 341 | + new_trap = 0; | ||
| 342 | + } | ||
| 343 | + | ||
| 344 | + /* NOTE: we must avoid memory exceptions during the task switch, | ||
| 345 | + so we make dummy accesses before */ | ||
| 346 | + /* XXX: it can still fail in some cases, so a bigger hack is | ||
| 347 | + necessary to valid the TLB after having done the accesses */ | ||
| 348 | + | ||
| 349 | + v1 = ldub_kernel(env->tr.base); | ||
| 350 | + v2 = ldub(env->tr.base + old_tss_limit_max); | ||
| 351 | + stb_kernel(env->tr.base, v1); | ||
| 352 | + stb_kernel(env->tr.base + old_tss_limit_max, v2); | ||
| 353 | + | ||
| 354 | + /* clear busy bit (it is restartable) */ | ||
| 355 | + if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { | ||
| 356 | + uint8_t *ptr; | ||
| 357 | + uint32_t e2; | ||
| 358 | + ptr = env->gdt.base + (env->tr.selector << 3); | ||
| 359 | + e2 = ldl_kernel(ptr + 4); | ||
| 360 | + e2 &= ~DESC_TSS_BUSY_MASK; | ||
| 361 | + stl_kernel(ptr + 4, e2); | ||
| 362 | + } | ||
| 363 | + old_eflags = compute_eflags(); | ||
| 364 | + if (source == SWITCH_TSS_IRET) | ||
| 365 | + old_eflags &= ~NT_MASK; | ||
| 366 | + | ||
| 367 | + /* save the current state in the old TSS */ | ||
| 368 | + if (type & 8) { | ||
| 369 | + /* 32 bit */ | ||
| 370 | + stl_kernel(env->tr.base + 0x20, env->eip); | ||
| 371 | + stl_kernel(env->tr.base + 0x24, old_eflags); | ||
| 372 | + for(i = 0; i < 8; i++) | ||
| 373 | + stl_kernel(env->tr.base + (0x28 + i * 4), env->regs[i]); | ||
| 374 | + for(i = 0; i < 6; i++) | ||
| 375 | + stw_kernel(env->tr.base + (0x48 + i * 4), env->segs[i].selector); | ||
| 376 | + } else { | ||
| 377 | + /* 16 bit */ | ||
| 378 | + stw_kernel(env->tr.base + 0x0e, new_eip); | ||
| 379 | + stw_kernel(env->tr.base + 0x10, old_eflags); | ||
| 380 | + for(i = 0; i < 8; i++) | ||
| 381 | + stw_kernel(env->tr.base + (0x12 + i * 2), env->regs[i]); | ||
| 382 | + for(i = 0; i < 4; i++) | ||
| 383 | + stw_kernel(env->tr.base + (0x22 + i * 4), env->segs[i].selector); | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + /* now if an exception occurs, it will occurs in the next task | ||
| 387 | + context */ | ||
| 388 | + | ||
| 389 | + if (source == SWITCH_TSS_CALL) { | ||
| 390 | + stw_kernel(tss_base, env->tr.selector); | ||
| 391 | + new_eflags |= NT_MASK; | ||
| 392 | + } | ||
| 393 | + | ||
| 394 | + /* set busy bit */ | ||
| 395 | + if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { | ||
| 396 | + uint8_t *ptr; | ||
| 397 | + uint32_t e2; | ||
| 398 | + ptr = env->gdt.base + (tss_selector << 3); | ||
| 399 | + e2 = ldl_kernel(ptr + 4); | ||
| 400 | + e2 |= DESC_TSS_BUSY_MASK; | ||
| 401 | + stl_kernel(ptr + 4, e2); | ||
| 402 | + } | ||
| 403 | + | ||
| 404 | + /* set the new CPU state */ | ||
| 405 | + /* from this point, any exception which occurs can give problems */ | ||
| 406 | + env->cr[0] |= CR0_TS_MASK; | ||
| 407 | + env->tr.selector = tss_selector; | ||
| 408 | + env->tr.base = tss_base; | ||
| 409 | + env->tr.limit = tss_limit; | ||
| 410 | + env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK; | ||
| 411 | + | ||
| 412 | + if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) { | ||
| 413 | + env->cr[3] = new_cr3; | ||
| 414 | + cpu_x86_update_cr3(env); | ||
| 415 | + } | ||
| 416 | + | ||
| 417 | + /* load all registers without an exception, then reload them with | ||
| 418 | + possible exception */ | ||
| 419 | + env->eip = new_eip; | ||
| 420 | + eflags_mask = FL_UPDATE_CPL0_MASK; | ||
| 421 | + if (!(type & 8)) | ||
| 422 | + eflags_mask &= 0xffff; | ||
| 423 | + load_eflags(new_eflags, eflags_mask); | ||
| 424 | + for(i = 0; i < 8; i++) | ||
| 425 | + env->regs[i] = new_regs[i]; | ||
| 426 | + if (new_eflags & VM_MASK) { | ||
| 427 | + for(i = 0; i < 6; i++) | ||
| 428 | + load_seg_vm(i, new_segs[i]); | ||
| 429 | + /* in vm86, CPL is always 3 */ | ||
| 430 | + cpu_x86_set_cpl(env, 3); | ||
| 431 | + } else { | ||
| 432 | + /* CPL is set the RPL of CS */ | ||
| 433 | + cpu_x86_set_cpl(env, new_segs[R_CS] & 3); | ||
| 434 | + /* first just selectors as the rest may trigger exceptions */ | ||
| 435 | + for(i = 0; i < 6; i++) | ||
| 436 | + cpu_x86_load_seg_cache(env, i, new_segs[i], NULL, 0, 0); | ||
| 437 | + } | ||
| 438 | + | ||
| 439 | + env->ldt.selector = new_ldt & ~4; | ||
| 440 | + env->ldt.base = NULL; | ||
| 441 | + env->ldt.limit = 0; | ||
| 442 | + env->ldt.flags = 0; | ||
| 443 | + | ||
| 444 | + /* load the LDT */ | ||
| 445 | + if (new_ldt & 4) | ||
| 446 | + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); | ||
| 447 | + | ||
| 448 | + dt = &env->gdt; | ||
| 449 | + index = new_ldt & ~7; | ||
| 177 | if ((index + 7) > dt->limit) | 450 | if ((index + 7) > dt->limit) |
| 178 | - return -1; | 451 | + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); |
| 179 | ptr = dt->base + index; | 452 | ptr = dt->base + index; |
| 180 | - *e1_ptr = ldl_kernel(ptr); | ||
| 181 | - *e2_ptr = ldl_kernel(ptr + 4); | ||
| 182 | - return 0; | 453 | + e1 = ldl_kernel(ptr); |
| 454 | + e2 = ldl_kernel(ptr + 4); | ||
| 455 | + if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) | ||
| 456 | + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); | ||
| 457 | + if (!(e2 & DESC_P_MASK)) | ||
| 458 | + raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc); | ||
| 459 | + load_seg_cache_raw_dt(&env->ldt, e1, e2); | ||
| 460 | + | ||
| 461 | + /* load the segments */ | ||
| 462 | + if (!(new_eflags & VM_MASK)) { | ||
| 463 | + tss_load_seg(R_CS, new_segs[R_CS]); | ||
| 464 | + tss_load_seg(R_SS, new_segs[R_SS]); | ||
| 465 | + tss_load_seg(R_ES, new_segs[R_ES]); | ||
| 466 | + tss_load_seg(R_DS, new_segs[R_DS]); | ||
| 467 | + tss_load_seg(R_FS, new_segs[R_FS]); | ||
| 468 | + tss_load_seg(R_GS, new_segs[R_GS]); | ||
| 469 | + } | ||
| 470 | + | ||
| 471 | + /* check that EIP is in the CS segment limits */ | ||
| 472 | + if (new_eip > env->segs[R_CS].limit) { | ||
| 473 | + raise_exception_err(EXCP0D_GPF, 0); | ||
| 474 | + } | ||
| 183 | } | 475 | } |
| 184 | - | ||
| 185 | -static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) | 476 | + |
| 477 | +/* check if Port I/O is allowed in TSS */ | ||
| 478 | +static inline void check_io(int addr, int size) | ||
| 186 | { | 479 | { |
| 187 | - unsigned int limit; | ||
| 188 | - limit = (e1 & 0xffff) | (e2 & 0x000f0000); | ||
| 189 | - if (e2 & DESC_G_MASK) | ||
| 190 | - limit = (limit << 12) | 0xfff; | ||
| 191 | - return limit; | 480 | + int io_offset, val, mask; |
| 481 | + | ||
| 482 | + /* TSS must be a valid 32 bit one */ | ||
| 483 | + if (!(env->tr.flags & DESC_P_MASK) || | ||
| 484 | + ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 || | ||
| 485 | + env->tr.limit < 103) | ||
| 486 | + goto fail; | ||
| 487 | + io_offset = lduw_kernel(env->tr.base + 0x66); | ||
| 488 | + io_offset += (addr >> 3); | ||
| 489 | + /* Note: the check needs two bytes */ | ||
| 490 | + if ((io_offset + 1) > env->tr.limit) | ||
| 491 | + goto fail; | ||
| 492 | + val = lduw_kernel(env->tr.base + io_offset); | ||
| 493 | + val >>= (addr & 7); | ||
| 494 | + mask = (1 << size) - 1; | ||
| 495 | + /* all bits must be zero to allow the I/O */ | ||
| 496 | + if ((val & mask) != 0) { | ||
| 497 | + fail: | ||
| 498 | + raise_exception_err(EXCP0D_GPF, 0); | ||
| 499 | + } | ||
| 192 | } | 500 | } |
| 193 | 501 | ||
| 194 | -static inline uint8_t *get_seg_base(uint32_t e1, uint32_t e2) | 502 | +void check_iob_T0(void) |
| 195 | { | 503 | { |
| 196 | - return (uint8_t *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000)); | 504 | + check_io(T0, 1); |
| 197 | } | 505 | } |
| 198 | 506 | ||
| 199 | -static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2) | 507 | +void check_iow_T0(void) |
| 200 | { | 508 | { |
| 201 | - sc->base = get_seg_base(e1, e2); | ||
| 202 | - sc->limit = get_seg_limit(e1, e2); | ||
| 203 | - sc->flags = e2; | 509 | + check_io(T0, 2); |
| 204 | } | 510 | } |
| 205 | 511 | ||
| 206 | -/* init the segment cache in vm86 mode. */ | ||
| 207 | -static inline void load_seg_vm(int seg, int selector) | 512 | +void check_iol_T0(void) |
| 208 | { | 513 | { |
| 209 | - selector &= 0xffff; | ||
| 210 | - cpu_x86_load_seg_cache(env, seg, selector, | ||
| 211 | - (uint8_t *)(selector << 4), 0xffff, 0); | 514 | + check_io(T0, 4); |
| 515 | +} | ||
| 516 | + | ||
| 517 | +void check_iob_DX(void) | ||
| 518 | +{ | ||
| 519 | + check_io(EDX & 0xffff, 1); | ||
| 520 | +} | ||
| 521 | + | ||
| 522 | +void check_iow_DX(void) | ||
| 523 | +{ | ||
| 524 | + check_io(EDX & 0xffff, 2); | ||
| 525 | +} | ||
| 526 | + | ||
| 527 | +void check_iol_DX(void) | ||
| 528 | +{ | ||
| 529 | + check_io(EDX & 0xffff, 4); | ||
| 212 | } | 530 | } |
| 213 | 531 | ||
| 214 | /* protected mode interrupt */ | 532 | /* protected mode interrupt */ |
| @@ -222,6 +540,21 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, | @@ -222,6 +540,21 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, | ||
| 222 | uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2, push_size; | 540 | uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2, push_size; |
| 223 | uint32_t old_cs, old_ss, old_esp, old_eip; | 541 | uint32_t old_cs, old_ss, old_esp, old_eip; |
| 224 | 542 | ||
| 543 | + has_error_code = 0; | ||
| 544 | + if (!is_int && !is_hw) { | ||
| 545 | + switch(intno) { | ||
| 546 | + case 8: | ||
| 547 | + case 10: | ||
| 548 | + case 11: | ||
| 549 | + case 12: | ||
| 550 | + case 13: | ||
| 551 | + case 14: | ||
| 552 | + case 17: | ||
| 553 | + has_error_code = 1; | ||
| 554 | + break; | ||
| 555 | + } | ||
| 556 | + } | ||
| 557 | + | ||
| 225 | dt = &env->idt; | 558 | dt = &env->idt; |
| 226 | if (intno * 8 + 7 > dt->limit) | 559 | if (intno * 8 + 7 > dt->limit) |
| 227 | raise_exception_err(EXCP0D_GPF, intno * 8 + 2); | 560 | raise_exception_err(EXCP0D_GPF, intno * 8 + 2); |
| @@ -232,8 +565,27 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, | @@ -232,8 +565,27 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, | ||
| 232 | type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; | 565 | type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; |
| 233 | switch(type) { | 566 | switch(type) { |
| 234 | case 5: /* task gate */ | 567 | case 5: /* task gate */ |
| 235 | - cpu_abort(env, "task gate not supported"); | ||
| 236 | - break; | 568 | + /* must do that check here to return the correct error code */ |
| 569 | + if (!(e2 & DESC_P_MASK)) | ||
| 570 | + raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2); | ||
| 571 | + switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL); | ||
| 572 | + if (has_error_code) { | ||
| 573 | + int mask; | ||
| 574 | + /* push the error code */ | ||
| 575 | + shift = (env->segs[R_CS].flags >> DESC_B_SHIFT) & 1; | ||
| 576 | + if (env->segs[R_SS].flags & DESC_B_MASK) | ||
| 577 | + mask = 0xffffffff; | ||
| 578 | + else | ||
| 579 | + mask = 0xffff; | ||
| 580 | + esp = (env->regs[R_ESP] - (2 << shift)) & mask; | ||
| 581 | + ssp = env->segs[R_SS].base + esp; | ||
| 582 | + if (shift) | ||
| 583 | + stl_kernel(ssp, error_code); | ||
| 584 | + else | ||
| 585 | + stw_kernel(ssp, error_code); | ||
| 586 | + env->regs[R_ESP] = (esp & mask) | (env->regs[R_ESP] & ~mask); | ||
| 587 | + } | ||
| 588 | + return; | ||
| 237 | case 6: /* 286 interrupt gate */ | 589 | case 6: /* 286 interrupt gate */ |
| 238 | case 7: /* 286 trap gate */ | 590 | case 7: /* 286 trap gate */ |
| 239 | case 14: /* 386 interrupt gate */ | 591 | case 14: /* 386 interrupt gate */ |
| @@ -293,20 +645,6 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, | @@ -293,20 +645,6 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, | ||
| 293 | } | 645 | } |
| 294 | 646 | ||
| 295 | shift = type >> 3; | 647 | shift = type >> 3; |
| 296 | - has_error_code = 0; | ||
| 297 | - if (!is_int && !is_hw) { | ||
| 298 | - switch(intno) { | ||
| 299 | - case 8: | ||
| 300 | - case 10: | ||
| 301 | - case 11: | ||
| 302 | - case 12: | ||
| 303 | - case 13: | ||
| 304 | - case 14: | ||
| 305 | - case 17: | ||
| 306 | - has_error_code = 1; | ||
| 307 | - break; | ||
| 308 | - } | ||
| 309 | - } | ||
| 310 | push_size = 6 + (new_stack << 2) + (has_error_code << 1); | 648 | push_size = 6 + (new_stack << 2) + (has_error_code << 1); |
| 311 | if (env->eflags & VM_MASK) | 649 | if (env->eflags & VM_MASK) |
| 312 | push_size += 8; | 650 | push_size += 8; |
| @@ -688,7 +1026,7 @@ void helper_ltr_T0(void) | @@ -688,7 +1026,7 @@ void helper_ltr_T0(void) | ||
| 688 | e2 = ldl_kernel(ptr + 4); | 1026 | e2 = ldl_kernel(ptr + 4); |
| 689 | type = (e2 >> DESC_TYPE_SHIFT) & 0xf; | 1027 | type = (e2 >> DESC_TYPE_SHIFT) & 0xf; |
| 690 | if ((e2 & DESC_S_MASK) || | 1028 | if ((e2 & DESC_S_MASK) || |
| 691 | - (type != 2 && type != 9)) | 1029 | + (type != 1 && type != 9)) |
| 692 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); | 1030 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); |
| 693 | if (!(e2 & DESC_P_MASK)) | 1031 | if (!(e2 & DESC_P_MASK)) |
| 694 | raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); | 1032 | raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); |
| @@ -701,6 +1039,7 @@ void helper_ltr_T0(void) | @@ -701,6 +1039,7 @@ void helper_ltr_T0(void) | ||
| 701 | 1039 | ||
| 702 | /* only works if protected mode and not VM86. Calling load_seg with | 1040 | /* only works if protected mode and not VM86. Calling load_seg with |
| 703 | seg_reg == R_CS is discouraged */ | 1041 | seg_reg == R_CS is discouraged */ |
| 1042 | +/* XXX: add ring level checks */ | ||
| 704 | void load_seg(int seg_reg, int selector, unsigned int cur_eip) | 1043 | void load_seg(int seg_reg, int selector, unsigned int cur_eip) |
| 705 | { | 1044 | { |
| 706 | uint32_t e1, e2; | 1045 | uint32_t e1, e2; |
| @@ -725,7 +1064,7 @@ void load_seg(int seg_reg, int selector, unsigned int cur_eip) | @@ -725,7 +1064,7 @@ void load_seg(int seg_reg, int selector, unsigned int cur_eip) | ||
| 725 | } | 1064 | } |
| 726 | 1065 | ||
| 727 | if (seg_reg == R_SS) { | 1066 | if (seg_reg == R_SS) { |
| 728 | - if ((e2 & (DESC_CS_MASK | DESC_W_MASK)) == 0) { | 1067 | + if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { |
| 729 | EIP = cur_eip; | 1068 | EIP = cur_eip; |
| 730 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); | 1069 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); |
| 731 | } | 1070 | } |
| @@ -757,7 +1096,7 @@ void load_seg(int seg_reg, int selector, unsigned int cur_eip) | @@ -757,7 +1096,7 @@ void load_seg(int seg_reg, int selector, unsigned int cur_eip) | ||
| 757 | /* protected mode jump */ | 1096 | /* protected mode jump */ |
| 758 | void helper_ljmp_protected_T0_T1(void) | 1097 | void helper_ljmp_protected_T0_T1(void) |
| 759 | { | 1098 | { |
| 760 | - int new_cs, new_eip; | 1099 | + int new_cs, new_eip, gate_cs, type; |
| 761 | uint32_t e1, e2, cpl, dpl, rpl, limit; | 1100 | uint32_t e1, e2, cpl, dpl, rpl, limit; |
| 762 | 1101 | ||
| 763 | new_cs = T0; | 1102 | new_cs = T0; |
| @@ -771,7 +1110,7 @@ void helper_ljmp_protected_T0_T1(void) | @@ -771,7 +1110,7 @@ void helper_ljmp_protected_T0_T1(void) | ||
| 771 | if (!(e2 & DESC_CS_MASK)) | 1110 | if (!(e2 & DESC_CS_MASK)) |
| 772 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | 1111 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| 773 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; | 1112 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; |
| 774 | - if (e2 & DESC_CS_MASK) { | 1113 | + if (e2 & DESC_C_MASK) { |
| 775 | /* conforming code segment */ | 1114 | /* conforming code segment */ |
| 776 | if (dpl > cpl) | 1115 | if (dpl > cpl) |
| 777 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | 1116 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| @@ -792,8 +1131,52 @@ void helper_ljmp_protected_T0_T1(void) | @@ -792,8 +1131,52 @@ void helper_ljmp_protected_T0_T1(void) | ||
| 792 | get_seg_base(e1, e2), limit, e2); | 1131 | get_seg_base(e1, e2), limit, e2); |
| 793 | EIP = new_eip; | 1132 | EIP = new_eip; |
| 794 | } else { | 1133 | } else { |
| 795 | - cpu_abort(env, "jmp to call/task gate not supported 0x%04x:0x%08x", | ||
| 796 | - new_cs, new_eip); | 1134 | + /* jump to call or task gate */ |
| 1135 | + dpl = (e2 >> DESC_DPL_SHIFT) & 3; | ||
| 1136 | + rpl = new_cs & 3; | ||
| 1137 | + cpl = env->hflags & HF_CPL_MASK; | ||
| 1138 | + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; | ||
| 1139 | + switch(type) { | ||
| 1140 | + case 1: /* 286 TSS */ | ||
| 1141 | + case 9: /* 386 TSS */ | ||
| 1142 | + case 5: /* task gate */ | ||
| 1143 | + if (dpl < cpl || dpl < rpl) | ||
| 1144 | + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | ||
| 1145 | + switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP); | ||
| 1146 | + break; | ||
| 1147 | + case 4: /* 286 call gate */ | ||
| 1148 | + case 12: /* 386 call gate */ | ||
| 1149 | + if ((dpl < cpl) || (dpl < rpl)) | ||
| 1150 | + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | ||
| 1151 | + if (!(e2 & DESC_P_MASK)) | ||
| 1152 | + raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc); | ||
| 1153 | + gate_cs = e1 >> 16; | ||
| 1154 | + if (load_segment(&e1, &e2, gate_cs) != 0) | ||
| 1155 | + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); | ||
| 1156 | + dpl = (e2 >> DESC_DPL_SHIFT) & 3; | ||
| 1157 | + /* must be code segment */ | ||
| 1158 | + if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) != | ||
| 1159 | + (DESC_S_MASK | DESC_CS_MASK))) | ||
| 1160 | + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); | ||
| 1161 | + if (((e2 & DESC_C_MASK) && (dpl > cpl)) || | ||
| 1162 | + (!(e2 & DESC_C_MASK) && (dpl != cpl))) | ||
| 1163 | + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); | ||
| 1164 | + if (!(e2 & DESC_P_MASK)) | ||
| 1165 | + raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc); | ||
| 1166 | + new_eip = (e1 & 0xffff); | ||
| 1167 | + if (type == 12) | ||
| 1168 | + new_eip |= (e2 & 0xffff0000); | ||
| 1169 | + limit = get_seg_limit(e1, e2); | ||
| 1170 | + if (new_eip > limit) | ||
| 1171 | + raise_exception_err(EXCP0D_GPF, 0); | ||
| 1172 | + cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, | ||
| 1173 | + get_seg_base(e1, e2), limit, e2); | ||
| 1174 | + EIP = new_eip; | ||
| 1175 | + break; | ||
| 1176 | + default: | ||
| 1177 | + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | ||
| 1178 | + break; | ||
| 1179 | + } | ||
| 797 | } | 1180 | } |
| 798 | } | 1181 | } |
| 799 | 1182 | ||
| @@ -852,7 +1235,7 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | @@ -852,7 +1235,7 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | ||
| 852 | if (!(e2 & DESC_CS_MASK)) | 1235 | if (!(e2 & DESC_CS_MASK)) |
| 853 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | 1236 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| 854 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; | 1237 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; |
| 855 | - if (e2 & DESC_CS_MASK) { | 1238 | + if (e2 & DESC_C_MASK) { |
| 856 | /* conforming code segment */ | 1239 | /* conforming code segment */ |
| 857 | if (dpl > cpl) | 1240 | if (dpl > cpl) |
| 858 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | 1241 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| @@ -898,11 +1281,15 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | @@ -898,11 +1281,15 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | ||
| 898 | } else { | 1281 | } else { |
| 899 | /* check gate type */ | 1282 | /* check gate type */ |
| 900 | type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; | 1283 | type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; |
| 1284 | + dpl = (e2 >> DESC_DPL_SHIFT) & 3; | ||
| 1285 | + rpl = new_cs & 3; | ||
| 901 | switch(type) { | 1286 | switch(type) { |
| 902 | case 1: /* available 286 TSS */ | 1287 | case 1: /* available 286 TSS */ |
| 903 | case 9: /* available 386 TSS */ | 1288 | case 9: /* available 386 TSS */ |
| 904 | case 5: /* task gate */ | 1289 | case 5: /* task gate */ |
| 905 | - cpu_abort(env, "task gate not supported"); | 1290 | + if (dpl < cpl || dpl < rpl) |
| 1291 | + raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | ||
| 1292 | + switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL); | ||
| 906 | break; | 1293 | break; |
| 907 | case 4: /* 286 call gate */ | 1294 | case 4: /* 286 call gate */ |
| 908 | case 12: /* 386 call gate */ | 1295 | case 12: /* 386 call gate */ |
| @@ -913,8 +1300,6 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | @@ -913,8 +1300,6 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | ||
| 913 | } | 1300 | } |
| 914 | shift = type >> 3; | 1301 | shift = type >> 3; |
| 915 | 1302 | ||
| 916 | - dpl = (e2 >> DESC_DPL_SHIFT) & 3; | ||
| 917 | - rpl = new_cs & 3; | ||
| 918 | if (dpl < cpl || dpl < rpl) | 1303 | if (dpl < cpl || dpl < rpl) |
| 919 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | 1304 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| 920 | /* check valid bit */ | 1305 | /* check valid bit */ |
| @@ -1031,13 +1416,13 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | @@ -1031,13 +1416,13 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) | ||
| 1031 | } | 1416 | } |
| 1032 | } | 1417 | } |
| 1033 | 1418 | ||
| 1034 | -/* real mode iret */ | 1419 | +/* real and vm86 mode iret */ |
| 1035 | void helper_iret_real(int shift) | 1420 | void helper_iret_real(int shift) |
| 1036 | { | 1421 | { |
| 1037 | uint32_t sp, new_cs, new_eip, new_eflags, new_esp; | 1422 | uint32_t sp, new_cs, new_eip, new_eflags, new_esp; |
| 1038 | uint8_t *ssp; | 1423 | uint8_t *ssp; |
| 1039 | int eflags_mask; | 1424 | int eflags_mask; |
| 1040 | - | 1425 | + |
| 1041 | sp = ESP & 0xffff; | 1426 | sp = ESP & 0xffff; |
| 1042 | ssp = env->segs[R_SS].base + sp; | 1427 | ssp = env->segs[R_SS].base + sp; |
| 1043 | if (shift == 1) { | 1428 | if (shift == 1) { |
| @@ -1056,7 +1441,10 @@ void helper_iret_real(int shift) | @@ -1056,7 +1441,10 @@ void helper_iret_real(int shift) | ||
| 1056 | (new_esp & 0xffff); | 1441 | (new_esp & 0xffff); |
| 1057 | load_seg_vm(R_CS, new_cs); | 1442 | load_seg_vm(R_CS, new_cs); |
| 1058 | env->eip = new_eip; | 1443 | env->eip = new_eip; |
| 1059 | - eflags_mask = FL_UPDATE_CPL0_MASK; | 1444 | + if (env->eflags & VM_MASK) |
| 1445 | + eflags_mask = FL_UPDATE_MASK32 | IF_MASK | RF_MASK; | ||
| 1446 | + else | ||
| 1447 | + eflags_mask = FL_UPDATE_CPL0_MASK; | ||
| 1060 | if (shift == 0) | 1448 | if (shift == 0) |
| 1061 | eflags_mask &= 0xffff; | 1449 | eflags_mask &= 0xffff; |
| 1062 | load_eflags(new_eflags, eflags_mask); | 1450 | load_eflags(new_eflags, eflags_mask); |
| @@ -1102,7 +1490,7 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend) | @@ -1102,7 +1490,7 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend) | ||
| 1102 | if (rpl < cpl) | 1490 | if (rpl < cpl) |
| 1103 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | 1491 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| 1104 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; | 1492 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; |
| 1105 | - if (e2 & DESC_CS_MASK) { | 1493 | + if (e2 & DESC_C_MASK) { |
| 1106 | if (dpl > rpl) | 1494 | if (dpl > rpl) |
| 1107 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); | 1495 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| 1108 | } else { | 1496 | } else { |
| @@ -1198,7 +1586,24 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend) | @@ -1198,7 +1586,24 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend) | ||
| 1198 | 1586 | ||
| 1199 | void helper_iret_protected(int shift) | 1587 | void helper_iret_protected(int shift) |
| 1200 | { | 1588 | { |
| 1201 | - helper_ret_protected(shift, 1, 0); | 1589 | + int tss_selector, type; |
| 1590 | + uint32_t e1, e2; | ||
| 1591 | + | ||
| 1592 | + /* specific case for TSS */ | ||
| 1593 | + if (env->eflags & NT_MASK) { | ||
| 1594 | + tss_selector = lduw_kernel(env->tr.base + 0); | ||
| 1595 | + if (tss_selector & 4) | ||
| 1596 | + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); | ||
| 1597 | + if (load_segment(&e1, &e2, tss_selector) != 0) | ||
| 1598 | + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); | ||
| 1599 | + type = (e2 >> DESC_TYPE_SHIFT) & 0x17; | ||
| 1600 | + /* NOTE: we check both segment and busy TSS */ | ||
| 1601 | + if (type != 3) | ||
| 1602 | + raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); | ||
| 1603 | + switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET); | ||
| 1604 | + } else { | ||
| 1605 | + helper_ret_protected(shift, 1, 0); | ||
| 1606 | + } | ||
| 1202 | } | 1607 | } |
| 1203 | 1608 | ||
| 1204 | void helper_lret_protected(int shift, int addend) | 1609 | void helper_lret_protected(int shift, int addend) |