xref: /reactos/win32ss/drivers/videoprt/int10.c (revision 74ec94e1)
1 /*
2  * VideoPort driver
3  *
4  * Copyright (C) 2002, 2003, 2004 ReactOS Team
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21 
22 #include "videoprt.h"
23 
24 #include <ndk/kefuncs.h>
25 #include <ndk/halfuncs.h>
26 #include <ndk/mmfuncs.h>
27 
28 #define NDEBUG
29 #include <debug.h>
30 
31 /* PRIVATE FUNCTIONS **********************************************************/
32 
33 #define IsLowV86Mem(_Seg, _Off) ((((_Seg) << 4) + (_Off)) < (0xa0000))
34 
35 /* Those two functions below are there so that CSRSS can't access low mem.
36  * Expecially, MAKE IT CRASH ON NULL ACCESS */
37 static
38 VOID
39 ProtectLowV86Mem(VOID)
40 {
41     /* We pass a non-NULL address so that ZwAllocateVirtualMemory really does it
42      * And we truncate one page to get the right range spanned. */
43     PVOID BaseAddress = (PVOID)1;
44     NTSTATUS Status;
45     SIZE_T ViewSize = 0xa0000 - PAGE_SIZE;
46 
47     /* We should only do that for CSRSS. */
48     ASSERT(PsGetCurrentProcess() == (PEPROCESS)CsrProcess);
49 
50     /* Commit (again) the pages, but with PAGE_NOACCESS protection */
51     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
52                                      &BaseAddress,
53                                      0,
54                                      &ViewSize,
55                                      MEM_COMMIT,
56                                      PAGE_NOACCESS);
57     ASSERT(NT_SUCCESS(Status));
58 }
59 
60 static
61 VOID
62 UnprotectLowV86Mem(VOID)
63 {
64     /* We pass a non-NULL address so that ZwAllocateVirtualMemory really does it
65      * And we truncate one page to get the right range spanned. */
66     PVOID BaseAddress = (PVOID)1;
67     NTSTATUS Status;
68     SIZE_T ViewSize = 0xa0000 - PAGE_SIZE;
69 
70     /* We should only do that for CSRSS, for the v86 address space */
71     ASSERT(PsGetCurrentProcess() == (PEPROCESS)CsrProcess);
72 
73     /* Commit (again) the pages, but with PAGE_READWRITE protection */
74     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
75                                      &BaseAddress,
76                                      0,
77                                      &ViewSize,
78                                      MEM_COMMIT,
79                                      PAGE_READWRITE);
80     ASSERT(NT_SUCCESS(Status));
81 }
82 
83 #if defined(_M_IX86) || defined(_M_AMD64)
84 NTSTATUS
85 NTAPI
86 IntInitializeVideoAddressSpace(VOID)
87 {
88     OBJECT_ATTRIBUTES ObjectAttributes;
89     UNICODE_STRING PhysMemName = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
90     NTSTATUS Status;
91     HANDLE PhysMemHandle;
92     PVOID BaseAddress;
93     LARGE_INTEGER Offset;
94     SIZE_T ViewSize;
95 #ifdef _M_IX86
96     CHAR IVTAndBda[1024 + 256];
97 #endif // _M_IX86
98 
99     /* Free the 1MB pre-reserved region. In reality, ReactOS should simply support us mapping the view into the reserved area, but it doesn't. */
100     BaseAddress = 0;
101     ViewSize = 1024 * 1024;
102     Status = ZwFreeVirtualMemory(NtCurrentProcess(),
103                                  &BaseAddress,
104                                  &ViewSize,
105                                  MEM_RELEASE);
106     if (!NT_SUCCESS(Status))
107     {
108         DPRINT1("Couldn't unmap reserved memory (%x)\n", Status);
109         return 0;
110     }
111 
112     /* Open the physical memory section */
113     InitializeObjectAttributes(&ObjectAttributes,
114                                &PhysMemName,
115                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
116                                NULL,
117                                NULL);
118     Status = ZwOpenSection(&PhysMemHandle,
119                            SECTION_ALL_ACCESS,
120                            &ObjectAttributes);
121     if (!NT_SUCCESS(Status))
122     {
123         DPRINT1("Couldn't open \\Device\\PhysicalMemory\n");
124         return Status;
125     }
126 
127     /* Map the BIOS and device registers into the address space */
128     Offset.QuadPart = 0xa0000;
129     ViewSize = 0x100000 - 0xa0000;
130     BaseAddress = (PVOID)0xa0000;
131     Status = ZwMapViewOfSection(PhysMemHandle,
132                                 NtCurrentProcess(),
133                                 &BaseAddress,
134                                 0,
135                                 ViewSize,
136                                 &Offset,
137                                 &ViewSize,
138                                 ViewUnmap,
139                                 0,
140                                 PAGE_EXECUTE_READWRITE);
141     if (!NT_SUCCESS(Status))
142     {
143         DPRINT1("Couldn't map physical memory (%x)\n", Status);
144         ZwClose(PhysMemHandle);
145         return Status;
146     }
147 
148     /* Close physical memory section handle */
149     ZwClose(PhysMemHandle);
150 
151     if (BaseAddress != (PVOID)0xa0000)
152     {
153         DPRINT1("Couldn't map physical memory at the right address (was %x)\n",
154                 BaseAddress);
155         return STATUS_UNSUCCESSFUL;
156     }
157 
158     /* Allocate some low memory to use for the non-BIOS
159      * parts of the v86 mode address space
160      */
161     BaseAddress = (PVOID)0x1;
162     ViewSize = 0xa0000 - 0x1000;
163     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
164                                      &BaseAddress,
165                                      0,
166                                      &ViewSize,
167                                      MEM_RESERVE | MEM_COMMIT,
168                                      PAGE_EXECUTE_READWRITE);
169     if (!NT_SUCCESS(Status))
170     {
171         DPRINT1("Failed to allocate virtual memory (Status %x)\n", Status);
172         return Status;
173     }
174     if (BaseAddress != (PVOID)0x0)
175     {
176         DPRINT1("Failed to allocate virtual memory at right address (was %x)\n",
177                 BaseAddress);
178         return 0;
179     }
180 
181 #ifdef _M_IX86
182     /* Get the real mode IVT and BDA from the kernel */
183     Status = NtVdmControl(VdmInitialize, IVTAndBda);
184     if (!NT_SUCCESS(Status))
185     {
186         DPRINT1("NtVdmControl failed (status %x)\n", Status);
187         return Status;
188     }
189 #endif // _M_IX86
190 
191     /* Protect the V86 address space after this */
192     ProtectLowV86Mem();
193 
194     /* Return success */
195     return STATUS_SUCCESS;
196 }
197 #else
198 NTSTATUS
199 NTAPI
200 IntInitializeVideoAddressSpace(VOID)
201 {
202     UNIMPLEMENTED;
203     NT_ASSERT(FALSE);
204     return STATUS_NOT_IMPLEMENTED;
205 }
206 #endif
207 
208 VP_STATUS
209 NTAPI
210 IntInt10AllocateBuffer(
211     IN PVOID Context,
212     OUT PUSHORT Seg,
213     OUT PUSHORT Off,
214     IN OUT PULONG Length)
215 {
216     NTSTATUS Status;
217 #ifdef _M_IX86
218     PVOID MemoryAddress;
219     PKPROCESS CallingProcess = (PKPROCESS)PsGetCurrentProcess();
220     KAPC_STATE ApcState;
221     SIZE_T Size;
222 
223     TRACE_(VIDEOPRT, "IntInt10AllocateBuffer\n");
224 
225     IntAttachToCSRSS(&CallingProcess, &ApcState);
226 
227     Size = *Length;
228     MemoryAddress = (PVOID)0x20000;
229 
230     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
231                                      &MemoryAddress,
232                                      0,
233                                      &Size,
234                                      MEM_COMMIT,
235                                      PAGE_EXECUTE_READWRITE);
236     if (!NT_SUCCESS(Status))
237     {
238         WARN_(VIDEOPRT, "- ZwAllocateVirtualMemory failed\n");
239         IntDetachFromCSRSS(&CallingProcess, &ApcState);
240         return ERROR_NOT_ENOUGH_MEMORY;
241     }
242 
243     if (MemoryAddress > (PVOID)(0x100000 - Size))
244     {
245         ZwFreeVirtualMemory(NtCurrentProcess(),
246                             &MemoryAddress,
247                             &Size,
248                             MEM_RELEASE);
249         WARN_(VIDEOPRT, "- Unacceptable memory allocated\n");
250         IntDetachFromCSRSS(&CallingProcess, &ApcState);
251         return ERROR_NOT_ENOUGH_MEMORY;
252     }
253 
254     *Length = (ULONG)Size;
255     *Seg = (USHORT)((ULONG_PTR)MemoryAddress >> 4);
256     *Off = (USHORT)((ULONG_PTR)MemoryAddress & 0xF);
257 
258     INFO_(VIDEOPRT, "- Segment: %x\n", (ULONG_PTR)MemoryAddress >> 4);
259     INFO_(VIDEOPRT, "- Offset: %x\n", (ULONG_PTR)MemoryAddress & 0xF);
260     INFO_(VIDEOPRT, "- Length: %x\n", *Length);
261 
262     IntDetachFromCSRSS(&CallingProcess, &ApcState);
263 
264     return NO_ERROR;
265 #else
266     Status = x86BiosAllocateBuffer(Length, Seg, Off);
267     return NT_SUCCESS(Status) ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY;
268 #endif
269 }
270 
271 VP_STATUS
272 NTAPI
273 IntInt10FreeBuffer(
274     IN PVOID Context,
275     IN USHORT Seg,
276     IN USHORT Off)
277 {
278     NTSTATUS Status;
279 #ifdef _M_IX86
280     PVOID MemoryAddress = (PVOID)((ULONG_PTR)(Seg << 4) | Off);
281     PKPROCESS CallingProcess = (PKPROCESS)PsGetCurrentProcess();
282     KAPC_STATE ApcState;
283     SIZE_T Size = 0;
284 
285     TRACE_(VIDEOPRT, "IntInt10FreeBuffer\n");
286     INFO_(VIDEOPRT, "- Segment: %x\n", Seg);
287     INFO_(VIDEOPRT, "- Offset: %x\n", Off);
288 
289     IntAttachToCSRSS(&CallingProcess, &ApcState);
290     Status = ZwFreeVirtualMemory(NtCurrentProcess(),
291                                  &MemoryAddress,
292                                  &Size,
293                                  MEM_RELEASE);
294 
295     IntDetachFromCSRSS(&CallingProcess, &ApcState);
296 
297     return Status;
298 #else
299     Status = x86BiosFreeBuffer(Seg, Off);
300     return NT_SUCCESS(Status) ? NO_ERROR : ERROR_INVALID_PARAMETER;
301 #endif
302 }
303 
304 VP_STATUS
305 NTAPI
306 IntInt10ReadMemory(
307     IN PVOID Context,
308     IN USHORT Seg,
309     IN USHORT Off,
310     OUT PVOID Buffer,
311     IN ULONG Length)
312 {
313 #ifdef _M_IX86
314     PKPROCESS CallingProcess = (PKPROCESS)PsGetCurrentProcess();
315     KAPC_STATE ApcState;
316 
317     TRACE_(VIDEOPRT, "IntInt10ReadMemory\n");
318     INFO_(VIDEOPRT, "- Segment: %x\n", Seg);
319     INFO_(VIDEOPRT, "- Offset: %x\n", Off);
320     INFO_(VIDEOPRT, "- Buffer: %x\n", Buffer);
321     INFO_(VIDEOPRT, "- Length: %x\n", Length);
322 
323     IntAttachToCSRSS(&CallingProcess, &ApcState);
324 
325     if (IsLowV86Mem(Seg, Off))
326         UnprotectLowV86Mem();
327     RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)(Seg << 4) | Off), Length);
328     if (IsLowV86Mem(Seg, Off))
329         ProtectLowV86Mem();
330 
331     IntDetachFromCSRSS(&CallingProcess, &ApcState);
332 
333     return NO_ERROR;
334 #else
335     NTSTATUS Status;
336 
337     Status = x86BiosReadMemory(Seg, Off, Buffer, Length);
338     return NT_SUCCESS(Status) ? NO_ERROR : ERROR_INVALID_PARAMETER;
339 #endif
340 }
341 
342 VP_STATUS
343 NTAPI
344 IntInt10WriteMemory(
345     IN PVOID Context,
346     IN USHORT Seg,
347     IN USHORT Off,
348     IN PVOID Buffer,
349     IN ULONG Length)
350 {
351 #ifdef _M_IX86
352     PKPROCESS CallingProcess = (PKPROCESS)PsGetCurrentProcess();
353     KAPC_STATE ApcState;
354 
355     TRACE_(VIDEOPRT, "IntInt10WriteMemory\n");
356     INFO_(VIDEOPRT, "- Segment: %x\n", Seg);
357     INFO_(VIDEOPRT, "- Offset: %x\n", Off);
358     INFO_(VIDEOPRT, "- Buffer: %x\n", Buffer);
359     INFO_(VIDEOPRT, "- Length: %x\n", Length);
360 
361     IntAttachToCSRSS(&CallingProcess, &ApcState);
362     if (IsLowV86Mem(Seg, Off))
363         UnprotectLowV86Mem();
364     RtlCopyMemory((PVOID)((ULONG_PTR)(Seg << 4) | Off), Buffer, Length);
365     if (IsLowV86Mem(Seg, Off))
366         ProtectLowV86Mem();
367     IntDetachFromCSRSS(&CallingProcess, &ApcState);
368 
369     return NO_ERROR;
370 #else
371     NTSTATUS Status;
372 
373     Status = x86BiosWriteMemory(Seg, Off, Buffer, Length);
374     return NT_SUCCESS(Status) ? NO_ERROR : ERROR_INVALID_PARAMETER;
375 #endif
376 }
377 
378 VP_STATUS
379 NTAPI
380 IntInt10CallBios(
381     IN PVOID Context,
382     IN OUT PINT10_BIOS_ARGUMENTS BiosArguments)
383 {
384 #ifdef _M_AMD64
385     X86_BIOS_REGISTERS BiosContext;
386 #else
387     CONTEXT BiosContext;
388 #endif
389     NTSTATUS Status;
390     PKPROCESS CallingProcess = (PKPROCESS)PsGetCurrentProcess();
391     KAPC_STATE ApcState;
392 
393     /* Attach to CSRSS */
394     IntAttachToCSRSS(&CallingProcess, &ApcState);
395 
396     /* Clear the context */
397     RtlZeroMemory(&BiosContext, sizeof(BiosContext));
398 
399     /* Fill out the bios arguments */
400     BiosContext.Eax = BiosArguments->Eax;
401     BiosContext.Ebx = BiosArguments->Ebx;
402     BiosContext.Ecx = BiosArguments->Ecx;
403     BiosContext.Edx = BiosArguments->Edx;
404     BiosContext.Esi = BiosArguments->Esi;
405     BiosContext.Edi = BiosArguments->Edi;
406     BiosContext.Ebp = BiosArguments->Ebp;
407     BiosContext.SegDs = BiosArguments->SegDs;
408     BiosContext.SegEs = BiosArguments->SegEs;
409 
410     /* Do the ROM BIOS call */
411     (void)KeWaitForMutexObject(&VideoPortInt10Mutex,
412                                Executive,
413                                KernelMode,
414                                FALSE,
415                                NULL);
416 
417     /* The kernel needs access here */
418     UnprotectLowV86Mem();
419 #ifdef _M_AMD64
420     Status = x86BiosCall(0x10, &BiosContext) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
421 #else
422     Status = Ke386CallBios(0x10, &BiosContext);
423 #endif
424     ProtectLowV86Mem();
425 
426     KeReleaseMutex(&VideoPortInt10Mutex, FALSE);
427 
428     /* Return the arguments */
429     BiosArguments->Eax = BiosContext.Eax;
430     BiosArguments->Ebx = BiosContext.Ebx;
431     BiosArguments->Ecx = BiosContext.Ecx;
432     BiosArguments->Edx = BiosContext.Edx;
433     BiosArguments->Esi = BiosContext.Esi;
434     BiosArguments->Edi = BiosContext.Edi;
435     BiosArguments->Ebp = BiosContext.Ebp;
436     BiosArguments->SegDs = (USHORT)BiosContext.SegDs;
437     BiosArguments->SegEs = (USHORT)BiosContext.SegEs;
438 
439     /* Detach and return status */
440     IntDetachFromCSRSS(&CallingProcess, &ApcState);
441 
442     if (NT_SUCCESS(Status))
443     {
444         return NO_ERROR;
445     }
446 
447     return ERROR_INVALID_PARAMETER;
448 }
449 
450 /* PUBLIC FUNCTIONS ***********************************************************/
451 
452 /*
453  * @implemented
454  */
455 VP_STATUS
456 NTAPI
457 VideoPortInt10(
458     IN PVOID HwDeviceExtension,
459     IN PVIDEO_X86_BIOS_ARGUMENTS BiosArguments)
460 {
461     INT10_BIOS_ARGUMENTS Int10BiosArguments;
462     VP_STATUS Status;
463 
464     if (!CsrProcess)
465     {
466         return ERROR_INVALID_PARAMETER;
467     }
468 
469     /* Copy arguments to other format */
470     RtlCopyMemory(&Int10BiosArguments, BiosArguments, sizeof(*BiosArguments));
471     Int10BiosArguments.SegDs = 0;
472     Int10BiosArguments.SegEs = 0;
473 
474     /* Do the BIOS call */
475     Status = IntInt10CallBios(NULL, &Int10BiosArguments);
476 
477     /* Copy results back */
478     RtlCopyMemory(BiosArguments, &Int10BiosArguments, sizeof(*BiosArguments));
479 
480     return Status;
481 }
482