3 * LICENSE: GPL, See COPYING in the top level directory
4 * FILE: hal/halx86/amd64/x86bios.c
6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
9 /* INCLUDES ******************************************************************/
17 /* This page serves as fallback for pages used by Mm */
18 #define DEFAULT_PAGE 0x21
20 /* GLOBALS *******************************************************************/
22 BOOLEAN x86BiosIsInitialized
;
23 LONG x86BiosBufferIsAllocated
= 0;
24 PUCHAR x86BiosMemoryMapping
;
29 DbgDumpPage(PUCHAR MemBuffer
, USHORT Segment
)
33 for (y
= 0; y
< 0x100; y
++)
35 for (x
= 0; x
< 0x10; x
++)
37 Offset
= Segment
* 16 + y
* 16 + x
;
38 DbgPrint("%02x ", MemBuffer
[Offset
]);
48 _In_ PLOADER_PARAMETER_BLOCK LoaderBlock
)
52 PMEMORY_ALLOCATION_DESCRIPTOR Descriptor
;
53 PLIST_ENTRY ListEntry
;
56 /* Allocate an MDL for 1MB */
57 Mdl
= IoAllocateMdl(NULL
, 0x100000, FALSE
, FALSE
, NULL
);
63 /* Get pointer to the pfn array */
64 PfnArray
= MmGetMdlPfnArray(Mdl
);
66 /* Fill the array with low memory PFNs */
67 for (Pfn
= 0; Pfn
< 0x100; Pfn
++)
72 /* Loop the memory descriptors */
73 for (ListEntry
= LoaderBlock
->MemoryDescriptorListHead
.Flink
;
74 ListEntry
!= &LoaderBlock
->MemoryDescriptorListHead
;
75 ListEntry
= ListEntry
->Flink
)
77 /* Get the memory descriptor */
78 Descriptor
= CONTAINING_RECORD(ListEntry
,
79 MEMORY_ALLOCATION_DESCRIPTOR
,
82 /* Check if the memory is in the low range */
83 if (Descriptor
->BasePage
< 0x100)
85 /* Check if the memory type is firmware */
86 if (Descriptor
->MemoryType
!= LoaderFirmwarePermanent
&&
87 Descriptor
->MemoryType
!= LoaderSpecialMemory
)
89 /* It's something else, so don't use it! */
90 Last
= min(Descriptor
->BasePage
+ Descriptor
->PageCount
, 0x100);
91 for (Pfn
= Descriptor
->BasePage
; Pfn
< Last
; Pfn
++)
93 /* Set each page to the default page */
94 PfnArray
[Pfn
] = DEFAULT_PAGE
;
100 Mdl
->MdlFlags
= MDL_PAGES_LOCKED
;
102 /* Map the MDL to system space */
103 x86BiosMemoryMapping
= MmGetSystemAddressForMdlSafe(Mdl
, HighPagePriority
);
104 ASSERT(x86BiosMemoryMapping
);
106 DPRINT1("memory: %p, %p\n", *(PVOID
*)x86BiosMemoryMapping
, *(PVOID
*)(x86BiosMemoryMapping
+ 8));
107 //DbgDumpPage(x86BiosMemoryMapping, 0xc351);
109 x86BiosIsInitialized
= TRUE
;
111 HalpBiosDisplayReset();
116 x86BiosAllocateBuffer(
118 _In_ USHORT
*Segment
,
121 /* Check if the system is initialized and the buffer is large enough */
122 if (!x86BiosIsInitialized
|| (*Size
> PAGE_SIZE
))
124 /* Something was wrong, fail! */
125 return STATUS_INSUFFICIENT_RESOURCES
;
128 /* Check if the buffer is already allocated */
129 if (InterlockedBitTestAndSet(&x86BiosBufferIsAllocated
, 0))
131 /* Buffer was already allocated, fail */
132 return STATUS_INSUFFICIENT_RESOURCES
;
135 /* The buffer is sufficient, return hardcoded address and size */
140 return STATUS_SUCCESS
;
149 /* Check if the system is initialized and if the address matches */
150 if (!x86BiosIsInitialized
|| (Segment
!= 0x2000) || (Offset
!= 0))
152 /* Something was wrong, fail */
153 return STATUS_INVALID_PARAMETER
;
156 /* Check if the buffer was allocated */
157 if (!InterlockedBitTestAndReset(&x86BiosBufferIsAllocated
, 0))
159 /* It was not, fail */
160 return STATUS_INVALID_PARAMETER
;
163 /* Buffer is freed, nothing more to do */
164 return STATUS_SUCCESS
;
172 _Out_writes_bytes_(Size
) PVOID Buffer
,
177 /* Calculate the physical address */
178 Address
= (Segment
<< 4) + Offset
;
180 /* Check if it's valid */
181 if (!x86BiosIsInitialized
|| ((Address
+ Size
) > 0x100000))
184 return STATUS_INVALID_PARAMETER
;
187 /* Copy the memory to the buffer */
188 RtlCopyMemory(Buffer
, x86BiosMemoryMapping
+ Address
, Size
);
191 return STATUS_SUCCESS
;
199 _In_reads_bytes_(Size
) PVOID Buffer
,
204 /* Calculate the physical address */
205 Address
= (Segment
<< 4) + Offset
;
207 /* Check if it's valid */
208 if (!x86BiosIsInitialized
|| ((Address
+ Size
) > 0x100000))
211 return STATUS_INVALID_PARAMETER
;
214 /* Copy the memory from the buffer */
215 RtlCopyMemory(x86BiosMemoryMapping
+ Address
, Buffer
, Size
);
218 return STATUS_SUCCESS
;
225 PFAST486_STATE State
,
230 /* Validate the address range */
231 if (((ULONG64
)Address
+ Size
) < 0x100000)
233 RtlCopyMemory(Buffer
, x86BiosMemoryMapping
+ Address
, Size
);
237 RtlFillMemory(Buffer
, Size
, 0xCC);
238 DPRINT1("x86MemRead: invalid read at 0x%lx (size 0x%lx)", Address
, Size
);
246 PFAST486_STATE State
,
251 /* Validate the address range */
252 if (((ULONG64
)Address
+ Size
) < 0x100000)
254 RtlCopyMemory(x86BiosMemoryMapping
+ Address
, Buffer
, Size
);
258 DPRINT1("x86MemWrite: invalid write at 0x%lx (size 0x%lx)", Address
, Size
);
271 // VGA: https://wiki.osdev.org/VGA_Hardware#Port_0x3C0
272 case 0x3C0: return (Size
== 1) && IsWrite
;
273 case 0x3C1: return (Size
== 1) && !IsWrite
;
274 case 0x3C2: return (Size
== 1) && IsWrite
;
275 case 0x3C4: return IsWrite
;
276 case 0x3C5: return (Size
<= 2);
277 case 0x3C7: return (Size
== 1) && IsWrite
;
278 case 0x3CC: return (Size
== 1) && !IsWrite
;
279 case 0x3CE: return IsWrite
;
280 case 0x3CF: return (Size
<= 2);
281 case 0x3D4: return IsWrite
;
282 case 0x3D5: return (Size
<= 2);
283 case 0x3C6: return (Size
== 1);
284 case 0x3C8: return (Size
== 1) && IsWrite
;
285 case 0x3C9: return (Size
== 1);
286 case 0x3DA: return (Size
== 1) && !IsWrite
;
289 case 0x1CE: return (Size
== 1) && IsWrite
;
290 case 0x1CF: return (Size
== 1);
291 case 0x3B6: return (Size
<= 2);
301 PFAST486_STATE State
,
307 /* Validate the port */
308 if (!ValidatePort(Port
, DataSize
, FALSE
))
310 DPRINT1("Invalid IO port read access (port: 0x%x, count: 0x%x)\n", Port
, DataSize
);
315 case 1: READ_PORT_BUFFER_UCHAR((PUCHAR
)(ULONG_PTR
)Port
, Buffer
, DataCount
); return;
316 case 2: READ_PORT_BUFFER_USHORT((PUSHORT
)(ULONG_PTR
)Port
, Buffer
, DataCount
); return;
317 case 4: READ_PORT_BUFFER_ULONG((PULONG
)(ULONG_PTR
)Port
, Buffer
, DataCount
); return;
325 PFAST486_STATE State
,
331 /* Validate the port */
332 if (!ValidatePort(Port
, DataSize
, TRUE
))
334 DPRINT1("Invalid IO port write access (port: 0x%x, count: 0x%x)\n", Port
, DataSize
);
339 case 1: WRITE_PORT_BUFFER_UCHAR((PUCHAR
)(ULONG_PTR
)Port
, Buffer
, DataCount
); return;
340 case 2: WRITE_PORT_BUFFER_USHORT((PUSHORT
)(ULONG_PTR
)Port
, Buffer
, DataCount
); return;
341 case 4: WRITE_PORT_BUFFER_ULONG((PULONG
)(ULONG_PTR
)Port
, Buffer
, DataCount
); return;
349 PFAST486_STATE State
,
359 PFAST486_STATE State
)
368 _In_ ULONG InterruptNumber
,
369 _Inout_ PX86_BIOS_REGISTERS Registers
)
371 FAST486_STATE EmulatorContext
;
378 PUCHAR InstructionPointer
;
380 /* Initialize the emulator context */
381 Fast486Initialize(&EmulatorContext
,
388 NULL
, // FpuCallback,
391 //RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
393 /* Copy the registers */
394 EmulatorContext
.GeneralRegs
[FAST486_REG_EAX
].Long
= Registers
->Eax
;
395 EmulatorContext
.GeneralRegs
[FAST486_REG_EBX
].Long
= Registers
->Ebx
;
396 EmulatorContext
.GeneralRegs
[FAST486_REG_ECX
].Long
= Registers
->Ecx
;
397 EmulatorContext
.GeneralRegs
[FAST486_REG_EDX
].Long
= Registers
->Edx
;
398 EmulatorContext
.GeneralRegs
[FAST486_REG_ESI
].Long
= Registers
->Esi
;
399 EmulatorContext
.GeneralRegs
[FAST486_REG_EDI
].Long
= Registers
->Edi
;
400 EmulatorContext
.SegmentRegs
[FAST486_REG_DS
].Selector
= Registers
->SegDs
;
401 EmulatorContext
.SegmentRegs
[FAST486_REG_ES
].Selector
= Registers
->SegEs
;
404 EmulatorContext
.Flags
.Long
= 0;
405 EmulatorContext
.Flags
.AlwaysSet
= 1;
406 EmulatorContext
.Flags
.If
= 1;
408 /* Set the stack pointer */
409 Fast486SetStack(&EmulatorContext
, 0, 0x2000 - 2); // FIXME
411 /* Set CS:EIP from the IVT entry */
412 Ivt
= (PVOID
)x86BiosMemoryMapping
;
413 Fast486ExecuteAt(&EmulatorContext
,
414 Ivt
[InterruptNumber
].SegCs
,
415 Ivt
[InterruptNumber
].Ip
);
419 /* Step one instruction */
420 Fast486StepInto(&EmulatorContext
);
423 FlatIp
= (EmulatorContext
.SegmentRegs
[FAST486_REG_CS
].Selector
<< 4) +
424 EmulatorContext
.InstPtr
.Long
;
425 if (FlatIp
>= 0x100000)
427 DPRINT1("x86BiosCall: invalid IP (0x%lx) during BIOS execution", FlatIp
);
431 /* Read the next instruction and check if it's IRET */
432 InstructionPointer
= x86BiosMemoryMapping
+ FlatIp
;
433 if (*InstructionPointer
== 0xCF)
440 /* Copy the registers back */
441 Registers
->Eax
= EmulatorContext
.GeneralRegs
[FAST486_REG_EAX
].Long
;
442 Registers
->Ebx
= EmulatorContext
.GeneralRegs
[FAST486_REG_EBX
].Long
;
443 Registers
->Ecx
= EmulatorContext
.GeneralRegs
[FAST486_REG_ECX
].Long
;
444 Registers
->Edx
= EmulatorContext
.GeneralRegs
[FAST486_REG_EDX
].Long
;
445 Registers
->Esi
= EmulatorContext
.GeneralRegs
[FAST486_REG_ESI
].Long
;
446 Registers
->Edi
= EmulatorContext
.GeneralRegs
[FAST486_REG_EDI
].Long
;
447 Registers
->SegDs
= EmulatorContext
.SegmentRegs
[FAST486_REG_DS
].Selector
;
448 Registers
->SegEs
= EmulatorContext
.SegmentRegs
[FAST486_REG_ES
].Selector
;
455 HalpBiosDisplayReset(VOID
)
458 X86_BIOS_REGISTERS Registers
;
461 /* Save flags and disable interrupts */
462 OldEflags
= __readeflags();
465 /* Set AH = 0 (Set video mode), AL = 0x12 (640x480x16 vga) */
466 Registers
.Eax
= 0x12;
469 x86BiosCall(0x10, &Registers
);
471 // FIXME: check result
473 /* Restore previous flags */
474 __writeeflags(OldEflags
);