]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/cpu/gdt_enum.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / cpu / gdt_enum.c
1 /* gdt_enum.c
2  *
3  * Library for reading the Global Descriptor Table if possible.
4  * (C) 2009-2012 Jonathan Campbell.
5  * Hackipedia DOS library.
6  *
7  * This code is licensed under the LGPL.
8  * <insert LGPL legal text here>
9  *
10  * Compiles for intended target environments:
11  *   - MS-DOS
12  *   - Windows 3.0/3.1/95/98/ME
13  *   - Windows NT 3.1/3.51/4.0/2000/XP/Vista/7
14  *   - OS/2 16-bit
15  *   - OS/2 32-bit
16  *
17  * If the host OS is loose, or unprotected, this library can abuse the
18  * loophole to read the contents of the Global Descriptor Table. Note
19  * that this is possible under Windows 3.0/3.1/95/98/ME because they
20  * leave it out in the open. Windows NT does NOT let us do this!! Neither
21  * does OS/2!
22  */
23
24 #include <stdio.h>
25 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <dos.h>
32
33 #include <hw/cpu/cpu.h>
34 #include <hw/dos/dos.h>
35 #include <hw/dos/doswin.h>
36 #include <hw/cpu/gdt_enum.h>
37
38 unsigned char                           cpu_gdtlib_can_read = 0,cpu_gdtlib_can_write = 0,cpu_gdtlib_result = 0xFF;
39 uint16_t                                cpu_gdtlib_ldtr = 0;
40 struct x86_gdtr                         cpu_gdtlib_gdtr;
41 struct cpu_gdtlib_entry                 cpu_gdtlib_ldt_ent;
42 #if TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
43 uint16_t                                cpu_gdtlib_gdt_sel = 0; /* selector for reading GDT table */
44 uint16_t                                cpu_gdtlib_ldt_sel = 0; /* selector for reading LDT table */
45 #endif
46
47 #if TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
48 uint32_t                                cpu_gdtlib_lin_bias = 0UL;
49 #endif
50
51 unsigned int cpu_gdtlib_gdt_entries(struct x86_gdtr *r) {
52         if (r->limit == 0xFFFFU) return 0x2000;
53         return ((unsigned int)r->limit + 1U) >> 3;
54 }
55
56 unsigned int cpu_gdtlib_ldt_entries(struct cpu_gdtlib_entry *r) {
57         return ((unsigned int)(r->limit + 1UL)) >> 3UL;
58 }
59
60 int cpu_gdtlib_read_ldtr(uint16_t *sel) {
61         if (!cpu_gdtlib_can_read)
62                 return 0;
63
64 #if TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS) && !defined(TARGET_OS2)
65         /* in what is likely a strange inversion of priorities, the Win 9x kernel allows
66          * DOS programs like us to read the GDTR register. But... attempts to read the LDTR
67          * are not permitted. */
68         if (windows_mode == WINDOWS_ENHANCED)
69                 return 0;
70 #endif
71
72 #if TARGET_MSDOS == 16
73 # if defined(__LARGE__) || defined(__COMPACT__)
74         __asm {
75                 .286p
76                 push    ds
77                 push    si
78                 lds     si,sel
79                 sldt    [si]
80                 pop     si
81                 pop     ds
82         }
83 # else
84         __asm {
85                 .286p
86                 push    si
87                 mov     si,sel
88                 sldt    [si]
89                 pop     si
90         }
91 # endif
92 #else
93         __asm {
94                 push    eax
95                 mov     eax,sel
96                 sldt    [eax]
97                 pop     eax
98         }
99 #endif
100         return 1;
101 }
102
103 int cpu_gdtlib_read_gdtr(struct x86_gdtr *raw) {
104         if (!cpu_gdtlib_can_read)
105                 return 0;
106
107 #if TARGET_MSDOS == 16
108 # if defined(__LARGE__) || defined(__COMPACT__)
109         __asm {
110                 .286p
111                 push    ds
112                 push    si
113                 lds     si,raw
114                 sgdt    [si]
115                 pop     si
116                 pop     ds
117         }
118 # else
119         __asm {
120                 .286p
121                 push    si
122                 mov     si,raw
123                 sgdt    [si]
124                 pop     si
125         }
126 # endif
127 #else
128         __asm {
129                 push    eax
130                 mov     eax,raw
131                 sgdt    [eax]
132                 pop     eax
133         }
134 #endif
135         return 1;
136 }
137
138 int cpu_gdtlib_init() {
139         if (cpu_gdtlib_result == 0xFF) {
140                 cpu_gdtlib_ldtr = 0;
141                 cpu_gdtlib_can_read = cpu_gdtlib_can_write = 0;
142                 if (cpu_basic_level < 0) cpu_probe();
143                 if (cpu_basic_level < 3) return (cpu_gdtlib_result=0);
144                 probe_dos();
145                 detect_windows();
146
147 #if defined(TARGET_OS2)
148                 /* OS/2 16-bit or 32-bit: Not gonna happen */
149                 return (cpu_gdtlib_result=0);
150 #elif TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS)
151         /* 16-bit MS-DOS:
152          *    Either:
153          *       - We're in real mode, and there is no such GDT installed
154          *       - We're in v86 mode, we might be able to locate the GDT and read it
155          *       - We're in v86 mode and VCPI is present: uhhhmmmm.... we'll we could enter via VCPI but that only installs our OWN GDT tables, so...
156          *       - We're in v86 mode under Windows 3.x/9x/ME: DPMI is likely available, we could take advantage of the 9x kernel's permissiveness to read the GDT
157          *       - We're in v86 mode under Windows NT/2000/XP: don't try, not gonna happen. */
158                 if (windows_mode == WINDOWS_NT)
159                         return (cpu_gdtlib_result=0);
160                 if (windows_mode == WINDOWS_REAL)
161                         return (cpu_gdtlib_result=0);
162                 if (!cpu_v86_active) /* if there's no v86 mode, then there's no protected mode GDT table */
163                         return (cpu_gdtlib_result=0);
164                 if (!dpmi_lin2fmemcpy_init())
165                         return (cpu_gdtlib_result=0);
166
167                 /* FIXME: Windows 3.0: This code hangs */
168                 if (windows_mode == WINDOWS_ENHANCED && windows_version < 0x30A) /* Anything less than Windows 3.1 */
169                         return (cpu_gdtlib_result=0);
170
171                 cpu_gdtlib_can_read = 1;
172                 return (cpu_gdtlib_result=1);
173 #elif TARGET_MSDOS == 32 && !defined(TARGET_WINDOWS)
174         /* 32-bit MS-DOS:
175          *    Either:
176          *       - Pure DOS mode is active, the DOS extender did not enable paging, and we're ring 0. we can just read the GDT directly. No problem.
177          *       - Pure DOS mode with EMM386.EXE. The DOS extender used VCPI to enter protected mode, we just have to locate the GDT somehow.
178          *         Usually it ends up in the first 1MB (DOS area) where 1:1 mapping is still retained. We can read it
179          *       - Windows 3.x/9x/ME DOS Box: We're running at ring 3, but we can take advantage of the 9x kernel's apparent permissiveness to
180          *         locate the GDT and read it.
181          *       - Windows NT/2000/XP: Not gonna happen. Even reading the control register will fault our application. */
182                 if (windows_mode == WINDOWS_NT)
183                         return (cpu_gdtlib_result=0);
184
185                 cpu_gdtlib_can_read = 1;
186                 return (cpu_gdtlib_result=1);
187 #elif TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
188         /* 16-bit Win16 application:
189          *    Either:
190          *       - Windows 3.x/9x/ME: As a Win16 app we can use Win16 functions like AllocateSelector(), etc. to give ourself access to the GDT.
191          *       - Windows NT/2000/XP: We might be able to use AllocateSelector(), etc. NTVDM.EXE allows "SGDT" but result seems to point to garbage
192          *
193          * For obvious reasons, we do not attempt to read the GDT in real-mode Windows 3.0 because there is no GDT */
194                 if (windows_mode == WINDOWS_NT)
195                         return (cpu_gdtlib_result=0);
196                 if (windows_mode == WINDOWS_REAL)
197                         return (cpu_gdtlib_result=0);
198
199                 cpu_gdtlib_can_read = 1;
200                 return (cpu_gdtlib_result=1);
201 #elif TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
202         /* 32-bit Windows application:
203          *    Either:
204          *       - Windows 3.1/9x/ME: We can use "sgdt" to read the GDTR, and then just refer to that location in memory.
205          *       - Windows NT/2000/XP: Not gonna happen */
206                 if (windows_mode == WINDOWS_NT)
207                         return (cpu_gdtlib_result=0);
208
209 # ifdef WIN386
210                 {
211                         unsigned short s=0;
212
213                         __asm {
214                                 mov     ax,ds
215                                 mov     s,ax
216                         }
217
218                         /* #1: We need to know what Watcom Win386's data selector bias is */
219                         cpu_gdtlib_lin_bias = GetSelectorBase(s);
220
221                         /* #2: Whatever the base is, Win386 usually sets the limit to 0xFFFFFFFF.
222                          *     The base is usually something like 0x80302330, and the GDT is usually
223                          *     something like 0x80112000, so if we have to rollover BACKWARDS we
224                          *     want to make sure the limit is 0xFFFFFFFF */
225                         if (GetSelectorLimit(s) != 0xFFFFFFFFUL)
226                                 return (cpu_gdtlib_result=0);
227                 }
228 # else
229                 /* Windows 3.1 with Win32s sets the flat selectors to base = 0xFFFF0000 for whatever reason.
230                  * So to properly read the GDT we have to compensate for that */
231                 if (windows_mode == WINDOWS_ENHANCED && windows_version < 0x35F)
232                         cpu_gdtlib_lin_bias = 0xFFFF0000UL;
233 # endif
234
235                 cpu_gdtlib_can_read = 1;
236                 return (cpu_gdtlib_result=1);
237 #endif
238         }
239
240         return cpu_gdtlib_result;
241 }
242
243 void cpu_gdtlib_free() {
244 #if TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
245         if (cpu_gdtlib_gdt_sel != 0) {
246                 FreeSelector(cpu_gdtlib_gdt_sel);
247                 cpu_gdtlib_gdt_sel=0;
248         }
249         if (cpu_gdtlib_ldt_sel != 0) {
250                 FreeSelector(cpu_gdtlib_ldt_sel);
251                 cpu_gdtlib_ldt_sel=0;
252         }
253 #endif
254 }
255
256 int cpu_gdtlib_ldt_read_entry(struct cpu_gdtlib_entry *e,unsigned int i) {
257         if (i >= cpu_gdtlib_ldt_entries(&cpu_gdtlib_ldt_ent))
258                 return 0;
259         if (!cpu_gdtlib_can_read)
260                 return 0;
261
262 #if defined(TARGET_OS2)
263         return 0; /* NOPE */
264 #elif TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS)
265         { /* 16-bit real mode DOS */
266                 unsigned char tmp[8];
267         
268                 if (dpmi_lin2fmemcpy(tmp,cpu_gdtlib_ldt_ent.base + (i << 3),8) == 0)
269                         return 0;
270
271                 e->limit = (uint32_t)( *((uint16_t*)(tmp+0)) );
272                 e->base = (uint32_t)( *((uint32_t*)(tmp+2)) & 0xFFFFFFUL );
273                 e->access = tmp[5];
274                 e->granularity = tmp[6];
275                 e->base |= ((uint32_t)tmp[7]) << 24UL;
276                 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
277         }
278 #elif TARGET_MSDOS == 32 && !defined(TARGET_WINDOWS)
279         { /* 32-bit protected mode DOS */
280                 unsigned char *p = (unsigned char*)cpu_gdtlib_ldt_ent.base + (i << 3);
281                 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
282                 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
283                 e->access = p[5];
284                 e->granularity = p[6];
285                 e->base |= ((uint32_t)p[7]) << 24UL;
286                 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
287         }
288 #elif TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
289         { /* 16-bit Win16 app */
290                 unsigned char far *p;
291
292                 if (cpu_gdtlib_ldt_sel == 0) {
293                         uint16_t myds=0;
294                         __asm mov myds,ds
295                         cpu_gdtlib_ldt_sel = AllocSelector(myds);
296                         if (cpu_gdtlib_ldt_sel == 0) return 0;
297                         SetSelectorLimit(cpu_gdtlib_ldt_sel,(DWORD)cpu_gdtlib_ldt_ent.limit);
298                         SetSelectorBase(cpu_gdtlib_ldt_sel,(DWORD)cpu_gdtlib_ldt_ent.base);
299                 }
300
301                 if (cpu_gdtlib_ldt_sel != 0) {
302                         p = MK_FP(cpu_gdtlib_ldt_sel,i << 3);
303                         e->limit = (uint32_t)( *((uint16_t far*)(p+0)) );
304                         e->base = (uint32_t)( *((uint32_t far*)(p+2)) & 0xFFFFFFUL );
305                         e->access = p[5];
306                         e->granularity = p[6];
307                         e->base |= ((uint32_t)p[7]) << 24UL;
308                         e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
309                 }
310         }
311 #elif TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
312         { /* 32-bit Windows app */
313                 unsigned char *p = (unsigned char*)cpu_gdtlib_ldt_ent.base + (i << 3) - cpu_gdtlib_lin_bias;
314                 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
315                 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
316                 e->access = p[5];
317                 e->granularity = p[6];
318                 e->base |= ((uint32_t)p[7]) << 24UL;
319                 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
320
321         }
322 #endif
323
324 #if !defined(TARGET_OS2)
325         if (e->granularity & 0x80)
326                 e->limit = (e->limit << 12UL) | 0xFFFUL;
327
328         return 1;
329 #endif
330 }
331
332 int cpu_gdtlib_gdt_read_entry(struct cpu_gdtlib_entry *e,unsigned int i) {
333         if (i >= cpu_gdtlib_gdt_entries(&cpu_gdtlib_gdtr))
334                 return 0;
335         if (!cpu_gdtlib_can_read)
336                 return 0;
337
338 #if defined(TARGET_OS2)
339         return 0; /* NOPE */
340 #elif TARGET_MSDOS == 16 && !defined(TARGET_WINDOWS)
341         { /* 16-bit real mode DOS */
342                 unsigned char tmp[8];
343         
344                 if (dpmi_lin2fmemcpy(tmp,cpu_gdtlib_gdtr.base + (i << 3),8) == 0)
345                         return 0;
346
347                 e->limit = (uint32_t)( *((uint16_t*)(tmp+0)) );
348                 e->base = (uint32_t)( *((uint32_t*)(tmp+2)) & 0xFFFFFFUL );
349                 e->access = tmp[5];
350                 e->granularity = tmp[6];
351                 e->base |= ((uint32_t)tmp[7]) << 24UL;
352                 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
353         }
354 #elif TARGET_MSDOS == 32 && !defined(TARGET_WINDOWS)
355         { /* 32-bit protected mode DOS */
356                 unsigned char *p = (unsigned char*)cpu_gdtlib_gdtr.base + (i << 3);
357                 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
358                 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
359                 e->access = p[5];
360                 e->granularity = p[6];
361                 e->base |= ((uint32_t)p[7]) << 24UL;
362                 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
363         }
364 #elif TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
365         { /* 16-bit Win16 app */
366                 unsigned char far *p;
367
368                 if (cpu_gdtlib_gdt_sel == 0) {
369                         uint16_t myds=0;
370                         __asm mov myds,ds
371                         cpu_gdtlib_gdt_sel = AllocSelector(myds);
372                         if (cpu_gdtlib_gdt_sel == 0) return 0;
373                         SetSelectorLimit(cpu_gdtlib_gdt_sel,(DWORD)cpu_gdtlib_gdtr.limit);
374                         SetSelectorBase(cpu_gdtlib_gdt_sel,(DWORD)cpu_gdtlib_gdtr.base);
375                 }
376
377                 if (cpu_gdtlib_gdt_sel != 0) {
378                         p = MK_FP(cpu_gdtlib_gdt_sel,i << 3);
379                         e->limit = (uint32_t)( *((uint16_t far*)(p+0)) );
380                         e->base = (uint32_t)( *((uint32_t far*)(p+2)) & 0xFFFFFFUL );
381                         e->access = p[5];
382                         e->granularity = p[6];
383                         e->base |= ((uint32_t)p[7]) << 24UL;
384                         e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
385                 }
386         }
387 #elif TARGET_MSDOS == 32 && defined(TARGET_WINDOWS)
388         { /* 32-bit Windows app */
389                 unsigned char *p = (unsigned char*)cpu_gdtlib_gdtr.base + (i << 3) - cpu_gdtlib_lin_bias;
390                 e->limit = (uint32_t)( *((uint16_t*)(p+0)) );
391                 e->base = (uint32_t)( *((uint32_t*)(p+2)) & 0xFFFFFFUL );
392                 e->access = p[5];
393                 e->granularity = p[6];
394                 e->base |= ((uint32_t)p[7]) << 24UL;
395                 e->limit |= ((uint32_t)e->granularity & 0xFUL) << 16UL;
396
397         }
398 #endif
399
400 #if !defined(TARGET_OS2)
401         if (e->granularity & 0x80)
402                 e->limit = (e->limit << 12UL) | 0xFFFUL;
403
404         return 1;
405 #endif
406 }
407
408 int cpu_gdtlib_empty_gdt_entry(struct cpu_gdtlib_entry *e) {
409         if (e->limit == 0 && e->base == 0 && e->access == 0 && e->granularity == 0)
410                 return 1;
411
412         return 0;
413 }
414
415 #define cpu_gdtlib_empty_ldt_entry cpu_gdtlib_empty_gdt_entry
416
417 int cpu_gdtlib_entry_is_special(struct cpu_gdtlib_entry *e) {
418         return !(e->access & 0x10);
419 }
420
421 int cpu_gdtlib_entry_is_executable(struct cpu_gdtlib_entry *e) {
422         return (e->access & 0x08);
423 }
424
425 int cpu_gdtlib_read_current_regs() {
426 #if TARGET_MSDOS == 16 && defined(TARGET_WINDOWS)
427         if (cpu_gdtlib_ldt_sel != 0) {
428                 FreeSelector(cpu_gdtlib_ldt_sel);
429                 cpu_gdtlib_ldt_sel=0;
430         }
431 #endif
432
433         /* it matters if we can't read the GDTR */
434         if (!cpu_gdtlib_read_gdtr(&cpu_gdtlib_gdtr))
435                 return 0;
436
437         /* but if we can't read the LDTR, we don't care */
438         memset(&cpu_gdtlib_ldt_ent,0,sizeof(cpu_gdtlib_ldt_ent));
439         cpu_gdtlib_ldtr = 0;
440         cpu_gdtlib_read_ldtr(&cpu_gdtlib_ldtr);
441
442         return 1;
443 }
444
445 int cpu_gdtlib_prepare_to_read_ldt() {
446         if (cpu_gdtlib_ldtr == 0) return 0;
447         if (cpu_gdtlib_ldt_ent.limit == 0 && cpu_gdtlib_ldt_ent.access == 0) {
448                 if (cpu_gdtlib_gdt_read_entry(&cpu_gdtlib_ldt_ent,cpu_gdtlib_ldtr>>3) == 0)
449                         return 0;
450                 if (cpu_gdtlib_empty_gdt_entry(&cpu_gdtlib_ldt_ent))
451                         return 0;
452         }
453
454         return 1;
455 }
456