diff --git a/OVMF/OVMF_VARS_4M.fd b/OVMF/OVMF_VARS_4M.fd index d868e5a..1ee7a92 100644 Binary files a/OVMF/OVMF_VARS_4M.fd and b/OVMF/OVMF_VARS_4M.fd differ diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index 0341b41..a362ae0 100644 Binary files a/OVMF/OVMF_VARS_4M_d.fd and b/OVMF/OVMF_VARS_4M_d.fd differ diff --git a/code/boot/uefi/main.c b/code/boot/uefi/main.c index de961a2..ecad020 100644 --- a/code/boot/uefi/main.c +++ b/code/boot/uefi/main.c @@ -866,8 +866,11 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable for (UINTN i = 0; i < mapSize / descSz; i++) { EFI_MEMORY_DESCRIPTOR* desc = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)efiMap + i * descSz); - // Map everything except unusable memory to the Physical Window. - if (desc->Type != EfiUnusableMemory && desc->Type != EfiMemoryMappedIOPortSpace) { + // Map normal firmware-described regions into the physical window. + // PCI/firmware MMIO is mapped explicitly by the owning driver. + if (desc->Type != EfiUnusableMemory && + desc->Type != EfiMemoryMappedIO && + desc->Type != EfiMemoryMappedIOPortSpace) { UINT64 start = desc->PhysicalStart; UINT64 end = start + (desc->NumberOfPages * PAGE_SIZE); @@ -890,10 +893,8 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable // We explicitly map the region from 0xFEC00000 up through 0xFEE00FFF // to guarantee the HAL can access them via the physical window. for (UINT64 phys = 0xFEC00000; phys < 0xFEE01000; phys += PAGE_SIZE) { - // In a production OS you'd use Cache-Disable flags (0x13 or 0x1B), - // but 0x03 is fine for hobby OS initialization before PAT is set up. - Map4K(phys, phys, 0x03); // Identity Map - Map4K(PHYS_MAP_BASE + phys, phys, 0x03); // Physical Window Map + Map4K(phys, phys, 0x13); // Identity Map, P|W|PCD + Map4K(PHYS_MAP_BASE + phys, phys, 0x13); // Physical Window Map, P|W|PCD } //Print(L"8\n"); diff --git a/code/build/build.c b/code/build/build.c index 319d413..9ffbda8 100644 --- a/code/build/build.c +++ b/code/build/build.c @@ -33,6 +33,7 @@ static const Driver_Target STANDALONE_DRIVERS[] = { DRIVER_TARGET_LIBS("ata", "obj\\drivers\\ata", "code\\drivers\\storage\\ata\\ata.c", "bin\\pci.lib"), DRIVER_TARGET_LIBS("ahci", "obj\\drivers\\ahci", "code\\drivers\\storage\\ahci\\ahci.c code\\drivers\\storage\\ahci\\ahci_port.c", "bin\\pci.lib"), DRIVER_TARGET("fat32", "obj\\drivers\\fat32", "code\\drivers\\fs\\fat32\\fat32.c code\\drivers\\fs\\fat32\\fat32_mount.c"), + DRIVER_TARGET_LIBS("vmwaresvga", "obj\\drivers\\vmwaresvga", "code\\drivers\\display\\vmwaresvga\\vmwaresvga.c", "bin\\pci.lib"), DRIVER_TARGET_LIBS("usb", "obj\\drivers\\usb", "code\\drivers\\bus\\usb\\usb.c code\\drivers\\bus\\usb\\usb_bus.c code\\drivers\\bus\\usb\\usb_composite.c code\\drivers\\bus\\usb\\usb_core.c code\\drivers\\bus\\usb\\usb_xhci.c code\\drivers\\bus\\usb\\usb_hid.c code\\drivers\\bus\\usb\\usb_storage.c code\\drivers\\bus\\usb\\usb_hub.c", "bin\\pci.lib"), DRIVER_TARGET_LIBS("rtl816x", "obj\\drivers\\rtl816x", "code\\drivers\\net\\rtl816x\\rtl816x.c", "bin\\pci.lib"), DRIVER_TARGET_LIBS("rtl8139", "obj\\drivers\\rtl8139", "code\\drivers\\net\\rtl8139\\rtl8139.c", "bin\\pci.lib"), @@ -102,7 +103,7 @@ int main(int argc, char **argv) { nob_temp_sprintf("cl %s /MP /c /Foobj\\hal\\ code\\kernel\\hal\\*.c && link %s /DLL /OUT:bin\\hal.dll /ENTRY:HalInitialize /IGNORE:4216 obj\\printf.obj obj\\libc.obj obj\\hal\\*.obj", CFLAGS, LFLAGS), // Kernel - nob_temp_sprintf("cl %s /MP /c /Foobj\\kernel\\ code\\kernel\\*.c && link %s /SUBSYSTEM:NATIVE /ENTRY:KernelMain /OUT:bin\\kernel.exe bin\\hal.lib obj\\ini_parser.obj obj\\printf.obj obj\\libc.obj obj\\kernel\\*.obj", CFLAGS, LFLAGS), + nob_temp_sprintf("cl %s /DKE_DISPLAY_BUILD /MP /c /Foobj\\kernel\\ code\\kernel\\*.c && link %s /SUBSYSTEM:NATIVE /ENTRY:KernelMain /OUT:bin\\kernel.exe bin\\hal.lib obj\\ini_parser.obj obj\\printf.obj obj\\libc.obj obj\\kernel\\*.obj", CFLAGS, LFLAGS), // Subsystem DLLs "link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:bin\\syscall.dll obj\\syscall\\stubs.obj", @@ -218,8 +219,9 @@ int main(int argc, char **argv) { {"bin\\pci.sys", "Z:\\drivers\\pci.sys"}, {"bin\\ps2.sys", "Z:\\drivers\\ps2.sys"}, {"bin\\ata.sys", "Z:\\drivers\\ata.sys"}, {"bin\\ahci.sys", "Z:\\drivers\\ahci.sys"}, - {"bin\\fat32.sys", "Z:\\drivers\\fat32.sys"}, {"bin\\lwip.sys", "Z:\\drivers\\lwip.sys"}, - {"bin\\usb.sys", "Z:\\drivers\\usb.sys"}, {"bin\\rtl816x.sys", "Z:\\drivers\\rtl816x.sys"}, + {"bin\\fat32.sys", "Z:\\drivers\\fat32.sys"}, {"bin\\vmwaresvga.sys", "Z:\\drivers\\vmwaresvga.sys"}, + {"bin\\lwip.sys", "Z:\\drivers\\lwip.sys"}, {"bin\\usb.sys", "Z:\\drivers\\usb.sys"}, + {"bin\\rtl816x.sys", "Z:\\drivers\\rtl816x.sys"}, {"bin\\rtl8139.sys", "Z:\\drivers\\rtl8139.sys"}, {"code\\drivers\\drivers.ini", "Z:\\drivers\\drivers.ini"}, {"code\\boot.ini", "Z:\\boot.ini"}, diff --git a/code/drivers/bus/pci/pci.c b/code/drivers/bus/pci/pci.c index e188d03..e5c069f 100644 --- a/code/drivers/bus/pci/pci.c +++ b/code/drivers/bus/pci/pci.c @@ -20,6 +20,40 @@ static const HAL_API* gHal; static DRIVER_OBJECT gPciDriver; static uint32_t gPciDeviceCount; +/* + * PCI MMIO BARs use a private virtual aperture instead of the physical + * direct map. This avoids cacheability aliasing with large direct-map pages + * and keeps BAR mappings page-granular. + */ +#define PCI_MMIO_VA_BASE 0xFFFF900000000000ULL +#define PCI_MMIO_VA_LIMIT 0xFFFF980000000000ULL + +static uintptr_t gPciMmioNext = PCI_MMIO_VA_BASE; + +static uint64_t PciAlignUp64(uint64_t value, uint64_t alignment) { + return (value + alignment - 1u) & ~(alignment - 1u); +} + +static uint64_t PciDefaultMmioFlags(void) { + return PAGE_PRESENT | PAGE_WRITE | PAGE_WRITETHROUGH | PAGE_NOCACHE | PAGE_NX; +} + +static KSTATUS PciAllocateMmioVa(uint64_t length, uintptr_t* outVa) { + if (!outVa || !length) + return STATUS_INVALID_PARAMETER; + + uint64_t alignedLength = PciAlignUp64(length, PAGE_SIZE); + uintptr_t va = (uintptr_t)PciAlignUp64((uint64_t)gPciMmioNext, PAGE_SIZE); + if (va < PCI_MMIO_VA_BASE || va >= PCI_MMIO_VA_LIMIT) + return STATUS_NO_MEMORY; + if (alignedLength > (uint64_t)(PCI_MMIO_VA_LIMIT - va)) + return STATUS_NO_MEMORY; + + gPciMmioNext = va + (uintptr_t)alignedLength; + *outVa = va; + return STATUS_SUCCESS; +} + static bool PciIsPdo(DEVICE_OBJECT* pdo, PCI_PDO_EXTENSION** outExt) { if (!pdo || pdo->Type != DeviceObjectPhysical || pdo->u.Pdo.BusType != IoBusPci) return false; @@ -455,27 +489,35 @@ __declspec(dllexport) KSTATUS PciGetBar(DEVICE_OBJECT* pdo, uint8_t index, PCI_B __declspec(dllexport) KSTATUS PciMapBar(DEVICE_OBJECT* pdo, uint8_t index, uint64_t pageFlags, void** outVirtual, uint64_t* outLength) { PCI_BAR bar; if (!outVirtual) return STATUS_INVALID_PARAMETER; + *outVirtual = NULL; KSTATUS st = PciGetBar(pdo, index, &bar); if (!KSUCCESS(st)) return st; if (bar.Type != PciBarMemory32 && bar.Type != PciBarMemory64) return STATUS_NOT_SUPPORTED; if (!bar.Length) return STATUS_DEVICE_ERROR; + if (bar.Base + bar.Length < bar.Base) + return STATUS_INVALID_PARAMETER; - uint64_t flags = pageFlags ? pageFlags : (PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE); - uint64_t pageBase = bar.Base & ~(uint64_t)(PAGE_SIZE - 1u); - uint64_t pageEnd = (bar.Base + bar.Length + PAGE_SIZE - 1u) & ~(uint64_t)(PAGE_SIZE - 1u); + uint64_t flags = pageFlags ? pageFlags : PciDefaultMmioFlags(); + flags |= PAGE_PRESENT; - for (uint64_t phys = pageBase; phys < pageEnd; phys += PAGE_SIZE) { - uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys); - st = MmMapPage(virt, phys, flags, MmMapFailIfPresent); - if (st == STATUS_ALREADY_EXISTS) - st = MmSetPageProtect(virt, PAGE_SIZE, flags); + uint64_t physBase = bar.Base & ~(uint64_t)(PAGE_SIZE - 1u); + uint64_t physOffset = bar.Base - physBase; + uint64_t mapLength = PciAlignUp64(physOffset + bar.Length, PAGE_SIZE); + + uintptr_t virtBase = 0; + st = PciAllocateMmioVa(mapLength, &virtBase); + if (!KSUCCESS(st)) + return st; + + for (uint64_t off = 0; off < mapLength; off += PAGE_SIZE) { + st = MmMapPage(virtBase + (uintptr_t)off, physBase + off, flags, MmMapFailIfPresent); if (!KSUCCESS(st)) return st; } - *outVirtual = (void*)((uintptr_t)gHal->PhysToVirt(pageBase) + (uintptr_t)(bar.Base - pageBase)); + *outVirtual = (void*)(virtBase + (uintptr_t)physOffset); if (outLength) *outLength = bar.Length; return STATUS_SUCCESS; } diff --git a/code/drivers/bus/usb/usb.h b/code/drivers/bus/usb/usb.h index cbf62b3..3554c15 100644 --- a/code/drivers/bus/usb/usb.h +++ b/code/drivers/bus/usb/usb.h @@ -115,6 +115,7 @@ static __inline void UsbTrace(const char* fmt, ...) { (void)fmt; } #define XHCI_CC_SUCCESS 1u +#define XHCI_CC_SHORT_PACKET 13u #define XHCI_TRB_ISP (1u << 2) #define XHCI_TRB_IOC (1u << 5) @@ -458,6 +459,7 @@ struct USB_URB { PHYSICAL_ADDRESS BufferPhys; uint32_t Length; uint32_t ActualLength; + uint32_t HcdCompletionCode; KSTATUS Status; IRP* Irp; USB_URB_COMPLETION Complete; @@ -562,6 +564,11 @@ typedef struct USB_HID_EXTENSION { bool Removed; bool Keyboard; bool Mouse; + bool AbsoluteMouse; + uint16_t AbsoluteMaxX; + uint16_t AbsoluteMaxY; + uint16_t LastAbsoluteX; + uint16_t LastAbsoluteY; uint8_t InterfaceNumber; uint8_t Protocol; uint8_t EndpointAddress; @@ -954,9 +961,6 @@ KSTATUS XhciStartHidInterrupts(XHCI_CONTROLLER* xhci); KSTATUS XhciSetHidBootProtocol(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev); KSTATUS XhciAllocateHidMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev); KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev); -const IO_PUBLISHED_USB_ENDPOINT* UsbFindEndpoint(const IO_PUBLISHED_DEVICE* pub, uint8_t endpointType, bool wantIn); -bool UsbHidMatch(const IO_PUBLISHED_DEVICE* pub, void* context); -KSTATUS UsbHidBind(IO_PUBLISHED_DEVICE* pub, void* context); KSTATUS UsbHidAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject); KSTATUS UsbHidDispatchPnp(DEVICE_OBJECT* device, IRP* irp); void XhciInitialiseTransferRing(XHCI_TRB* ring, PHYSICAL_ADDRESS ringPhys, uint32_t trbCount, uint8_t cycle); @@ -982,8 +986,6 @@ KSTATUS XhciAllocateMassStorageMemory(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev); KSTATUS XhciConfigureMassStorageEndpoints(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev); uint8_t UsbFindFreeDiskIndex(void); KSTATUS UsbStorageRegisterDisk(USB_STORAGE_EXTENSION* storage); -bool UsbStorageMatch(const IO_PUBLISHED_DEVICE* pub, void* context); -KSTATUS UsbStorageBind(IO_PUBLISHED_DEVICE* pub, void* context); KSTATUS XhciSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, uint8_t configurationValue); void XhciLogConfigurationTree(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, const USB_CONFIGURATION_DESCRIPTOR* config, uint16_t totalLength); KSTATUS XhciDiscoverAndSetConfiguration(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev, const USB_DEVICE_DESCRIPTOR* devDesc); diff --git a/code/drivers/bus/usb/usb_hid.c b/code/drivers/bus/usb/usb_hid.c index 41cdf3b..1478dd2 100644 --- a/code/drivers/bus/usb/usb_hid.c +++ b/code/drivers/bus/usb/usb_hid.c @@ -135,7 +135,7 @@ static void XhciHandleLegacyHidTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trb return; dev->HidArmed = false; - if (completionCode != XHCI_CC_SUCCESS) { + if (completionCode != XHCI_CC_SUCCESS && completionCode != XHCI_CC_SHORT_PACKET) { DbgPrint("usb: HID transfer failed slot=%u ep=%u cc=%u residue=%u\n", slotId, endpointId, completionCode, residue); return; @@ -298,73 +298,6 @@ KSTATUS XhciConfigureHidEndpoint(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev) { } -const IO_PUBLISHED_USB_ENDPOINT* UsbFindEndpoint(const IO_PUBLISHED_DEVICE* pub, - uint8_t endpointType, - bool wantIn) { - if (!pub || pub->BusType != IoBusUsb) return (const IO_PUBLISHED_USB_ENDPOINT*)0; - - for (uint8_t i = 0; i < pub->u.Usb.EndpointCount; i++) { - const IO_PUBLISHED_USB_ENDPOINT* ep = &pub->u.Usb.Endpoints[i]; - uint8_t type = ep->Attributes & USB_ENDPOINT_ATTR_TYPE_MASK; - bool in = (ep->Address & USB_ENDPOINT_DIR_IN) != 0; - if (type == endpointType && in == wantIn) - return ep; - } - - return (const IO_PUBLISHED_USB_ENDPOINT*)0; -} - - -bool UsbHidMatch(const IO_PUBLISHED_DEVICE* pub, void* context) { - (void)context; - if (!pub || pub->BusType != IoBusUsb) - return false; - if (pub->u.Usb.InterfaceClass != USB_CLASS_HID) - return false; - if (pub->u.Usb.InterfaceSubclass != USB_HID_SUBCLASS_BOOT) - return false; - if (pub->u.Usb.InterfaceProtocol != USB_HID_PROTOCOL_KEYBOARD && - pub->u.Usb.InterfaceProtocol != USB_HID_PROTOCOL_MOUSE) - return false; - - return UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_INTERRUPT, true) != (const IO_PUBLISHED_USB_ENDPOINT*)0; -} - - -KSTATUS UsbHidBind(IO_PUBLISHED_DEVICE* pub, void* context) { - (void)context; - if (!pub || !pub->BusContext || !pub->DeviceContext) - return STATUS_INVALID_PARAMETER; - - XHCI_CONTROLLER* xhci = (XHCI_CONTROLLER*)pub->BusContext; - XHCI_DEVICE* dev = (XHCI_DEVICE*)pub->DeviceContext; - - if (dev->HidActive || dev->HidCandidate) - return STATUS_ALREADY_EXISTS; - - const IO_PUBLISHED_USB_ENDPOINT* ep = UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_INTERRUPT, true); - if (!ep) - return STATUS_NOT_FOUND; - - dev->HidCandidate = true; - dev->HidKeyboard = (pub->u.Usb.InterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD); - dev->HidMouse = (pub->u.Usb.InterfaceProtocol == USB_HID_PROTOCOL_MOUSE); - dev->HidInterface = pub->u.Usb.InterfaceNumber; - dev->HidProtocol = pub->u.Usb.InterfaceProtocol; - dev->HidEndpointAddress = ep->Address; - dev->HidEndpointId = XhciEndpointIdFromAddress(ep->Address); - dev->HidInterval = ep->Interval; - dev->HidMaxPacket = ep->MaxPacketSize; - - KSTATUS st = XhciConfigureHidEndpoint(xhci, dev); - if (!KSUCCESS(st)) { - dev->HidCandidate = false; - dev->HidKeyboard = false; - dev->HidMouse = false; - } - return st; -} - // ------------------------------------------------------------------------- // PDO/FDO boot HID function driver path. // ------------------------------------------------------------------------- @@ -374,6 +307,7 @@ DRIVER_OBJECT gUsbHidDriver; static const IO_DEVICE_ID gUsbHidIds[] = { { IoBusUsb, { "USB\\CLASS_03&SUBCLASS_01&PROT_01", sizeof("USB\\CLASS_03&SUBCLASS_01&PROT_01") - 1 } }, { IoBusUsb, { "USB\\CLASS_03&SUBCLASS_01&PROT_02", sizeof("USB\\CLASS_03&SUBCLASS_01&PROT_02") - 1 } }, + { IoBusUsb, { "USB\\CLASS_03&SUBCLASS_00&PROT_00", sizeof("USB\\CLASS_03&SUBCLASS_00&PROT_00") - 1 } }, }; static const IO_DRIVER_ID_TABLE gUsbHidIdTable = { @@ -381,6 +315,26 @@ static const IO_DRIVER_ID_TABLE gUsbHidIdTable = { sizeof(gUsbHidIds) / sizeof(gUsbHidIds[0]), }; + +static bool UsbHidIsQemuAbsoluteTabletPdo(const USB_PDO_EXTENSION* pdoExt, const USB_ENDPOINT_DESCRIPTOR* ep) { + if (!pdoExt || !pdoExt->Device || !ep) + return false; + if (pdoExt->InterfaceDescriptor.bInterfaceClass != USB_CLASS_HID) + return false; + if (pdoExt->InterfaceDescriptor.bInterfaceSubClass != 0 || + pdoExt->InterfaceDescriptor.bInterfaceProtocol != 0) + return false; + if (pdoExt->Device->VendorId != 0x0627u || pdoExt->Device->ProductId != 0x0001u) + return false; + + /* + * QEMU's usb-kbd and usb-tablet commonly share VID:PID 0627:0001. + * Do not classify by VID:PID alone: the boot keyboard is subclass/protocol + * 01/01, while usb-tablet is the non-boot HID interface 00/00. + */ + return (ep->wMaxPacketSize & 0x07FFu) >= 6u; +} + static const USB_ENDPOINT_DESCRIPTOR* UsbHidFindInterruptInPdo(const USB_PDO_EXTENSION* ext) { if (!ext) return (const USB_ENDPOINT_DESCRIPTOR*)0; @@ -443,6 +397,24 @@ static void UsbHidProcessBootMouseReport(USB_HID_EXTENSION* ext, const uint8_t* ext->LastButtons = buttons; } +static void UsbHidProcessQemuTabletReport(USB_HID_EXTENSION* ext, const uint8_t* report, uint16_t len) { + if (!ext || len < 5) return; + + uint8_t buttons = report[0] & 0x07u; + uint16_t x = (uint16_t)report[1] | ((uint16_t)report[2] << 8); + uint16_t y = (uint16_t)report[3] | ((uint16_t)report[4] << 8); + int wheel = (len >= 6) ? (int)(int8_t)report[5] : 0; + + if (x != ext->LastAbsoluteX || y != ext->LastAbsoluteY || + wheel || buttons != ext->LastButtons) { + KeInputSubmitMouseAbsoluteScaled(x, y, ext->AbsoluteMaxX, ext->AbsoluteMaxY, wheel, buttons); + } + + ext->LastAbsoluteX = x; + ext->LastAbsoluteY = y; + ext->LastButtons = buttons; +} + static KSTATUS UsbHidArmInterrupt(USB_HID_EXTENSION* ext); static void UsbHidInterruptComplete(USB_URB* urb, void* context) { @@ -460,14 +432,19 @@ static void UsbHidInterruptComplete(USB_URB* urb, void* context) { if (ext->Keyboard) UsbHidProcessBootKeyboardReport(ext, ext->ReportBuffer, length); + else if (ext->AbsoluteMouse) + UsbHidProcessQemuTabletReport(ext, ext->ReportBuffer, length); else if (ext->Mouse) UsbHidProcessBootMouseReport(ext, ext->ReportBuffer, length); } else if (urb->Status != STATUS_CANCELLED) { - DbgPrint("usb: HID interrupt URB failed slot=%u if=%u ep=%02x st=0x%x\n", + DbgPrint("usb: HID interrupt URB failed slot=%u if=%u ep=%02x st=0x%x cc=%u actual=%u/%u\n", ext->Device ? ext->Device->SlotId : 0, ext->InterfaceNumber, ext->EndpointAddress, - urb->Status); + urb->Status, + urb->HcdCompletionCode, + urb->ActualLength, + urb->Length); } if (!ext->SurpriseRemoved && !ext->Removed) @@ -563,8 +540,14 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext) if (!ep) return STATUS_NOT_FOUND; - ext->Keyboard = (pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD); - ext->Mouse = (pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_MOUSE); + ext->Keyboard = (pdoExt->InterfaceDescriptor.bInterfaceSubClass == USB_HID_SUBCLASS_BOOT && + pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD); + ext->AbsoluteMouse = UsbHidIsQemuAbsoluteTabletPdo(pdoExt, ep); + ext->Mouse = ext->AbsoluteMouse || + (pdoExt->InterfaceDescriptor.bInterfaceSubClass == USB_HID_SUBCLASS_BOOT && + pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_MOUSE); + ext->AbsoluteMaxX = 0x7FFFu; + ext->AbsoluteMaxY = 0x7FFFu; ext->InterfaceNumber = pdoExt->InterfaceDescriptor.bInterfaceNumber; ext->Protocol = pdoExt->InterfaceDescriptor.bInterfaceProtocol; ext->EndpointAddress = ep->bEndpointAddress; @@ -572,9 +555,10 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext) ext->Interval = ep->bInterval; uint16_t reportLength = ext->MaxPacketSize & 0x07FFu; - if (reportLength == 0) reportLength = ext->Keyboard ? 8u : 4u; + if (reportLength == 0) reportLength = ext->Keyboard ? 8u : (ext->AbsoluteMouse ? 6u : 4u); if (ext->Keyboard && reportLength < 8) reportLength = 8; - if (ext->Mouse && reportLength < 3) reportLength = 3; + if (ext->AbsoluteMouse && reportLength < 6) reportLength = 6; + if (ext->Mouse && !ext->AbsoluteMouse && reportLength < 3) reportLength = 3; if (reportLength > XHCI_MAX_HID_REPORT) reportLength = XHCI_MAX_HID_REPORT; ext->ReportLength = reportLength; @@ -591,15 +575,17 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext) if (!KSUCCESS(st)) return st; - st = UsbHidSendBootProtocol(ext); - if (!KSUCCESS(st)) - return st; + if (!ext->AbsoluteMouse) { + st = UsbHidSendBootProtocol(ext); + if (!KSUCCESS(st)) + return st; + } memset(ext->LastKeys, 0, sizeof(ext->LastKeys)); ext->LastModifiers = 0; ext->LastButtons = 0; - KeInputRegisterDevice(ext->Keyboard ? "usb-hid-keyboard" : "usb-hid-mouse", 0, &ext->InputDeviceId); + KeInputRegisterDevice(ext->Keyboard ? "usb-hid-keyboard" : (ext->AbsoluteMouse ? "usb-hid-tablet" : "usb-hid-mouse"), 0, &ext->InputDeviceId); ext->Started = true; device->PnpState = DevicePnpStarted; @@ -616,8 +602,8 @@ static KSTATUS UsbHidStartDevice(DEVICE_OBJECT* device, USB_HID_EXTENSION* ext) if (xhci) xhci->HidDeviceCount++; - DbgPrint("usb: HID boot %s bound slot=%u if=%u ep=%02x maxpkt=%u interval=%u input=%u urb\n", - ext->Keyboard ? "keyboard" : "mouse", + DbgPrint("usb: HID %s bound slot=%u if=%u ep=%02x maxpkt=%u interval=%u input=%u urb\n", + ext->Keyboard ? "boot-keyboard" : (ext->AbsoluteMouse ? "qemu-tablet" : "boot-mouse"), ext->Device->SlotId, ext->InterfaceNumber, ext->EndpointAddress, @@ -720,24 +706,31 @@ KSTATUS UsbHidAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObje USB_PDO_EXTENSION* pdoExt = (USB_PDO_EXTENSION*)physicalDeviceObject->DeviceExtension; if (!pdoExt || pdoExt->Kind != UsbPdoInterface || !pdoExt->Device) return STATUS_INVALID_PARAMETER; - if (pdoExt->InterfaceDescriptor.bInterfaceClass != USB_CLASS_HID || - pdoExt->InterfaceDescriptor.bInterfaceSubClass != USB_HID_SUBCLASS_BOOT) + if (pdoExt->InterfaceDescriptor.bInterfaceClass != USB_CLASS_HID) return STATUS_INVALID_PARAMETER; - if (pdoExt->InterfaceDescriptor.bInterfaceProtocol != USB_HID_PROTOCOL_KEYBOARD && - pdoExt->InterfaceDescriptor.bInterfaceProtocol != USB_HID_PROTOCOL_MOUSE) - return STATUS_INVALID_PARAMETER; - if (!UsbHidFindInterruptInPdo(pdoExt)) + + const USB_ENDPOINT_DESCRIPTOR* ep = UsbHidFindInterruptInPdo(pdoExt); + if (!ep) return STATUS_NOT_FOUND; + bool bootKeyboard = pdoExt->InterfaceDescriptor.bInterfaceSubClass == USB_HID_SUBCLASS_BOOT && + pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD; + bool bootMouse = pdoExt->InterfaceDescriptor.bInterfaceSubClass == USB_HID_SUBCLASS_BOOT && + pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_MOUSE; + bool qemuTablet = UsbHidIsQemuAbsoluteTabletPdo(pdoExt, ep); + if (!bootKeyboard && !bootMouse && !qemuTablet) + return STATUS_INVALID_PARAMETER; + DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0; KSTATUS st = IoCreateDevice(driver, sizeof(USB_HID_EXTENSION), &fdo); if (!KSUCCESS(st)) return st; (void)IoSetDeviceName(fdo, - pdoExt->InterfaceDescriptor.bInterfaceProtocol == USB_HID_PROTOCOL_KEYBOARD + bootKeyboard ? SYS_SV_LITERAL("usb-hid-keyboard") - : SYS_SV_LITERAL("usb-hid-mouse")); + : (qemuTablet ? SYS_SV_LITERAL("usb-hid-tablet") + : SYS_SV_LITERAL("usb-hid-mouse"))); USB_HID_EXTENSION* ext = (USB_HID_EXTENSION*)fdo->DeviceExtension; ext->Self = fdo; @@ -758,10 +751,11 @@ KSTATUS UsbHidAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObje return STATUS_FAILURE; } - DbgPrint("usb: HID FDO attached slot=%u if=%u proto=%u\n", + DbgPrint("usb: HID FDO attached slot=%u if=%u proto=%u kind=%s\n", ext->Device->SlotId, pdoExt->InterfaceDescriptor.bInterfaceNumber, - pdoExt->InterfaceDescriptor.bInterfaceProtocol); + pdoExt->InterfaceDescriptor.bInterfaceProtocol, + qemuTablet ? "qemu-tablet" : (bootKeyboard ? "keyboard" : "mouse")); return STATUS_SUCCESS; } diff --git a/code/drivers/bus/usb/usb_storage.c b/code/drivers/bus/usb/usb_storage.c index 9f74339..c4c96f4 100644 --- a/code/drivers/bus/usb/usb_storage.c +++ b/code/drivers/bus/usb/usb_storage.c @@ -764,26 +764,6 @@ KSTATUS UsbStorageDispatchPnp(DEVICE_OBJECT* device, IRP* irp) { return STATUS_INVALID_PARAMETER; } -bool UsbStorageMatch(const IO_PUBLISHED_DEVICE* pub, void* context) { - (void)context; - if (!pub || pub->BusType != IoBusUsb) - return false; - if (pub->u.Usb.InterfaceClass != USB_CLASS_MASS_STORAGE) - return false; - if (pub->u.Usb.InterfaceSubclass != USB_MASS_SUBCLASS_SCSI) - return false; - if (pub->u.Usb.InterfaceProtocol != USB_MASS_PROTOCOL_BOT) - return false; - - return UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_BULK, true) && - UsbFindEndpoint(pub, USB_ENDPOINT_TYPE_BULK, false); -} - -KSTATUS UsbStorageBind(IO_PUBLISHED_DEVICE* pub, void* context) { - (void)pub; - (void)context; - return STATUS_NOT_SUPPORTED; -} KSTATUS UsbStorageAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) { if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical) diff --git a/code/drivers/bus/usb/usb_xhci.c b/code/drivers/bus/usb/usb_xhci.c index edb03a5..ee6679e 100644 --- a/code/drivers/bus/usb/usb_xhci.c +++ b/code/drivers/bus/usb/usb_xhci.c @@ -1667,6 +1667,7 @@ static KSTATUS XhciHcdSubmitInterruptUrb(XHCI_CONTROLLER* xhci, XHCI_DEVICE* dev urb->Cancelled = false; urb->Status = STATUS_PENDING; urb->ActualLength = 0; + urb->HcdCompletionCode = 0; XhciBarrier(); XhciWrite32(xhci->Doorbells, (uint32_t)dev->SlotId * 4u, ctx->EndpointId); @@ -1699,7 +1700,8 @@ void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys, KSTATUS st = STATUS_SUCCESS; if (urb->Cancelled) { st = STATUS_CANCELLED; - } else if (completionCode != XHCI_CC_SUCCESS) { + } else if (completionCode != XHCI_CC_SUCCESS && + completionCode != XHCI_CC_SHORT_PACKET) { st = STATUS_DEVICE_ERROR; } @@ -1711,6 +1713,7 @@ void XhciHandleAsyncTransferEvent(XHCI_CONTROLLER* xhci, uint64_t trbPhys, urb->Pending = false; urb->Status = st; urb->ActualLength = actual; + urb->HcdCompletionCode = completionCode; IRP* completeIrp = urb->Irp; urb->Irp = (IRP*)0; diff --git a/code/drivers/display/vmwaresvga/vmwaresvga.c b/code/drivers/display/vmwaresvga/vmwaresvga.c new file mode 100644 index 0000000..1495639 --- /dev/null +++ b/code/drivers/display/vmwaresvga/vmwaresvga.c @@ -0,0 +1,1416 @@ +// vmwaresvga.c - VMware SVGA-II PCI display provider. +// +// The default renderer is the legacy guest-framebuffer path: draw into BAR1, +// enqueue SVGA_CMD_UPDATE through BAR2, and synchronize with the device. +// Screen objects remain available behind VMWSVGA_ENABLE_SCREEN_OBJECTS for +// future validation. + +#include "vmwaresvga.h" + +const HAL_API* gVmwSvgaHal; + +static DRIVER_OBJECT gVmwSvgaDriver; + +static const IO_DEVICE_ID gVmwSvgaIds[] = { + { IoBusPci, { "PCI\\VEN_15AD&DEV_0405", sizeof("PCI\\VEN_15AD&DEV_0405") - 1 } }, +}; + +static const IO_DRIVER_ID_TABLE gVmwSvgaIdTable = { + gVmwSvgaIds, + sizeof(gVmwSvgaIds) / sizeof(gVmwSvgaIds[0]), +}; + +static KSTATUS VmwSvgaSyncDevice(VMWSVGA_DEVICE* v); +static void VmwSvgaWakeFifo(VMWSVGA_DEVICE* v); + +static uint32_t VmwSvgaReadReg(VMWSVGA_DEVICE* v, uint32_t reg) { + __outdword((unsigned short)(v->IoBase + SVGA_INDEX_PORT), reg); + return __indword((unsigned short)(v->IoBase + SVGA_VALUE_PORT)); +} + +static void VmwSvgaWriteReg(VMWSVGA_DEVICE* v, uint32_t reg, uint32_t value) { + __outdword((unsigned short)(v->IoBase + SVGA_INDEX_PORT), reg); + __outdword((unsigned short)(v->IoBase + SVGA_VALUE_PORT), value); +} + +static uint32_t VmwSvgaProbeId(VMWSVGA_DEVICE* v) { + for (uint32_t id = SVGA_ID_2; id >= SVGA_ID_0; id--) { + VmwSvgaWriteReg(v, SVGA_REG_ID, id); + uint32_t got = VmwSvgaReadReg(v, SVGA_REG_ID); + if (got == id) + return id; + if (id == SVGA_ID_0) + break; + } + return SVGA_ID_INVALID; +} + +static KSTATUS VmwSvgaChooseMode(uint32_t* outWidth, uint32_t* outHeight) { + if (!outWidth || !outHeight) + return STATUS_INVALID_PARAMETER; + + DISPLAY_MODE current; + memset(¤t, 0, sizeof(current)); + if (KSUCCESS(KeDisplayGetMode(¤t)) && + current.Format == DisplayPixelFormatX8R8G8B8 && + current.Width > 0 && current.Height > 0) { + *outWidth = current.Width; + *outHeight = current.Height; + return STATUS_SUCCESS; + } + + *outWidth = 1024; + *outHeight = 768; + return STATUS_SUCCESS; +} + +static void VmwSvgaProgramLegacyDisplayTopology(VMWSVGA_DEVICE* v, uint32_t width, uint32_t height, uint32_t caps) { + if (!v || (caps & SVGA_CAP_DISPLAY_TOPOLOGY) == 0) + return; + + /* Legacy GFB rendering still needs one visible primary display rectangle. */ + VmwSvgaWriteReg(v, SVGA_REG_NUM_GUEST_DISPLAYS, 1u); + VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_ID, 0u); + VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_IS_PRIMARY, 1u); + VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_POSITION_X, 0u); + VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_POSITION_Y, 0u); + VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_WIDTH, width); + VmwSvgaWriteReg(v, SVGA_REG_DISPLAY_HEIGHT, height); +} + +static KSTATUS VmwSvgaSetMode(VMWSVGA_DEVICE* v) { + if (!v) + return STATUS_INVALID_PARAMETER; + + uint32_t desiredWidth = 0; + uint32_t desiredHeight = 0; + KSTATUS st = VmwSvgaChooseMode(&desiredWidth, &desiredHeight); + if (!KSUCCESS(st)) + return st; + + uint32_t maxWidth = VmwSvgaReadReg(v, SVGA_REG_MAX_WIDTH); + uint32_t maxHeight = VmwSvgaReadReg(v, SVGA_REG_MAX_HEIGHT); + uint32_t caps = VmwSvgaReadReg(v, SVGA_REG_CAPABILITIES); + if (maxWidth && desiredWidth > maxWidth) + desiredWidth = maxWidth; + if (maxHeight && desiredHeight > maxHeight) + desiredHeight = maxHeight; + if (desiredWidth == 0 || desiredHeight == 0) + return STATUS_DEVICE_ERROR; + + VmwSvgaWriteReg(v, SVGA_REG_CONFIG_DONE, 0u); + VmwSvgaWriteReg(v, SVGA_REG_ENABLE, 0u); + + VmwSvgaWriteReg(v, SVGA_REG_WIDTH, desiredWidth); + VmwSvgaWriteReg(v, SVGA_REG_HEIGHT, desiredHeight); + VmwSvgaWriteReg(v, SVGA_REG_BITS_PER_PIXEL, 32u); + if (caps & SVGA_CAP_PITCHLOCK) + VmwSvgaWriteReg(v, SVGA_REG_PITCHLOCK, desiredWidth * sizeof(uint32_t)); + +#if !VMWSVGA_ENABLE_SCREEN_OBJECTS + VmwSvgaProgramLegacyDisplayTopology(v, desiredWidth, desiredHeight, caps); +#endif + + VmwSvgaWriteReg(v, SVGA_REG_ENABLE, 1u); + + DbgPrint("VMWSVGA: requested mode %ux%u max=%ux%u caps=0x%x\n", + desiredWidth, desiredHeight, maxWidth, maxHeight, caps); + return STATUS_SUCCESS; +} + +static bool VmwSvgaClipRect(VMWSVGA_DEVICE* v, DISPLAY_RECT* r) { + if (!v || !r || r->W <= 0 || r->H <= 0) + return false; + + int64_t x0 = r->X; + int64_t y0 = r->Y; + int64_t x1 = x0 + r->W; + int64_t y1 = y0 + r->H; + + if (x0 < 0) x0 = 0; + if (y0 < 0) y0 = 0; + if (x1 > (int64_t)v->Width) x1 = v->Width; + if (y1 > (int64_t)v->Height) y1 = v->Height; + if (x0 >= x1 || y0 >= y1) + return false; + + r->X = (int32_t)x0; + r->Y = (int32_t)y0; + r->W = (int32_t)(x1 - x0); + r->H = (int32_t)(y1 - y0); + return true; +} + +static uint32_t VmwSvgaFifoNext(VMWSVGA_DEVICE* v, uint32_t offset) { + uint32_t next = offset + sizeof(uint32_t); + uint32_t min = v->Fifo[SVGA_FIFO_MIN]; + uint32_t max = v->Fifo[SVGA_FIFO_MAX]; + if (next >= max) + next = min; + return next; +} + +static KSTATUS VmwSvgaFifoWrite(VMWSVGA_DEVICE* v, uint32_t value) { + if (!v || !v->FifoReady || !v->Fifo) + return STATUS_NOT_SUPPORTED; + + uint32_t min = v->Fifo[SVGA_FIFO_MIN]; + uint32_t max = v->Fifo[SVGA_FIFO_MAX]; + if (min < SVGA_FIFO_NUM_LEGACY_REGS * sizeof(uint32_t) || max > v->FifoBytes || min >= max) + return STATUS_DEVICE_ERROR; + + for (uint32_t guard = 0; guard < VMWSVGA_MAX_WAIT_ITERS; guard++) { + uint32_t next = v->Fifo[SVGA_FIFO_NEXT_CMD]; + uint32_t stop = v->Fifo[SVGA_FIFO_STOP]; + if (next < min || next >= max || stop < min || stop >= max) + return STATUS_DEVICE_ERROR; + + uint32_t nextAfter = VmwSvgaFifoNext(v, next); + if (nextAfter != stop) { + v->Fifo[next / sizeof(uint32_t)] = value; + _ReadWriteBarrier(); + v->Fifo[SVGA_FIFO_NEXT_CMD] = nextAfter; + _ReadWriteBarrier(); + return STATUS_SUCCESS; + } + + KSTATUS syncSt = VmwSvgaSyncDevice(v); + if (!KSUCCESS(syncSt)) + return syncSt; + } + + return STATUS_TIMEOUT; +} + +static bool VmwSvgaHasFifoRegister(VMWSVGA_DEVICE* v, uint32_t index) { + if (!v || !v->Fifo || !v->FifoReady) + return false; + return v->FifoRegCount > index && + v->Fifo[SVGA_FIFO_MIN] >= (index + 1u) * sizeof(uint32_t); +} + +static void VmwSvgaWakeFifo(VMWSVGA_DEVICE* v) { + if (!v || !v->FifoReady) + return; + + _ReadWriteBarrier(); + + if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_BUSY)) { + if (v->Fifo[SVGA_FIFO_BUSY]) + return; + v->Fifo[SVGA_FIFO_BUSY] = 1u; + _ReadWriteBarrier(); + } + + VmwSvgaWriteReg(v, SVGA_REG_SYNC, 1u); +} + +static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels); + +static uint32_t VmwSvgaPremultiplyArgbForBgra(uint32_t argb) { + uint32_t a = argb >> 24; + if (a == 0) + return 0u; + if (a == 255u) + return argb; + + uint32_t r = (argb >> 16) & 0xFFu; + uint32_t g = (argb >> 8) & 0xFFu; + uint32_t b = argb & 0xFFu; + + r = (r * a + 127u) / 255u; + g = (g * a + 127u) / 255u; + b = (b * a + 127u) / 255u; + return (a << 24) | (r << 16) | (g << 8) | b; +} + +static void VmwSvgaCursorCommit(VMWSVGA_DEVICE* v) { + if (!v || !v->HardwareCursor || !v->CursorImageDefined) + return; + + if (v->CursorBypass3 && + VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_ON) && + VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_X) && + VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_Y)) { + v->Fifo[SVGA_FIFO_CURSOR_X] = (uint32_t)v->CursorX; + v->Fifo[SVGA_FIFO_CURSOR_Y] = (uint32_t)v->CursorY; + if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_SCREEN_ID)) + v->Fifo[SVGA_FIFO_CURSOR_SCREEN_ID] = SVGA_ID_INVALID; + v->Fifo[SVGA_FIFO_CURSOR_ON] = v->CursorVisible ? SVGA_CURSOR_ON_SHOW : SVGA_CURSOR_ON_HIDE; + _ReadWriteBarrier(); + if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_COUNT)) + v->Fifo[SVGA_FIFO_CURSOR_COUNT]++; + _ReadWriteBarrier(); + VmwSvgaWakeFifo(v); + return; + } + + /* + * Legacy QEMU SVGA cursors are frontend/host cursors. Re-defining the + * cursor image from the input path can stall the FIFO/frontend after a + * GTK focus/grab transition, so cursor activity must only update the + * legacy position/visibility registers. The image is defined once when + * SetCursorImage runs. + */ + VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ID, 0u); + VmwSvgaWriteReg(v, SVGA_REG_CURSOR_X, (uint32_t)v->CursorX); + VmwSvgaWriteReg(v, SVGA_REG_CURSOR_Y, (uint32_t)v->CursorY); + VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ON, v->CursorVisible ? SVGA_CURSOR_ON_SHOW : SVGA_CURSOR_ON_HIDE); +} + +static void VmwSvgaCursorSetVisible(VMWSVGA_DEVICE* v, bool visible) { + if (!v || !v->HardwareCursor) + return; + v->CursorVisible = visible; + VmwSvgaCursorCommit(v); +} + +static void VmwSvgaCursorSetPosition(VMWSVGA_DEVICE* v, int x, int y) { + if (!v || !v->HardwareCursor) + return; + v->CursorX = x; + v->CursorY = y; + VmwSvgaCursorCommit(v); +} + +static KSTATUS VmwSvgaDefineAlphaCursor(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { + KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_ALPHA_CURSOR); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, 0u); // cursor id, reserved + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)hotX); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)hotY); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)width); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)height); + if (!KSUCCESS(st)) return st; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uint32_t p = argbPixels[(size_t)y * (size_t)width + (size_t)x]; + st = VmwSvgaFifoWrite(v, VmwSvgaPremultiplyArgbForBgra(p)); + if (!KSUCCESS(st)) return st; + } + } + + return VmwSvgaSyncDevice(v); +} + +static uint32_t VmwSvgaPackMonoCursorDword(const uint8_t* bits, + int width, + int height, + int y, + uint32_t dwordIndex) { + /* + * SVGA classic cursor scanlines are padded to 32 bits. QEMU stores + * FIFO dwords in guest-endian order and then interprets the result as + * bytes via cursor_set_mono(), whose first pixel is bit 0x80 of byte 0. + * Therefore byte 0 of the numeric dword must contain pixels 0..7, + * byte 1 pixels 8..15, and so on. + */ + uint8_t bytes[4] = { 0, 0, 0, 0 }; + uint32_t baseX = dwordIndex * 32u; + + for (uint32_t bit = 0; bit < 32u; bit++) { + uint32_t x = baseX + bit; + if (y >= 0 && y < height && x < (uint32_t)width) { + uint8_t v = bits[(size_t)y * (size_t)width + (size_t)x]; + if (v) { + uint32_t byteIndex = bit >> 3; + uint8_t bitMask = (uint8_t)(0x80u >> (bit & 7u)); + bytes[byteIndex] |= bitMask; + } + } + } + + return ((uint32_t)bytes[0]) | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); +} + +static int VmwSvgaClassicCursorWidth(int width) { + /* + * QEMU's VMware SVGA implementation reads classic cursor masks as + * SVGA dword-padded scanlines, then passes the raw byte stream to + * cursor_set_mono(), which advances rows by ceil(width / 8) bytes. + * For widths that are not a multiple of 32 pixels, those two row + * strides differ and every other row is decoded from padding bytes. + * Define the classic cursor with a 32-pixel-aligned visible width and + * leave the extra pixels transparent. The real cursor image remains + * at the left edge and the hotspot is unchanged. + */ + return (width + 31) & ~31; +} + +static void VmwSvgaBuildMonoCursorBits(const uint32_t* argbPixels, + int sourceWidth, + int encodedWidth, + int height, + uint8_t* andBits, + uint8_t* xorBits) { + /* + * Classic SVGA cursors are AND/XOR masks. The most compatible path is + * a 1bpp AND mask plus a 1bpp XOR mask: + * transparent: AND=1 XOR=0 + * black: AND=0 XOR=0 + * white: AND=0 XOR=1 + * This intentionally discards antialiasing, but avoids QEMU/GTK's + * fragile 32bpp classic host-cursor path. + */ + for (int y = 0; y < height; y++) { + for (int x = 0; x < encodedWidth; x++) { + size_t out = (size_t)y * (size_t)encodedWidth + (size_t)x; + if (x >= sourceWidth) { + andBits[out] = 1u; + xorBits[out] = 0u; + continue; + } + + size_t in = (size_t)y * (size_t)sourceWidth + (size_t)x; + uint32_t p = argbPixels[in]; + uint32_t a = p >> 24; + uint32_t r = (p >> 16) & 0xFFu; + uint32_t g = (p >> 8) & 0xFFu; + uint32_t b = p & 0xFFu; + if (a < 128u) { + andBits[out] = 1u; + xorBits[out] = 0u; + } else { + uint32_t luma = r * 30u + g * 59u + b * 11u; + andBits[out] = 0u; + xorBits[out] = (luma >= 12800u) ? 1u : 0u; + } + } + } +} + +static KSTATUS VmwSvgaDefineClassicCursor(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { + int encodedWidth = VmwSvgaClassicCursorWidth(width); + uint32_t dwordsPerRow = ((uint32_t)encodedWidth + 31u) / 32u; + size_t pixelCount = (size_t)encodedWidth * (size_t)height; + + if (encodedWidth > 256 || pixelCount > 256u * 256u) + return STATUS_INVALID_PARAMETER; + + uint8_t* andBits = (uint8_t*)MmAllocatePool(pixelCount); + uint8_t* xorBits = (uint8_t*)MmAllocatePool(pixelCount); + if (!andBits || !xorBits) { + if (andBits) MmFreePool(andBits); + if (xorBits) MmFreePool(xorBits); + return STATUS_NO_MEMORY; + } + + VmwSvgaBuildMonoCursorBits(argbPixels, width, encodedWidth, height, andBits, xorBits); + + DbgPrint("VMWSVGA: classic cursor define source=%dx%d encoded=%dx%d hot=%d,%d\n", + width, height, encodedWidth, height, hotX, hotY); + + KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_CURSOR); + if (!KSUCCESS(st)) goto done; + st = VmwSvgaFifoWrite(v, 0u); // cursor id, reserved + if (!KSUCCESS(st)) goto done; + st = VmwSvgaFifoWrite(v, (uint32_t)hotX); + if (!KSUCCESS(st)) goto done; + st = VmwSvgaFifoWrite(v, (uint32_t)hotY); + if (!KSUCCESS(st)) goto done; + st = VmwSvgaFifoWrite(v, (uint32_t)encodedWidth); + if (!KSUCCESS(st)) goto done; + st = VmwSvgaFifoWrite(v, (uint32_t)height); + if (!KSUCCESS(st)) goto done; + st = VmwSvgaFifoWrite(v, 1u); // AND mask depth + if (!KSUCCESS(st)) goto done; + st = VmwSvgaFifoWrite(v, 1u); // XOR mask depth + if (!KSUCCESS(st)) goto done; + + for (int y = 0; y < height; y++) { + for (uint32_t d = 0; d < dwordsPerRow; d++) { + st = VmwSvgaFifoWrite(v, VmwSvgaPackMonoCursorDword(andBits, encodedWidth, height, y, d)); + if (!KSUCCESS(st)) goto done; + } + } + + for (int y = 0; y < height; y++) { + for (uint32_t d = 0; d < dwordsPerRow; d++) { + st = VmwSvgaFifoWrite(v, VmwSvgaPackMonoCursorDword(xorBits, encodedWidth, height, y, d)); + if (!KSUCCESS(st)) goto done; + } + } + + st = VmwSvgaSyncDevice(v); + +done: + MmFreePool(andBits); + MmFreePool(xorBits); + return st; +} + +static KSTATUS VmwSvgaUploadCursorImage(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { + if (!v || !v->HardwareCursor || !argbPixels || width <= 0 || height <= 0) + return STATUS_INVALID_PARAMETER; + if (width > 256 || height > 256 || hotX < 0 || hotY < 0 || hotX >= width || hotY >= height) + return STATUS_INVALID_PARAMETER; + + KSTATUS st; + if (v->AlphaCursor) + st = VmwSvgaDefineAlphaCursor(v, width, height, hotX, hotY, argbPixels); + else + st = VmwSvgaDefineClassicCursor(v, width, height, hotX, hotY, argbPixels); + + if (KSUCCESS(st)) + VmwSvgaWriteReg(v, SVGA_REG_CURSOR_ID, 0u); + return st; +} + +static KSTATUS VmwSvgaDefineCursorImage(VMWSVGA_DEVICE* v, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { + if (!v || !v->HardwareCursor || !argbPixels || width <= 0 || height <= 0) + return STATUS_INVALID_PARAMETER; + if (width > 256 || height > 256 || hotX < 0 || hotY < 0 || hotX >= width || hotY >= height) + return STATUS_INVALID_PARAMETER; + + size_t bytes = (size_t)width * (size_t)height * sizeof(uint32_t); + uint32_t* copy = (uint32_t*)MmAllocatePool(bytes); + if (!copy) + return STATUS_NO_MEMORY; + memcpy(copy, argbPixels, bytes); + + KSTATUS st = VmwSvgaUploadCursorImage(v, width, height, hotX, hotY, copy); + if (!KSUCCESS(st)) { + MmFreePool(copy); + return st; + } + + if (v->CursorImageArgb) + MmFreePool(v->CursorImageArgb); + v->CursorImageArgb = copy; + v->CursorImageBytes = bytes; + v->CursorImageWidth = width; + v->CursorImageHeight = height; + v->CursorHotX = hotX; + v->CursorHotY = hotY; + v->CursorImageDefined = true; + return STATUS_SUCCESS; +} + + +static uint32_t VmwSvgaAlignUp32(uint32_t value, uint32_t alignment) { + return (value + alignment - 1u) & ~(alignment - 1u); +} + +static KSTATUS VmwSvgaDefineScreenObject(VMWSVGA_DEVICE* v) { + if (!v || !v->FifoReady || !v->UseScreenObject) + return STATUS_INVALID_PARAMETER; + + KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_SCREEN); + if (!KSUCCESS(st)) return st; + + /* SVGAScreenObject, structSize = 11 dwords with backingStore+cloneCount. */ + st = VmwSvgaFifoWrite(v, 44u); /* structSize */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->ScreenId); /* id */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, SVGA_SCREEN_MUST_BE_SET | SVGA_SCREEN_IS_PRIMARY); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->Width); /* size.width */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->Height); /* size.height */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, 0u); /* root.x */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, 0u); /* root.y */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, SVGA_GMR_FRAMEBUFFER); /* backingStore.ptr.gmrId */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->ScreenBackingOffset); /* backingStore.ptr.offset */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->BytesPerLine); /* backingStore.pitch */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, 0u); /* cloneCount */ + if (!KSUCCESS(st)) return st; + + return VmwSvgaSyncDevice(v); +} + +static KSTATUS VmwSvgaDefineGmrfb(VMWSVGA_DEVICE* v) { + if (!v || !v->FifoReady || !v->UseScreenObject) + return STATUS_INVALID_PARAMETER; + + KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_DEFINE_GMRFB); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, SVGA_GMR_FRAMEBUFFER); /* ptr.gmrId */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->ScreenSourceOffset); /* ptr.offset */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->BytesPerLine); /* bytesPerLine */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, SVGA_GMRFB_FORMAT_32BPP_24DEPTH); + if (!KSUCCESS(st)) return st; + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaBlitGmrfbToScreen(VMWSVGA_DEVICE* v, int x, int y, int w, int h) { + DISPLAY_RECT r = { x, y, w, h }; + if (!VmwSvgaClipRect(v, &r)) + return STATUS_SUCCESS; + + KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_BLIT_GMRFB_TO_SCREEN); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.X); /* srcOrigin.x */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.Y); /* srcOrigin.y */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.X); /* destRect.left */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.Y); /* destRect.top */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)(r.X + r.W)); /* destRect.right */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)(r.Y + r.H)); /* destRect.bottom */ + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, v->ScreenId); /* destScreenId */ + if (!KSUCCESS(st)) return st; + + VmwSvgaWakeFifo(v); + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaInitializeScreenObject(VMWSVGA_DEVICE* v) { + if (!v || !v->FifoReady) + return STATUS_INVALID_PARAMETER; + + DbgPrint("VMWSVGA: screen object probe caps=0x%x fifoCaps=0x%x\n", v->Capabilities, v->FifoCaps); + + bool hasScreenObject1 = (v->FifoCaps & SVGA_FIFO_CAP_SCREEN_OBJECT) != 0; + bool hasScreenObject2 = ((v->FifoCaps & SVGA_FIFO_CAP_SCREEN_OBJECT_2) != 0) && + ((v->Capabilities & SVGA_CAP_SCREEN_OBJECT_2) != 0); + + /* + * Real VMware can advertise SCREEN_OBJECT_2 without also setting the + * original FIFO_CAP_SCREEN_OBJECT bit. The command set is still the + * Screen Object command set: DEFINE_SCREEN, DEFINE_GMRFB, and + * BLIT_GMRFB_TO_SCREEN. Treat either capability generation as usable. + */ + if (!hasScreenObject1 && !hasScreenObject2) { + DbgPrint("VMWSVGA: screen object unavailable: screenObject1=%u screenObject2=%u caps=0x%x fifoCaps=0x%x\n", + hasScreenObject1 ? 1u : 0u, + hasScreenObject2 ? 1u : 0u, + v->Capabilities, + v->FifoCaps); + return STATUS_NOT_SUPPORTED; + } + + DbgPrint("VMWSVGA: using screen object generation %u\n", hasScreenObject2 ? 2u : 1u); + + uint32_t sourceBytes = (uint32_t)VmwSvgaAlignUp32((uint32_t)v->BackbufferBytes, 4096u); + uint32_t sourceOffset = VmwSvgaAlignUp32(v->FramebufferOffset, 4096u); + uint32_t backingOffset = VmwSvgaAlignUp32(sourceOffset + sourceBytes, 4096u); + uint32_t backingBytes = sourceBytes; + + if ((uint64_t)sourceOffset + sourceBytes > v->FramebufferBytes || + (uint64_t)backingOffset + backingBytes > v->FramebufferBytes) { + DbgPrint("VMWSVGA: screen object disabled; insufficient GFB source=0x%x backing=0x%x fbLen=0x%llx\n", + sourceBytes, backingBytes, (unsigned long long)v->FramebufferBytes); + return STATUS_NOT_SUPPORTED; + } + + v->ScreenId = SVGA_SCREEN_OBJECT_ID_PRIMARY; + v->ScreenSourceOffset = sourceOffset; + v->ScreenSourceBytes = sourceBytes; + v->ScreenBackingOffset = backingOffset; + v->ScreenBackingBytes = backingBytes; + v->FramebufferOffset = sourceOffset; + v->Framebuffer = v->FramebufferMapping + sourceOffset; + v->UseScreenObject = true; + + KSTATUS st = VmwSvgaDefineGmrfb(v); + if (!KSUCCESS(st)) { + v->UseScreenObject = false; + return st; + } + st = VmwSvgaDefineScreenObject(v); + if (!KSUCCESS(st)) { + v->UseScreenObject = false; + return st; + } + + DbgPrint("VMWSVGA: screen object enabled id=%u sourceOff=0x%x backingOff=0x%x bytes=0x%x\n", + v->ScreenId, v->ScreenSourceOffset, v->ScreenBackingOffset, v->ScreenSourceBytes); + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaLegacyUpdate(VMWSVGA_DEVICE* v, int x, int y, int w, int h) { + DISPLAY_RECT r = { x, y, w, h }; + if (!VmwSvgaClipRect(v, &r)) + return STATUS_SUCCESS; + + KSTATUS st = VmwSvgaFifoWrite(v, SVGA_CMD_UPDATE); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.X); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.Y); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.W); + if (!KSUCCESS(st)) return st; + st = VmwSvgaFifoWrite(v, (uint32_t)r.H); + if (!KSUCCESS(st)) return st; + + VmwSvgaWakeFifo(v); + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaUpdate(VMWSVGA_DEVICE* v, int x, int y, int w, int h) { + DISPLAY_RECT r = { x, y, w, h }; + if (!VmwSvgaClipRect(v, &r)) + return STATUS_SUCCESS; + + v->Stats.DirtyRectCount++; + + if (v->UseScreenObject) { + KSTATUS st = VmwSvgaBlitGmrfbToScreen(v, r.X, r.Y, r.W, r.H); + KSTATUS up = VmwSvgaLegacyUpdate(v, r.X, r.Y, r.W, r.H); + return KSUCCESS(st) ? up : st; + } + + return VmwSvgaLegacyUpdate(v, r.X, r.Y, r.W, r.H); +} + +static KSTATUS VmwSvgaSyncDevice(VMWSVGA_DEVICE* v) { + if (!v) + return STATUS_INVALID_PARAMETER; + v->Stats.SyncCount++; + VmwSvgaWriteReg(v, SVGA_REG_SYNC, 1u); + for (uint32_t guard = 0; guard < VMWSVGA_MAX_WAIT_ITERS; guard++) { + if (VmwSvgaReadReg(v, SVGA_REG_BUSY) == 0) { + if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_BUSY)) + v->Fifo[SVGA_FIFO_BUSY] = 0u; + return STATUS_SUCCESS; + } + } + return STATUS_TIMEOUT; +} + +static uint32_t VmwSvgaBlendPixel(uint32_t dst, uint32_t src) { + uint8_t a = (uint8_t)(src >> 24); + if (a == 0) return dst; + if (a == 255) return src & 0x00FFFFFFu; + + uint8_t sr = (uint8_t)(src >> 16); + uint8_t sg = (uint8_t)(src >> 8); + uint8_t sb = (uint8_t)src; + uint8_t dr = (uint8_t)(dst >> 16); + uint8_t dg = (uint8_t)(dst >> 8); + uint8_t db = (uint8_t)dst; + uint8_t na = (uint8_t)(255 - a); + uint8_t rr = (uint8_t)((a * sr + na * dr) >> 8); + uint8_t gg = (uint8_t)((a * sg + na * dg) >> 8); + uint8_t bb = (uint8_t)((a * sb + na * db) >> 8); + return ((uint32_t)rr << 16) | ((uint32_t)gg << 8) | bb; +} + +static VMWSVGA_DEVICE* VmwSvgaFromDisplay(DISPLAY_DEVICE* device) { + return device ? (VMWSVGA_DEVICE*)device->DriverContext : NULL; +} + +static KSTATUS VmwSvgaDisplayGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !outMode || !v->Framebuffer) + return STATUS_INVALID_PARAMETER; + outMode->Width = v->Width; + outMode->Height = v->Height; + outMode->Stride = v->Stride; + outMode->Format = DisplayPixelFormatX8R8G8B8; + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaDisplayBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !v->Backbuffer || !pixels || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + DISPLAY_RECT r = { x, y, w, h }; + if (!VmwSvgaClipRect(v, &r)) + return STATUS_SUCCESS; + + v->Stats.BlitCount++; + v->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); + + int srcOffsetX = r.X - x; + int srcOffsetY = r.Y - y; + size_t bytesPerRow = (size_t)r.W * sizeof(uint32_t); + for (int row = 0; row < r.H; row++) { + uint32_t* dst = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; + const uint32_t* src = pixels + ((size_t)(srcOffsetY + row) * (size_t)w) + srcOffsetX; + memcpy(dst, src, bytesPerRow); + } + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaDisplayFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !v->Backbuffer || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + DISPLAY_RECT r = { x, y, w, h }; + if (!VmwSvgaClipRect(v, &r)) + return STATUS_SUCCESS; + + v->Stats.FillRectCount++; + v->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); + + for (int yy = r.Y; yy < r.Y + r.H; yy++) { + uint32_t* dst = v->Backbuffer + ((size_t)yy * v->Stride) + r.X; + for (int xx = 0; xx < r.W; xx++) + *dst++ = color; + } + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaDisplayCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !v->Backbuffer || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + if (srcX < 0) { int d = -srcX; srcX = 0; dstX += d; w -= d; } + if (srcY < 0) { int d = -srcY; srcY = 0; dstY += d; h -= d; } + if (dstX < 0) { int d = -dstX; dstX = 0; srcX += d; w -= d; } + if (dstY < 0) { int d = -dstY; dstY = 0; srcY += d; h -= d; } + if (srcX + w > (int)v->Width) w = (int)v->Width - srcX; + if (dstX + w > (int)v->Width) w = (int)v->Width - dstX; + if (srcY + h > (int)v->Height) h = (int)v->Height - srcY; + if (dstY + h > (int)v->Height) h = (int)v->Height - dstY; + if (w <= 0 || h <= 0) + return STATUS_SUCCESS; + + v->Stats.CopyRectCount++; + v->Stats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); + + size_t rowBytes = (size_t)w * sizeof(uint32_t); + if (dstY > srcY) { + for (int row = h - 1; row >= 0; row--) { + uint32_t* dst = v->Backbuffer + ((size_t)(dstY + row) * v->Stride) + dstX; + uint32_t* src = v->Backbuffer + ((size_t)(srcY + row) * v->Stride) + srcX; + memmove(dst, src, rowBytes); + } + } else { + for (int row = 0; row < h; row++) { + uint32_t* dst = v->Backbuffer + ((size_t)(dstY + row) * v->Stride) + dstX; + uint32_t* src = v->Backbuffer + ((size_t)(srcY + row) * v->Stride) + srcX; + memmove(dst, src, rowBytes); + } + } + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaDisplayReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !v->Backbuffer || !outPixels || outStride == 0 || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + DISPLAY_RECT r = { x, y, w, h }; + if (!VmwSvgaClipRect(v, &r)) + return STATUS_SUCCESS; + + v->Stats.ReadRectCount++; + v->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); + + int dstOffsetX = r.X - x; + int dstOffsetY = r.Y - y; + for (int row = 0; row < r.H; row++) { + const uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; + uint32_t* dst = outPixels + ((size_t)(dstOffsetY + row) * outStride) + dstOffsetX; + memcpy(dst, src, (size_t)r.W * sizeof(uint32_t)); + } + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaDisplayPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, size_t rectCount) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !v->Framebuffer || !v->Backbuffer) + return STATUS_INVALID_PARAMETER; + if (!rects || rectCount == 0) + return STATUS_SUCCESS; + + v->Stats.PresentCount++; + v->Stats.PresentRectCount += rectCount; + + KSTATUS result = STATUS_SUCCESS; + for (size_t i = 0; i < rectCount; i++) { + DISPLAY_RECT r = rects[i]; + if (!VmwSvgaClipRect(v, &r)) + continue; + v->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); + size_t rowBytes = (size_t)r.W * sizeof(uint32_t); + for (int row = 0; row < r.H; row++) { + volatile uint8_t* dstBytes = v->Framebuffer + ((size_t)(r.Y + row) * v->BytesPerLine) + ((size_t)r.X * sizeof(uint32_t)); + uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; + memcpy((void*)dstBytes, src, rowBytes); + } + _ReadWriteBarrier(); + + KSTATUS st = VmwSvgaUpdate(v, r.X, r.Y, r.W, r.H); + if (!KSUCCESS(st)) + result = st; + } + + if (KSUCCESS(result)) { + KSTATUS syncSt = VmwSvgaSyncDevice(v); + if (!KSUCCESS(syncSt)) + result = syncSt; + } + + if (v->HardwareCursor && v->CursorVisible) + VmwSvgaCursorCommit(v); + + return result; +} + +static KSTATUS VmwSvgaDisplaySync(DISPLAY_DEVICE* device) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + KSTATUS st = VmwSvgaSyncDevice(v); + if (v && v->HardwareCursor && v->CursorVisible) + VmwSvgaCursorCommit(v); + return st; +} + +static KSTATUS VmwSvgaDisplaySetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !v->HardwareCursor) + return STATUS_NOT_SUPPORTED; + return VmwSvgaDefineCursorImage(v, width, height, hotX, hotY, argbPixels); +} + +static KSTATUS VmwSvgaDisplaySetCursorPosition(DISPLAY_DEVICE* device, int x, int y) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v) + return STATUS_INVALID_PARAMETER; + v->CursorX = x; + v->CursorY = y; + v->Stats.CursorMoveCount++; + if (v->HardwareCursor && v->CursorImageDefined) + VmwSvgaCursorSetPosition(v, x, y); + return STATUS_SUCCESS; +} + +static void VmwSvgaRestoreCursor(VMWSVGA_DEVICE* v) { + if (!v || !v->CursorDrawn || !v->Backbuffer || !v->Framebuffer) + return; + + DISPLAY_RECT r = { v->CursorDrawX, v->CursorDrawY, VMWSVGA_CURSOR_W, VMWSVGA_CURSOR_H }; + if (VmwSvgaClipRect(v, &r)) { + for (int row = 0; row < r.H; row++) { + volatile uint8_t* dstBytes = v->Framebuffer + ((size_t)(r.Y + row) * v->BytesPerLine) + ((size_t)r.X * sizeof(uint32_t)); + uint32_t* src = v->Backbuffer + ((size_t)(r.Y + row) * v->Stride) + r.X; + memcpy((void*)dstBytes, src, (size_t)r.W * sizeof(uint32_t)); + } + (void)VmwSvgaUpdate(v, r.X, r.Y, r.W, r.H); + } + + v->CursorDrawn = false; + v->CursorDrawX = -1; + v->CursorDrawY = -1; +} + +static void VmwSvgaDrawCursor(VMWSVGA_DEVICE* v) { + if (!v || !v->Backbuffer || !v->Framebuffer) + return; + + int x = v->CursorX; + int y = v->CursorY; + v->CursorDrawX = x; + v->CursorDrawY = y; + + DISPLAY_RECT dirty = { x, y, VMWSVGA_CURSOR_W, VMWSVGA_CURSOR_H }; + for (int row = 0; row < VMWSVGA_CURSOR_H; row++) { + int fy = y + row; + if ((unsigned)fy >= v->Height) + continue; + for (int col = 0; col < VMWSVGA_CURSOR_W; col++) { + int fx = x + col; + if ((unsigned)fx >= v->Width) + continue; + uint32_t src = gDisplayDefaultCursorArgb[row][col]; + if ((src >> 24) == 0) + continue; + uint32_t dst = v->Backbuffer[(size_t)fy * v->Stride + fx]; + uint32_t pixel = VmwSvgaBlendPixel(dst, src); + volatile uint32_t* fb = (volatile uint32_t*)(v->Framebuffer + ((size_t)fy * v->BytesPerLine) + ((size_t)fx * sizeof(uint32_t))); + *fb = pixel; + } + } + v->CursorDrawn = true; + if (VmwSvgaClipRect(v, &dirty)) + (void)VmwSvgaUpdate(v, dirty.X, dirty.Y, dirty.W, dirty.H); +} + +static KSTATUS VmwSvgaDisplayShowCursor(DISPLAY_DEVICE* device, bool show) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v) + return STATUS_INVALID_PARAMETER; + + if (v->HardwareCursor) { + if (!v->CursorImageDefined) + return STATUS_NOT_SUPPORTED; + if (show) { + v->Stats.CursorShowCount++; + VmwSvgaCursorSetPosition(v, v->CursorX, v->CursorY); + VmwSvgaCursorSetVisible(v, true); + } else { + v->Stats.CursorHideCount++; + VmwSvgaCursorSetVisible(v, false); + } + return STATUS_SUCCESS; + } + + if (!v->Framebuffer || !v->Backbuffer) + return STATUS_NOT_SUPPORTED; + + if (show) { + v->Stats.CursorShowCount++; + if (v->CursorDrawn) + VmwSvgaRestoreCursor(v); + VmwSvgaDrawCursor(v); + } else { + v->Stats.CursorHideCount++; + VmwSvgaRestoreCursor(v); + } + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaDisplayQueryStats(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats) { + VMWSVGA_DEVICE* v = VmwSvgaFromDisplay(device); + if (!v || !outStats) + return STATUS_INVALID_PARAMETER; + *outStats = v->Stats; + return STATUS_SUCCESS; +} + +static const DISPLAY_DEVICE_OPS gVmwSvgaDisplayOps = { + .GetMode = VmwSvgaDisplayGetMode, + .Blit = VmwSvgaDisplayBlit, + .FillRect = VmwSvgaDisplayFillRect, + .CopyRect = VmwSvgaDisplayCopyRect, + .ReadRect = VmwSvgaDisplayReadRect, + .Present = VmwSvgaDisplayPresent, + .Sync = VmwSvgaDisplaySync, + .SetCursorImage = VmwSvgaDisplaySetCursorImage, + .SetCursorPosition = VmwSvgaDisplaySetCursorPosition, + .ShowCursor = VmwSvgaDisplayShowCursor, + .QueryStats = VmwSvgaDisplayQueryStats, +}; + +static KSTATUS VmwSvgaSelectBars(VMWSVGA_DEVICE* v) { + PCI_BAR bar; + memset(&bar, 0, sizeof(bar)); + + KSTATUS st = PciGetBar(v->Pdo, 0, &bar); + if (!KSUCCESS(st) || bar.Type != PciBarIo || bar.Base > 0xFFFFu) { + DbgPrint("VMWSVGA: BAR0 is not a usable I/O register BAR st=0x%x type=%u base=0x%llx\n", + st, bar.Type, (unsigned long long)bar.Base); + return KSUCCESS(st) ? STATUS_NOT_SUPPORTED : st; + } + v->IoBar = bar; + v->IoBase = (uint16_t)bar.Base; + + st = PciGetBar(v->Pdo, 1, &bar); + if (!KSUCCESS(st) || (bar.Type != PciBarMemory32 && bar.Type != PciBarMemory64)) { + DbgPrint("VMWSVGA: BAR1 is not a usable framebuffer BAR st=0x%x type=%u\n", st, bar.Type); + return KSUCCESS(st) ? STATUS_NOT_SUPPORTED : st; + } + v->FramebufferBar = bar; + + st = PciGetBar(v->Pdo, 2, &bar); + if (!KSUCCESS(st) || (bar.Type != PciBarMemory32 && bar.Type != PciBarMemory64)) { + DbgPrint("VMWSVGA: BAR2 is not a usable FIFO BAR st=0x%x type=%u\n", st, bar.Type); + return KSUCCESS(st) ? STATUS_NOT_SUPPORTED : st; + } + v->FifoBar = bar; + return STATUS_SUCCESS; +} + +static uint64_t VmwSvgaMmioPageFlags(void) { + return PAGE_PRESENT | PAGE_WRITE | PAGE_WRITETHROUGH | PAGE_NOCACHE | PAGE_NX; +} + +static KSTATUS VmwSvgaMapBars(VMWSVGA_DEVICE* v) { + uint64_t mmioFlags = VmwSvgaMmioPageFlags(); + KSTATUS st = PciMapBar(v->Pdo, 1, mmioFlags, + (void**)&v->FramebufferMapping, &v->FramebufferBytes); + if (!KSUCCESS(st) || !v->FramebufferMapping) { + DbgPrint("VMWSVGA: PciMapBar framebuffer failed st=0x%x virt=0x%llx base=0x%llx len=0x%llx\n", + st, + (unsigned long long)(uintptr_t)v->FramebufferMapping, + (unsigned long long)v->FramebufferBar.Base, + (unsigned long long)v->FramebufferBar.Length); + return KSUCCESS(st) ? STATUS_DEVICE_ERROR : st; + } + + st = PciMapBar(v->Pdo, 2, mmioFlags, + (void**)&v->Fifo, &v->FifoBytes); + if (!KSUCCESS(st) || !v->Fifo) { + DbgPrint("VMWSVGA: PciMapBar FIFO failed st=0x%x virt=0x%llx base=0x%llx len=0x%llx\n", + st, + (unsigned long long)(uintptr_t)v->Fifo, + (unsigned long long)v->FifoBar.Base, + (unsigned long long)v->FifoBar.Length); + return KSUCCESS(st) ? STATUS_DEVICE_ERROR : st; + } + + DbgPrint("VMWSVGA: BARs io=0x%04x fbBase=0x%llx fbLen=0x%llx fbVirt=0x%llx fifoBase=0x%llx fifoLen=0x%llx fifoVirt=0x%llx\n", + v->IoBase, + (unsigned long long)v->FramebufferBar.Base, + (unsigned long long)v->FramebufferBar.Length, + (unsigned long long)(uintptr_t)v->FramebufferMapping, + (unsigned long long)v->FifoBar.Base, + (unsigned long long)v->FifoBar.Length, + (unsigned long long)(uintptr_t)v->Fifo); + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaReadMode(VMWSVGA_DEVICE* v) { + v->Width = VmwSvgaReadReg(v, SVGA_REG_WIDTH); + v->Height = VmwSvgaReadReg(v, SVGA_REG_HEIGHT); + v->BytesPerLine = VmwSvgaReadReg(v, SVGA_REG_BYTES_PER_LINE); + v->BitsPerPixel = VmwSvgaReadReg(v, SVGA_REG_BITS_PER_PIXEL); + if (v->BitsPerPixel == 0) + v->BitsPerPixel = VmwSvgaReadReg(v, SVGA_REG_HOST_BITS_PER_PIXEL); + + uint32_t fbSize = VmwSvgaReadReg(v, SVGA_REG_FB_SIZE); + uint32_t fifoSize = VmwSvgaReadReg(v, SVGA_REG_MEM_SIZE); + uint32_t fbOffset = VmwSvgaReadReg(v, SVGA_REG_FB_OFFSET); + uint32_t fbStart = VmwSvgaReadReg(v, SVGA_REG_FB_START); + + v->FifoDeviceBytes = fifoSize; + v->Capabilities = VmwSvgaReadReg(v, SVGA_REG_CAPABILITIES); + + if (v->Width == 0 || v->Height == 0 || v->BytesPerLine == 0) { + DbgPrint("VMWSVGA: invalid mode %ux%u pitch=%u\n", v->Width, v->Height, v->BytesPerLine); + return STATUS_DEVICE_ERROR; + } + if (v->BitsPerPixel != 32) { + DbgPrint("VMWSVGA: unsupported bpp=%u\n", v->BitsPerPixel); + return STATUS_NOT_SUPPORTED; + } + if ((v->BytesPerLine & 3u) != 0) { + DbgPrint("VMWSVGA: unsupported non-dword pitch=%u\n", v->BytesPerLine); + return STATUS_NOT_SUPPORTED; + } + + v->Stride = v->BytesPerLine / sizeof(uint32_t); + if (v->Stride < v->Width) + return STATUS_DEVICE_ERROR; + + if (!v->FramebufferMapping) + return STATUS_DEVICE_ERROR; + + uint64_t needed = (uint64_t)v->BytesPerLine * (uint64_t)v->Height; + if (fbSize && needed > fbSize) + return STATUS_DEVICE_ERROR; + if ((uint64_t)fbOffset > v->FramebufferBytes || needed > v->FramebufferBytes - (uint64_t)fbOffset) + return STATUS_DEVICE_ERROR; + + v->FramebufferOffset = fbOffset; + v->Framebuffer = v->FramebufferMapping + fbOffset; + + v->BackbufferBytes = (size_t)needed; + v->Backbuffer = (uint32_t*)MmAllocatePool(v->BackbufferBytes); + if (!v->Backbuffer) + return STATUS_NO_MEMORY; + + memset(v->Backbuffer, 0, v->BackbufferBytes); + + DbgPrint("VMWSVGA: mode %ux%u stride=%u bpp=%u fbSize=0x%x fbOff=0x%x fbStart=0x%x fifoSize=0x%x caps=0x%x\n", + v->Width, + v->Height, + v->Stride, + v->BitsPerPixel, + fbSize, + fbOffset, + fbStart, + fifoSize, + v->Capabilities); + return STATUS_SUCCESS; +} + +static uint32_t VmwSvgaGetFifoMaxBytes(VMWSVGA_DEVICE* v) { + uint64_t limit = v->FifoBytes; + if (v->FifoDeviceBytes != 0 && (uint64_t)v->FifoDeviceBytes < limit) + limit = v->FifoDeviceBytes; + if (limit > 0xFFFFFFFFu) + limit = 0xFFFFFFFFu; + return (uint32_t)limit & ~3u; +} + +static KSTATUS VmwSvgaInitializeFifo(VMWSVGA_DEVICE* v) { + if (!v->Fifo || v->FifoBytes < 4096u) + return STATUS_DEVICE_ERROR; + + VmwSvgaWriteReg(v, SVGA_REG_CONFIG_DONE, 0u); + + uint32_t regs = VmwSvgaReadReg(v, SVGA_REG_MEM_REGS); + if (regs < SVGA_FIFO_NUM_LEGACY_REGS || regs > 4096u) + regs = SVGA_FIFO_NUM_REGS_FALLBACK; + + uint32_t min = regs * sizeof(uint32_t); + uint32_t max = VmwSvgaGetFifoMaxBytes(v); + + if (max < (SVGA_FIFO_NUM_REGS_FALLBACK * sizeof(uint32_t)) + (10u * 1024u)) + min = SVGA_FIFO_NUM_LEGACY_REGS * sizeof(uint32_t); + + if (max <= min + 32u) + return STATUS_DEVICE_ERROR; + + v->FifoRegCount = min / sizeof(uint32_t); + + memset((void*)v->Fifo, 0, min); + v->Fifo[SVGA_FIFO_MIN] = min; + v->Fifo[SVGA_FIFO_MAX] = max; + v->Fifo[SVGA_FIFO_NEXT_CMD] = min; + v->Fifo[SVGA_FIFO_STOP] = min; + _ReadWriteBarrier(); + + VmwSvgaWriteReg(v, SVGA_REG_CONFIG_DONE, 1u); + v->FifoReady = true; + + if (v->FifoRegCount > SVGA_FIFO_CAPABILITIES) + v->FifoCaps = v->Fifo[SVGA_FIFO_CAPABILITIES]; + if (VmwSvgaHasFifoRegister(v, SVGA_FIFO_BUSY)) + v->Fifo[SVGA_FIFO_BUSY] = 0u; + + DbgPrint("VMWSVGA: FIFO init regs=%u min=0x%x max=0x%x fifoCaps=0x%x deviceBytes=0x%x barBytes=0x%llx\n", + v->FifoRegCount, + min, + max, + v->FifoCaps, + v->FifoDeviceBytes, + (unsigned long long)v->FifoBytes); + return STATUS_SUCCESS; +} + +static KSTATUS VmwSvgaStartDevice(VMWSVGA_DEVICE* v) { + if (!v || v->Magic != VMWSVGA_DEVICE_MAGIC || !v->Fdo || !v->Pdo) + return STATUS_INVALID_PARAMETER; + if (v->Started) + return STATUS_SUCCESS; + + KSTATUS st = PciGetDeviceInfo(v->Pdo, &v->PciInfo); + if (!KSUCCESS(st)) + return st; + + DbgPrint("VMWSVGA: controller at PCI %02x:%02x.%x vendor=%04x device=%04x\n", + v->PciInfo.Address.Bus, + v->PciInfo.Address.Device, + v->PciInfo.Address.Function, + v->PciInfo.VendorId, + v->PciInfo.DeviceId); + + st = VmwSvgaSelectBars(v); + if (!KSUCCESS(st)) + return st; + + st = PciEnableDevice(v->Pdo, PCI_ENABLE_IO | PCI_ENABLE_MEMORY | PCI_ENABLE_BUSMASTER | PCI_ENABLE_INTX); + if (!KSUCCESS(st)) + return st; + + v->DeviceId = VmwSvgaProbeId(v); + if (v->DeviceId == SVGA_ID_INVALID) { + DbgPrint("VMWSVGA: SVGA ID probe failed\n"); + return STATUS_NOT_SUPPORTED; + } + VmwSvgaWriteReg(v, SVGA_REG_GUEST_ID, 0u); + + st = VmwSvgaMapBars(v); + if (!KSUCCESS(st)) + return st; + + KeDisplaySuspendConsole(true); + + st = VmwSvgaSetMode(v); + if (!KSUCCESS(st)) { + KeDisplaySuspendConsole(false); + return st; + } + + st = VmwSvgaReadMode(v); + if (!KSUCCESS(st)) { + KeDisplaySuspendConsole(false); + return st; + } + + st = VmwSvgaInitializeFifo(v); + if (!KSUCCESS(st)) { + KeDisplaySuspendConsole(false); + DbgPrint("VMWSVGA: FIFO init failed st=0x%x\n", st); + return st; + } + +#if VMWSVGA_ENABLE_SCREEN_OBJECTS + KSTATUS screenSt = VmwSvgaInitializeScreenObject(v); + if (!KSUCCESS(screenSt) && screenSt != STATUS_NOT_SUPPORTED) + DbgPrint("VMWSVGA: screen object init failed st=0x%x; using legacy GFB UPDATE\n", screenSt); +#else + v->UseScreenObject = false; + DbgPrint("VMWSVGA: screen objects disabled; using legacy GFB UPDATE path\n"); +#endif + + v->CursorDrawX = -1; + v->CursorDrawY = -1; + + bool cursorCap = (v->Capabilities & SVGA_CAP_CURSOR) != 0; + v->AlphaCursor = (v->Capabilities & SVGA_CAP_ALPHA_CURSOR) != 0; + + v->HardwareCursor = cursorCap; + + v->CursorBypass3 = v->HardwareCursor && + (v->FifoCaps & SVGA_FIFO_CAP_CURSOR_BYPASS_3) && + VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_ON) && + VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_X) && + VmwSvgaHasFifoRegister(v, SVGA_FIFO_CURSOR_Y); + + memset(&v->DisplayDevice, 0, sizeof(v->DisplayDevice)); + v->DisplayDevice.Name = "vmwaresvga"; + v->DisplayDevice.Caps = v->HardwareCursor ? VMWSVGA_CAPS_HW_CURSOR : VMWSVGA_CAPS_MINIMAL; + v->DisplayDevice.DriverContext = v; + v->DisplayDevice.Ops = &gVmwSvgaDisplayOps; + + DbgPrint("VMWSVGA: cursor caps cursor=%u alpha=%u bypass3=%u active=%u\n", + cursorCap ? 1u : 0u, + v->AlphaCursor ? 1u : 0u, + v->CursorBypass3 ? 1u : 0u, + v->HardwareCursor ? 1u : 0u); + + if (v->HardwareCursor) { + st = VmwSvgaDefineCursorImage(v, + DISPLAY_DEFAULT_CURSOR_W, + DISPLAY_DEFAULT_CURSOR_H, + 0, + 0, + &gDisplayDefaultCursorArgb[0][0]); + if (!KSUCCESS(st)) { + DbgPrint("VMWSVGA: hardware cursor init failed st=0x%x; using software cursor fallback\n", st); + v->HardwareCursor = false; + v->AlphaCursor = false; + v->CursorBypass3 = false; + v->DisplayDevice.Caps = VMWSVGA_CAPS_MINIMAL; + } else { + VmwSvgaCursorSetPosition(v, v->CursorX, v->CursorY); + VmwSvgaCursorSetVisible(v, false); + DbgPrint("VMWSVGA: hardware cursor ready alpha=%u bypass3=%u\n", + v->AlphaCursor ? 1u : 0u, + v->CursorBypass3 ? 1u : 0u); + } + } + + st = KeDisplayRegisterDevice(&v->DisplayDevice); + if (!KSUCCESS(st) && st != STATUS_ALREADY_EXISTS) { + KeDisplaySuspendConsole(false); + return st; + } + + st = KeDisplaySetPrimary(&v->DisplayDevice); + KeDisplaySuspendConsole(false); + if (!KSUCCESS(st)) + return st; + + v->Started = true; + v->Fdo->PnpState = DevicePnpStarted; + + (void)VmwSvgaDisplaySync(&v->DisplayDevice); + + DbgPrint("VMWSVGA: initialized as primary display id=0x%x io=0x%04x\n", v->DeviceId, v->IoBase); + return STATUS_SUCCESS; +} + +KSTATUS VmwSvgaDispatchPnp(DEVICE_OBJECT* device, IRP* irp) { + if (!device || !irp || !device->DeviceExtension) + return STATUS_INVALID_PARAMETER; + + VMWSVGA_DEVICE* v = (VMWSVGA_DEVICE*)device->DeviceExtension; + if (v->Magic != VMWSVGA_DEVICE_MAGIC) + return STATUS_INVALID_PARAMETER; + + IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); + if (!ios || ios->MajorFunction != IRP_MJ_PNP) + return STATUS_INVALID_PARAMETER; + + switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) { + case IRP_MN_START_DEVICE: + return VmwSvgaStartDevice(v); + + case IRP_MN_QUERY_REMOVE_DEVICE: + case IRP_MN_CANCEL_REMOVE_DEVICE: + return STATUS_SUCCESS; + + case IRP_MN_SURPRISE_REMOVAL: + v->SurpriseRemoved = true; + device->PnpState = DevicePnpSurpriseRemoved; + return STATUS_SUCCESS; + + case IRP_MN_REMOVE_DEVICE: + v->Removed = true; + device->PnpState = DevicePnpDeleted; + if (v->Backbuffer) { + MmFreePool(v->Backbuffer); + v->Backbuffer = NULL; + } + if (v->CursorImageArgb) { + MmFreePool(v->CursorImageArgb); + v->CursorImageArgb = NULL; + v->CursorImageBytes = 0; + } + IoDetachDevice(device); + IoDeleteDevice(device); + return STATUS_SUCCESS; + + default: + return IoForwardIrpToLower(device, irp); + } +} + +KSTATUS VmwSvgaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) { + if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical) + return STATUS_INVALID_PARAMETER; + + DEVICE_OBJECT* fdo = NULL; + KSTATUS st = IoCreateDevice(driver, sizeof(VMWSVGA_DEVICE), &fdo); + if (!KSUCCESS(st)) + return st; + (void)IoSetDeviceName(fdo, SYS_SV_LITERAL("vmwaresvga")); + + VMWSVGA_DEVICE* v = (VMWSVGA_DEVICE*)fdo->DeviceExtension; + memset(v, 0, sizeof(*v)); + v->Magic = VMWSVGA_DEVICE_MAGIC; + v->Fdo = fdo; + v->Pdo = physicalDeviceObject; + v->CursorDrawX = -1; + v->CursorDrawY = -1; + + v->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject); + if (!v->LowerDevice) { + IoDeleteDevice(fdo); + return STATUS_FAILURE; + } + + DbgPrint("VMWSVGA: FDO attached to %v\n", physicalDeviceObject->Name); + return STATUS_SUCCESS; +} + +KSTATUS DriverEntry(const HAL_API* hal) { + if (!hal) + return STATUS_INVALID_PARAMETER; + + DbgPrint("VMWSVGA: driver loading...\n"); + gVmwSvgaHal = hal; + + memset(&gVmwSvgaDriver, 0, sizeof(gVmwSvgaDriver)); + IoInitializeDriverObject(&gVmwSvgaDriver, SYS_SV_LITERAL("vmwaresvga")); + gVmwSvgaDriver.IdTable = &gVmwSvgaIdTable; + gVmwSvgaDriver.DriverAddDevice = VmwSvgaAddDevice; + gVmwSvgaDriver.MajorFunction[IRP_MJ_PNP] = VmwSvgaDispatchPnp; + + KSTATUS st = IoRegisterDriverObject(&gVmwSvgaDriver); + if (!KSUCCESS(st)) + DbgPrint("VMWSVGA: IoRegisterDriverObject failed: 0x%x\n", st); + return st; +} diff --git a/code/drivers/display/vmwaresvga/vmwaresvga.h b/code/drivers/display/vmwaresvga/vmwaresvga.h new file mode 100644 index 0000000..fbe6f7c --- /dev/null +++ b/code/drivers/display/vmwaresvga/vmwaresvga.h @@ -0,0 +1,215 @@ +#pragma once +// vmwaresvga.h - VMware SVGA-II display provider internals. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VMWSVGA_VENDOR_VMWARE 0x15ADu +#define VMWSVGA_DEVICE_SVGA2 0x0405u +#define VMWSVGA_DEVICE_MAGIC 0x564D5356u /* VMSV */ + +#define SVGA_ID_INVALID 0xFFFFFFFFu +#define SVGA_ID_0 0x90000000u +#define SVGA_ID_1 0x90000001u +#define SVGA_ID_2 0x90000002u + +#define SVGA_INDEX_PORT 0u +#define SVGA_VALUE_PORT 1u + +#define SVGA_REG_ID 0u +#define SVGA_REG_ENABLE 1u +#define SVGA_REG_WIDTH 2u +#define SVGA_REG_HEIGHT 3u +#define SVGA_REG_MAX_WIDTH 4u +#define SVGA_REG_MAX_HEIGHT 5u +#define SVGA_REG_DEPTH 6u +#define SVGA_REG_BITS_PER_PIXEL 7u +#define SVGA_REG_BYTES_PER_LINE 12u +#define SVGA_REG_FB_START 13u +#define SVGA_REG_FB_OFFSET 14u +#define SVGA_REG_VRAM_SIZE 15u +#define SVGA_REG_FB_SIZE 16u +#define SVGA_REG_CAPABILITIES 17u +#define SVGA_REG_MEM_START 18u +#define SVGA_REG_MEM_SIZE 19u +#define SVGA_REG_CONFIG_DONE 20u +#define SVGA_REG_SYNC 21u +#define SVGA_REG_BUSY 22u +#define SVGA_REG_GUEST_ID 23u +#define SVGA_REG_CURSOR_ID 24u +#define SVGA_REG_CURSOR_X 25u +#define SVGA_REG_CURSOR_Y 26u +#define SVGA_REG_CURSOR_ON 27u +#define SVGA_REG_HOST_BITS_PER_PIXEL 28u +#define SVGA_REG_MEM_REGS 30u +#define SVGA_REG_NUM_GUEST_DISPLAYS 34u +#define SVGA_REG_DISPLAY_ID 35u +#define SVGA_REG_DISPLAY_IS_PRIMARY 36u +#define SVGA_REG_DISPLAY_POSITION_X 37u +#define SVGA_REG_DISPLAY_POSITION_Y 38u +#define SVGA_REG_DISPLAY_WIDTH 39u +#define SVGA_REG_DISPLAY_HEIGHT 40u +#define SVGA_REG_PITCHLOCK 32u + +#define SVGA_CMD_INVALID_CMD 0u +#define SVGA_CMD_UPDATE 1u +#define SVGA_CMD_RECT_FILL 2u +#define SVGA_CMD_RECT_COPY 3u +#define SVGA_CMD_DEFINE_CURSOR 19u +#define SVGA_CMD_DISPLAY_CURSOR 20u +#define SVGA_CMD_DEFINE_ALPHA_CURSOR 22u +#define SVGA_CMD_DEFINE_SCREEN 34u +#define SVGA_CMD_DESTROY_SCREEN 35u +#define SVGA_CMD_DEFINE_GMRFB 36u +#define SVGA_CMD_BLIT_GMRFB_TO_SCREEN 37u + +#define SVGA_FIFO_MIN 0u +#define SVGA_FIFO_MAX 1u +#define SVGA_FIFO_NEXT_CMD 2u +#define SVGA_FIFO_STOP 3u +#define SVGA_FIFO_CAPABILITIES 4u +#define SVGA_FIFO_FLAGS 5u +#define SVGA_FIFO_FENCE 6u +#define SVGA_FIFO_3D_HWVERSION 7u +#define SVGA_FIFO_PITCHLOCK 8u +#define SVGA_FIFO_CURSOR_ON 9u +#define SVGA_FIFO_CURSOR_X 10u +#define SVGA_FIFO_CURSOR_Y 11u +#define SVGA_FIFO_CURSOR_COUNT 12u +#define SVGA_FIFO_CURSOR_LAST_UPDATED 13u +#define SVGA_FIFO_RESERVED 14u +#define SVGA_FIFO_CURSOR_SCREEN_ID 15u +#define SVGA_FIFO_3D_CAPS 32u +#define SVGA_FIFO_3D_CAPS_LAST (SVGA_FIFO_3D_CAPS + 255u) +#define SVGA_FIFO_GUEST_3D_HWVERSION (SVGA_FIFO_3D_CAPS_LAST + 1u) +#define SVGA_FIFO_FENCE_GOAL (SVGA_FIFO_3D_CAPS_LAST + 2u) +#define SVGA_FIFO_BUSY (SVGA_FIFO_3D_CAPS_LAST + 3u) +#define SVGA_FIFO_NUM_LEGACY_REGS 4u +#define SVGA_FIFO_NUM_REGS_FALLBACK (SVGA_FIFO_BUSY + 3u) + +#define SVGA_CAP_CURSOR 0x00000020u +#define SVGA_CAP_CURSOR_BYPASS 0x00000040u +#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080u +#define SVGA_CAP_ALPHA_CURSOR 0x00000200u +#define SVGA_CAP_EXTENDED_FIFO 0x00008000u +#define SVGA_CAP_MULTIMON 0x00010000u +#define SVGA_CAP_PITCHLOCK 0x00020000u +#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000u +#define SVGA_CAP_GMR 0x00100000u +#define SVGA_CAP_GMR2 0x00400000u +#define SVGA_CAP_SCREEN_OBJECT_2 0x00800000u + +#define SVGA_CURSOR_ON_HIDE 0u +#define SVGA_CURSOR_ON_SHOW 1u + +#define SVGA_FIFO_CAP_CURSOR_BYPASS_3 0x00000010u +#define SVGA_FIFO_CAP_SCREEN_OBJECT 0x00000080u +#define SVGA_FIFO_CAP_SCREEN_OBJECT_2 0x00000200u + +#define SVGA_GMR_FRAMEBUFFER 0xFFFFFFFEu +#define SVGA_SCREEN_MUST_BE_SET 0x00000001u +#define SVGA_SCREEN_IS_PRIMARY 0x00000002u +#define SVGA_SCREEN_OBJECT_ID_PRIMARY 0u +#define SVGA_GMRFB_FORMAT_32BPP_24DEPTH ((32u) | (24u << 8)) + +/* + * The production path is legacy GFB + UPDATE. Screen objects are kept behind + * an opt-in switch until the SO/GMRFB path is independently validated on real + * VMware hosts. + */ +#ifndef VMWSVGA_ENABLE_SCREEN_OBJECTS +#define VMWSVGA_ENABLE_SCREEN_OBJECTS 0 +#endif + +#define VMWSVGA_MAX_WAIT_ITERS 1000000u +#define VMWSVGA_CURSOR_W DISPLAY_DEFAULT_CURSOR_W +#define VMWSVGA_CURSOR_H DISPLAY_DEFAULT_CURSOR_H + +#define VMWSVGA_CAPS_MINIMAL (DISPLAY_CAP_FRAMEBUFFER | DISPLAY_CAP_SOFTWARE_CURSOR | DISPLAY_CAP_PRESENT_SYNC) +#define VMWSVGA_CAPS_HW_CURSOR (DISPLAY_CAP_FRAMEBUFFER | DISPLAY_CAP_HARDWARE_CURSOR | DISPLAY_CAP_PRESENT_SYNC) + +typedef struct VMWSVGA_DEVICE { + uint32_t Magic; + + DEVICE_OBJECT* Fdo; + DEVICE_OBJECT* Pdo; + DEVICE_OBJECT* LowerDevice; + + PCI_DEVICE_INFO PciInfo; + PCI_BAR IoBar; + PCI_BAR FramebufferBar; + PCI_BAR FifoBar; + + uint16_t IoBase; + volatile uint8_t* FramebufferMapping; + volatile uint8_t* Framebuffer; + volatile uint32_t* Fifo; + uint64_t FramebufferBytes; + uint32_t FramebufferOffset; + uint64_t FifoBytes; + uint32_t FifoDeviceBytes; + + uint32_t DeviceId; + uint32_t Capabilities; + uint32_t Width; + uint32_t Height; + uint32_t Stride; + uint32_t BytesPerLine; + uint32_t BitsPerPixel; + + uint32_t* Backbuffer; + size_t BackbufferBytes; + + uint32_t FifoRegCount; + uint32_t FifoCaps; + + bool UseScreenObject; + uint32_t ScreenId; + uint32_t ScreenSourceOffset; + uint32_t ScreenSourceBytes; + uint32_t ScreenBackingOffset; + uint32_t ScreenBackingBytes; + + bool FifoReady; + bool HardwareCursor; + bool AlphaCursor; + bool CursorBypass3; + bool CursorImageDefined; + bool CursorVisible; + uint32_t* CursorImageArgb; + size_t CursorImageBytes; + int CursorImageWidth; + int CursorImageHeight; + int CursorHotX; + int CursorHotY; + bool Started; + bool Removed; + bool SurpriseRemoved; + + int CursorX; + int CursorY; + int CursorDrawX; + int CursorDrawY; + bool CursorDrawn; + + DISPLAY_STATS Stats; + DISPLAY_DEVICE DisplayDevice; +} VMWSVGA_DEVICE; + +extern const HAL_API* gVmwSvgaHal; + +KSTATUS VmwSvgaAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject); +KSTATUS VmwSvgaDispatchPnp(DEVICE_OBJECT* device, IRP* irp); diff --git a/code/drivers/drivers.ini b/code/drivers/drivers.ini index 0ad70e4..a183c58 100644 --- a/code/drivers/drivers.ini +++ b/code/drivers/drivers.ini @@ -8,5 +8,6 @@ fat32.sys [runtime] lwip.sys +vmwaresvga.sys rtl8139.sys rtl816x.sys diff --git a/code/kernel/hal/hal_screen.c b/code/kernel/hal/hal_screen.c index e7d097e..70057b4 100644 --- a/code/kernel/hal/hal_screen.c +++ b/code/kernel/hal/hal_screen.c @@ -115,6 +115,18 @@ static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) { static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; } +static KSTATUS ScreenGetFramebuffer(HAL_FRAMEBUFFER_INFO* outInfo) { + if (!outInfo || !gFb || gWidth <= 0 || gHeight <= 0 || gStride <= 0) + return STATUS_INVALID_PARAMETER; + + outInfo->Base = (uintptr_t)gFb; + outInfo->Width = (uint32_t)gWidth; + outInfo->Height = (uint32_t)gHeight; + outInfo->Stride = (uint32_t)gStride; + outInfo->Format = 1; // X8R8G8B8 + return STATUS_SUCCESS; +} + static void ScreenScroll(int lines) { if (lines <= 0) return; @@ -327,6 +339,7 @@ void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int w out->PutPixel = ScreenPutPixel; out->Blit = ScreenBlit; out->GetSize = ScreenGetSize; + out->GetFramebuffer = ScreenGetFramebuffer; out->Scroll = ScreenScroll; out->PutChar = ScreenPutChar; out->PrintChar = ScreenPrintChar; diff --git a/code/kernel/include/kernel/display_cursor.h b/code/kernel/include/kernel/display_cursor.h new file mode 100644 index 0000000..ed5de0c --- /dev/null +++ b/code/kernel/include/kernel/display_cursor.h @@ -0,0 +1,35 @@ +#pragma once +// display_cursor.h - shared default software cursor image + +#include + +#define DISPLAY_DEFAULT_CURSOR_W 16 +#define DISPLAY_DEFAULT_CURSOR_H 25 + +static const uint32_t gDisplayDefaultCursorArgb[DISPLAY_DEFAULT_CURSOR_H][DISPLAY_DEFAULT_CURSOR_W] = { + { 0x7F575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFAFAFA, 0x855D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFEFEFE, 0xFFFFFFFF, 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFFFFFF, 0x8A626262, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8D656565, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8E666666, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x91696969, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x946C6C6C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x956D6D6D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9B737373, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9D757575, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0xA0787878, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFF9F9F9, 0xFFFFFFFF, 0xA1797979, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xAE868686, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFFFFFF, 0xFDD5D5D5, 0xF4CCCCCC, 0xF4CCCCCC, 0xF4CCCCCC, 0xF3CBCBCB, 0xF1C9C9C9, 0xFAD2D2D2, 0x7E565656 }, + { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFEFEFEF, 0xFFFCFCFC, 0xFFFFFFFF, 0x9A727272, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x7F575757, 0x8F676767, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE0E0E0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0xFFEBEBEB, 0xFFFFFFFF, 0xFFFFFFFF, 0x81595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFDFDFD, 0xFFFFFFFF, 0x8C646464, 0x00000000, 0x00000000, 0x00000000, 0xB78F8F8F, 0xFFFFFFFF, 0xFFFEFEFE, 0xECC4C4C4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50282828, 0xFFF9F9F9, 0xFFFFFFFF, 0xFFFFFFFF, 0x623A3A3A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xD8B0B0B0, 0xFFFFFFFF, 0xFFFFFFFF, 0xCDA5A5A5, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6C444444, 0xFFFFFFFF, 0xFFFCFCFC, 0xFFF4F4F4, 0x4D252525, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF2CACACA, 0xFFFFFFFF, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8E666666, 0xEFC7C7C7, 0x7D555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; diff --git a/code/kernel/include/kernel/ke_debug.h b/code/kernel/include/kernel/ke_debug.h index a10788b..8982908 100644 --- a/code/kernel/include/kernel/ke_debug.h +++ b/code/kernel/include/kernel/ke_debug.h @@ -11,6 +11,7 @@ #define KE_DEBUG_FILE_LOG 0x00000004u void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), uint32_t flags); +void KeSetDebugConsoleWriter(void (*PrintPtr)(char, uint32_t, uint32_t)); uint32_t KeGetDebugFlags(void); __declspec(dllexport) void DbgPrint(const char* fmt, ...); __declspec(dllexport) void KePrintConsole(const char* buf, size_t len); diff --git a/code/kernel/include/kernel/ke_display.h b/code/kernel/include/kernel/ke_display.h new file mode 100644 index 0000000..418fee9 --- /dev/null +++ b/code/kernel/include/kernel/ke_display.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include +#include +#include + +#ifndef DISPLAY_API +#ifdef KE_DISPLAY_BUILD +#define DISPLAY_API __declspec(dllexport) +#else +#define DISPLAY_API __declspec(dllimport) +#endif +#endif +#include + + +typedef struct KE_CONSOLE_INFO { + uint32_t Columns; + uint32_t Rows; + uint32_t CursorX; + uint32_t CursorY; + uint16_t Attribute; +} KE_CONSOLE_INFO; + +typedef struct KE_DISPLAY_INFO { + char PrimaryName[32]; + DISPLAY_MODE Mode; + uint32_t Caps; + uint32_t DeviceCount; + DISPLAY_STATS ManagerStats; + DISPLAY_STATS DeviceStats; +} KE_DISPLAY_INFO; + +void KeDisplayInitialize(const HAL_API* hal); + +DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device); +DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device); +DISPLAY_API void KeDisplaySuspendConsole(bool suspend); + +DISPLAY_API KSTATUS KeDisplayGetMode(DISPLAY_MODE* outMode); +DISPLAY_API KSTATUS KeDisplayGetSize(int* outWidth, int* outHeight); +DISPLAY_API KSTATUS KeDisplayBlit(int x, int y, int w, int h, const uint32_t* pixels); +DISPLAY_API KSTATUS KeDisplayFillRect(int x, int y, int w, int h, uint32_t color); +DISPLAY_API KSTATUS KeDisplayClear(uint32_t color); +DISPLAY_API KSTATUS KeDisplayCopyRect(int dstX, int dstY, int srcX, int srcY, int w, int h); +DISPLAY_API KSTATUS KeDisplayScroll(int lines); +DISPLAY_API KSTATUS KeDisplayPutChar(char c, int x, int y, uint32_t fg, uint32_t bg); +DISPLAY_API KSTATUS KeDisplayPrintChar(char c, uint32_t fg, uint32_t bg); +DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo); +DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row); +DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t length, size_t* writtenOut); +DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, size_t length, size_t* writtenOut); +void KeDisplayDebugPrintChar(char c, uint32_t fg, uint32_t bg); +DISPLAY_API KSTATUS KeDisplayPresent(const DISPLAY_RECT* rects, size_t rectCount); +DISPLAY_API KSTATUS KeDisplaySync(void); +DISPLAY_API KSTATUS KeDisplayQueryInfo(KE_DISPLAY_INFO* outInfo); + +DISPLAY_API KSTATUS KeDisplaySetCursorImage(int width, int height, int hotX, int hotY, const uint32_t* argbPixels); +DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int x, int y); +DISPLAY_API KSTATUS KeDisplayShowCursor(bool show); diff --git a/code/kernel/include/kernel/ke_input.h b/code/kernel/include/kernel/ke_input.h index c2b318b..d2b8358 100644 --- a/code/kernel/include/kernel/ke_input.h +++ b/code/kernel/include/kernel/ke_input.h @@ -10,6 +10,8 @@ void KeInputInitialize(const HAL_API* hal); __declspec(dllexport) KSTATUS KeInputRegisterDevice(const char* name, uint32_t flags, uint32_t* outDeviceId); __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event); __declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event); +__declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE_EVENT* event); +__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons); INPUT_KEY KeInputKbPoll(void); bool KeInputIsKeyDown(INPUT_KEY key); diff --git a/code/kernel/include/kernel/mm_virt.h b/code/kernel/include/kernel/mm_virt.h index 8f311cc..ad5f332 100644 --- a/code/kernel/include/kernel/mm_virt.h +++ b/code/kernel/include/kernel/mm_virt.h @@ -36,6 +36,7 @@ typedef enum USER_REGION_TYPE { #define PAGE_PRESENT (1ULL << 0) #define PAGE_WRITE (1ULL << 1) #define PAGE_USER (1ULL << 2) +#define PAGE_WRITETHROUGH (1ULL << 3) #define PAGE_NOCACHE (1ULL << 4) #define PAGE_NX (1ULL << 63) diff --git a/code/kernel/include/kernel/syscall_nr.h b/code/kernel/include/kernel/syscall_nr.h index 7c191a1..8560228 100644 --- a/code/kernel/include/kernel/syscall_nr.h +++ b/code/kernel/include/kernel/syscall_nr.h @@ -66,38 +66,42 @@ #define SYS_REMOVE_DIR 57 #define SYS_SET_CWD 58 #define SYS_GET_CWD 59 -#define SYS_WRITE_CONSOLE 60 -#define SYS_BLIT_SCREEN 61 -#define SYS_CLEAR_SCREEN 62 -#define SYS_GET_SCREEN_SIZE 63 -#define SYS_PUT_SCREEN_CHAR 64 -#define SYS_POLL_KEYBOARD 65 -#define SYS_IS_KEY_DOWN 66 -#define SYS_READ_KEYBOARD_CHAR 67 -#define SYS_READ_KEY 68 -#define SYS_GET_KEY_MODIFIERS 69 -#define SYS_POLL_MOUSE 70 -#define SYS_READ_MOUSE 71 -#define SYS_GET_MOUSE_POS 72 -#define SYS_GET_TICK 73 -#define SYS_PRINT_DEBUG 74 -#define SYS_QUERY_TRAP_FRAME 75 -#define SYS_CREATE_SOCKET 76 -#define SYS_BIND_SOCKET 77 -#define SYS_LISTEN_SOCKET 78 -#define SYS_ACCEPT_SOCKET 79 -#define SYS_CONNECT_SOCKET 80 -#define SYS_SEND_SOCKET 81 -#define SYS_RECV_SOCKET 82 -#define SYS_SHUTDOWN_SOCKET 83 -#define SYS_SEND_TO_SOCKET 84 -#define SYS_RECV_FROM_SOCKET 85 -#define SYS_RESOLVE_HOST 86 -#define SYS_SET_SOCKET_OPTION 87 -#define SYS_GET_SOCKET_OPTION 88 -#define SYS_POLL 89 -#define SYS_GET_MEMORY_STATUS 90 -#define SYS_QUERY_KERNEL_STATS 91 -#define SYS_GET_SYSTEM_TIME 92 - -#define SYS_MAX 93 +#define SYS_WRITE_CONSOLE 60 +#define SYS_GET_CONSOLE_INFO 61 +#define SYS_SET_CONSOLE_CURSOR 62 +#define SYS_FILL_CONSOLE_CHARACTER 63 +#define SYS_FILL_CONSOLE_ATTRIBUTE 64 +#define SYS_PUT_SCREEN_CHAR 65 +#define SYS_BLIT_SCREEN 66 +#define SYS_CLEAR_SCREEN 67 +#define SYS_GET_SCREEN_SIZE 68 +#define SYS_QUERY_DISPLAY_INFO 69 +#define SYS_POLL_KEYBOARD 70 +#define SYS_IS_KEY_DOWN 71 +#define SYS_READ_KEYBOARD_CHAR 72 +#define SYS_READ_KEY 73 +#define SYS_GET_KEY_MODIFIERS 74 +#define SYS_POLL_MOUSE 75 +#define SYS_READ_MOUSE 76 +#define SYS_GET_MOUSE_POS 77 +#define SYS_GET_TICK 78 +#define SYS_PRINT_DEBUG 79 +#define SYS_QUERY_TRAP_FRAME 80 +#define SYS_CREATE_SOCKET 81 +#define SYS_BIND_SOCKET 82 +#define SYS_LISTEN_SOCKET 83 +#define SYS_ACCEPT_SOCKET 84 +#define SYS_CONNECT_SOCKET 85 +#define SYS_SEND_SOCKET 86 +#define SYS_RECV_SOCKET 87 +#define SYS_SHUTDOWN_SOCKET 88 +#define SYS_SEND_TO_SOCKET 89 +#define SYS_RECV_FROM_SOCKET 90 +#define SYS_RESOLVE_HOST 91 +#define SYS_SET_SOCKET_OPTION 92 +#define SYS_GET_SOCKET_OPTION 93 +#define SYS_POLL 94 +#define SYS_GET_MEMORY_STATUS 95 +#define SYS_QUERY_KERNEL_STATS 96 +#define SYS_GET_SYSTEM_TIME 97 +#define SYS_MAX 98 diff --git a/code/kernel/io_device.c b/code/kernel/io_device.c index 5f0ad39..4f111d2 100644 --- a/code/kernel/io_device.c +++ b/code/kernel/io_device.c @@ -7,8 +7,6 @@ #include #include -#define MAX_PUBLISHED_DEVICES 64 -#define MAX_DRIVER_MATCHERS 32 #define MAX_DRIVER_OBJECTS 64 #define MAX_PDO_OBJECTS 128 @@ -415,104 +413,6 @@ __declspec(dllexport) KSTATUS IoSendPnpIrpSync(DEVICE_OBJECT* device, return st; } -// ------------------------------------------------------------------------- -// Legacy publish/matcher path -// ------------------------------------------------------------------------- - -typedef struct IO_DRIVER_MATCHER_ENTRY { - bool Used; - const char* DriverName; - IO_BUS_TYPE BusType; - IO_DRIVER_MATCH_CALLBACK Match; - IO_DRIVER_BIND_CALLBACK Bind; - void* Context; -} IO_DRIVER_MATCHER_ENTRY; - -static IO_PUBLISHED_DEVICE gPublishedDevices[MAX_PUBLISHED_DEVICES]; -static uint32_t gPublishedDeviceCount; -static IO_DRIVER_MATCHER_ENTRY gDriverMatchers[MAX_DRIVER_MATCHERS]; -static uint32_t gDriverMatcherCount; - -static bool IoTryBindDevice(IO_PUBLISHED_DEVICE* device, - IO_DRIVER_MATCHER_ENTRY* matcher) { - if (!device || !matcher || !matcher->Used || device->Bound) - return false; - if (matcher->BusType != IoBusUnknown && matcher->BusType != device->BusType) - return false; - if (!matcher->Match || !matcher->Bind) - return false; - if (!matcher->Match(device, matcher->Context)) - return false; - - KSTATUS st = matcher->Bind(device, matcher->Context); - if (KSUCCESS(st)) { - device->Bound = true; - device->BoundDriverName = matcher->DriverName; - DbgPrint("io: %s device %s bound by %s\n", - IoBusTypeName(device->BusType), - device->Name ? device->Name : "", - matcher->DriverName ? matcher->DriverName : ""); - return true; - } - - DbgPrint("io: %s matcher %s accepted %s but bind failed st=0x%x\n", - IoBusTypeName(device->BusType), - matcher->DriverName ? matcher->DriverName : "", - device->Name ? device->Name : "", - st); - return false; -} - -__declspec(dllexport) KSTATUS IoRegisterDriverMatcher(const char* driverName, - IO_BUS_TYPE busType, - IO_DRIVER_MATCH_CALLBACK match, - IO_DRIVER_BIND_CALLBACK bind, - void* context) { - if (!driverName || !match || !bind) - return STATUS_INVALID_PARAMETER; - if (gDriverMatcherCount >= MAX_DRIVER_MATCHERS) - return STATUS_NO_MEMORY; - - IO_DRIVER_MATCHER_ENTRY* entry = &gDriverMatchers[gDriverMatcherCount++]; - memset(entry, 0, sizeof(*entry)); - entry->Used = true; - entry->DriverName = driverName; - entry->BusType = busType; - entry->Match = match; - entry->Bind = bind; - entry->Context = context; - - DbgPrint("io: registered %s matcher %s\n", IoBusTypeName(busType), driverName); - - for (uint32_t i = 0; i < gPublishedDeviceCount; i++) - IoTryBindDevice(&gPublishedDevices[i], entry); - - return STATUS_SUCCESS; -} - -__declspec(dllexport) KSTATUS IoPublishDevice(const IO_PUBLISHED_DEVICE* device) { - if (!device || device->BusType == IoBusUnknown) - return STATUS_INVALID_PARAMETER; - if (gPublishedDeviceCount >= MAX_PUBLISHED_DEVICES) - return STATUS_NO_MEMORY; - - IO_PUBLISHED_DEVICE* stored = &gPublishedDevices[gPublishedDeviceCount++]; - memcpy(stored, device, sizeof(*stored)); - stored->Bound = false; - stored->BoundDriverName = (const char*)0; - - for (uint32_t i = 0; i < gDriverMatcherCount; i++) { - if (IoTryBindDevice(stored, &gDriverMatchers[i])) - break; - } - - return STATUS_SUCCESS; -} - -__declspec(dllexport) uint32_t IoPublishedDeviceCount(void) { - return gPublishedDeviceCount; -} - // ------------------------------------------------------------------------- // Disk namespace // ------------------------------------------------------------------------- diff --git a/code/kernel/ke_debug.c b/code/kernel/ke_debug.c index 3f4114e..b202035 100644 --- a/code/kernel/ke_debug.c +++ b/code/kernel/ke_debug.c @@ -61,6 +61,7 @@ static void KiDebugConsoleWrite(const char* text, size_t len) { } static volatile long gPanicInProgress = 0; +static volatile uintptr_t gPanicOwnerToken = 0; static volatile long gPanicStoppedCpuCount = 0; static _Noreturn void KiPanicSecondaryHalt(void) { @@ -69,10 +70,36 @@ static _Noreturn void KiPanicSecondaryHalt(void) { for (;;) __halt(); } +static uintptr_t KiPanicCurrentToken(void) { + THREAD* thread = KeGetCurrentThread(); + if (thread) + return (uintptr_t)thread; + KCPU* cpu = KeGetCurrentCpu(); + if (cpu) + return (uintptr_t)cpu; + return 1u; +} + static int KiPanicIsActive(void) { return _InterlockedCompareExchange((volatile long*)&gPanicInProgress, 0, 0) != 0; } +static int KiPanicCurrentCpuOwnsReport(void) { + uintptr_t token = KiPanicCurrentToken(); + return token != 0 && gPanicOwnerToken == token; +} + +static void KiBeginFatalReport(void) { + _disable(); + uintptr_t token = KiPanicCurrentToken(); + if (_InterlockedCompareExchange((volatile long*)&gPanicInProgress, 1, 0) == 0) { + gPanicOwnerToken = token; + return; + } + if (gPanicOwnerToken != token) + KiPanicSecondaryHalt(); +} + // Returning NMI handler used for SMP control IPIs. Panic NMIs stop the // target CPU permanently; TLB shootdown NMIs flush and return. A genuinely // unexpected NMI is not handled here and will fall through to the normal fault @@ -88,7 +115,8 @@ __declspec(dllexport) int KeHandleNmiIpi(void) { } void KiKernelPutChar(char c) { - PrintChar(c, 0xCCCCCC, 0x012456); + if (PrintChar) + PrintChar(c, 0xCCCCCC, 0x012456); } void KeInitDebug(void (*PrintPtr)(char, uint32_t, uint32_t), uint32_t flags) { @@ -112,6 +140,13 @@ uint32_t KeGetDebugFlags(void) { return gDebugFlags; } +void KeSetDebugConsoleWriter(void (*PrintPtr)(char, uint32_t, uint32_t)) { + if (!PrintPtr) + return; + PrintChar = PrintPtr; + printf_set_putchar(KiKernelPutChar); +} + __declspec(dllexport) void DbgPrint(const char* fmt, ...) { if (!gDebugEnabled || !fmt) return; @@ -128,6 +163,14 @@ __declspec(dllexport) void DbgPrint(const char* fmt, ...) { len = sizeof(line) - 1u; } + if (KiPanicIsActive()) { + if (!KiPanicCurrentCpuOwnsReport()) + KiPanicSecondaryHalt(); + if (gDebugFlags & KE_DEBUG_CONSOLE) + KiDebugConsoleWrite(line, len); + return; + } + uintptr_t flags = KeAcquireSpinlock(&gDebugPrintLock); if (!_InterlockedCompareExchange((volatile long*)&gDebugLogFlushing, 0, 0)) KiDebugLogAppendLocked(line, len); @@ -187,17 +230,11 @@ __declspec(dllexport) KSTATUS KeFlushDebugLogToFile(SYS_STRING_VIEW path) { // KePanic __declspec(dllexport) _Noreturn void KePanic(int code, const char* fmt, ...) { - // Silence this CPU first. - _disable(); + KiBeginFatalReport(); - // Only one CPU owns the visible panic report. CPUs that fault while the - // system is already panicking immediately join the stopped set. - if (_InterlockedCompareExchange((volatile long*)&gPanicInProgress, 1, 0) != 0) { - KiPanicSecondaryHalt(); - } - - // Stop every other CPU as early as possible. This uses an NMI IPI so it - // still reaches CPUs with IF cleared inside spinlock/high-IRQL regions. + // Stop every other CPU for the final panic banner. Kernel fault dispatch + // claims panic ownership before its detailed dump, so this does not steal + // ownership from the CPU that found the fatal error. HalApicSendPanicIpi(); if (PrintChar) { @@ -550,7 +587,8 @@ static void KiDumpFaultFrame(PROCESS* proc, const KFAULT_FRAME* f) { } static void KiPrintCrashChar(char c) { - PrintChar(c, 0xFFAA00, 0x000000); // amber - distinct from green debug / red panic + if (PrintChar) + PrintChar(c, 0xFFAA00, 0x000000); // amber - distinct from green debug / red panic } static void KiPrintCrashHeader(PROCESS* proc, int vector, uintptr_t rip, @@ -617,6 +655,8 @@ __declspec(dllexport) _Noreturn void KeFaultDispatchFrame(const KFAULT_FRAME* fr } if ((frame->Cs & 3) == 0) { + KiBeginFatalReport(); + int vector = (int)frame->Vector; const char* name = KiFaultVectorName(vector); diff --git a/code/kernel/ke_display.c b/code/kernel/ke_display.c new file mode 100644 index 0000000..afa3ef7 --- /dev/null +++ b/code/kernel/ke_display.c @@ -0,0 +1,1339 @@ +// ke_display.c - kernel display manager +// +// The display manager owns the primary display device, cursor policy, dirty +// rectangles, and the kernel/user screen API. The first provider is a boot +// framebuffer backend over the HAL GOP framebuffer; later GPU drivers register +// DISPLAY_DEVICE providers here. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DISPLAY_DEFAULT_FG 0x00CCCCCCu +#define DISPLAY_DEFAULT_BG 0x00012456u + +#define DISPLAY_MAX_DIRTY_RECTS 64u +#define DISPLAY_CURSOR_W DISPLAY_DEFAULT_CURSOR_W +#define DISPLAY_CURSOR_H DISPLAY_DEFAULT_CURSOR_H +#define FONT_W 8 +#define FONT_H 16 +#define DISPLAY_CONSOLE_MAX_COLS 256 +#define DISPLAY_CONSOLE_MAX_ROWS 128 +#define DISPLAY_CONSOLE_MAX_CELLS (DISPLAY_CONSOLE_MAX_COLS * DISPLAY_CONSOLE_MAX_ROWS) +#define DISPLAY_CONSOLE_DEFAULT_ATTR 0x07u + +typedef struct BOOTFB_CONTEXT { + const HAL_API* Hal; + uint32_t* Framebuffer; + uint32_t* Backbuffer; + uint32_t Width; + uint32_t Height; + uint32_t Stride; + DISPLAY_STATS Stats; +} BOOTFB_CONTEXT; + +static const HAL_API* gDisplayHal; +static DISPLAY_DEVICE* gDisplayList; +static DISPLAY_DEVICE* gPrimaryDisplay; +static SPINLOCK gDisplayLock = SPINLOCK_INIT; + +static DISPLAY_DEVICE gBootFbDevice; +static BOOTFB_CONTEXT gBootFb; + +static DISPLAY_RECT gDirtyRects[DISPLAY_MAX_DIRTY_RECTS]; +static size_t gDirtyRectCount; +static bool gDirtyAll; +static uint32_t gDrawDepth; +static DISPLAY_STATS gDisplayStats; + +static bool gCursorVisible; +static bool gCursorDrawn; +static int gCursorX; +static int gCursorY; +static int gCursorDrawX; +static int gCursorDrawY; + +static int gConsoleX; +static int gConsoleY; +static uint16_t gConsoleAttribute; +static char gConsoleCells[DISPLAY_CONSOLE_MAX_CELLS]; +static uint8_t gConsoleAttrs[DISPLAY_CONSOLE_MAX_CELLS]; +static volatile long gDisplayConsoleSuspended; + +static KSTATUS BootFbGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode); +static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels); +static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color); +static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h); +static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride); +static KSTATUS BootFbPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, size_t rectCount); +static KSTATUS BootFbSync(DISPLAY_DEVICE* device); +static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels); +static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int x, int y); +static KSTATUS BootFbShowCursor(DISPLAY_DEVICE* device, bool show); +static KSTATUS BootFbQueryStats(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats); + +static const DISPLAY_DEVICE_OPS gBootFbOps = { + .GetMode = BootFbGetMode, + .Blit = BootFbBlit, + .FillRect = BootFbFillRect, + .CopyRect = BootFbCopyRect, + .ReadRect = BootFbReadRect, + .Present = BootFbPresent, + .Sync = BootFbSync, + .SetCursorImage = BootFbSetCursorImage, + .SetCursorPosition = BootFbSetCursorPosition, + .ShowCursor = BootFbShowCursor, + .QueryStats = BootFbQueryStats, +}; + + +static bool KiDisplayHasDevice(void) { + return gPrimaryDisplay && gPrimaryDisplay->Ops; +} + +static bool KiDisplayUsesSoftwareCursor(void) { + return KiDisplayHasDevice() && + (gPrimaryDisplay->Caps & DISPLAY_CAP_SOFTWARE_CURSOR) && + gPrimaryDisplay->Ops->ShowCursor; +} + +static bool KiDisplayUsesHardwareCursor(void) { + return KiDisplayHasDevice() && + (gPrimaryDisplay->Caps & DISPLAY_CAP_HARDWARE_CURSOR) && + gPrimaryDisplay->Ops->ShowCursor; +} + +static bool KiDisplayHasCursorOps(void) { + return KiDisplayHasDevice() && + (gPrimaryDisplay->Caps & (DISPLAY_CAP_SOFTWARE_CURSOR | DISPLAY_CAP_HARDWARE_CURSOR)) && + gPrimaryDisplay->Ops->ShowCursor; +} + +static bool KiDisplayClipRect(const DISPLAY_MODE* mode, DISPLAY_RECT* rect) { + if (!mode || !rect || rect->W <= 0 || rect->H <= 0) + return false; + + int64_t x0 = rect->X; + int64_t y0 = rect->Y; + int64_t x1 = x0 + rect->W; + int64_t y1 = y0 + rect->H; + + if (x0 < 0) x0 = 0; + if (y0 < 0) y0 = 0; + if (x1 > (int64_t)mode->Width) x1 = mode->Width; + if (y1 > (int64_t)mode->Height) y1 = mode->Height; + if (x0 >= x1 || y0 >= y1) + return false; + + rect->X = (int32_t)x0; + rect->Y = (int32_t)y0; + rect->W = (int32_t)(x1 - x0); + rect->H = (int32_t)(y1 - y0); + return true; +} + +static void KiDisplayDirtyAll(void) { + gDisplayStats.DirtyAllCount++; + gDirtyAll = true; + gDirtyRectCount = 0; +} + +static void KiDisplayAddDirty(int x, int y, int w, int h) { + if (!KiDisplayHasDevice() || gDirtyAll || w <= 0 || h <= 0) + return; + + DISPLAY_MODE mode; + if (!KSUCCESS(gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode))) + return; + + DISPLAY_RECT rect = { x, y, w, h }; + if (!KiDisplayClipRect(&mode, &rect)) + return; + + if (rect.X == 0 && rect.Y == 0 && + rect.W == (int32_t)mode.Width && rect.H == (int32_t)mode.Height) { + KiDisplayDirtyAll(); + return; + } + + if (gDirtyRectCount >= DISPLAY_MAX_DIRTY_RECTS) { + KiDisplayDirtyAll(); + return; + } + + gDisplayStats.DirtyRectCount++; + gDirtyRects[gDirtyRectCount++] = rect; +} + +static KSTATUS KiDisplayFlushDirty(void) { + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Present) + return STATUS_SUCCESS; + if (!gDirtyAll && gDirtyRectCount == 0) + return STATUS_SUCCESS; + + DISPLAY_RECT full; + const DISPLAY_RECT* rects = gDirtyRects; + size_t count = gDirtyRectCount; + + if (gDirtyAll) { + DISPLAY_MODE mode; + KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode); + if (!KSUCCESS(s)) + return s; + full.X = 0; + full.Y = 0; + full.W = (int32_t)mode.Width; + full.H = (int32_t)mode.Height; + rects = &full; + count = 1; + } + + gDisplayStats.PresentCount++; + gDisplayStats.PresentRectCount += count; + KSTATUS s = gPrimaryDisplay->Ops->Present(gPrimaryDisplay, rects, count); + if (KSUCCESS(s)) { + gDirtyAll = false; + gDirtyRectCount = 0; + } + return s; +} + +static void KiDisplayHideCursorForDraw(void) { + if (!gCursorDrawn || !KiDisplayUsesSoftwareCursor()) + return; + + (void)gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, false); + gCursorDrawn = false; +} + +static void KiDisplayRestoreCursorAfterDraw(void) { + if (!gCursorVisible || gCursorDrawn || !KiDisplayHasCursorOps()) + return; + + if (gPrimaryDisplay->Ops->SetCursorPosition) + (void)gPrimaryDisplay->Ops->SetCursorPosition(gPrimaryDisplay, gCursorX, gCursorY); + + if (KSUCCESS(gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, true))) + gCursorDrawn = true; +} + +static void KiDisplayBeginDraw(void) { + if (gDrawDepth++ == 0) + KiDisplayHideCursorForDraw(); +} + +static KSTATUS KiDisplayEndDraw(void) { + KSTATUS s = STATUS_SUCCESS; + if (gDrawDepth > 0) + gDrawDepth--; + + if (gDrawDepth == 0) { + s = KiDisplayFlushDirty(); + KiDisplayRestoreCursorAfterDraw(); + } + return s; +} + +static KSTATUS KiDisplayDrawBlitNoCursor(int x, int y, int w, int h, const uint32_t* pixels) { + if (!pixels || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Blit) + return STATUS_NOT_SUPPORTED; + + KSTATUS s = gPrimaryDisplay->Ops->Blit(gPrimaryDisplay, x, y, w, h, pixels); + if (KSUCCESS(s)) { + gDisplayStats.BlitCount++; + gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); + KiDisplayAddDirty(x, y, w, h); + } + return s; +} + +static KSTATUS KiDisplayFillRectNoCursor(int x, int y, int w, int h, uint32_t color) { + if (w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->FillRect) + return STATUS_NOT_SUPPORTED; + + KSTATUS s = gPrimaryDisplay->Ops->FillRect(gPrimaryDisplay, x, y, w, h, color); + if (KSUCCESS(s)) { + gDisplayStats.FillRectCount++; + gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); + KiDisplayAddDirty(x, y, w, h); + } + return s; +} + +static KSTATUS KiDisplayCopyRectNoCursor(int dstX, int dstY, int srcX, int srcY, int w, int h) { + if (w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->CopyRect) + return STATUS_NOT_SUPPORTED; + + KSTATUS s = gPrimaryDisplay->Ops->CopyRect(gPrimaryDisplay, dstX, dstY, srcX, srcY, w, h); + if (KSUCCESS(s)) { + gDisplayStats.CopyRectCount++; + gDisplayStats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); + KiDisplayAddDirty(dstX, dstY, w, h); + } + return s; +} + +static size_t KiDisplayConsoleIndex(int col, int row) { + return (size_t)row * DISPLAY_CONSOLE_MAX_COLS + (size_t)col; +} + +static uint32_t KiDisplayConsoleColor(uint8_t index) { + static const uint32_t colors[16] = { + 0x00000000u, 0x000000AAu, 0x0000AA00u, 0x0000AAAAu, + 0x00AA0000u, 0x00AA00AAu, 0x00AA5500u, 0x00AAAAAAu, + 0x00555555u, 0x005555FFu, 0x0055FF55u, 0x0055FFFFu, + 0x00FF5555u, 0x00FF55FFu, 0x00FFFF55u, 0x00FFFFFFu, + }; + return colors[index & 0x0Fu]; +} + +static uint32_t KiDisplayConsoleFg(uint16_t attr) { + return KiDisplayConsoleColor((uint8_t)(attr & 0x0Fu)); +} + +static uint32_t KiDisplayConsoleBg(uint16_t attr) { + return KiDisplayConsoleColor((uint8_t)((attr >> 4) & 0x0Fu)); +} + +static bool KiDisplayGetConsoleGridLocked(int* outCols, int* outRows) { + DISPLAY_MODE mode; + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode) + return false; + if (!KSUCCESS(gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode))) + return false; + + int cols = (int)mode.Width / FONT_W; + int rows = (int)mode.Height / FONT_H; + if (cols > DISPLAY_CONSOLE_MAX_COLS) cols = DISPLAY_CONSOLE_MAX_COLS; + if (rows > DISPLAY_CONSOLE_MAX_ROWS) rows = DISPLAY_CONSOLE_MAX_ROWS; + if (cols <= 0 || rows <= 0) + return false; + + if (outCols) *outCols = cols; + if (outRows) *outRows = rows; + return true; +} + +static void KiDisplayConsoleResetCellsLocked(void) { + for (size_t i = 0; i < DISPLAY_CONSOLE_MAX_CELLS; i++) { + gConsoleCells[i] = ' '; + gConsoleAttrs[i] = (uint8_t)DISPLAY_CONSOLE_DEFAULT_ATTR; + } + gConsoleAttribute = DISPLAY_CONSOLE_DEFAULT_ATTR; +} + +static void KiDisplayConsoleScrollCellsLocked(int cols, int rows, int cellLines) { + if (cols <= 0 || rows <= 0 || cellLines <= 0) + return; + + if (cellLines >= rows) { + for (int y = 0; y < rows; y++) { + for (int x = 0; x < cols; x++) { + size_t i = KiDisplayConsoleIndex(x, y); + gConsoleCells[i] = ' '; + gConsoleAttrs[i] = (uint8_t)gConsoleAttribute; + } + } + return; + } + + for (int y = 0; y < rows - cellLines; y++) { + for (int x = 0; x < cols; x++) { + size_t dst = KiDisplayConsoleIndex(x, y); + size_t src = KiDisplayConsoleIndex(x, y + cellLines); + gConsoleCells[dst] = gConsoleCells[src]; + gConsoleAttrs[dst] = gConsoleAttrs[src]; + } + } + + for (int y = rows - cellLines; y < rows; y++) { + for (int x = 0; x < cols; x++) { + size_t i = KiDisplayConsoleIndex(x, y); + gConsoleCells[i] = ' '; + gConsoleAttrs[i] = (uint8_t)gConsoleAttribute; + } + } +} + +static KSTATUS KiDisplayDrawCharPixelsLocked(char c, int x, int y, uint32_t fg, uint32_t bg) { + uint32_t glyphPixels[FONT_W * FONT_H]; + const uint8_t* glyph = Font8x16 + ((uint8_t)c * 16u); + + for (int row = 0; row < FONT_H; row++) { + uint8_t bits = glyph[row]; + for (int col = 0; col < FONT_W; col++) + glyphPixels[row * FONT_W + col] = (bits & (0x80u >> col)) ? fg : bg; + } + + return KiDisplayDrawBlitNoCursor(x, y, FONT_W, FONT_H, glyphPixels); +} + +static KSTATUS KiDisplayDrawConsoleCellLocked(int col, int row, char c, uint16_t attr, bool updateCell) { + if (updateCell) { + size_t i = KiDisplayConsoleIndex(col, row); + gConsoleCells[i] = c; + gConsoleAttrs[i] = (uint8_t)attr; + } + + return KiDisplayDrawCharPixelsLocked(c, col * FONT_W, row * FONT_H, + KiDisplayConsoleFg(attr), + KiDisplayConsoleBg(attr)); +} + +static void KiDisplaySerialWriteChar(char c) { + while ((__inbyte(0x3FD) & 0x20) == 0) { } + if (c == '\n') + __outbyte(0x3F8, '\r'); + __outbyte(0x3F8, (uint8_t)c); +} + +void KeDisplayInitialize(const HAL_API* hal) { + gDisplayHal = hal; + gDisplayList = NULL; + gPrimaryDisplay = NULL; + gDirtyRectCount = 0; + gDirtyAll = false; + gDrawDepth = 0; + memset(&gDisplayStats, 0, sizeof(gDisplayStats)); + gCursorVisible = false; + gCursorDrawn = false; + gCursorX = 0; + gCursorY = 0; + gCursorDrawX = -1; + gCursorDrawY = -1; + gConsoleX = 0; + gConsoleY = 0; + KiDisplayConsoleResetCellsLocked(); + _InterlockedExchange((volatile long*)&gDisplayConsoleSuspended, 0); + memset(&gBootFb, 0, sizeof(gBootFb)); + memset(&gBootFbDevice, 0, sizeof(gBootFbDevice)); + + if (!hal || !hal->Screen.GetFramebuffer) + return; + + HAL_FRAMEBUFFER_INFO fb; + memset(&fb, 0, sizeof(fb)); + if (!KSUCCESS(hal->Screen.GetFramebuffer(&fb)) || !fb.Base || fb.Width == 0 || fb.Height == 0 || fb.Stride == 0) + return; + + gBootFb.Hal = hal; + gBootFb.Framebuffer = (uint32_t*)fb.Base; + gBootFb.Width = fb.Width; + gBootFb.Height = fb.Height; + gBootFb.Stride = fb.Stride; + + size_t pixels = (size_t)gBootFb.Stride * (size_t)gBootFb.Height; + size_t bytes = pixels * sizeof(uint32_t); + if (gBootFb.Stride > 0 && gBootFb.Height > 0 && + pixels / (size_t)gBootFb.Stride == (size_t)gBootFb.Height && + bytes / sizeof(uint32_t) == pixels) { + gBootFb.Backbuffer = (uint32_t*)MmAllocatePool(bytes); + if (gBootFb.Backbuffer) + memcpy(gBootFb.Backbuffer, gBootFb.Framebuffer, bytes); + } + + gBootFbDevice.Name = "bootfb"; + gBootFbDevice.Caps = DISPLAY_CAP_FRAMEBUFFER; + if (gBootFb.Backbuffer) + gBootFbDevice.Caps |= DISPLAY_CAP_SOFTWARE_CURSOR; + gBootFbDevice.DriverContext = &gBootFb; + gBootFbDevice.Ops = &gBootFbOps; + + KSTATUS s = KeDisplayRegisterDevice(&gBootFbDevice); + if (KSUCCESS(s)) { + (void)KeDisplaySetPrimary(&gBootFbDevice); + DISPLAY_MODE mode; + if (KSUCCESS(KeDisplayGetMode(&mode))) { + DbgPrint("Display: bootfb primary %ux%u stride=%u\n", + mode.Width, mode.Height, mode.Stride); + } + + // KeDisplayInitialize runs while the debug console still points at the + // HAL early console, so the registration messages above are written + // directly through HAL_SCREEN. Resync the bootfb backbuffer once before + // kmain switches DbgPrint to KeDisplayDebugPrintChar. + if (gBootFb.Backbuffer) { + size_t pixels2 = (size_t)gBootFb.Stride * (size_t)gBootFb.Height; + memcpy(gBootFb.Backbuffer, gBootFb.Framebuffer, pixels2 * sizeof(uint32_t)); + } + } +} + +DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device) { + if (!device || !device->Name || !device->Ops || !device->Ops->GetMode) + return STATUS_INVALID_PARAMETER; + + KSTATUS result = STATUS_SUCCESS; + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next) { + if (it == device) { + result = STATUS_ALREADY_EXISTS; + break; + } + } + + if (KSUCCESS(result)) { + device->Next = gDisplayList; + gDisplayList = device; + } + KeReleaseSpinlock(&gDisplayLock, flags); + + if (KSUCCESS(result)) + DbgPrint("Display: registered device %s caps=0x%x\n", device->Name, device->Caps); + return result; +} + +static KSTATUS KiDisplaySeedNewPrimary(DISPLAY_DEVICE* oldDevice, DISPLAY_DEVICE* newDevice) { + if (!oldDevice || !newDevice || oldDevice == newDevice) + return STATUS_SUCCESS; + if (!oldDevice->Ops || !oldDevice->Ops->ReadRect || + !newDevice->Ops || !newDevice->Ops->GetMode || !newDevice->Ops->Blit) + return STATUS_NOT_SUPPORTED; + + DISPLAY_MODE oldMode; + DISPLAY_MODE newMode; + KSTATUS s = oldDevice->Ops->GetMode(oldDevice, &oldMode); + if (!KSUCCESS(s)) + return s; + s = newDevice->Ops->GetMode(newDevice, &newMode); + if (!KSUCCESS(s)) + return s; + if (oldMode.Format != DisplayPixelFormatX8R8G8B8 || + newMode.Format != DisplayPixelFormatX8R8G8B8 || + oldMode.Width != newMode.Width || oldMode.Height != newMode.Height) + return STATUS_NOT_SUPPORTED; + + uint32_t* row = (uint32_t*)MmAllocatePool((size_t)newMode.Width * sizeof(uint32_t)); + if (!row) + return STATUS_NO_MEMORY; + + for (uint32_t y = 0; y < newMode.Height; y++) { + s = oldDevice->Ops->ReadRect(oldDevice, 0, (int)y, (int)newMode.Width, 1, row, newMode.Width); + if (!KSUCCESS(s)) + break; + s = newDevice->Ops->Blit(newDevice, 0, (int)y, (int)newMode.Width, 1, row); + if (!KSUCCESS(s)) + break; + } + + MmFreePool(row); + return s; +} + + +DISPLAY_API void KeDisplaySuspendConsole(bool suspend) { + _InterlockedExchange((volatile long*)&gDisplayConsoleSuspended, suspend ? 1 : 0); +} + +static bool KiDisplayOutputSuspended(void) { + return _InterlockedCompareExchange((volatile long*)&gDisplayConsoleSuspended, 0, 0) != 0; +} + +DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device) { + if (!device || !device->Ops || !device->Ops->GetMode) + return STATUS_INVALID_PARAMETER; + + const char* name = device->Name ? device->Name : "?"; + KSTATUS result = STATUS_SUCCESS; + KSTATUS seedResult = STATUS_SUCCESS; + const char* seedOldName = NULL; + + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + + bool found = false; + for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next) { + if (it == device) { + found = true; + break; + } + } + if (!found) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_FOUND; + } + + DISPLAY_DEVICE* oldDevice = gPrimaryDisplay; + bool cursorWasVisible = gCursorVisible; + + if (oldDevice && gCursorDrawn && oldDevice->Ops && oldDevice->Ops->ShowCursor) + (void)oldDevice->Ops->ShowCursor(oldDevice, false); + + if (oldDevice && oldDevice != device) { + seedOldName = oldDevice->Name; + seedResult = KiDisplaySeedNewPrimary(oldDevice, device); + } + + gPrimaryDisplay = device; + gCursorDrawn = false; + gDirtyAll = false; + gDirtyRectCount = 0; + + if (device->Ops->Present) { + DISPLAY_MODE mode; + if (KSUCCESS(device->Ops->GetMode(device, &mode))) { + DISPLAY_RECT full = { 0, 0, (int32_t)mode.Width, (int32_t)mode.Height }; + (void)device->Ops->Present(device, &full, 1); + } + } + + if (cursorWasVisible) + KiDisplayRestoreCursorAfterDraw(); + + KeReleaseSpinlock(&gDisplayLock, flags); + + if (!KSUCCESS(seedResult) && seedResult != STATUS_NOT_SUPPORTED) { + DbgPrint("Display: primary handoff seed %s -> %s failed st=0x%x\n", + seedOldName ? seedOldName : "?", name, seedResult); + } + if (KSUCCESS(result)) + DbgPrint("Display: primary is %s\n", name); + return result; +} + +DISPLAY_API KSTATUS KeDisplayGetMode(DISPLAY_MODE* outMode) { + if (!outMode) + return STATUS_INVALID_PARAMETER; + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KSTATUS s = (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode) + ? STATUS_INVALID_PARAMETER + : gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, outMode); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +DISPLAY_API KSTATUS KeDisplayGetSize(int* outWidth, int* outHeight) { + DISPLAY_MODE mode; + KSTATUS s = KeDisplayGetMode(&mode); + if (!KSUCCESS(s)) + return s; + if (outWidth) *outWidth = (int)mode.Width; + if (outHeight) *outHeight = (int)mode.Height; + return STATUS_SUCCESS; +} + +DISPLAY_API KSTATUS KeDisplayBlit(int x, int y, int w, int h, const uint32_t* pixels) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KiDisplayBeginDraw(); + KSTATUS s = KiDisplayDrawBlitNoCursor(x, y, w, h, pixels); + KSTATUS ps = KiDisplayEndDraw(); + KeReleaseSpinlock(&gDisplayLock, flags); + return KSUCCESS(s) ? ps : s; +} + +DISPLAY_API KSTATUS KeDisplayFillRect(int x, int y, int w, int h, uint32_t color) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KiDisplayBeginDraw(); + KSTATUS s = KiDisplayFillRectNoCursor(x, y, w, h, color); + KSTATUS ps = KiDisplayEndDraw(); + KeReleaseSpinlock(&gDisplayLock, flags); + return KSUCCESS(s) ? ps : s; +} + +static KSTATUS KiDisplayClearLocked(uint32_t color) { + DISPLAY_MODE mode; + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode) + return STATUS_NOT_SUPPORTED; + KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode); + if (!KSUCCESS(s)) + return s; + KiDisplayBeginDraw(); + s = KiDisplayFillRectNoCursor(0, 0, (int)mode.Width, (int)mode.Height, color); + if (KSUCCESS(s)) + KiDisplayConsoleResetCellsLocked(); + KSTATUS ps = KiDisplayEndDraw(); + return KSUCCESS(s) ? ps : s; +} + +DISPLAY_API KSTATUS KeDisplayClear(uint32_t color) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KSTATUS s = KiDisplayClearLocked(color); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +DISPLAY_API KSTATUS KeDisplayCopyRect(int dstX, int dstY, int srcX, int srcY, int w, int h) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KiDisplayBeginDraw(); + KSTATUS s = KiDisplayCopyRectNoCursor(dstX, dstY, srcX, srcY, w, h); + KSTATUS ps = KiDisplayEndDraw(); + KeReleaseSpinlock(&gDisplayLock, flags); + return KSUCCESS(s) ? ps : s; +} + +static KSTATUS KiDisplayScrollLocked(int lines) { + if (lines <= 0) + return STATUS_SUCCESS; + + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode) + return STATUS_NOT_SUPPORTED; + DISPLAY_MODE mode; + KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode); + if (!KSUCCESS(s)) + return s; + if ((uint32_t)lines >= mode.Height) + return KiDisplayClearLocked(DISPLAY_DEFAULT_BG); + + KiDisplayBeginDraw(); + s = KiDisplayCopyRectNoCursor(0, 0, 0, lines, (int)mode.Width, (int)(mode.Height - (uint32_t)lines)); + if (KSUCCESS(s)) { + s = KiDisplayFillRectNoCursor(0, (int)(mode.Height - (uint32_t)lines), + (int)mode.Width, lines, DISPLAY_DEFAULT_BG); + } + KSTATUS ps = KiDisplayEndDraw(); + return KSUCCESS(s) ? ps : s; +} + +DISPLAY_API KSTATUS KeDisplayScroll(int lines) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KSTATUS s = KiDisplayScrollLocked(lines); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +static KSTATUS KiDisplayPutCharLocked(char c, int x, int y, uint32_t fg, uint32_t bg) { + KiDisplayBeginDraw(); + KSTATUS s = KiDisplayDrawCharPixelsLocked(c, x, y, fg, bg); + KSTATUS ps = KiDisplayEndDraw(); + return KSUCCESS(s) ? ps : s; +} + +DISPLAY_API KSTATUS KeDisplayPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KSTATUS s = KiDisplayPutCharLocked(c, x, y, fg, bg); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +DISPLAY_API KSTATUS KeDisplayPrintChar(char c, uint32_t fg, uint32_t bg) { + KiDisplaySerialWriteChar(c); + + if (KiDisplayOutputSuspended()) + return STATUS_SUCCESS; + + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + + DISPLAY_MODE mode; + KSTATUS s = (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->GetMode) + ? STATUS_NOT_SUPPORTED + : gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &mode); + if (!KSUCCESS(s)) { + const HAL_API* hal = gDisplayHal; + KeReleaseSpinlock(&gDisplayLock, flags); + if (hal && hal->Screen.PrintChar) + hal->Screen.PrintChar(c, fg, bg); + return s; + } + + int cols = 0; + int rows = 0; + if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_DEVICE_ERROR; + } + + if (c == '\n') { + gConsoleX = 0; + gConsoleY++; + } else if (c == '\r') { + gConsoleX = 0; + } else if (c == '\b') { + if (gConsoleX > 0) { + gConsoleX--; + } else if (gConsoleY > 0) { + gConsoleY--; + gConsoleX = cols - 1; + } + } else { + s = KiDisplayPutCharLocked(c, gConsoleX * FONT_W, gConsoleY * FONT_H, fg, bg); + if (!KSUCCESS(s)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return s; + } + if (gConsoleX >= 0 && gConsoleX < cols && gConsoleY >= 0 && gConsoleY < rows) { + size_t i = KiDisplayConsoleIndex(gConsoleX, gConsoleY); + gConsoleCells[i] = c; + gConsoleAttrs[i] = (uint8_t)gConsoleAttribute; + } + if (++gConsoleX >= cols) { + gConsoleX = 0; + gConsoleY++; + } + } + + if (gConsoleY >= rows) { + s = KiDisplayScrollLocked(FONT_H); + if (!KSUCCESS(s)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return s; + } + KiDisplayConsoleScrollCellsLocked(cols, rows, 1); + gConsoleY = rows - 1; + } + + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; +} + +void KeDisplayDebugPrintChar(char c, uint32_t fg, uint32_t bg) { + (void)KeDisplayPrintChar(c, fg, bg); +} + +DISPLAY_API KSTATUS KeDisplayGetConsoleInfo(KE_CONSOLE_INFO* outInfo) { + if (!outInfo) + return STATUS_INVALID_PARAMETER; + + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + int cols = 0; + int rows = 0; + if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_SUPPORTED; + } + + outInfo->Columns = (uint32_t)cols; + outInfo->Rows = (uint32_t)rows; + outInfo->CursorX = (uint32_t)gConsoleX; + outInfo->CursorY = (uint32_t)gConsoleY; + outInfo->Attribute = gConsoleAttribute; + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; +} + +DISPLAY_API KSTATUS KeDisplaySetConsoleCursor(int col, int row) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + int cols = 0; + int rows = 0; + if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_SUPPORTED; + } + if (col < 0 || row < 0 || col >= cols || row >= rows) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_INVALID_PARAMETER; + } + + gConsoleX = col; + gConsoleY = row; + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; +} + +DISPLAY_API KSTATUS KeDisplayFillConsoleChars(char c, int col, int row, size_t length, size_t* writtenOut) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + int cols = 0; + int rows = 0; + if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_SUPPORTED; + } + if (col < 0 || row < 0 || col >= cols || row >= rows) { + KeReleaseSpinlock(&gDisplayLock, flags); + if (writtenOut) *writtenOut = 0; + return STATUS_INVALID_PARAMETER; + } + + size_t written = 0; + KiDisplayBeginDraw(); + KSTATUS s = STATUS_SUCCESS; + int x = col; + int y = row; + while (written < length && y < rows) { + size_t i = KiDisplayConsoleIndex(x, y); + uint16_t attr = gConsoleAttrs[i]; + s = KiDisplayDrawConsoleCellLocked(x, y, c, attr, true); + if (!KSUCCESS(s)) + break; + written++; + if (++x >= cols) { + x = 0; + y++; + } + } + KSTATUS ps = KiDisplayEndDraw(); + KeReleaseSpinlock(&gDisplayLock, flags); + + if (writtenOut) *writtenOut = written; + return KSUCCESS(s) ? ps : s; +} + +DISPLAY_API KSTATUS KeDisplayFillConsoleAttrs(uint16_t attr, int col, int row, size_t length, size_t* writtenOut) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + int cols = 0; + int rows = 0; + if (!KiDisplayGetConsoleGridLocked(&cols, &rows)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_SUPPORTED; + } + if (col < 0 || row < 0 || col >= cols || row >= rows) { + KeReleaseSpinlock(&gDisplayLock, flags); + if (writtenOut) *writtenOut = 0; + return STATUS_INVALID_PARAMETER; + } + + size_t written = 0; + KiDisplayBeginDraw(); + KSTATUS s = STATUS_SUCCESS; + int x = col; + int y = row; + while (written < length && y < rows) { + size_t i = KiDisplayConsoleIndex(x, y); + char c = gConsoleCells[i]; + s = KiDisplayDrawConsoleCellLocked(x, y, c, attr, true); + if (!KSUCCESS(s)) + break; + written++; + if (++x >= cols) { + x = 0; + y++; + } + } + KSTATUS ps = KiDisplayEndDraw(); + KeReleaseSpinlock(&gDisplayLock, flags); + + if (writtenOut) *writtenOut = written; + return KSUCCESS(s) ? ps : s; +} + +DISPLAY_API KSTATUS KeDisplayPresent(const DISPLAY_RECT* rects, size_t rectCount) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Present) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_SUPPORTED; + } + + if (!rects || rectCount == 0) { + KSTATUS s = KiDisplayFlushDirty(); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; + } + + bool hideCursor = gCursorDrawn && KiDisplayUsesSoftwareCursor(); + if (hideCursor) + KiDisplayHideCursorForDraw(); + + gDisplayStats.PresentCount++; + gDisplayStats.PresentRectCount += rectCount; + KSTATUS s = gPrimaryDisplay->Ops->Present(gPrimaryDisplay, rects, rectCount); + + if (hideCursor) + KiDisplayRestoreCursorAfterDraw(); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +DISPLAY_API KSTATUS KeDisplaySync(void) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + if (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->Sync) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_SUPPORTED; + } + gDisplayStats.SyncCount++; + KSTATUS s = gPrimaryDisplay->Ops->Sync(gPrimaryDisplay); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +DISPLAY_API KSTATUS KeDisplayQueryInfo(KE_DISPLAY_INFO* outInfo) { + if (!outInfo) + return STATUS_INVALID_PARAMETER; + + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + memset(outInfo, 0, sizeof(*outInfo)); + + uint32_t count = 0; + for (DISPLAY_DEVICE* it = gDisplayList; it; it = it->Next) + count++; + outInfo->DeviceCount = count; + outInfo->ManagerStats = gDisplayStats; + + if (!KiDisplayHasDevice()) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_NOT_FOUND; + } + + const char* name = gPrimaryDisplay->Name ? gPrimaryDisplay->Name : "?"; + size_t len = strlen(name); + if (len >= sizeof(outInfo->PrimaryName)) + len = sizeof(outInfo->PrimaryName) - 1; + memcpy(outInfo->PrimaryName, name, len); + outInfo->PrimaryName[len] = '\0'; + + outInfo->Caps = gPrimaryDisplay->Caps; + if (gPrimaryDisplay->Ops->GetMode) { + KSTATUS s = gPrimaryDisplay->Ops->GetMode(gPrimaryDisplay, &outInfo->Mode); + if (!KSUCCESS(s)) { + KeReleaseSpinlock(&gDisplayLock, flags); + return s; + } + } + + if (gPrimaryDisplay->Ops->QueryStats) + (void)gPrimaryDisplay->Ops->QueryStats(gPrimaryDisplay, &outInfo->DeviceStats); + + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; +} + +DISPLAY_API KSTATUS KeDisplaySetCursorImage(int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + KSTATUS s = (!KiDisplayHasDevice() || !gPrimaryDisplay->Ops->SetCursorImage) + ? STATUS_NOT_SUPPORTED + : gPrimaryDisplay->Ops->SetCursorImage(gPrimaryDisplay, width, height, hotX, hotY, argbPixels); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +DISPLAY_API KSTATUS KeDisplaySetCursorPosition(int x, int y) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + gCursorX = x; + gCursorY = y; + gDisplayStats.CursorMoveCount++; + + if (KiDisplayOutputSuspended()) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; + } + + if (!gCursorVisible || !KiDisplayHasCursorOps()) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; + } + + if (gCursorDrawn && KiDisplayUsesSoftwareCursor()) + (void)gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, false); + + if (gPrimaryDisplay->Ops->SetCursorPosition) + (void)gPrimaryDisplay->Ops->SetCursorPosition(gPrimaryDisplay, x, y); + + if (gCursorDrawn && KiDisplayUsesHardwareCursor()) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; + } + + KSTATUS s = gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, true); + gCursorDrawn = KSUCCESS(s); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +DISPLAY_API KSTATUS KeDisplayShowCursor(bool show) { + uintptr_t flags = KeAcquireSpinlock(&gDisplayLock); + gCursorVisible = show; + if (show) gDisplayStats.CursorShowCount++; else gDisplayStats.CursorHideCount++; + + if (KiDisplayOutputSuspended()) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; + } + + if (!KiDisplayHasCursorOps()) { + gCursorDrawn = false; + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; + } + + if (!show) { + if (gCursorDrawn) + (void)gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, false); + gCursorDrawn = false; + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; + } + + if (gCursorDrawn) { + KeReleaseSpinlock(&gDisplayLock, flags); + return STATUS_SUCCESS; + } + + if (gPrimaryDisplay->Ops->SetCursorPosition) + (void)gPrimaryDisplay->Ops->SetCursorPosition(gPrimaryDisplay, gCursorX, gCursorY); + + KSTATUS s = gPrimaryDisplay->Ops->ShowCursor(gPrimaryDisplay, true); + gCursorDrawn = KSUCCESS(s); + KeReleaseSpinlock(&gDisplayLock, flags); + return s; +} + +static BOOTFB_CONTEXT* BootFbContext(DISPLAY_DEVICE* device) { + return device ? (BOOTFB_CONTEXT*)device->DriverContext : NULL; +} + +static KSTATUS BootFbGetMode(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !fb->Framebuffer || !outMode) + return STATUS_INVALID_PARAMETER; + + outMode->Width = fb->Width; + outMode->Height = fb->Height; + outMode->Stride = fb->Stride; + outMode->Format = DisplayPixelFormatX8R8G8B8; + return STATUS_SUCCESS; +} + +static KSTATUS BootFbBlit(DISPLAY_DEVICE* device, int x, int y, int w, int h, const uint32_t* pixels) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !fb->Framebuffer || !pixels || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + uint32_t* dstBase = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer; + + int startX = x < 0 ? 0 : x; + int startY = y < 0 ? 0 : y; + int endX = x + w > (int)fb->Width ? (int)fb->Width : x + w; + int endY = y + h > (int)fb->Height ? (int)fb->Height : y + h; + if (startX >= endX || startY >= endY) + return STATUS_SUCCESS; + + int srcOffsetX = startX - x; + int srcOffsetY = startY - y; + size_t bytesPerRow = (size_t)(endX - startX) * sizeof(uint32_t); + + fb->Stats.BlitCount++; + fb->Stats.BytesCopied += (uint64_t)(endX - startX) * (uint64_t)(endY - startY) * sizeof(uint32_t); + + for (int row = 0; row < endY - startY; row++) { + uint32_t* dst = dstBase + ((size_t)(startY + row) * fb->Stride) + startX; + const uint32_t* src = pixels + ((size_t)(srcOffsetY + row) * (size_t)w) + srcOffsetX; + memcpy(dst, src, bytesPerRow); + } + return STATUS_SUCCESS; +} + +static KSTATUS BootFbFillRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t color) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !fb->Framebuffer || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + uint32_t* dstBase = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer; + + int startX = x < 0 ? 0 : x; + int startY = y < 0 ? 0 : y; + int endX = x + w > (int)fb->Width ? (int)fb->Width : x + w; + int endY = y + h > (int)fb->Height ? (int)fb->Height : y + h; + if (startX >= endX || startY >= endY) + return STATUS_SUCCESS; + + fb->Stats.FillRectCount++; + fb->Stats.BytesCopied += (uint64_t)(endX - startX) * (uint64_t)(endY - startY) * sizeof(uint32_t); + + for (int yy = startY; yy < endY; yy++) { + uint32_t* dst = dstBase + ((size_t)yy * fb->Stride) + startX; + for (int xx = startX; xx < endX; xx++) + *dst++ = color; + } + return STATUS_SUCCESS; +} + +static KSTATUS BootFbCopyRect(DISPLAY_DEVICE* device, int dstX, int dstY, int srcX, int srcY, int w, int h) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !fb->Framebuffer || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + uint32_t* base = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer; + + if (srcX < 0) { int d = -srcX; srcX = 0; dstX += d; w -= d; } + if (srcY < 0) { int d = -srcY; srcY = 0; dstY += d; h -= d; } + if (dstX < 0) { int d = -dstX; dstX = 0; srcX += d; w -= d; } + if (dstY < 0) { int d = -dstY; dstY = 0; srcY += d; h -= d; } + if (srcX + w > (int)fb->Width) w = (int)fb->Width - srcX; + if (dstX + w > (int)fb->Width) w = (int)fb->Width - dstX; + if (srcY + h > (int)fb->Height) h = (int)fb->Height - srcY; + if (dstY + h > (int)fb->Height) h = (int)fb->Height - dstY; + if (w <= 0 || h <= 0) + return STATUS_SUCCESS; + + fb->Stats.CopyRectCount++; + fb->Stats.BytesCopied += (uint64_t)(uint32_t)w * (uint64_t)(uint32_t)h * sizeof(uint32_t); + + size_t rowBytes = (size_t)w * sizeof(uint32_t); + if (dstY > srcY) { + for (int row = h - 1; row >= 0; row--) { + uint32_t* dst = base + ((size_t)(dstY + row) * fb->Stride) + dstX; + uint32_t* src = base + ((size_t)(srcY + row) * fb->Stride) + srcX; + memmove(dst, src, rowBytes); + } + } else { + for (int row = 0; row < h; row++) { + uint32_t* dst = base + ((size_t)(dstY + row) * fb->Stride) + dstX; + uint32_t* src = base + ((size_t)(srcY + row) * fb->Stride) + srcX; + memmove(dst, src, rowBytes); + } + } + return STATUS_SUCCESS; +} + +static KSTATUS BootFbReadRect(DISPLAY_DEVICE* device, int x, int y, int w, int h, uint32_t* outPixels, uint32_t outStride) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !fb->Framebuffer || !outPixels || outStride == 0 || w <= 0 || h <= 0) + return STATUS_INVALID_PARAMETER; + + uint32_t* srcBase = fb->Backbuffer ? fb->Backbuffer : fb->Framebuffer; + DISPLAY_MODE mode = { fb->Width, fb->Height, fb->Stride, DisplayPixelFormatX8R8G8B8 }; + DISPLAY_RECT r = { x, y, w, h }; + if (!KiDisplayClipRect(&mode, &r)) + return STATUS_SUCCESS; + + fb->Stats.ReadRectCount++; + fb->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); + + int dstOffsetX = r.X - x; + int dstOffsetY = r.Y - y; + for (int row = 0; row < r.H; row++) { + const uint32_t* src = srcBase + ((size_t)(r.Y + row) * fb->Stride) + r.X; + uint32_t* dst = outPixels + ((size_t)(dstOffsetY + row) * outStride) + dstOffsetX; + memcpy(dst, src, (size_t)r.W * sizeof(uint32_t)); + } + return STATUS_SUCCESS; +} + +static KSTATUS BootFbPresent(DISPLAY_DEVICE* device, const DISPLAY_RECT* rects, size_t rectCount) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !fb->Framebuffer) + return STATUS_INVALID_PARAMETER; + if (!fb->Backbuffer || !rects || rectCount == 0) + return STATUS_SUCCESS; + + DISPLAY_MODE mode; + KSTATUS s = BootFbGetMode(device, &mode); + if (!KSUCCESS(s)) + return s; + + fb->Stats.PresentCount++; + fb->Stats.PresentRectCount += rectCount; + + for (size_t i = 0; i < rectCount; i++) { + DISPLAY_RECT r = rects[i]; + if (!KiDisplayClipRect(&mode, &r)) + continue; + fb->Stats.BytesCopied += (uint64_t)(uint32_t)r.W * (uint64_t)(uint32_t)r.H * sizeof(uint32_t); + size_t bytesPerRow = (size_t)r.W * sizeof(uint32_t); + for (int row = 0; row < r.H; row++) { + uint32_t* dst = fb->Framebuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; + uint32_t* src = fb->Backbuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; + memcpy(dst, src, bytesPerRow); + } + } + return STATUS_SUCCESS; +} + +static KSTATUS BootFbSync(DISPLAY_DEVICE* device) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (fb) fb->Stats.SyncCount++; + return STATUS_SUCCESS; +} + +static KSTATUS BootFbSetCursorImage(DISPLAY_DEVICE* device, int width, int height, int hotX, int hotY, const uint32_t* argbPixels) { + (void)device; + (void)width; + (void)height; + (void)hotX; + (void)hotY; + (void)argbPixels; + return STATUS_NOT_SUPPORTED; +} + +static KSTATUS BootFbSetCursorPosition(DISPLAY_DEVICE* device, int x, int y) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (fb) fb->Stats.CursorMoveCount++; + (void)x; + (void)y; + return STATUS_SUCCESS; +} + +static inline uint32_t KiDisplayBlendPixel(uint32_t dst, uint32_t src) { + uint8_t a = (uint8_t)(src >> 24); + if (a == 0) return dst; + if (a == 255) return src & 0x00FFFFFFu; + + uint8_t sr = (uint8_t)(src >> 16); + uint8_t sg = (uint8_t)(src >> 8); + uint8_t sb = (uint8_t)src; + uint8_t dr = (uint8_t)(dst >> 16); + uint8_t dg = (uint8_t)(dst >> 8); + uint8_t db = (uint8_t)dst; + uint8_t na = (uint8_t)(255 - a); + uint8_t rr = (uint8_t)((a * sr + na * dr) >> 8); + uint8_t gg = (uint8_t)((a * sg + na * dg) >> 8); + uint8_t bb = (uint8_t)((a * sb + na * db) >> 8); + return ((uint32_t)rr << 16) | ((uint32_t)gg << 8) | bb; +} + +static void BootFbRestoreCursor(BOOTFB_CONTEXT* fb) { + if (!fb || !fb->Framebuffer || !fb->Backbuffer || gCursorDrawX < 0) + return; + + DISPLAY_RECT r = { gCursorDrawX, gCursorDrawY, DISPLAY_CURSOR_W, DISPLAY_CURSOR_H }; + DISPLAY_MODE mode = { fb->Width, fb->Height, fb->Stride, DisplayPixelFormatX8R8G8B8 }; + if (KiDisplayClipRect(&mode, &r)) { + for (int row = 0; row < r.H; row++) { + uint32_t* dst = fb->Framebuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; + uint32_t* src = fb->Backbuffer + ((size_t)(r.Y + row) * fb->Stride) + r.X; + memcpy(dst, src, (size_t)r.W * sizeof(uint32_t)); + } + } + + gCursorDrawX = -1; + gCursorDrawY = -1; +} + +static void BootFbDrawCursor(BOOTFB_CONTEXT* fb, int x, int y) { + if (!fb || !fb->Framebuffer || !fb->Backbuffer) + return; + + gCursorDrawX = x; + gCursorDrawY = y; + + for (int row = 0; row < DISPLAY_CURSOR_H; row++) { + int fy = y + row; + if ((unsigned)fy >= fb->Height) + continue; + for (int col = 0; col < DISPLAY_CURSOR_W; col++) { + int fx = x + col; + if ((unsigned)fx >= fb->Width) + continue; + uint32_t src = gDisplayDefaultCursorArgb[row][col]; + if ((src >> 24) == 0) + continue; + uint32_t dst = fb->Backbuffer[(size_t)fy * fb->Stride + fx]; + fb->Framebuffer[(size_t)fy * fb->Stride + fx] = KiDisplayBlendPixel(dst, src); + } + } +} + +static KSTATUS BootFbShowCursor(DISPLAY_DEVICE* device, bool show) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !fb->Framebuffer || !fb->Backbuffer) + return STATUS_NOT_SUPPORTED; + + if (show) { + fb->Stats.CursorShowCount++; + BootFbDrawCursor(fb, gCursorX, gCursorY); + } else { + fb->Stats.CursorHideCount++; + BootFbRestoreCursor(fb); + } + return STATUS_SUCCESS; +} + +static KSTATUS BootFbQueryStats(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats) { + BOOTFB_CONTEXT* fb = BootFbContext(device); + if (!fb || !outStats) + return STATUS_INVALID_PARAMETER; + *outStats = fb->Stats; + return STATUS_SUCCESS; +} diff --git a/code/kernel/ke_input.c b/code/kernel/ke_input.c index 6c50e38..2a0a29d 100644 --- a/code/kernel/ke_input.c +++ b/code/kernel/ke_input.c @@ -6,6 +6,7 @@ #include #include +#include #define KEY_QUEUE_SIZE 256u #define KEY_QUEUE_MASK (KEY_QUEUE_SIZE - 1u) @@ -200,12 +201,15 @@ void KeInputInitialize(const HAL_API* hal) { gMouseButtons = 0; int w = 1920, h = 1080; - if (hal && hal->Screen.GetSize) hal->Screen.GetSize(&w, &h); + DISPLAY_MODE mode; + if (KSUCCESS(KeDisplayGetMode(&mode))) { + w = (int)mode.Width; + h = (int)mode.Height; + } + KeInputMouseSetBounds(w, h); KeInputMouseSetPosition(w / 2, h / 2); - - if (hal && hal->Screen.DrawCursor) - hal->Screen.DrawCursor(gMouseX, gMouseY); + KeDisplayShowCursor(true); DbgPrint("Input: kernel input core initialized (%dx%d)\n", w, h); } @@ -247,11 +251,7 @@ __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event) { } } -__declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event) { - if (!event) return; - - int nx = gMouseX + event->dx; - int ny = gMouseY + event->dy; +static void KiInputCommitMousePosition(int nx, int ny, uint8_t buttons) { int bw = gMouseBoundsW; int bh = gMouseBoundsH; @@ -262,15 +262,21 @@ __declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event) { if (nx >= bw) nx = bw - 1; if (ny >= bh) ny = bh - 1; - if (gInputHal && gInputHal->Screen.EraseCursor) - gInputHal->Screen.EraseCursor(); - gMouseX = nx; gMouseY = ny; - gMouseButtons = event->buttons; + gMouseButtons = buttons; + KeDisplaySetCursorPosition(nx, ny); +} - if (gInputHal && gInputHal->Screen.DrawCursor) - gInputHal->Screen.DrawCursor(nx, ny); +__declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event) { + if (!event) return; + + int oldX = gMouseX; + int oldY = gMouseY; + int nx = oldX + event->dx; + int ny = oldY + event->dy; + + KiInputCommitMousePosition(nx, ny, event->buttons); int next = (gMouseTail + 1) & (int)MOUSE_QUEUE_MASK; if (next != gMouseHead) { @@ -279,6 +285,40 @@ __declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event) { } } +__declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE_EVENT* event) { + if (!event) return; + + int oldX = gMouseX; + int oldY = gMouseY; + KiInputCommitMousePosition(event->x, event->y, event->buttons); + + INPUT_MOUSE_EVENT rel; + rel.dx = gMouseX - oldX; + rel.dy = gMouseY - oldY; + rel.wheel = event->wheel; + rel.buttons = event->buttons; + + int next = (gMouseTail + 1) & (int)MOUSE_QUEUE_MASK; + if (next != gMouseHead) { + gMouseQueue[gMouseTail] = rel; + gMouseTail = next; + } +} + +__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons) { + int bw = gMouseBoundsW; + int bh = gMouseBoundsH; + if (bw < 1) bw = 1; + if (bh < 1) bh = 1; + + INPUT_MOUSE_ABSOLUTE_EVENT ev; + ev.x = maxX ? (int)(((uint64_t)rawX * (uint64_t)(bw - 1)) / (uint64_t)maxX) : 0; + ev.y = maxY ? (int)(((uint64_t)rawY * (uint64_t)(bh - 1)) / (uint64_t)maxY) : 0; + ev.wheel = wheel; + ev.buttons = buttons; + KeInputSubmitMouseAbsolute(&ev); +} + INPUT_KEY KeInputKbPoll(void) { if (gKeyHead == gKeyTail) KiInputServiceRepeat(); return (gKeyHead == gKeyTail) ? INPUT_KEY_NONE : gKeyQueue[gKeyHead].key; @@ -341,6 +381,7 @@ void KeInputMouseSetPosition(int x, int y) { if (y >= bh) y = bh - 1; gMouseX = x; gMouseY = y; + KeDisplaySetCursorPosition(x, y); } void KeInputMouseSetBounds(int w, int h) { diff --git a/code/kernel/ke_syscall.c b/code/kernel/ke_syscall.c index 6e9ba4a..3fd845f 100644 --- a/code/kernel/ke_syscall.c +++ b/code/kernel/ke_syscall.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include @@ -1204,12 +1205,12 @@ static uint64_t SyscallGetFileSize(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_BLIT_SCREEN (36) a0=x a1=y a2=w a3=h a4=*pixels +// SYS_BLIT_SCREEN (66) a0=x a1=y a2=w a3=h a4=*pixels // --------------------------------------------------------------------------- -static uint64_t SyscallScreenBlit(uint64_t a0, uint64_t a1, uint64_t a2, +static uint64_t SyscallBlitScreen(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; - if (!gHal || !a4) return STATUS_FAILURE; + if (!a4) return STATUS_FAILURE; int w = (int)a2; int h = (int)a3; @@ -1227,32 +1228,30 @@ static uint64_t SyscallScreenBlit(uint64_t a0, uint64_t a1, uint64_t a2, KSTATUS s = CopyFromUser(kpixels, (const void*)(uintptr_t)a4, bytes); if (KSUCCESS(s)) - gHal->Screen.Blit((int)a0, (int)a1, w, h, kpixels); + s = KeDisplayBlit((int)a0, (int)a1, w, h, kpixels); MmFreePool(kpixels); return s; } // --------------------------------------------------------------------------- -// SYS_CLEAR_SCREEN (37) a0=color +// SYS_CLEAR_SCREEN (67) a0=color // --------------------------------------------------------------------------- -static uint64_t SyscallScreenClear(uint64_t a0, uint64_t a1, uint64_t a2, +static uint64_t SyscallClearScreen(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; - if (!gHal) return STATUS_FAILURE; - gHal->Screen.Clear((uint32_t)a0); - return STATUS_SUCCESS; + return KeDisplayClear((uint32_t)a0); } // --------------------------------------------------------------------------- -// SYS_GET_SCREEN_SIZE (38) a0=*widthOut a1=*heightOut +// SYS_GET_SCREEN_SIZE (68) a0=*widthOut a1=*heightOut // --------------------------------------------------------------------------- -static uint64_t SyscallScreenGetSize(uint64_t a0, uint64_t a1, uint64_t a2, +static uint64_t SyscallGetScreenSize(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a2; (void)a3; (void)a4; (void)a5; - if (!gHal) return STATUS_FAILURE; int w = 0, h = 0; - gHal->Screen.GetSize(&w, &h); + KSTATUS gs = KeDisplayGetSize(&w, &h); + if (!KSUCCESS(gs)) return gs; if (a0) { KSTATUS s = KiCopyIntToUser(a0, w); if (!KSUCCESS(s)) return s; @@ -1265,19 +1264,89 @@ static uint64_t SyscallScreenGetSize(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_PUT_SCREEN_CHAR (39) a0=c a1=x a2=y a3=fg a4=bg +// SYS_PUT_SCREEN_CHAR (65) a0=c a1=x a2=y a3=fg a4=bg // --------------------------------------------------------------------------- -static uint64_t SyscallScreenPutChar(uint64_t a0, uint64_t a1, uint64_t a2, +static uint64_t SyscallPutScreenChar(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a5; - if (!gHal) return STATUS_FAILURE; - gHal->Screen.PutChar((char)a0, (int)a1, (int)a2, - (uint32_t)a3, (uint32_t)a4); - return STATUS_SUCCESS; + return KeDisplayPutChar((char)a0, (int)a1, (int)a2, + (uint32_t)a3, (uint32_t)a4); } // --------------------------------------------------------------------------- -// SYS_POLL_KEYBOARD (64) -> next INPUT_KEY or INPUT_KEY_NONE +// SYS_GET_CONSOLE_INFO (61) a0=*SYS_CONSOLE_INFO +// --------------------------------------------------------------------------- +static uint64_t SyscallGetConsoleInfo(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; + if (!a0) return STATUS_INVALID_PARAMETER; + + KE_CONSOLE_INFO ki; + KSTATUS s = KeDisplayGetConsoleInfo(&ki); + if (!KSUCCESS(s)) return s; + + SYS_CONSOLE_INFO out; + memset(&out, 0, sizeof(out)); + out.Columns = ki.Columns; + out.Rows = ki.Rows; + out.CursorX = ki.CursorX; + out.CursorY = ki.CursorY; + out.Attribute = ki.Attribute; + return CopyToUser((void*)(uintptr_t)a0, &out, sizeof(out)); +} + +// --------------------------------------------------------------------------- +// SYS_SET_CONSOLE_CURSOR (62) a0=column a1=row +// --------------------------------------------------------------------------- +static uint64_t SyscallSetConsoleCursor(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a2; (void)a3; (void)a4; (void)a5; + int col = (int)(int32_t)a0; + int row = (int)(int32_t)a1; + if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER; + return KeDisplaySetConsoleCursor(col, row); +} + +// --------------------------------------------------------------------------- +// SYS_FILL_CONSOLE_CHARACTER (63) a0=char a1=column a2=row a3=count a4=*writtenOut +// --------------------------------------------------------------------------- +static uint64_t SyscallFillConsoleCharacter(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a5; + int col = (int)(int32_t)a1; + int row = (int)(int32_t)a2; + if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER; + + size_t written = 0; + KSTATUS s = KeDisplayFillConsoleChars((char)a0, col, row, (size_t)a3, &written); + if (a4) { + KSTATUS cs = KiCopySizeToUser(a4, written); + if (KSUCCESS(s) && !KSUCCESS(cs)) s = cs; + } + return s; +} + +// --------------------------------------------------------------------------- +// SYS_FILL_CONSOLE_ATTRIBUTE (64) a0=attribute a1=column a2=row a3=count a4=*writtenOut +// --------------------------------------------------------------------------- +static uint64_t SyscallFillConsoleAttribute(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a5; + int col = (int)(int32_t)a1; + int row = (int)(int32_t)a2; + if (col < 0 || row < 0) return STATUS_INVALID_PARAMETER; + + size_t written = 0; + KSTATUS s = KeDisplayFillConsoleAttrs((uint16_t)a0, col, row, (size_t)a3, &written); + if (a4) { + KSTATUS cs = KiCopySizeToUser(a4, written); + if (KSUCCESS(s) && !KSUCCESS(cs)) s = cs; + } + return s; +} + +// --------------------------------------------------------------------------- +// SYS_POLL_KEYBOARD (70) -> next INPUT_KEY or INPUT_KEY_NONE // --------------------------------------------------------------------------- static uint64_t SyscallKbPoll(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1286,7 +1355,7 @@ static uint64_t SyscallKbPoll(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_IS_KEY_DOWN (65) a0=INPUT_KEY -> 1 or 0 +// SYS_IS_KEY_DOWN (71) a0=INPUT_KEY -> 1 or 0 // --------------------------------------------------------------------------- static uint64_t SyscallKbIsKeyDown(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1295,7 +1364,7 @@ static uint64_t SyscallKbIsKeyDown(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_READ_KEYBOARD_CHAR (66) -> char (blocks, yields while waiting) +// SYS_READ_KEYBOARD_CHAR (72) -> char (blocks, yields while waiting) // --------------------------------------------------------------------------- static uint64_t SyscallKbReadChar(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1305,7 +1374,7 @@ static uint64_t SyscallKbReadChar(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_READ_KEY (67) -> INPUT_KEY (blocks, yields while waiting) +// SYS_READ_KEY (73) -> INPUT_KEY (blocks, yields while waiting) // --------------------------------------------------------------------------- static uint64_t SyscallKbReadKey(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1315,7 +1384,7 @@ static uint64_t SyscallKbReadKey(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_GET_KEY_MODIFIERS (68) -> uint32_t INPUT_MOD_* flags +// SYS_GET_KEY_MODIFIERS (74) -> uint32_t INPUT_MOD_* flags // --------------------------------------------------------------------------- static uint64_t SyscallKbGetModifiers(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1324,7 +1393,7 @@ static uint64_t SyscallKbGetModifiers(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_POLL_MOUSE (69) -> 1 or 0 +// SYS_POLL_MOUSE (75) -> 1 or 0 // --------------------------------------------------------------------------- static uint64_t SyscallMousePoll(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1333,7 +1402,7 @@ static uint64_t SyscallMousePoll(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_READ_MOUSE (70) a0=*dx a1=*dy a2=*buttons -> 1 or 0 +// SYS_READ_MOUSE (76) a0=*dx a1=*dy a2=*buttons -> 1 or 0 // --------------------------------------------------------------------------- static uint64_t SyscallMouseRead(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1351,7 +1420,7 @@ static uint64_t SyscallMouseRead(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_GET_MOUSE_POS (71) a0=*x a1=*y +// SYS_GET_MOUSE_POS (77) a0=*x a1=*y // --------------------------------------------------------------------------- static uint64_t SyscallMouseGetPos(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1366,7 +1435,7 @@ static uint64_t SyscallMouseGetPos(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_GET_TICK (48) -> uint64_t microseconds +// SYS_GET_TICK (78) -> uint64_t microseconds // --------------------------------------------------------------------------- static uint64_t SyscallGetTick(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1389,7 +1458,7 @@ static uint64_t SyscallGetSystemTime(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_PRINT_DEBUG (49) a0=*str +// SYS_PRINT_DEBUG (79) a0=*str // --------------------------------------------------------------------------- static uint64_t SyscallDebugPrint(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { @@ -1607,7 +1676,7 @@ static uint64_t SyscallGetProcessExitCode(uint64_t a0, uint64_t a1, uint64_t a2, } // --------------------------------------------------------------------------- -// SYS_WRITE_CONSOLE (53) a0=*buf a1=len +// SYS_WRITE_CONSOLE (60) a0=*buf a1=len // Writes user console text in white; distinct from green SYS_PRINT_DEBUG. // --------------------------------------------------------------------------- static uint64_t SyscallWriteConsole(uint64_t a0, uint64_t a1, uint64_t a2, @@ -2716,6 +2785,27 @@ static KSTATUS KiReturnKernelStats(uint64_t bufferArg, size_t bufferSize, return CopyToUser((void*)(uintptr_t)bufferArg, src, size); } +static uint64_t SyscallQueryDisplayInfo(uint64_t a0, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5) { + (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; + if (!a0) return STATUS_INVALID_PARAMETER; + + KE_DISPLAY_INFO ki; + KSTATUS s = KeDisplayQueryInfo(&ki); + if (!KSUCCESS(s)) return s; + + SYS_DISPLAY_INFO out; + memset(&out, 0, sizeof(out)); + memcpy(out.PrimaryName, ki.PrimaryName, sizeof(out.PrimaryName)); + out.Mode = ki.Mode; + out.Caps = ki.Caps; + out.DeviceCount = ki.DeviceCount; + out.ManagerStats = ki.ManagerStats; + out.DeviceStats = ki.DeviceStats; + + return CopyToUser((void*)(uintptr_t)a0, &out, sizeof(out)); +} + static uint64_t SyscallQueryKernelStats(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { (void)a4; (void)a5; @@ -2856,38 +2946,43 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = { /* 58 SYS_SET_CWD */ SyscallSetCwd, /* 59 SYS_GET_CWD */ SyscallGetCwd, /* 60 SYS_WRITE_CONSOLE */ SyscallWriteConsole, - /* 61 SYS_BLIT_SCREEN */ SyscallScreenBlit, - /* 62 SYS_CLEAR_SCREEN */ SyscallScreenClear, - /* 63 SYS_GET_SCREEN_SIZE */ SyscallScreenGetSize, - /* 64 SYS_PUT_SCREEN_CHAR */ SyscallScreenPutChar, - /* 65 SYS_POLL_KEYBOARD */ SyscallKbPoll, - /* 66 SYS_IS_KEY_DOWN */ SyscallKbIsKeyDown, - /* 67 SYS_READ_KEYBOARD_CHAR */ SyscallKbReadChar, - /* 68 SYS_READ_KEY */ SyscallKbReadKey, - /* 69 SYS_GET_KEY_MODIFIERS */ SyscallKbGetModifiers, - /* 70 SYS_POLL_MOUSE */ SyscallMousePoll, - /* 71 SYS_READ_MOUSE */ SyscallMouseRead, - /* 72 SYS_GET_MOUSE_POS */ SyscallMouseGetPos, - /* 73 SYS_GET_TICK */ SyscallGetTick, - /* 74 SYS_PRINT_DEBUG */ SyscallDebugPrint, - /* 75 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame, - /* 76 SYS_CREATE_SOCKET */ SyscallSocket, - /* 77 SYS_BIND_SOCKET */ SyscallBind, - /* 78 SYS_LISTEN_SOCKET */ SyscallListen, - /* 79 SYS_ACCEPT_SOCKET */ SyscallAccept, - /* 80 SYS_CONNECT_SOCKET */ SyscallConnect, - /* 81 SYS_SEND_SOCKET */ SyscallSend, - /* 82 SYS_RECV_SOCKET */ SyscallRecv, - /* 83 SYS_SHUTDOWN_SOCKET */ SyscallShutdown, - /* 84 SYS_SEND_TO_SOCKET */ SyscallSendTo, - /* 85 SYS_RECV_FROM_SOCKET */ SyscallRecvFrom, - /* 86 SYS_RESOLVE_HOST */ SyscallResolveHost, - /* 87 SYS_SET_SOCKET_OPTION */ SyscallSetSocketOption, - /* 88 SYS_GET_SOCKET_OPTION */ SyscallGetSocketOption, - /* 89 SYS_POLL */ SyscallPoll, - /* 90 SYS_GET_MEMORY_STATUS */ SyscallGetMemoryStatus, - /* 91 SYS_QUERY_KERNEL_STATS */ SyscallQueryKernelStats, - /* 92 SYS_GET_SYSTEM_TIME */ SyscallGetSystemTime + /* 61 SYS_GET_CONSOLE_INFO */ SyscallGetConsoleInfo, + /* 62 SYS_SET_CONSOLE_CURSOR */ SyscallSetConsoleCursor, + /* 63 SYS_FILL_CONSOLE_CHARACTER */ SyscallFillConsoleCharacter, + /* 64 SYS_FILL_CONSOLE_ATTRIBUTE */ SyscallFillConsoleAttribute, + /* 65 SYS_PUT_SCREEN_CHAR */ SyscallPutScreenChar, + /* 66 SYS_BLIT_SCREEN */ SyscallBlitScreen, + /* 67 SYS_CLEAR_SCREEN */ SyscallClearScreen, + /* 68 SYS_GET_SCREEN_SIZE */ SyscallGetScreenSize, + /* 69 SYS_QUERY_DISPLAY_INFO */ SyscallQueryDisplayInfo, + /* 70 SYS_POLL_KEYBOARD */ SyscallKbPoll, + /* 71 SYS_IS_KEY_DOWN */ SyscallKbIsKeyDown, + /* 72 SYS_READ_KEYBOARD_CHAR */ SyscallKbReadChar, + /* 73 SYS_READ_KEY */ SyscallKbReadKey, + /* 74 SYS_GET_KEY_MODIFIERS */ SyscallKbGetModifiers, + /* 75 SYS_POLL_MOUSE */ SyscallMousePoll, + /* 76 SYS_READ_MOUSE */ SyscallMouseRead, + /* 77 SYS_GET_MOUSE_POS */ SyscallMouseGetPos, + /* 78 SYS_GET_TICK */ SyscallGetTick, + /* 79 SYS_PRINT_DEBUG */ SyscallDebugPrint, + /* 80 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame, + /* 81 SYS_CREATE_SOCKET */ SyscallSocket, + /* 82 SYS_BIND_SOCKET */ SyscallBind, + /* 83 SYS_LISTEN_SOCKET */ SyscallListen, + /* 84 SYS_ACCEPT_SOCKET */ SyscallAccept, + /* 85 SYS_CONNECT_SOCKET */ SyscallConnect, + /* 86 SYS_SEND_SOCKET */ SyscallSend, + /* 87 SYS_RECV_SOCKET */ SyscallRecv, + /* 88 SYS_SHUTDOWN_SOCKET */ SyscallShutdown, + /* 89 SYS_SEND_TO_SOCKET */ SyscallSendTo, + /* 90 SYS_RECV_FROM_SOCKET */ SyscallRecvFrom, + /* 91 SYS_RESOLVE_HOST */ SyscallResolveHost, + /* 92 SYS_SET_SOCKET_OPTION */ SyscallSetSocketOption, + /* 93 SYS_GET_SOCKET_OPTION */ SyscallGetSocketOption, + /* 94 SYS_POLL */ SyscallPoll, + /* 95 SYS_GET_MEMORY_STATUS */ SyscallGetMemoryStatus, + /* 96 SYS_QUERY_KERNEL_STATS */ SyscallQueryKernelStats, + /* 97 SYS_GET_SYSTEM_TIME */ SyscallGetSystemTime }; // --------------------------------------------------------------------------- diff --git a/code/kernel/kmain.c b/code/kernel/kmain.c index da9d232..33e9fdd 100644 --- a/code/kernel/kmain.c +++ b/code/kernel/kmain.c @@ -26,6 +26,7 @@ #include #include #include +#include #include typedef struct { @@ -429,6 +430,8 @@ extern void KernelMain(BOOT_INFO* bootInfo) { KeInitDebug(hal->Screen.PrintChar, bootInfo->Debug); KeTimeInitialize(bootInfo, hal); + KeDisplayInitialize(hal); + KeSetDebugConsoleWriter(KeDisplayDebugPrintChar); // CPU startup intentionally lives after HAL initialization but before IRQs // and scheduler startup. HAL owns ACPI/MADT discovery and LAPIC INIT/SIPI; diff --git a/code/kernel/mm_virt.c b/code/kernel/mm_virt.c index 965ab2e..df14b40 100644 --- a/code/kernel/mm_virt.c +++ b/code/kernel/mm_virt.c @@ -248,11 +248,19 @@ __declspec(dllexport) KSTATUS MmSetPageProtect(uintptr_t virt, size_t size, uint status = STATUS_NOT_FOUND; break; } + if (*e3 & PAGE_PS) { + status = STATUS_NOT_SUPPORTED; + break; + } uint64_t* e2 = KiPtePtr(*e3 & PAGE_FRAME_4K_MASK, PDT_IDX(va)); if (!(*e2 & PAGE_PRESENT)) { status = STATUS_NOT_FOUND; break; } + if (*e2 & PAGE_PS) { + status = STATUS_NOT_SUPPORTED; + break; + } uint64_t* pte = KiPtePtr(*e2 & PAGE_FRAME_4K_MASK, PT_IDX(va)); if (!(*pte & PAGE_PRESENT)) { status = STATUS_NOT_FOUND; diff --git a/code/public/ntlite/ddi/display.h b/code/public/ntlite/ddi/display.h new file mode 100644 index 0000000..b9a0936 --- /dev/null +++ b/code/public/ntlite/ddi/display.h @@ -0,0 +1,67 @@ +#pragma once +// display.h - display device DDI +// +// Display providers register DISPLAY_DEVICE objects with the kernel display +// manager. The boot framebuffer backend is registered by the kernel itself; +// PCI/virtual GPU drivers register additional providers later. + +#include +#include +#include +#include + +#ifndef DISPLAY_API +#define DISPLAY_API __declspec(dllimport) +#endif + +#include + +typedef struct DISPLAY_DEVICE DISPLAY_DEVICE; + +typedef struct DISPLAY_DEVICE_OPS { + KSTATUS (*GetMode)(DISPLAY_DEVICE* device, DISPLAY_MODE* outMode); + + KSTATUS (*Blit)(DISPLAY_DEVICE* device, + int x, int y, int w, int h, + const uint32_t* pixels); + + KSTATUS (*FillRect)(DISPLAY_DEVICE* device, + int x, int y, int w, int h, + uint32_t color); + + KSTATUS (*CopyRect)(DISPLAY_DEVICE* device, + int dstX, int dstY, + int srcX, int srcY, + int w, int h); + + KSTATUS (*ReadRect)(DISPLAY_DEVICE* device, + int x, int y, int w, int h, + uint32_t* outPixels, uint32_t outStride); + + KSTATUS (*Present)(DISPLAY_DEVICE* device, + const DISPLAY_RECT* rects, + size_t rectCount); + + KSTATUS (*Sync)(DISPLAY_DEVICE* device); + + KSTATUS (*SetCursorImage)(DISPLAY_DEVICE* device, + int width, int height, + int hotX, int hotY, + const uint32_t* argbPixels); + + KSTATUS (*SetCursorPosition)(DISPLAY_DEVICE* device, int x, int y); + KSTATUS (*ShowCursor)(DISPLAY_DEVICE* device, bool show); + + KSTATUS (*QueryStats)(DISPLAY_DEVICE* device, DISPLAY_STATS* outStats); +} DISPLAY_DEVICE_OPS; + +struct DISPLAY_DEVICE { + const char* Name; + uint32_t Caps; + void* DriverContext; + const DISPLAY_DEVICE_OPS* Ops; + DISPLAY_DEVICE* Next; +}; + +DISPLAY_API KSTATUS KeDisplayRegisterDevice(DISPLAY_DEVICE* device); +DISPLAY_API KSTATUS KeDisplaySetPrimary(DISPLAY_DEVICE* device); diff --git a/code/public/ntlite/ddi/hal_api.h b/code/public/ntlite/ddi/hal_api.h index c9c7ab4..ed8a3a0 100644 --- a/code/public/ntlite/ddi/hal_api.h +++ b/code/public/ntlite/ddi/hal_api.h @@ -7,15 +7,25 @@ typedef struct BOOT_INFO BOOT_INFO; +typedef struct HAL_FRAMEBUFFER_INFO { + uintptr_t Base; // kernel virtual address of the visible framebuffer + uint32_t Width; + uint32_t Height; + uint32_t Stride; // pixels per scanline + uint32_t Format; // currently 0x00RRGGBB / X8R8G8B8 +} HAL_FRAMEBUFFER_INFO; + typedef struct HAL_SCREEN { void (*Clear)(uint32_t color); void (*PutPixel)(int x, int y, uint32_t color); void (*Blit)(int x, int y, int w, int h, const uint32_t* pixels); void (*GetSize)(int* width, int* height); + KSTATUS (*GetFramebuffer)(HAL_FRAMEBUFFER_INFO* outInfo); void (*Scroll)(int lines); void (*PutChar)(char c, int x, int y, uint32_t fg, uint32_t bg); void (*PrintChar)(char c, uint32_t fg, uint32_t bg); - // Hardware mouse cursor: save/restore background and blit sprite + // Legacy software cursor hooks kept for early-boot/panic fallback only. + // Normal input cursor ownership lives in ke_display. void (*DrawCursor)(int x, int y); void (*EraseCursor)(void); } HAL_SCREEN; diff --git a/code/public/ntlite/ddi/input_device.h b/code/public/ntlite/ddi/input_device.h index 9b5e9ee..4070fa3 100644 --- a/code/public/ntlite/ddi/input_device.h +++ b/code/public/ntlite/ddi/input_device.h @@ -7,3 +7,5 @@ __declspec(dllexport) KSTATUS KeInputRegisterDevice(const char* name, uint32_t flags, uint32_t* outDeviceId); __declspec(dllexport) void KeInputSubmitKey(const INPUT_KEY_EVENT* event); __declspec(dllexport) void KeInputSubmitMouse(const INPUT_MOUSE_EVENT* event); +__declspec(dllexport) void KeInputSubmitMouseAbsolute(const INPUT_MOUSE_ABSOLUTE_EVENT* event); +__declspec(dllexport) void KeInputSubmitMouseAbsoluteScaled(uint32_t rawX, uint32_t rawY, uint32_t maxX, uint32_t maxY, int wheel, uint8_t buttons); diff --git a/code/public/ntlite/ddi/io_driver.h b/code/public/ntlite/ddi/io_driver.h index 38bc717..44475c7 100644 --- a/code/public/ntlite/ddi/io_driver.h +++ b/code/public/ntlite/ddi/io_driver.h @@ -165,72 +165,6 @@ __declspec(dllexport) uint8_t IoGetDeviceStackSize(DEVICE_OBJECT* device); __declspec(dllexport) KSTATUS IoSendPnpIrpSync(DEVICE_OBJECT* device, IO_PNP_MINOR_FUNCTION minor); -// ------------------------------------------------------------------------- -// Legacy USB publish/matcher path. This remains only until USB is migrated to -// PDO/interface-PDO enumeration in the later USB stages. -// ------------------------------------------------------------------------- - -#define IO_PUBLISHED_USB_MAX_ENDPOINTS 8u - -typedef struct IO_PUBLISHED_USB_ENDPOINT { - uint8_t Address; - uint8_t Attributes; - uint16_t MaxPacketSize; - uint8_t Interval; -} IO_PUBLISHED_USB_ENDPOINT; - -typedef struct IO_PUBLISHED_USB_DEVICE { - uint16_t VendorId; - uint16_t ProductId; - uint16_t BcdUsb; - uint16_t BcdDevice; - - uint8_t DeviceClass; - uint8_t DeviceSubclass; - uint8_t DeviceProtocol; - - uint8_t SlotId; - uint8_t Port; - uint8_t Speed; - uint8_t ConfigurationValue; - - uint8_t InterfaceNumber; - uint8_t AlternateSetting; - uint8_t InterfaceClass; - uint8_t InterfaceSubclass; - uint8_t InterfaceProtocol; - - uint8_t EndpointCount; - IO_PUBLISHED_USB_ENDPOINT Endpoints[IO_PUBLISHED_USB_MAX_ENDPOINTS]; -} IO_PUBLISHED_USB_DEVICE; - -typedef struct IO_PUBLISHED_DEVICE { - IO_BUS_TYPE BusType; - const char* Name; - void* BusContext; - void* DeviceContext; - uint32_t Flags; - bool Bound; - const char* BoundDriverName; - - union { - IO_PUBLISHED_USB_DEVICE Usb; - } u; -} IO_PUBLISHED_DEVICE; - -typedef bool (*IO_DRIVER_MATCH_CALLBACK)(const IO_PUBLISHED_DEVICE* device, - void* context); -typedef KSTATUS (*IO_DRIVER_BIND_CALLBACK)(IO_PUBLISHED_DEVICE* device, - void* context); - -__declspec(dllexport) KSTATUS IoRegisterDriverMatcher(const char* driverName, - IO_BUS_TYPE busType, - IO_DRIVER_MATCH_CALLBACK match, - IO_DRIVER_BIND_CALLBACK bind, - void* context); -__declspec(dllexport) KSTATUS IoPublishDevice(const IO_PUBLISHED_DEVICE* device); -__declspec(dllexport) uint32_t IoPublishedDeviceCount(void); - // ------------------------------------------------------------------------- // Disk namespace above storage DEVICE_OBJECTs // ------------------------------------------------------------------------- diff --git a/code/public/ntlite/display.h b/code/public/ntlite/display.h new file mode 100644 index 0000000..d0cf30c --- /dev/null +++ b/code/public/ntlite/display.h @@ -0,0 +1,71 @@ +#pragma once +// display.h - user/kernel shared display ABI types +// +// This header is safe for user-mode code. It contains only plain syscall ABI +// data types and capability bits. Kernel/driver provider interfaces live in +// . + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum DISPLAY_PIXEL_FORMAT { + DisplayPixelFormatUnknown = 0, + DisplayPixelFormatX8R8G8B8 = 1, +} DISPLAY_PIXEL_FORMAT; + +typedef struct DISPLAY_MODE { + uint32_t Width; + uint32_t Height; + uint32_t Stride; // pixels per scanline + DISPLAY_PIXEL_FORMAT Format; +} DISPLAY_MODE; + +typedef struct DISPLAY_RECT { + int32_t X; + int32_t Y; + int32_t W; + int32_t H; +} DISPLAY_RECT; + +typedef struct DISPLAY_STATS { + uint64_t BlitCount; + uint64_t FillRectCount; + uint64_t CopyRectCount; + uint64_t ReadRectCount; + uint64_t PresentCount; + uint64_t PresentRectCount; + uint64_t DirtyRectCount; + uint64_t DirtyAllCount; + uint64_t BytesCopied; + uint64_t SyncCount; + uint64_t CursorMoveCount; + uint64_t CursorShowCount; + uint64_t CursorHideCount; +} DISPLAY_STATS; + +#define DISPLAY_CAP_FRAMEBUFFER 0x00000001u +#define DISPLAY_CAP_SOFTWARE_CURSOR 0x00000002u +#define DISPLAY_CAP_HARDWARE_CURSOR 0x00000004u +#define DISPLAY_CAP_ACCEL_FILL 0x00000008u +#define DISPLAY_CAP_ACCEL_COPY 0x00000010u +#define DISPLAY_CAP_PRESENT_SYNC 0x00000020u +#define DISPLAY_CAP_VBLANK 0x00000040u +#define DISPLAY_CAP_3D 0x00000080u + +#define SYS_DISPLAY_NAME_MAX 32u + +typedef struct SYS_DISPLAY_INFO { + char PrimaryName[SYS_DISPLAY_NAME_MAX]; + DISPLAY_MODE Mode; + uint32_t Caps; + uint32_t DeviceCount; + DISPLAY_STATS ManagerStats; + DISPLAY_STATS DeviceStats; +} SYS_DISPLAY_INFO; + +#ifdef __cplusplus +} +#endif diff --git a/code/public/ntlite/input.h b/code/public/ntlite/input.h index 9989322..bd31755 100644 --- a/code/public/ntlite/input.h +++ b/code/public/ntlite/input.h @@ -140,3 +140,10 @@ typedef struct INPUT_MOUSE_EVENT { int wheel; uint8_t buttons; // bit0=left, bit1=right, bit2=middle } INPUT_MOUSE_EVENT; + +typedef struct INPUT_MOUSE_ABSOLUTE_EVENT { + int x; + int y; + int wheel; + uint8_t buttons; // bit0=left, bit1=right, bit2=middle +} INPUT_MOUSE_ABSOLUTE_EVENT; diff --git a/code/public/ntlite/syscall.h b/code/public/ntlite/syscall.h index 08cae45..4bc50d7 100644 --- a/code/public/ntlite/syscall.h +++ b/code/public/ntlite/syscall.h @@ -9,6 +9,7 @@ #include #include #include +#include #define SYS_STATUS_SUCCESS 0u #define SYS_STATUS_TIMEOUT 3u @@ -329,16 +330,29 @@ uint32_t SysSetCwd(const SYS_PATH_REQUEST* request); uint32_t SysGetCwd(const SYS_GET_CWD_REQUEST* request); // --------------------------------------------------------------------------- -// Console and graphics (57-61) +// Console and graphics. // --------------------------------------------------------------------------- +typedef struct SYS_CONSOLE_INFO { + uint32_t Columns; + uint32_t Rows; + uint32_t CursorX; + uint32_t CursorY; + uint16_t Attribute; +} SYS_CONSOLE_INFO; + uint32_t SysWriteConsole(const SYS_WRITE_CONSOLE_REQUEST* request); +uint32_t SysGetConsoleInfo(SYS_CONSOLE_INFO* infoOut); +uint32_t SysSetConsoleCursor(int x, int y); +uint32_t SysFillConsoleCharacter(char c, int x, int y, size_t length, size_t* writtenOut); +uint32_t SysFillConsoleAttribute(uint16_t attr, int x, int y, size_t length, size_t* writtenOut); +uint32_t SysPutScreenChar(char c, int x, int y, uint32_t fg, uint32_t bg); uint32_t SysBlitScreen(int x, int y, int w, int h, const uint32_t* pixels); uint32_t SysClearScreen(uint32_t color); uint32_t SysGetScreenSize(int* widthOut, int* heightOut); -uint32_t SysPutScreenChar(char c, int x, int y, uint32_t fg, uint32_t bg); +uint32_t SysQueryDisplayInfo(SYS_DISPLAY_INFO* infoOut); // --------------------------------------------------------------------------- -// Input (62-69) +// Input. // --------------------------------------------------------------------------- uint64_t SysPollKeyboard(void); uint64_t SysIsKeyDown(INPUT_KEY key); @@ -501,9 +515,9 @@ typedef struct SYS_CPU_STATS { // Time and diagnostics // --------------------------------------------------------------------------- uint64_t SysGetTick(void); -uint32_t SysGetSystemTime(SYS_SYSTEM_TIME* timeOut); uint32_t SysPrintDebug(const SYS_PRINT_DEBUG_REQUEST* request); uint32_t SysQueryTrapFrame(SYS_TRAP_FRAME_INFO* info); uint32_t SysGetMemoryStatus(SYS_MEMORY_STATUS* statusOut); uint32_t SysQueryKernelStats(uint32_t classId, void* buffer, size_t bufferSize, size_t* requiredSizeOut); +uint32_t SysGetSystemTime(SYS_SYSTEM_TIME* timeOut); diff --git a/code/user/apps/shell_native/shell_native.c b/code/user/apps/shell_native/shell_native.c index 812d1ce..0d2324b 100644 --- a/code/user/apps/shell_native/shell_native.c +++ b/code/user/apps/shell_native/shell_native.c @@ -89,6 +89,17 @@ static void printUInt64Width(uint64_t n, size_t width) { printUInt64(n); } +static void printHex32(uint32_t v) { + static const char hex[] = "0123456789abcdef"; + char buf[9]; + for (int i = 7; i >= 0; i--) { + buf[i] = hex[v & 0xFu]; + v >>= 4; + } + buf[8] = '\0'; + print(buf); +} + static void printDirTime(const SYS_SYSTEM_TIME* t) { if (!t || !(t->Flags & SYS_TIME_FLAG_VALID)) { print("---- -- -- --:-- "); @@ -144,6 +155,7 @@ static void cmd_help() { " help - Show this list\n" " mem - Show memory usage\n" " time - Show system wall-clock time\n" + " display - Show display provider and counters\n" " stats - Show scheduler/CPU/PMM telemetry\n" " ps - List processes\n" " dir [path] - List directory contents\n" @@ -278,6 +290,54 @@ static void cmd_time(void) { print("\n"); } +static void printDisplayCaps(uint32_t caps) { + bool any = false; + if (caps & DISPLAY_CAP_FRAMEBUFFER) { print("framebuffer"); any = true; } + if (caps & DISPLAY_CAP_SOFTWARE_CURSOR) { if (any) print(","); print("software-cursor"); any = true; } + if (caps & DISPLAY_CAP_HARDWARE_CURSOR) { if (any) print(","); print("hardware-cursor"); any = true; } + if (caps & DISPLAY_CAP_ACCEL_FILL) { if (any) print(","); print("accel-fill"); any = true; } + if (caps & DISPLAY_CAP_ACCEL_COPY) { if (any) print(","); print("accel-copy"); any = true; } + if (caps & DISPLAY_CAP_PRESENT_SYNC) { if (any) print(","); print("present-sync"); any = true; } + if (caps & DISPLAY_CAP_VBLANK) { if (any) print(","); print("vblank"); any = true; } + if (caps & DISPLAY_CAP_3D) { if (any) print(","); print("3d"); any = true; } + if (!any) print("none"); +} + +static void printDisplayStats(const char* title, const DISPLAY_STATS* st) { + print(title); print(":\n"); + print(" blit: "); printUInt64(st->BlitCount); print("\n"); + print(" fill: "); printUInt64(st->FillRectCount); print("\n"); + print(" copy: "); printUInt64(st->CopyRectCount); print("\n"); + print(" read: "); printUInt64(st->ReadRectCount); print("\n"); + print(" present: "); printUInt64(st->PresentCount); print(" rects="); printUInt64(st->PresentRectCount); print("\n"); + print(" dirty: "); printUInt64(st->DirtyRectCount); print(" all="); printUInt64(st->DirtyAllCount); print("\n"); + print(" bytes: "); printUInt64(st->BytesCopied); print("\n"); + print(" sync: "); printUInt64(st->SyncCount); print("\n"); + print(" cursor: move="); printUInt64(st->CursorMoveCount); + print(" show="); printUInt64(st->CursorShowCount); + print(" hide="); printUInt64(st->CursorHideCount); print("\n"); +} + +static void cmd_display(void) { + SYS_DISPLAY_INFO info; + uint32_t status = SysQueryDisplayInfo(&info); + if (status != 0) { + print("display: query failed: "); + printUInt(status); + print("\n"); + return; + } + + print("Display:\n"); + print(" primary: "); print(info.PrimaryName[0] ? info.PrimaryName : "?"); print("\n"); + print(" devices: "); printUInt(info.DeviceCount); print("\n"); + print(" mode: "); printUInt(info.Mode.Width); print("x"); printUInt(info.Mode.Height); + print(" stride="); printUInt(info.Mode.Stride); print(" format="); printUInt((uint32_t)info.Mode.Format); print("\n"); + print(" caps: 0x"); printHex32(info.Caps); print(" ["); printDisplayCaps(info.Caps); print("]\n"); + printDisplayStats("Manager counters", &info.ManagerStats); + printDisplayStats("Provider counters", &info.DeviceStats); +} + static void cmd_mem(void) { SYS_MEMORY_STATUS st; if (SysGetMemoryStatus(&st) != 0) { @@ -492,6 +552,7 @@ static int runCommand(const char* cmd) { } if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; } if (strcmp(cmd, "time") == 0 || strcmp(cmd, "date") == 0) { cmd_time(); return 0; } + if (strcmp(cmd, "display") == 0 || strcmp(cmd, "gpu") == 0) { cmd_display(); return 0; } if (strcmp(cmd, "stats") == 0 || strcmp(cmd, "telemetry") == 0) { cmd_stats(); return 0; } if (strcmp(cmd, "ps") == 0) { cmd_ps(); return 0; } if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } diff --git a/code/user/personalities/win32/kernel32.c b/code/user/personalities/win32/kernel32.c index 7df8d74..1c8f4a6 100644 --- a/code/user/personalities/win32/kernel32.c +++ b/code/user/personalities/win32/kernel32.c @@ -32,11 +32,22 @@ // --------------------------------------------------------------------------- typedef void* HANDLE; typedef unsigned long DWORD; +typedef unsigned short WORD; typedef int BOOL; typedef unsigned char BYTE; typedef short SHORT; typedef long LONG; typedef struct { LONG x; LONG y; } POINT; +typedef struct { SHORT X; SHORT Y; } COORD; +typedef struct { SHORT Left; SHORT Top; SHORT Right; SHORT Bottom; } SMALL_RECT; + +typedef struct _CONSOLE_SCREEN_BUFFER_INFO { + COORD dwSize; + COORD dwCursorPosition; + WORD wAttributes; + SMALL_RECT srWindow; + COORD dwMaximumWindowSize; +} CONSOLE_SCREEN_BUFFER_INFO; #define MAX_PATH 260 #define TRUE 1 @@ -959,6 +970,20 @@ __declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) { // in this win32.dll image. Full dynamic DLL loading remains a kernel-loader job. __declspec(dllexport) BOOL FreeLibrary(HANDLE hLibModule); __declspec(dllexport) void Sleep(DWORD dwMilliseconds); +__declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, + CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo); +__declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput, + char cCharacter, + DWORD nLength, + COORD dwWriteCoord, + DWORD* lpNumberOfCharsWritten); +__declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput, + WORD wAttribute, + DWORD nLength, + COORD dwWriteCoord, + DWORD* lpNumberOfAttrsWritten); +__declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput, + COORD dwCursorPosition); __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) { if (hModule != WIN32_HMODULE_WIN32 || !lpProcName) { gLastError = 127; return NULL; } @@ -975,6 +1000,8 @@ __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcNam GP(FreeLibrary); GP(VirtualAlloc); GP(VirtualFree); GP(VirtualProtect); GP(VirtualQuery); GP(GetTickCount); GP(GetTickCount64); GP(Sleep); GP(TlsAlloc); GP(TlsFree); GP(TlsGetValue); GP(TlsSetValue); + GP(GetConsoleScreenBufferInfo); GP(FillConsoleOutputCharacterA); + GP(FillConsoleOutputAttribute); GP(SetConsoleCursorPosition); #undef GP KiDbgWriteA("GetProcAddress miss: "); KiDbgWriteA(lpProcName); @@ -1250,6 +1277,34 @@ __declspec(dllexport) void Sleep(DWORD dwMilliseconds) { #define STD_OUTPUT_HANDLE ((DWORD)-11) #define STD_ERROR_HANDLE ((DWORD)-12) +static BOOL K32IsStdHandleValue(HANDLE h, DWORD stdHandle) { + uintptr_t value = (uintptr_t)h; + return value == (uintptr_t)stdHandle || value == (uintptr_t)(intptr_t)(int32_t)stdHandle; +} + +static BOOL K32IsConsoleOutputHandle(HANDLE h) { + return h && h != INVALID_HANDLE_VALUE && + (K32IsStdHandleValue(h, STD_OUTPUT_HANDLE) || + K32IsStdHandleValue(h, STD_ERROR_HANDLE)); +} + +static void K32SetConsoleLastError(uint32_t status) { + switch (status) { + case SYS_STATUS_SUCCESS: + gLastError = 0; + break; + case SYS_STATUS_INVALID_PARAMETER: + gLastError = 87; + break; + case SYS_STATUS_NO_MEMORY: + gLastError = 8; + break; + default: + gLastError = 1; + break; + } +} + // We have no real console handles; return a sentinel the other APIs recognise. __declspec(dllexport) HANDLE GetStdHandle(DWORD nStdHandle) { return (HANDLE)(uintptr_t)nStdHandle; @@ -1309,6 +1364,115 @@ __declspec(dllexport) BOOL WriteConsoleA(HANDLE hConsoleOutput, return TRUE; } +__declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, + CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo) { + if (!K32IsConsoleOutputHandle(hConsoleOutput)) { + gLastError = 6; + return FALSE; + } + if (!lpConsoleScreenBufferInfo) { + gLastError = 87; + return FALSE; + } + + SYS_CONSOLE_INFO info; + uint32_t s = SysGetConsoleInfo(&info); + if (s != SYS_STATUS_SUCCESS) { + K32SetConsoleLastError(s); + return FALSE; + } + + memset(lpConsoleScreenBufferInfo, 0, sizeof(*lpConsoleScreenBufferInfo)); + lpConsoleScreenBufferInfo->dwSize.X = (SHORT)info.Columns; + lpConsoleScreenBufferInfo->dwSize.Y = (SHORT)info.Rows; + lpConsoleScreenBufferInfo->dwCursorPosition.X = (SHORT)info.CursorX; + lpConsoleScreenBufferInfo->dwCursorPosition.Y = (SHORT)info.CursorY; + lpConsoleScreenBufferInfo->wAttributes = (WORD)info.Attribute; + lpConsoleScreenBufferInfo->srWindow.Left = 0; + lpConsoleScreenBufferInfo->srWindow.Top = 0; + lpConsoleScreenBufferInfo->srWindow.Right = info.Columns ? (SHORT)(info.Columns - 1u) : 0; + lpConsoleScreenBufferInfo->srWindow.Bottom = info.Rows ? (SHORT)(info.Rows - 1u) : 0; + lpConsoleScreenBufferInfo->dwMaximumWindowSize = lpConsoleScreenBufferInfo->dwSize; + return TRUE; +} + +__declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput, + COORD dwCursorPosition) { + if (!K32IsConsoleOutputHandle(hConsoleOutput)) { + gLastError = 6; + return FALSE; + } + if (dwCursorPosition.X < 0 || dwCursorPosition.Y < 0) { + gLastError = 87; + return FALSE; + } + + uint32_t s = SysSetConsoleCursor((int)dwCursorPosition.X, (int)dwCursorPosition.Y); + if (s != SYS_STATUS_SUCCESS) { + K32SetConsoleLastError(s); + return FALSE; + } + return TRUE; +} + +__declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput, + char cCharacter, + DWORD nLength, + COORD dwWriteCoord, + DWORD* lpNumberOfCharsWritten) { + if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = 0; + if (!K32IsConsoleOutputHandle(hConsoleOutput)) { + gLastError = 6; + return FALSE; + } + if (dwWriteCoord.X < 0 || dwWriteCoord.Y < 0) { + gLastError = 87; + return FALSE; + } + if (nLength == 0) + return TRUE; + + size_t written = 0; + uint32_t s = SysFillConsoleCharacter(cCharacter, (int)dwWriteCoord.X, + (int)dwWriteCoord.Y, (size_t)nLength, + &written); + if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = (DWORD)written; + if (s != SYS_STATUS_SUCCESS) { + K32SetConsoleLastError(s); + return FALSE; + } + return TRUE; +} + +__declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput, + WORD wAttribute, + DWORD nLength, + COORD dwWriteCoord, + DWORD* lpNumberOfAttrsWritten) { + if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = 0; + if (!K32IsConsoleOutputHandle(hConsoleOutput)) { + gLastError = 6; + return FALSE; + } + if (dwWriteCoord.X < 0 || dwWriteCoord.Y < 0) { + gLastError = 87; + return FALSE; + } + if (nLength == 0) + return TRUE; + + size_t written = 0; + uint32_t s = SysFillConsoleAttribute(wAttribute, (int)dwWriteCoord.X, + (int)dwWriteCoord.Y, (size_t)nLength, + &written); + if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = (DWORD)written; + if (s != SYS_STATUS_SUCCESS) { + K32SetConsoleLastError(s); + return FALSE; + } + return TRUE; +} + // --------------------------------------------------------------------------- // Directory enumeration (FindFirstFileA / FindNextFileA / FindClose) // --------------------------------------------------------------------------- @@ -1973,6 +2137,10 @@ __declspec(dllexport) unsigned int SetHandleCount(unsigned int uNumber) { __declspec(dllexport) DWORD GetFileType(HANDLE hFile) { if (!hFile || hFile == INVALID_HANDLE_VALUE) return FILE_TYPE_UNKNOWN; + if (K32IsStdHandleValue(hFile, STD_INPUT_HANDLE) || + K32IsStdHandleValue(hFile, STD_OUTPUT_HANDLE) || + K32IsStdHandleValue(hFile, STD_ERROR_HANDLE)) + return FILE_TYPE_CHAR; return FILE_TYPE_DISK; } diff --git a/code/user/syscall/syscall_nr.inc b/code/user/syscall/syscall_nr.inc index bbc0d07..a88f1fc 100644 --- a/code/user/syscall/syscall_nr.inc +++ b/code/user/syscall/syscall_nr.inc @@ -60,38 +60,42 @@ SYS_CREATE_DIR EQU 56 SYS_REMOVE_DIR EQU 57 SYS_SET_CWD EQU 58 SYS_GET_CWD EQU 59 -SYS_WRITE_CONSOLE EQU 60 -SYS_BLIT_SCREEN EQU 61 -SYS_CLEAR_SCREEN EQU 62 -SYS_GET_SCREEN_SIZE EQU 63 -SYS_PUT_SCREEN_CHAR EQU 64 -SYS_POLL_KEYBOARD EQU 65 -SYS_IS_KEY_DOWN EQU 66 -SYS_READ_KEYBOARD_CHAR EQU 67 -SYS_READ_KEY EQU 68 -SYS_GET_KEY_MODIFIERS EQU 69 -SYS_POLL_MOUSE EQU 70 -SYS_READ_MOUSE EQU 71 -SYS_GET_MOUSE_POS EQU 72 -SYS_GET_TICK EQU 73 -SYS_PRINT_DEBUG EQU 74 -SYS_QUERY_TRAP_FRAME EQU 75 -SYS_CREATE_SOCKET EQU 76 -SYS_BIND_SOCKET EQU 77 -SYS_LISTEN_SOCKET EQU 78 -SYS_ACCEPT_SOCKET EQU 79 -SYS_CONNECT_SOCKET EQU 80 -SYS_SEND_SOCKET EQU 81 -SYS_RECV_SOCKET EQU 82 -SYS_SHUTDOWN_SOCKET EQU 83 -SYS_SEND_TO_SOCKET EQU 84 -SYS_RECV_FROM_SOCKET EQU 85 -SYS_RESOLVE_HOST EQU 86 -SYS_SET_SOCKET_OPTION EQU 87 -SYS_GET_SOCKET_OPTION EQU 88 -SYS_POLL EQU 89 -SYS_GET_MEMORY_STATUS EQU 90 -SYS_QUERY_KERNEL_STATS EQU 91 -SYS_GET_SYSTEM_TIME EQU 92 - -SYS_MAX EQU 93 +SYS_WRITE_CONSOLE EQU 60 +SYS_GET_CONSOLE_INFO EQU 61 +SYS_SET_CONSOLE_CURSOR EQU 62 +SYS_FILL_CONSOLE_CHARACTER EQU 63 +SYS_FILL_CONSOLE_ATTRIBUTE EQU 64 +SYS_PUT_SCREEN_CHAR EQU 65 +SYS_BLIT_SCREEN EQU 66 +SYS_CLEAR_SCREEN EQU 67 +SYS_GET_SCREEN_SIZE EQU 68 +SYS_QUERY_DISPLAY_INFO EQU 69 +SYS_POLL_KEYBOARD EQU 70 +SYS_IS_KEY_DOWN EQU 71 +SYS_READ_KEYBOARD_CHAR EQU 72 +SYS_READ_KEY EQU 73 +SYS_GET_KEY_MODIFIERS EQU 74 +SYS_POLL_MOUSE EQU 75 +SYS_READ_MOUSE EQU 76 +SYS_GET_MOUSE_POS EQU 77 +SYS_GET_TICK EQU 78 +SYS_PRINT_DEBUG EQU 79 +SYS_QUERY_TRAP_FRAME EQU 80 +SYS_CREATE_SOCKET EQU 81 +SYS_BIND_SOCKET EQU 82 +SYS_LISTEN_SOCKET EQU 83 +SYS_ACCEPT_SOCKET EQU 84 +SYS_CONNECT_SOCKET EQU 85 +SYS_SEND_SOCKET EQU 86 +SYS_RECV_SOCKET EQU 87 +SYS_SHUTDOWN_SOCKET EQU 88 +SYS_SEND_TO_SOCKET EQU 89 +SYS_RECV_FROM_SOCKET EQU 90 +SYS_RESOLVE_HOST EQU 91 +SYS_SET_SOCKET_OPTION EQU 92 +SYS_GET_SOCKET_OPTION EQU 93 +SYS_POLL EQU 94 +SYS_GET_MEMORY_STATUS EQU 95 +SYS_QUERY_KERNEL_STATS EQU 96 +SYS_GET_SYSTEM_TIME EQU 97 +SYS_MAX EQU 98 diff --git a/code/user/syscall/syscall_stubs.asm b/code/user/syscall/syscall_stubs.asm index 9a20ec9..5eb9c13 100644 --- a/code/user/syscall/syscall_stubs.asm +++ b/code/user/syscall/syscall_stubs.asm @@ -173,10 +173,15 @@ SYSCALL_STUB2 GetCwd, SYS_GET_CWD ; Console and graphics ; --------------------------------------------------------------------------- SYSCALL_STUB2 WriteConsole, SYS_WRITE_CONSOLE +SYSCALL_STUB2 GetConsoleInfo, SYS_GET_CONSOLE_INFO +SYSCALL_STUB2 SetConsoleCursor, SYS_SET_CONSOLE_CURSOR +SYSCALL_STUB6 FillConsoleCharacter, SYS_FILL_CONSOLE_CHARACTER +SYSCALL_STUB6 FillConsoleAttribute, SYS_FILL_CONSOLE_ATTRIBUTE +SYSCALL_STUB6 PutScreenChar, SYS_PUT_SCREEN_CHAR SYSCALL_STUB6 BlitScreen, SYS_BLIT_SCREEN SYSCALL_STUB2 ClearScreen, SYS_CLEAR_SCREEN SYSCALL_STUB2 GetScreenSize, SYS_GET_SCREEN_SIZE -SYSCALL_STUB6 PutScreenChar, SYS_PUT_SCREEN_CHAR +SYSCALL_STUB2 QueryDisplayInfo, SYS_QUERY_DISPLAY_INFO ; --------------------------------------------------------------------------- ; Input @@ -195,7 +200,6 @@ SYSCALL_STUB2 GetMousePos, SYS_GET_MOUSE_POS ; Time and debug ; --------------------------------------------------------------------------- SYSCALL_STUB2 GetTick, SYS_GET_TICK -SYSCALL_STUB2 GetSystemTime, SYS_GET_SYSTEM_TIME SYSCALL_STUB2 PrintDebug, SYS_PRINT_DEBUG SYSCALL_STUB2 QueryTrapFrame, SYS_QUERY_TRAP_FRAME @@ -219,5 +223,6 @@ SYSCALL_STUB2 Poll, SYS_POLL SYSCALL_STUB2 GetMemoryStatus, SYS_GET_MEMORY_STATUS SYSCALL_STUB2 QueryKernelStats, SYS_QUERY_KERNEL_STATS +SYSCALL_STUB2 GetSystemTime, SYS_GET_SYSTEM_TIME END diff --git a/runa.bat b/runa.bat index 8bd5d12..7969a4d 100644 --- a/runa.bat +++ b/runa.bat @@ -1,4 +1,5 @@ @start qemu-system-x86_64 ^ +-vga vmware ^ -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd ^ -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd ^ -device ahci,id=ahci ^ @@ -10,9 +11,10 @@ -smp cpus=4,cores=4,threads=1,sockets=1 ^ -netdev user,id=net0,hostfwd=tcp::8080-:8080 ^ -device rtl8139,netdev=net0 ^ +-display gtk,show-cursor=on ^ -device qemu-xhci,id=usb ^ -device usb-kbd,bus=usb.0 ^ --device usb-mouse,bus=usb.0 ^ +-device usb-tablet,bus=usb.0 ^ -device usb-hub,id=myhub,bus=usb.0,port=3 ^ -drive if=none,id=usbflash,format=vpc,file=usb.vhd ^ -device usb-storage,drive=usbflash,bus=usb.0,port=3.1,id=myusbflash