[HAL] Implement amd64 BIOS call support
[reactos.git] / hal / halx86 / amd64 / x86bios.c
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 /* This page serves as fallback for pages used by Mm */
18 #define DEFAULT_PAGE 0x21
19
20 /* GLOBALS *******************************************************************/
21
22 BOOLEAN x86BiosIsInitialized;
23 LONG x86BiosBufferIsAllocated = 0;
24 PUCHAR x86BiosMemoryMapping;
25
26
27 VOID
28 NTAPI
29 DbgDumpPage(PUCHAR MemBuffer, USHORT Segment)
30 {
31 ULONG x, y, Offset;
32
33 for (y = 0; y < 0x100; y++)
34 {
35 for (x = 0; x < 0x10; x++)
36 {
37 Offset = Segment * 16 + y * 16 + x;
38 DbgPrint("%02x ", MemBuffer[Offset]);
39 }
40 DbgPrint("\n");
41 }
42 }
43
44 VOID
45 NTAPI
46 HalInitializeBios(
47 _In_ ULONG Unknown,
48 _In_ PLOADER_PARAMETER_BLOCK LoaderBlock)
49 {
50 PPFN_NUMBER PfnArray;
51 PFN_NUMBER Pfn, Last;
52 PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
53 PLIST_ENTRY ListEntry;
54 PMDL Mdl;
55
56 /* Allocate an MDL for 1MB */
57 Mdl = IoAllocateMdl(NULL, 0x100000, FALSE, FALSE, NULL);
58 if (!Mdl)
59 {
60 ASSERT(FALSE);
61 }
62
63 /* Get pointer to the pfn array */
64 PfnArray = MmGetMdlPfnArray(Mdl);
65
66 /* Fill the array with low memory PFNs */
67 for (Pfn = 0; Pfn < 0x100; Pfn++)
68 {
69 PfnArray[Pfn] = Pfn;
70 }
71
72 /* Loop the memory descriptors */
73 for (ListEntry = LoaderBlock->MemoryDescriptorListHead.Flink;
74 ListEntry != &LoaderBlock->MemoryDescriptorListHead;
75 ListEntry = ListEntry->Flink)
76 {
77 /* Get the memory descriptor */
78 Descriptor = CONTAINING_RECORD(ListEntry,
79 MEMORY_ALLOCATION_DESCRIPTOR,
80 ListEntry);
81
82 /* Check if the memory is in the low range */
83 if (Descriptor->BasePage < 0x100)
84 {
85 /* Check if the memory type is firmware */
86 if (Descriptor->MemoryType != LoaderFirmwarePermanent &&
87 Descriptor->MemoryType != LoaderSpecialMemory)
88 {
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++)
92 {
93 /* Set each page to the default page */
94 PfnArray[Pfn] = DEFAULT_PAGE;
95 }
96 }
97 }
98 }
99
100 Mdl->MdlFlags = MDL_PAGES_LOCKED;
101
102 /* Map the MDL to system space */
103 x86BiosMemoryMapping = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);
104 ASSERT(x86BiosMemoryMapping);
105
106 DPRINT1("memory: %p, %p\n", *(PVOID*)x86BiosMemoryMapping, *(PVOID*)(x86BiosMemoryMapping + 8));
107 //DbgDumpPage(x86BiosMemoryMapping, 0xc351);
108
109 x86BiosIsInitialized = TRUE;
110
111 HalpBiosDisplayReset();
112 }
113
114 NTSTATUS
115 NTAPI
116 x86BiosAllocateBuffer(
117 _In_ ULONG *Size,
118 _In_ USHORT *Segment,
119 _In_ USHORT *Offset)
120 {
121 /* Check if the system is initialized and the buffer is large enough */
122 if (!x86BiosIsInitialized || (*Size > PAGE_SIZE))
123 {
124 /* Something was wrong, fail! */
125 return STATUS_INSUFFICIENT_RESOURCES;
126 }
127
128 /* Check if the buffer is already allocated */
129 if (InterlockedBitTestAndSet(&x86BiosBufferIsAllocated, 0))
130 {
131 /* Buffer was already allocated, fail */
132 return STATUS_INSUFFICIENT_RESOURCES;
133 }
134
135 /* The buffer is sufficient, return hardcoded address and size */
136 *Size = PAGE_SIZE;
137 *Segment = 0x2000;
138 *Offset = 0;
139
140 return STATUS_SUCCESS;
141 }
142
143 NTSTATUS
144 NTAPI
145 x86BiosFreeBuffer(
146 _In_ USHORT Segment,
147 _In_ USHORT Offset)
148 {
149 /* Check if the system is initialized and if the address matches */
150 if (!x86BiosIsInitialized || (Segment != 0x2000) || (Offset != 0))
151 {
152 /* Something was wrong, fail */
153 return STATUS_INVALID_PARAMETER;
154 }
155
156 /* Check if the buffer was allocated */
157 if (!InterlockedBitTestAndReset(&x86BiosBufferIsAllocated, 0))
158 {
159 /* It was not, fail */
160 return STATUS_INVALID_PARAMETER;
161 }
162
163 /* Buffer is freed, nothing more to do */
164 return STATUS_SUCCESS;
165 }
166
167 NTSTATUS
168 NTAPI
169 x86BiosReadMemory(
170 _In_ USHORT Segment,
171 _In_ USHORT Offset,
172 _Out_writes_bytes_(Size) PVOID Buffer,
173 _In_ ULONG Size)
174 {
175 ULONG_PTR Address;
176
177 /* Calculate the physical address */
178 Address = (Segment << 4) + Offset;
179
180 /* Check if it's valid */
181 if (!x86BiosIsInitialized || ((Address + Size) > 0x100000))
182 {
183 /* Invalid */
184 return STATUS_INVALID_PARAMETER;
185 }
186
187 /* Copy the memory to the buffer */
188 RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size);
189
190 /* Return success */
191 return STATUS_SUCCESS;
192 }
193
194 NTSTATUS
195 NTAPI
196 x86BiosWriteMemory(
197 _In_ USHORT Segment,
198 _In_ USHORT Offset,
199 _In_reads_bytes_(Size) PVOID Buffer,
200 _In_ ULONG Size)
201 {
202 ULONG_PTR Address;
203
204 /* Calculate the physical address */
205 Address = (Segment << 4) + Offset;
206
207 /* Check if it's valid */
208 if (!x86BiosIsInitialized || ((Address + Size) > 0x100000))
209 {
210 /* Invalid */
211 return STATUS_INVALID_PARAMETER;
212 }
213
214 /* Copy the memory from the buffer */
215 RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size);
216
217 /* Return success */
218 return STATUS_SUCCESS;
219 }
220
221 static
222 VOID
223 FASTCALL
224 x86MemRead(
225 PFAST486_STATE State,
226 ULONG Address,
227 PVOID Buffer,
228 ULONG Size)
229 {
230 /* Validate the address range */
231 if (((ULONG64)Address + Size) < 0x100000)
232 {
233 RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size);
234 }
235 else
236 {
237 RtlFillMemory(Buffer, Size, 0xCC);
238 DPRINT1("x86MemRead: invalid read at 0x%lx (size 0x%lx)", Address, Size);
239 }
240 }
241
242 static
243 VOID
244 FASTCALL
245 x86MemWrite(
246 PFAST486_STATE State,
247 ULONG Address,
248 PVOID Buffer,
249 ULONG Size)
250 {
251 /* Validate the address range */
252 if (((ULONG64)Address + Size) < 0x100000)
253 {
254 RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size);
255 }
256 else
257 {
258 DPRINT1("x86MemWrite: invalid write at 0x%lx (size 0x%lx)", Address, Size);
259 }
260 }
261
262 static
263 BOOLEAN
264 ValidatePort(
265 USHORT Port,
266 UCHAR Size,
267 BOOLEAN IsWrite)
268 {
269 switch (Port)
270 {
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;
287
288 // CHECKME!
289 case 0x1CE: return (Size == 1) && IsWrite;
290 case 0x1CF: return (Size == 1);
291 case 0x3B6: return (Size <= 2);
292 }
293
294 return FALSE;
295 }
296
297 static
298 VOID
299 FASTCALL
300 x86IoRead(
301 PFAST486_STATE State,
302 USHORT Port,
303 PVOID Buffer,
304 ULONG DataCount,
305 UCHAR DataSize)
306 {
307 /* Validate the port */
308 if (!ValidatePort(Port, DataSize, FALSE))
309 {
310 DPRINT1("Invalid IO port read access (port: 0x%x, count: 0x%x)\n", Port, DataSize);
311 }
312
313 switch (DataSize)
314 {
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;
318 }
319 }
320
321 static
322 VOID
323 FASTCALL
324 x86IoWrite(
325 PFAST486_STATE State,
326 USHORT Port,
327 PVOID Buffer,
328 ULONG DataCount,
329 UCHAR DataSize)
330 {
331 /* Validate the port */
332 if (!ValidatePort(Port, DataSize, TRUE))
333 {
334 DPRINT1("Invalid IO port write access (port: 0x%x, count: 0x%x)\n", Port, DataSize);
335 }
336
337 switch (DataSize)
338 {
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;
342 }
343 }
344
345 static
346 VOID
347 FASTCALL
348 x86BOP(
349 PFAST486_STATE State,
350 UCHAR BopCode)
351 {
352 ASSERT(FALSE);
353 }
354
355 static
356 UCHAR
357 FASTCALL
358 x86IntAck (
359 PFAST486_STATE State)
360 {
361 ASSERT(FALSE);
362 return 0;
363 }
364
365 BOOLEAN
366 NTAPI
367 x86BiosCall(
368 _In_ ULONG InterruptNumber,
369 _Inout_ PX86_BIOS_REGISTERS Registers)
370 {
371 FAST486_STATE EmulatorContext;
372 struct
373 {
374 USHORT Ip;
375 USHORT SegCs;
376 } *Ivt;
377 ULONG FlatIp;
378 PUCHAR InstructionPointer;
379
380 /* Initialize the emulator context */
381 Fast486Initialize(&EmulatorContext,
382 x86MemRead,
383 x86MemWrite,
384 x86IoRead,
385 x86IoWrite,
386 x86BOP,
387 x86IntAck,
388 NULL, // FpuCallback,
389 NULL); // Tlb
390
391 //RegisterBop(BOP_UNSIMULATE, CpuUnsimulateBop);
392
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;
402
403 /* Set Eflags */
404 EmulatorContext.Flags.Long = 0;
405 EmulatorContext.Flags.AlwaysSet = 1;
406 EmulatorContext.Flags.If = 1;
407
408 /* Set the stack pointer */
409 Fast486SetStack(&EmulatorContext, 0, 0x2000 - 2); // FIXME
410
411 /* Set CS:EIP from the IVT entry */
412 Ivt = (PVOID)x86BiosMemoryMapping;
413 Fast486ExecuteAt(&EmulatorContext,
414 Ivt[InterruptNumber].SegCs,
415 Ivt[InterruptNumber].Ip);
416
417 while (TRUE)
418 {
419 /* Step one instruction */
420 Fast486StepInto(&EmulatorContext);
421
422 /* Check for iret */
423 FlatIp = (EmulatorContext.SegmentRegs[FAST486_REG_CS].Selector << 4) +
424 EmulatorContext.InstPtr.Long;
425 if (FlatIp >= 0x100000)
426 {
427 DPRINT1("x86BiosCall: invalid IP (0x%lx) during BIOS execution", FlatIp);
428 return FALSE;
429 }
430
431 /* Read the next instruction and check if it's IRET */
432 InstructionPointer = x86BiosMemoryMapping + FlatIp;
433 if (*InstructionPointer == 0xCF)
434 {
435 /* We are done! */
436 break;
437 }
438 }
439
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;
449
450 return TRUE;
451 }
452
453 BOOLEAN
454 NTAPI
455 HalpBiosDisplayReset(VOID)
456 {
457 #if 0
458 X86_BIOS_REGISTERS Registers;
459 ULONG OldEflags;
460
461 /* Save flags and disable interrupts */
462 OldEflags = __readeflags();
463 _disable();
464
465 /* Set AH = 0 (Set video mode), AL = 0x12 (640x480x16 vga) */
466 Registers.Eax = 0x12;
467
468 /* Call INT 0x10 */
469 x86BiosCall(0x10, &Registers);
470
471 // FIXME: check result
472
473 /* Restore previous flags */
474 __writeeflags(OldEflags);
475 #endif
476 return TRUE;
477 }
478