VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/invop.c@ 49469

Last change on this file since 49469 was 49286, checked in by vboxsync, 11 years ago

BIOS: Added invalid opcode handler.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.7 KB
Line 
1/* $Id: invop.c 49286 2013-10-25 10:09:21Z vboxsync $ */
2/** @file
3 * Real mode invalid opcode handler.
4 */
5
6/*
7 * Copyright (C) 2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <stdint.h>
19#include <string.h>
20#include "biosint.h"
21#include "inlines.h"
22
23/* The layout of 286 LOADALL descriptors. */
24typedef struct tag_ldall_desc {
25 uint16_t base_lo; /* Bits 0-15 of segment base. */
26 uint8_t base_hi; /* Bits 16-13 of segment base. */
27 uint8_t attr; /* Segment attributes. */
28 uint16_t limit; /* Segment limit. */
29} ldall_desc;
30
31/* The 286 LOADALL memory buffer at physical address 800h. From
32 * The Undocumented PC.
33 */
34typedef struct tag_ldall_286 {
35 uint16_t unused1[3];
36 uint16_t msw; /* 806h */
37 uint16_t unused2[7];
38 uint16_t tr; /* 816h */
39 uint16_t flags; /* 818h */
40 uint16_t ip; /* 81Ah */
41 uint16_t ldt; /* 81Ch */
42 uint16_t ds; /* 81Eh */
43 uint16_t ss; /* 820h */
44 uint16_t cs; /* 822h */
45 uint16_t es; /* 824h */
46 uint16_t di; /* 826h */
47 uint16_t si; /* 828h */
48 uint16_t bp; /* 82Ah */
49 uint16_t sp; /* 82Ch */
50 uint16_t bx; /* 82Eh */
51 uint16_t dx; /* 830h */
52 uint16_t cx; /* 832h */
53 uint16_t ax; /* 834h */
54 ldall_desc es_desc; /* 836h */
55 ldall_desc cs_desc; /* 83Ch */
56 ldall_desc ss_desc; /* 842h */
57 ldall_desc ds_desc; /* 848h */
58 ldall_desc gdt_desc; /* 84Eh */
59 ldall_desc ldt_desc; /* 854h */
60 ldall_desc idt_desc; /* 85Ah */
61 ldall_desc tss_desc; /* 860h */
62} ldall_286_s;
63ct_assert(sizeof(ldall_286_s) == 0x66);
64
65/*
66 * LOADALL emulation assumptions:
67 * - MSW indicates real mode
68 * - Standard real mode CS and SS is to be used
69 * - Segment values of non-RM segments (if any) do not matter
70 * - Standard segment attributes are used
71 */
72
73/* A wrapper for LIDT. */
74void load_idtr(uint32_t base, uint16_t limit);
75#pragma aux load_idtr = \
76 ".286p" \
77 "mov bx, sp" \
78 "lidt fword ptr ss:[bx]"\
79 parm caller reverse [] modify [bx] exact;
80
81/* A wrapper for LGDT. */
82void load_gdtr(uint32_t base, uint16_t limit);
83#pragma aux load_gdtr = \
84 ".286p" \
85 "mov bx, sp" \
86 "lgdt fword ptr ss:[bx]"\
87 parm caller reverse [] modify [bx] exact;
88
89/* Load DS/ES as real-mode segments. May be overwritten later.
90 * NB: Loads SS with 80h to address the LOADALL buffer. Must
91 * not touch CX!
92 */
93void load_rm_segs(int seg_flags);
94#pragma aux load_rm_segs = \
95 "mov ax, 80h" \
96 "mov ss, ax" \
97 "mov ax, ss:[1Eh]" \
98 "mov ds, ax" \
99 "mov ax, ss:[24h]" \
100 "mov es, ax" \
101 parm [cx] nomemory modify nomemory;
102
103/* Briefly switch to protected mode and load ES and/or DS if necessary.
104 * NB: Trashes high bits of EAX, but that should be safe. Expects flags
105 * in CX.
106 */
107void load_pm_segs(void);
108#pragma aux load_pm_segs = \
109 ".386p" \
110 "smsw ax" \
111 "inc ax" \
112 "lmsw ax" \
113 "mov ax, 8" \
114 "test cx, 1" \
115 "jz skip_es" \
116 "mov es, ax" \
117 "skip_es:" \
118 "test cx, 2" \
119 "jz skip_ds" \
120 "mov bx,ss:[00h]" \
121 "mov ss:[08h], bx" \
122 "mov bx,ss:[02h]" \
123 "mov ss:[0Ah], bx" \
124 "mov bx,ss:[04h]" \
125 "mov ss:[0Ch], bx" \
126 "mov ds, ax" \
127 "skip_ds:" \
128 "mov eax, cr0" \
129 "dec ax" \
130 "mov cr0, eax" \
131 parm nomemory modify nomemory;
132
133/* Complete LOADALL emulation: Restore general-purpose registers, stack
134 * pointer, and CS:IP. NB: The LOADALL instruction stores registers in
135 * the same order as PUSHA. Surprise, surprise!
136 */
137void ldall_finish(void);
138#pragma aux ldall_finish = \
139 ".286" \
140 "mov sp, 26h" \
141 "popa" \
142 "mov sp, ss:[2Ch]" \
143 "sub sp, 6" \
144 "mov ss, ss:[20h]" \
145 "iret" \
146 parm nomemory modify nomemory aborts;
147
148
149#define LOAD_ES 0x01 /* ES needs to be loaded in protected mode. */
150#define LOAD_DS 0x02 /* DS needs to be loaded in protected mode. */
151
152/*
153 * The invalid opcode handler exists to work around fishy application
154 * code and paper over CPU generation differences:
155 *
156 * - Skip redundant LOCK prefixes (allowed on 8086, #UD on 286+).
157 * - Emulate just enough of 286 LOADALL.
158 *
159 */
160void BIOSCALL inv_op_handler(uint16_t ds, uint16_t es, pusha_regs_t gr, volatile iret_addr_t ra)
161{
162 void __far *ins = ra.cs :> ra.ip;
163
164 if (*(uint8_t __far *)ins == 0xF0) {
165 /* LOCK prefix - skip over it and try again. */
166 ++ra.ip;
167 } else if (*(uint16_t __far *)ins == 0x050F) {
168 /* 286 LOADALL. NB: Same opcode as SYSCALL. */
169 ldall_286_s __far *ldbuf = 0 :> 0x800;
170 iret_addr_t __far *ret_addr;
171 uint32_t seg_base;
172 int seg_flags = 0;
173
174 /* One of the challenges is that we must restore SS:SP as well
175 * as CS:IP and FLAGS from the LOADALL buffer. We copy CS/IP/FLAGS
176 * from the buffer just below the SS:SP values from the buffer so
177 * that we can eventually IRET to the desired CS/IP/FLAGS/SS/SP
178 * values in one go.
179 */
180 ret_addr = ldbuf->ss :> (ldbuf->sp - sizeof(iret_addr_t));
181 ret_addr->ip = ldbuf->ip;
182 ret_addr->cs = ldbuf->cs;
183 ret_addr->flags.u.r16.flags = ldbuf->flags;
184
185 /* Examine ES/DS. */
186 seg_base = ldbuf->es_desc.base_lo | (uint32_t)ldbuf->es_desc.base_hi << 16;
187 if (seg_base != (uint32_t)ldbuf->es << 4)
188 seg_flags |= LOAD_ES;
189 seg_base = ldbuf->ds_desc.base_lo | (uint32_t)ldbuf->ds_desc.base_hi << 16;
190 if (seg_base != (uint32_t)ldbuf->ds << 4)
191 seg_flags |= LOAD_DS;
192
193 /* The LOADALL buffer doubles as a tiny GDT. */
194 load_gdtr(0x800, 4 * 8 - 1);
195
196 /* Store the ES base/limit/attributes in the unused words (GDT selector 8). */
197 ldbuf->unused2[0] = ldbuf->es_desc.limit;
198 ldbuf->unused2[1] = ldbuf->es_desc.base_lo;
199 ldbuf->unused2[2] = (ldbuf->es_desc.attr << 8) | ldbuf->es_desc.base_hi;
200 ldbuf->unused2[3] = 0;
201
202 /* Store the DS base/limit/attributes in other unused words. */
203 ldbuf->unused1[0] = ldbuf->ds_desc.limit;
204 ldbuf->unused1[1] = ldbuf->ds_desc.base_lo;
205 ldbuf->unused1[2] = (ldbuf->ds_desc.attr << 8) | ldbuf->ds_desc.base_hi;
206
207 /* Load the IDTR as specified. */
208 seg_base = ldbuf->idt_desc.base_lo | (uint32_t)ldbuf->idt_desc.base_hi << 16;
209 load_idtr(seg_base, ldbuf->idt_desc.limit);
210
211 /* Do the tricky bits now. */
212 load_rm_segs(seg_flags);
213 load_pm_segs();
214 ldall_finish();
215 } else {
216 /* There isn't much point in executing the invalid opcode handler
217 * in an endless loop, so halt right here.
218 */
219 int_enable();
220 halt_forever();
221 }
222}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette