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 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 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 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 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 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 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 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 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 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 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 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 391 x86BOP( 392 PFAST486_STATE State, 393 UCHAR BopCode) 394 { 395 ASSERT(FALSE); 396 } 397 398 static 399 UCHAR 400 FASTCALL 401 x86IntAck ( 402 PFAST486_STATE State) 403 { 404 ASSERT(FALSE); 405 return 0; 406 } 407 408 BOOLEAN 409 NTAPI 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 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