xref: /reactos/hal/halx86/generic/x86bios.c (revision 46831c53)
1 /*
2  * PROJECT:         ReactOS HAL
3  * LICENSE:         GPL, See COPYING in the top level directory
4  * FILE:            hal/halx86/amd64/x86bios.c
5  * PURPOSE:
6  * PROGRAMMERS:     Timo Kreuzer (timo.kreuzer@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <hal.h>
12 //#define NDEBUG
13 #include <debug.h>
14 
15 #include <fast486.h>
16 
17 /* GLOBALS *******************************************************************/
18 
19 /* This page serves as fallback for pages used by Mm */
20 PFN_NUMBER x86BiosFallbackPfn;
21 
22 BOOLEAN x86BiosIsInitialized;
23 LONG x86BiosBufferIsAllocated = 0;
24 PUCHAR x86BiosMemoryMapping;
25 
26 /* This the physical address of the bios buffer */
27 ULONG64 x86BiosBufferPhysical;
28 
29 VOID
30 NTAPI
DbgDumpPage(PUCHAR MemBuffer,USHORT Segment)31 DbgDumpPage(PUCHAR MemBuffer, USHORT Segment)
32 {
33     ULONG x, y, Offset;
34 
35     for (y = 0; y < 0x100; y++)
36     {
37         for (x = 0; x < 0x10; x++)
38         {
39             Offset = Segment * 16 + y * 16 + x;
40             DbgPrint("%02x ", MemBuffer[Offset]);
41         }
42         DbgPrint("\n");
43     }
44 }
45 
46 VOID
47 NTAPI
HalInitializeBios(_In_ ULONG Phase,_In_ PLOADER_PARAMETER_BLOCK LoaderBlock)48 HalInitializeBios(
49     _In_ ULONG Phase,
50     _In_ PLOADER_PARAMETER_BLOCK LoaderBlock)
51 {
52     PPFN_NUMBER PfnArray;
53     PFN_NUMBER Pfn, Last;
54     PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
55     PLIST_ENTRY ListEntry;
56     PMDL Mdl;
57     ULONG64 PhysicalAddress;
58 
59     if (Phase == 0)
60     {
61         /* Allocate one page for a fallback mapping */
62         PhysicalAddress = HalpAllocPhysicalMemory(LoaderBlock,
63                                                   0x100000,
64                                                   1,
65                                                   FALSE);
66         if (PhysicalAddress == 0)
67         {
68             ASSERT(FALSE);
69         }
70 
71         x86BiosFallbackPfn = PhysicalAddress / PAGE_SIZE;
72         ASSERT(x86BiosFallbackPfn != 0);
73 
74         /* Allocate a page for the buffer allocation */
75         x86BiosBufferPhysical = HalpAllocPhysicalMemory(LoaderBlock,
76                                                         0x100000,
77                                                         1,
78                                                         FALSE);
79         if (x86BiosBufferPhysical == 0)
80         {
81             ASSERT(FALSE);
82         }
83     }
84     else
85     {
86 
87         /* Allocate an MDL for 1MB */
88         Mdl = IoAllocateMdl(NULL, 0x100000, FALSE, FALSE, NULL);
89         if (!Mdl)
90         {
91             ASSERT(FALSE);
92         }
93 
94         /* Get pointer to the pfn array */
95         PfnArray = MmGetMdlPfnArray(Mdl);
96 
97         /* Fill the array with the fallback page */
98         for (Pfn = 0; Pfn < 0x100; Pfn++)
99         {
100             PfnArray[Pfn] = x86BiosFallbackPfn;
101         }
102 
103         /* Loop the memory descriptors */
104         for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
105              ListEntry != &LoaderBlock->MemoryDescriptorListHead;
106              ListEntry = ListEntry->Flink)
107         {
108             /* Get the memory descriptor */
109             Descriptor = CONTAINING_RECORD(ListEntry,
110                                            MEMORY_ALLOCATION_DESCRIPTOR,
111                                            ListEntry);
112 
113             /* Check if the memory is in the low 1 MB range */
114             if (Descriptor->BasePage < 0x100)
115             {
116                 /* Check if the memory type is firmware */
117                 if ((Descriptor->MemoryType == LoaderFirmwarePermanent) ||
118                     (Descriptor->MemoryType == LoaderSpecialMemory))
119                 {
120                     /* It's firmware, so map it! */
121                     Last = min(Descriptor->BasePage + Descriptor->PageCount, 0x100);
122                     for (Pfn = Descriptor->BasePage; Pfn < Last; Pfn++)
123                     {
124                         /* Set each physical page in the MDL */
125                         PfnArray[Pfn] = Pfn;
126                     }
127                 }
128             }
129         }
130 
131         /* Map this page proper, too */
132         Pfn = x86BiosBufferPhysical / PAGE_SIZE;
133         PfnArray[Pfn] = Pfn;
134 
135         Mdl->MdlFlags = MDL_PAGES_LOCKED;
136 
137         /* Map the MDL to system space */
138         x86BiosMemoryMapping = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);
139         ASSERT(x86BiosMemoryMapping);
140 
141         DPRINT1("*x86BiosMemoryMapping: %p, %p\n",
142                 *(PVOID*)x86BiosMemoryMapping, *(PVOID*)(x86BiosMemoryMapping + 8));
143         //DbgDumpPage(x86BiosMemoryMapping, 0xc351);
144 
145         x86BiosIsInitialized = TRUE;
146     }
147 }
148 
149 NTSTATUS
150 NTAPI
x86BiosAllocateBuffer(_In_ ULONG * Size,_In_ USHORT * Segment,_In_ USHORT * Offset)151 x86BiosAllocateBuffer(
152     _In_ ULONG *Size,
153     _In_ USHORT *Segment,
154     _In_ USHORT *Offset)
155 {
156     /* Check if the system is initialized and the buffer is large enough */
157     if (!x86BiosIsInitialized || (*Size > PAGE_SIZE))
158     {
159         /* Something was wrong, fail! */
160         return STATUS_INSUFFICIENT_RESOURCES;
161     }
162 
163     /* Check if the buffer is already allocated */
164     if (InterlockedBitTestAndSet(&x86BiosBufferIsAllocated, 0))
165     {
166         /* Buffer was already allocated, fail */
167         return STATUS_INSUFFICIENT_RESOURCES;
168     }
169 
170     /* The buffer is sufficient, return hardcoded address and size */
171     *Size = PAGE_SIZE;
172     *Segment = x86BiosBufferPhysical / 16;
173     *Offset = 0;
174 
175     return STATUS_SUCCESS;
176 }
177 
178 NTSTATUS
179 NTAPI
x86BiosFreeBuffer(_In_ USHORT Segment,_In_ USHORT Offset)180 x86BiosFreeBuffer(
181     _In_ USHORT Segment,
182     _In_ USHORT Offset)
183 {
184     /* Check if the system is initialized and if the address matches */
185     if (!x86BiosIsInitialized || (Segment != 0x2000) || (Offset != 0))
186     {
187         /* Something was wrong, fail */
188         return STATUS_INVALID_PARAMETER;
189     }
190 
191     /* Check if the buffer was allocated */
192     if (!InterlockedBitTestAndReset(&x86BiosBufferIsAllocated, 0))
193     {
194         /* It was not, fail */
195         return STATUS_INVALID_PARAMETER;
196     }
197 
198     /* Buffer is freed, nothing more to do */
199     return STATUS_SUCCESS;
200 }
201 
202 NTSTATUS
203 NTAPI
x86BiosReadMemory(_In_ USHORT Segment,_In_ USHORT Offset,_Out_writes_bytes_ (Size)PVOID Buffer,_In_ ULONG Size)204 x86BiosReadMemory(
205     _In_ USHORT Segment,
206     _In_ USHORT Offset,
207     _Out_writes_bytes_(Size) PVOID Buffer,
208     _In_ ULONG Size)
209 {
210     ULONG_PTR Address;
211 
212     /* Calculate the physical address */
213     Address = (Segment << 4) + Offset;
214 
215     /* Check if it's valid */
216     if (!x86BiosIsInitialized || ((Address + Size) > 0x100000))
217     {
218         /* Invalid */
219         return STATUS_INVALID_PARAMETER;
220     }
221 
222     /* Copy the memory to the buffer */
223     RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size);
224 
225     /* Return success */
226     return STATUS_SUCCESS;
227 }
228 
229 NTSTATUS
230 NTAPI
x86BiosWriteMemory(_In_ USHORT Segment,_In_ USHORT Offset,_In_reads_bytes_ (Size)PVOID Buffer,_In_ ULONG Size)231 x86BiosWriteMemory(
232     _In_ USHORT Segment,
233     _In_ USHORT Offset,
234     _In_reads_bytes_(Size) PVOID Buffer,
235     _In_ ULONG Size)
236 {
237     ULONG_PTR Address;
238 
239     /* Calculate the physical address */
240     Address = (Segment << 4) + Offset;
241 
242     /* Check if it's valid */
243     if (!x86BiosIsInitialized || ((Address + Size) > 0x100000))
244     {
245         /* Invalid */
246         return STATUS_INVALID_PARAMETER;
247     }
248 
249     /* Copy the memory from the buffer */
250     RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size);
251 
252     /* Return success */
253     return STATUS_SUCCESS;
254 }
255 
256 static
257 VOID
258 FASTCALL
x86MemRead(PFAST486_STATE State,ULONG Address,PVOID Buffer,ULONG Size)259 x86MemRead(
260     PFAST486_STATE State,
261     ULONG Address,
262     PVOID Buffer,
263     ULONG Size)
264 {
265     /* Validate the address range */
266     if (((ULONG64)Address + Size) < 0x100000)
267     {
268         RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size);
269     }
270     else
271     {
272         RtlFillMemory(Buffer, Size, 0xCC);
273         DPRINT1("x86MemRead: invalid read at 0x%lx (size 0x%lx)\n", Address, Size);
274     }
275 }
276 
277 static
278 VOID
279 FASTCALL
x86MemWrite(PFAST486_STATE State,ULONG Address,PVOID Buffer,ULONG Size)280 x86MemWrite(
281     PFAST486_STATE State,
282     ULONG Address,
283     PVOID Buffer,
284     ULONG Size)
285 {
286     /* Validate the address range */
287     if (((ULONG64)Address + Size) < 0x100000)
288     {
289         RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size);
290     }
291     else
292     {
293         DPRINT1("x86MemWrite: invalid write at 0x%lx (size 0x%lx)\n", Address, Size);
294     }
295 }
296 
297 static
298 BOOLEAN
ValidatePort(USHORT Port,UCHAR Size,BOOLEAN IsWrite)299 ValidatePort(
300     USHORT Port,
301     UCHAR Size,
302     BOOLEAN IsWrite)
303 {
304     switch (Port)
305     {
306         // VGA: https://wiki.osdev.org/VGA_Hardware#Port_0x3C0
307         case 0x3C0: return (Size == 1) && IsWrite;
308         case 0x3C1: return (Size == 1) && !IsWrite;
309         case 0x3C2: return (Size == 1) && IsWrite;
310         case 0x3C4: return IsWrite;
311         case 0x3C5: return (Size <= 2);
312         case 0x3C7: return (Size == 1) && IsWrite;
313         case 0x3CC: return (Size == 1) && !IsWrite;
314         case 0x3CE: return IsWrite;
315         case 0x3CF: return (Size <= 2);
316         case 0x3D4: return IsWrite;
317         case 0x3D5: return (Size <= 2);
318         case 0x3C6: return (Size == 1);
319         case 0x3C8: return (Size == 1) && IsWrite;
320         case 0x3C9: return (Size == 1);
321         case 0x3DA: return (Size == 1) && !IsWrite;
322 
323         // OVMF debug messages used by VBox / QEMU
324         // https://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/README
325         case 0x402: return (Size == 1) && IsWrite;
326 
327         // BOCHS VBE: https://forum.osdev.org/viewtopic.php?f=1&t=14639
328         case 0x1CE: return (Size == 1) && IsWrite;
329         case 0x1CF: return (Size == 1);
330 
331         // CHECKME!
332         case 0x3B6: return (Size <= 2);
333     }
334 
335     /* Allow but report unknown ports, we trust the BIOS for now */
336     DPRINT1("Unknown port 0x%x, size %d, write %d\n", Port, Size, IsWrite);
337     return TRUE;
338 }
339 
340 static
341 VOID
342 FASTCALL
x86IoRead(PFAST486_STATE State,USHORT Port,PVOID Buffer,ULONG DataCount,UCHAR DataSize)343 x86IoRead(
344     PFAST486_STATE State,
345     USHORT Port,
346     PVOID Buffer,
347     ULONG DataCount,
348     UCHAR DataSize)
349 {
350     /* Validate the port */
351     if (!ValidatePort(Port, DataSize, FALSE))
352     {
353         DPRINT1("Invalid IO port read access (port: 0x%x, count: 0x%x)\n", Port, DataSize);
354     }
355 
356     switch (DataSize)
357     {
358         case 1: READ_PORT_BUFFER_UCHAR((PUCHAR)(ULONG_PTR)Port, Buffer, DataCount); return;
359         case 2: READ_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)Port, Buffer, DataCount); return;
360         case 4: READ_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)Port, Buffer, DataCount); return;
361     }
362 }
363 
364 static
365 VOID
366 FASTCALL
x86IoWrite(PFAST486_STATE State,USHORT Port,PVOID Buffer,ULONG DataCount,UCHAR DataSize)367 x86IoWrite(
368     PFAST486_STATE State,
369     USHORT Port,
370     PVOID Buffer,
371     ULONG DataCount,
372     UCHAR DataSize)
373 {
374     /* Validate the port */
375     if (!ValidatePort(Port, DataSize, TRUE))
376     {
377         DPRINT1("Invalid IO port write access (port: 0x%x, count: 0x%x)\n", Port, DataSize);
378     }
379 
380     switch (DataSize)
381     {
382         case 1: WRITE_PORT_BUFFER_UCHAR((PUCHAR)(ULONG_PTR)Port, Buffer, DataCount); return;
383         case 2: WRITE_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)Port, Buffer, DataCount); return;
384         case 4: WRITE_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)Port, Buffer, DataCount); return;
385     }
386 }
387 
388 static
389 VOID
390 FASTCALL
x86BOP(PFAST486_STATE State,UCHAR BopCode)391 x86BOP(
392     PFAST486_STATE State,
393     UCHAR BopCode)
394 {
395     ASSERT(FALSE);
396 }
397 
398 static
399 UCHAR
400 FASTCALL
x86IntAck(PFAST486_STATE State)401 x86IntAck (
402     PFAST486_STATE State)
403 {
404     ASSERT(FALSE);
405     return 0;
406 }
407 
408 BOOLEAN
409 NTAPI
x86BiosCall(_In_ ULONG InterruptNumber,_Inout_ PX86_BIOS_REGISTERS Registers)410 x86BiosCall(
411     _In_ ULONG InterruptNumber,
412     _Inout_ PX86_BIOS_REGISTERS Registers)
413 {
414     const ULONG StackBase = 0x2000;
415     FAST486_STATE EmulatorContext;
416     ULONG FlatIp;
417     PUCHAR InstructionPointer;
418 
419     /* Initialize the emulator context */
420     Fast486Initialize(&EmulatorContext,
421                       x86MemRead,
422                       x86MemWrite,
423                       x86IoRead,
424                       x86IoWrite,
425                       x86BOP,
426                       x86IntAck,
427                       NULL,  // FpuCallback,
428                       NULL); // Tlb
429 
430     /* Copy the registers */
431     EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long = Registers->Eax;
432     EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long = Registers->Ebx;
433     EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long = Registers->Ecx;
434     EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long = Registers->Edx;
435     EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long = Registers->Esi;
436     EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long = Registers->Edi;
437     EmulatorContext.SegmentRegs[FAST486_REG_DS].Selector = Registers->SegDs;
438     EmulatorContext.SegmentRegs[FAST486_REG_ES].Selector = Registers->SegEs;
439 
440     /* Set Eflags */
441     EmulatorContext.Flags.Long = 0;
442     EmulatorContext.Flags.AlwaysSet = 1;
443     EmulatorContext.Flags.If = 1;
444 
445     /* Set up the INT stub */
446     FlatIp = StackBase - 4;
447     InstructionPointer = x86BiosMemoryMapping + FlatIp;
448     InstructionPointer[0] = 0xCD; // INT instruction
449     InstructionPointer[1] = (UCHAR)InterruptNumber;
450     InstructionPointer[2] = 0x90; // NOP. We will stop at this address.
451 
452     /* Set the stack pointer */
453     Fast486SetStack(&EmulatorContext, 0, StackBase - 8);
454 
455     /* Start execution at the INT stub */
456     Fast486ExecuteAt(&EmulatorContext, 0x00, FlatIp);
457 
458     while (TRUE)
459     {
460         /* Get the current flat IP */
461         FlatIp = (EmulatorContext.SegmentRegs[FAST486_REG_CS].Selector << 4) +
462                  EmulatorContext.InstPtr.Long;
463 
464         /* Make sure we haven't left the allowed memory range */
465         if (FlatIp >= 0x100000)
466         {
467             DPRINT1("x86BiosCall: invalid IP (0x%lx) during BIOS execution\n", FlatIp);
468             return FALSE;
469         }
470 
471         /* Check if we returned from our int stub */
472         if (FlatIp == (StackBase - 2))
473         {
474             /* We are done! */
475             break;
476         }
477 
478         /* Emulate one instruction */
479         Fast486StepInto(&EmulatorContext);
480     }
481 
482     /* Copy the registers back */
483     Registers->Eax = EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long;
484     Registers->Ebx = EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long;
485     Registers->Ecx = EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long;
486     Registers->Edx = EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long;
487     Registers->Esi = EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long;
488     Registers->Edi = EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long;
489     Registers->SegDs = EmulatorContext.SegmentRegs[FAST486_REG_DS].Selector;
490     Registers->SegEs = EmulatorContext.SegmentRegs[FAST486_REG_ES].Selector;
491 
492     return TRUE;
493 }
494 
495 #ifdef _M_AMD64
496 BOOLEAN
497 NTAPI
HalpBiosDisplayReset(VOID)498 HalpBiosDisplayReset(VOID)
499 {
500 #if 0
501     X86_BIOS_REGISTERS Registers;
502     ULONG OldEflags;
503 
504     /* Save flags and disable interrupts */
505     OldEflags = __readeflags();
506     _disable();
507 
508     /* Set AH = 0 (Set video mode), AL = 0x12 (640x480x16 vga) */
509     Registers.Eax = 0x12;
510 
511     /* Call INT 0x10 */
512     x86BiosCall(0x10, &Registers);
513 
514     // FIXME: check result
515 
516     /* Restore previous flags */
517     __writeeflags(OldEflags);
518     return TRUE;
519 #else
520     /* This x64 HAL does NOT currently handle display reset (TODO) */
521     return FALSE;
522 #endif
523 }
524 #endif // _M_AMD64
525