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