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("memory: %p, %p\n", *(PVOID*)x86BiosMemoryMapping, *(PVOID*)(x86BiosMemoryMapping + 8)); 142 //DbgDumpPage(x86BiosMemoryMapping, 0xc351); 143 144 x86BiosIsInitialized = TRUE; 145 146 HalpBiosDisplayReset(); 147 } 148 } 149 150 NTSTATUS 151 NTAPI 152 x86BiosAllocateBuffer( 153 _In_ ULONG *Size, 154 _In_ USHORT *Segment, 155 _In_ USHORT *Offset) 156 { 157 /* Check if the system is initialized and the buffer is large enough */ 158 if (!x86BiosIsInitialized || (*Size > PAGE_SIZE)) 159 { 160 /* Something was wrong, fail! */ 161 return STATUS_INSUFFICIENT_RESOURCES; 162 } 163 164 /* Check if the buffer is already allocated */ 165 if (InterlockedBitTestAndSet(&x86BiosBufferIsAllocated, 0)) 166 { 167 /* Buffer was already allocated, fail */ 168 return STATUS_INSUFFICIENT_RESOURCES; 169 } 170 171 /* The buffer is sufficient, return hardcoded address and size */ 172 *Size = PAGE_SIZE; 173 *Segment = x86BiosBufferPhysical / 16; 174 *Offset = 0; 175 176 return STATUS_SUCCESS; 177 } 178 179 NTSTATUS 180 NTAPI 181 x86BiosFreeBuffer( 182 _In_ USHORT Segment, 183 _In_ USHORT Offset) 184 { 185 /* Check if the system is initialized and if the address matches */ 186 if (!x86BiosIsInitialized || (Segment != 0x2000) || (Offset != 0)) 187 { 188 /* Something was wrong, fail */ 189 return STATUS_INVALID_PARAMETER; 190 } 191 192 /* Check if the buffer was allocated */ 193 if (!InterlockedBitTestAndReset(&x86BiosBufferIsAllocated, 0)) 194 { 195 /* It was not, fail */ 196 return STATUS_INVALID_PARAMETER; 197 } 198 199 /* Buffer is freed, nothing more to do */ 200 return STATUS_SUCCESS; 201 } 202 203 NTSTATUS 204 NTAPI 205 x86BiosReadMemory( 206 _In_ USHORT Segment, 207 _In_ USHORT Offset, 208 _Out_writes_bytes_(Size) PVOID Buffer, 209 _In_ ULONG Size) 210 { 211 ULONG_PTR Address; 212 213 /* Calculate the physical address */ 214 Address = (Segment << 4) + Offset; 215 216 /* Check if it's valid */ 217 if (!x86BiosIsInitialized || ((Address + Size) > 0x100000)) 218 { 219 /* Invalid */ 220 return STATUS_INVALID_PARAMETER; 221 } 222 223 /* Copy the memory to the buffer */ 224 RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size); 225 226 /* Return success */ 227 return STATUS_SUCCESS; 228 } 229 230 NTSTATUS 231 NTAPI 232 x86BiosWriteMemory( 233 _In_ USHORT Segment, 234 _In_ USHORT Offset, 235 _In_reads_bytes_(Size) PVOID Buffer, 236 _In_ ULONG Size) 237 { 238 ULONG_PTR Address; 239 240 /* Calculate the physical address */ 241 Address = (Segment << 4) + Offset; 242 243 /* Check if it's valid */ 244 if (!x86BiosIsInitialized || ((Address + Size) > 0x100000)) 245 { 246 /* Invalid */ 247 return STATUS_INVALID_PARAMETER; 248 } 249 250 /* Copy the memory from the buffer */ 251 RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size); 252 253 /* Return success */ 254 return STATUS_SUCCESS; 255 } 256 257 static 258 VOID 259 FASTCALL 260 x86MemRead( 261 PFAST486_STATE State, 262 ULONG Address, 263 PVOID Buffer, 264 ULONG Size) 265 { 266 /* Validate the address range */ 267 if (((ULONG64)Address + Size) < 0x100000) 268 { 269 RtlCopyMemory(Buffer, x86BiosMemoryMapping + Address, Size); 270 } 271 else 272 { 273 RtlFillMemory(Buffer, Size, 0xCC); 274 DPRINT1("x86MemRead: invalid read at 0x%lx (size 0x%lx)", Address, Size); 275 } 276 } 277 278 static 279 VOID 280 FASTCALL 281 x86MemWrite( 282 PFAST486_STATE State, 283 ULONG Address, 284 PVOID Buffer, 285 ULONG Size) 286 { 287 /* Validate the address range */ 288 if (((ULONG64)Address + Size) < 0x100000) 289 { 290 RtlCopyMemory(x86BiosMemoryMapping + Address, Buffer, Size); 291 } 292 else 293 { 294 DPRINT1("x86MemWrite: invalid write at 0x%lx (size 0x%lx)", Address, Size); 295 } 296 } 297 298 static 299 BOOLEAN 300 ValidatePort( 301 USHORT Port, 302 UCHAR Size, 303 BOOLEAN IsWrite) 304 { 305 switch (Port) 306 { 307 // VGA: https://wiki.osdev.org/VGA_Hardware#Port_0x3C0 308 case 0x3C0: return (Size == 1) && IsWrite; 309 case 0x3C1: return (Size == 1) && !IsWrite; 310 case 0x3C2: return (Size == 1) && IsWrite; 311 case 0x3C4: return IsWrite; 312 case 0x3C5: return (Size <= 2); 313 case 0x3C7: return (Size == 1) && IsWrite; 314 case 0x3CC: return (Size == 1) && !IsWrite; 315 case 0x3CE: return IsWrite; 316 case 0x3CF: return (Size <= 2); 317 case 0x3D4: return IsWrite; 318 case 0x3D5: return (Size <= 2); 319 case 0x3C6: return (Size == 1); 320 case 0x3C8: return (Size == 1) && IsWrite; 321 case 0x3C9: return (Size == 1); 322 case 0x3DA: return (Size == 1) && !IsWrite; 323 324 // OVMF debug messages used by VBox / QEMU 325 // https://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/README 326 case 0x402: return (Size == 1) && IsWrite; 327 328 // BOCHS VBE: https://forum.osdev.org/viewtopic.php?f=1&t=14639 329 case 0x1CE: return (Size == 1) && IsWrite; 330 case 0x1CF: return (Size == 1); 331 332 // CHECKME! 333 case 0x3B6: return (Size <= 2); 334 } 335 336 /* Allow but report unknown ports, we trust the BIOS for now */ 337 DPRINT1("Unknown port 0x%x, size %d, write %d\n", Port, Size, IsWrite); 338 return TRUE; 339 } 340 341 static 342 VOID 343 FASTCALL 344 x86IoRead( 345 PFAST486_STATE State, 346 USHORT Port, 347 PVOID Buffer, 348 ULONG DataCount, 349 UCHAR DataSize) 350 { 351 /* Validate the port */ 352 if (!ValidatePort(Port, DataSize, FALSE)) 353 { 354 DPRINT1("Invalid IO port read access (port: 0x%x, count: 0x%x)\n", Port, DataSize); 355 } 356 357 switch (DataSize) 358 { 359 case 1: READ_PORT_BUFFER_UCHAR((PUCHAR)(ULONG_PTR)Port, Buffer, DataCount); return; 360 case 2: READ_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)Port, Buffer, DataCount); return; 361 case 4: READ_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)Port, Buffer, DataCount); return; 362 } 363 } 364 365 static 366 VOID 367 FASTCALL 368 x86IoWrite( 369 PFAST486_STATE State, 370 USHORT Port, 371 PVOID Buffer, 372 ULONG DataCount, 373 UCHAR DataSize) 374 { 375 /* Validate the port */ 376 if (!ValidatePort(Port, DataSize, TRUE)) 377 { 378 DPRINT1("Invalid IO port write access (port: 0x%x, count: 0x%x)\n", Port, DataSize); 379 } 380 381 switch (DataSize) 382 { 383 case 1: WRITE_PORT_BUFFER_UCHAR((PUCHAR)(ULONG_PTR)Port, Buffer, DataCount); return; 384 case 2: WRITE_PORT_BUFFER_USHORT((PUSHORT)(ULONG_PTR)Port, Buffer, DataCount); return; 385 case 4: WRITE_PORT_BUFFER_ULONG((PULONG)(ULONG_PTR)Port, Buffer, DataCount); return; 386 } 387 } 388 389 static 390 VOID 391 FASTCALL 392 x86BOP( 393 PFAST486_STATE State, 394 UCHAR BopCode) 395 { 396 ASSERT(FALSE); 397 } 398 399 static 400 UCHAR 401 FASTCALL 402 x86IntAck ( 403 PFAST486_STATE State) 404 { 405 ASSERT(FALSE); 406 return 0; 407 } 408 409 BOOLEAN 410 NTAPI 411 x86BiosCall( 412 _In_ ULONG InterruptNumber, 413 _Inout_ PX86_BIOS_REGISTERS Registers) 414 { 415 const ULONG StackBase = 0x2000; 416 FAST486_STATE EmulatorContext; 417 ULONG FlatIp; 418 PUCHAR InstructionPointer; 419 420 /* Initialize the emulator context */ 421 Fast486Initialize(&EmulatorContext, 422 x86MemRead, 423 x86MemWrite, 424 x86IoRead, 425 x86IoWrite, 426 x86BOP, 427 x86IntAck, 428 NULL, // FpuCallback, 429 NULL); // Tlb 430 431 /* Copy the registers */ 432 EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long = Registers->Eax; 433 EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long = Registers->Ebx; 434 EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long = Registers->Ecx; 435 EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long = Registers->Edx; 436 EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long = Registers->Esi; 437 EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long = Registers->Edi; 438 EmulatorContext.SegmentRegs[FAST486_REG_DS].Selector = Registers->SegDs; 439 EmulatorContext.SegmentRegs[FAST486_REG_ES].Selector = Registers->SegEs; 440 441 /* Set Eflags */ 442 EmulatorContext.Flags.Long = 0; 443 EmulatorContext.Flags.AlwaysSet = 1; 444 EmulatorContext.Flags.If = 1; 445 446 /* Set up the INT stub */ 447 FlatIp = StackBase - 4; 448 InstructionPointer = x86BiosMemoryMapping + FlatIp; 449 InstructionPointer[0] = 0xCD; // INT instruction 450 InstructionPointer[1] = (UCHAR)InterruptNumber; 451 InstructionPointer[2] = 0x90; // NOP. We will stop at this address. 452 453 /* Set the stack pointer */ 454 Fast486SetStack(&EmulatorContext, 0, StackBase - 8); 455 456 /* Start execution at the INT stub */ 457 Fast486ExecuteAt(&EmulatorContext, 0x00, FlatIp); 458 459 while (TRUE) 460 { 461 /* Get the current flat IP */ 462 FlatIp = (EmulatorContext.SegmentRegs[FAST486_REG_CS].Selector << 4) + 463 EmulatorContext.InstPtr.Long; 464 465 /* Make sure we haven't left the allowed memory range */ 466 if (FlatIp >= 0x100000) 467 { 468 DPRINT1("x86BiosCall: invalid IP (0x%lx) during BIOS execution", FlatIp); 469 return FALSE; 470 } 471 472 /* Check if we returned from our int stub */ 473 if (FlatIp == (StackBase - 2)) 474 { 475 /* We are done! */ 476 break; 477 } 478 479 /* Emulate one instruction */ 480 Fast486StepInto(&EmulatorContext); 481 } 482 483 /* Copy the registers back */ 484 Registers->Eax = EmulatorContext.GeneralRegs[FAST486_REG_EAX].Long; 485 Registers->Ebx = EmulatorContext.GeneralRegs[FAST486_REG_EBX].Long; 486 Registers->Ecx = EmulatorContext.GeneralRegs[FAST486_REG_ECX].Long; 487 Registers->Edx = EmulatorContext.GeneralRegs[FAST486_REG_EDX].Long; 488 Registers->Esi = EmulatorContext.GeneralRegs[FAST486_REG_ESI].Long; 489 Registers->Edi = EmulatorContext.GeneralRegs[FAST486_REG_EDI].Long; 490 Registers->SegDs = EmulatorContext.SegmentRegs[FAST486_REG_DS].Selector; 491 Registers->SegEs = EmulatorContext.SegmentRegs[FAST486_REG_ES].Selector; 492 493 return TRUE; 494 } 495 496 #ifdef _M_AMD64 497 BOOLEAN 498 NTAPI 499 HalpBiosDisplayReset(VOID) 500 { 501 #if 0 502 X86_BIOS_REGISTERS Registers; 503 ULONG OldEflags; 504 505 /* Save flags and disable interrupts */ 506 OldEflags = __readeflags(); 507 _disable(); 508 509 /* Set AH = 0 (Set video mode), AL = 0x12 (640x480x16 vga) */ 510 Registers.Eax = 0x12; 511 512 /* Call INT 0x10 */ 513 x86BiosCall(0x10, &Registers); 514 515 // FIXME: check result 516 517 /* Restore previous flags */ 518 __writeeflags(OldEflags); 519 return TRUE; 520 #else 521 /* This x64 HAL does NOT currently handle display reset (TODO) */ 522 return FALSE; 523 #endif 524 } 525 #endif // _M_AMD64 526