diff --git a/headers/private/graphics/vesa/vesa_info.h b/headers/private/graphics/vesa/vesa_info.h index 318c0f2907..1de16a6c30 100644 --- a/headers/private/graphics/vesa/vesa_info.h +++ b/headers/private/graphics/vesa/vesa_info.h @@ -14,6 +14,7 @@ #define VESA_MODES_BOOT_INFO "vesa_modes/v1" struct vesa_mode { + uint16 mode; uint16 width; uint16 height; uint8 bits_per_pixel; @@ -26,7 +27,6 @@ struct vesa_shared_info { display_mode current_mode; uint32 bytes_per_row; - area_id registers_area; // area of memory mapped registers area_id frame_buffer_area; // area of frame buffer uint8 *frame_buffer; // pointer to frame buffer (visible by all apps!) @@ -42,6 +42,7 @@ struct vesa_shared_info { enum { VESA_GET_PRIVATE_DATA = B_DEVICE_OP_CODES_END + 1, VESA_GET_DEVICE_NAME, + VESA_SET_DISPLAY_MODE, VGA_SET_INDEXED_COLORS, VGA_PLANAR_BLIT, diff --git a/headers/private/kernel/arch/x86/vm86.h b/headers/private/kernel/arch/x86/vm86.h index 4689431021..a7d939b422 100644 --- a/headers/private/kernel/arch/x86/vm86.h +++ b/headers/private/kernel/arch/x86/vm86.h @@ -6,7 +6,7 @@ #define _VM86_H #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/src/add-ons/accelerants/vesa/mode.cpp b/src/add-ons/accelerants/vesa/mode.cpp index a862db3398..01f8066a4b 100644 --- a/src/add-ons/accelerants/vesa/mode.cpp +++ b/src/add-ons/accelerants/vesa/mode.cpp @@ -137,7 +137,17 @@ vesa_set_display_mode(display_mode *_mode) if (vesa_propose_display_mode(&mode, &mode, &mode) != B_OK) return B_BAD_VALUE; - // ToDo: unless we have a VBE3 device, we can't change the display mode + vesa_mode *modes = gInfo->vesa_modes; + for (unsigned int i = gInfo->shared_info->vesa_mode_count; i-- > 0;) { + // search mode in VESA mode list + // TODO: list is ordered, we could use binary search + if (modes[i].width == mode.virtual_width + && modes[i].height == mode.virtual_height + && get_color_space_for_depth(modes[i].bits_per_pixel) + == mode.space) + return ioctl(gInfo->device, VESA_SET_DISPLAY_MODE, &i, sizeof(i)); + } + return B_UNSUPPORTED; } diff --git a/src/add-ons/kernel/drivers/graphics/vesa/device.cpp b/src/add-ons/kernel/drivers/graphics/vesa/device.cpp index e7f288ccba..d6fd5b4daa 100644 --- a/src/add-ons/kernel/drivers/graphics/vesa/device.cpp +++ b/src/add-ons/kernel/drivers/graphics/vesa/device.cpp @@ -149,6 +149,17 @@ device_ioctl(void *cookie, uint32 msg, void *buffer, size_t bufferLength) return B_OK; + case VESA_SET_DISPLAY_MODE: + { + unsigned int mode; + + if (bufferLength != sizeof(mode) + || user_memcpy(&mode, buffer, sizeof(mode)) < B_OK) + return B_BAD_ADDRESS; + + return vesa_set_display_mode(*info, mode); + } + case VGA_SET_INDEXED_COLORS: { vga_set_indexed_colors_args args; diff --git a/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp b/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp index fa33c2f000..31971c671a 100644 --- a/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp +++ b/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp @@ -5,67 +5,20 @@ #include "vesa_private.h" +#include "vesa.h" #include #include #include #include +#include #include "driver.h" #include "utility.h" #include "vesa_info.h" -class PhysicalMemoryMapper { - public: - PhysicalMemoryMapper(); - ~PhysicalMemoryMapper(); - - area_id Map(const char *name, void *physicalAddress, size_t numBytes, - uint32 spec, uint32 protection, void **virtualAddress); - status_t InitCheck() { return fArea < B_OK ? (status_t)fArea : B_OK; } - void Keep(); - - private: - area_id fArea; -}; - - -PhysicalMemoryMapper::PhysicalMemoryMapper() - : - fArea(-1) -{ -} - - -PhysicalMemoryMapper::~PhysicalMemoryMapper() -{ - if (fArea >= B_OK) - delete_area(fArea); -} - - -area_id -PhysicalMemoryMapper::Map(const char *name, void *physicalAddress, - size_t numBytes, uint32 spec, uint32 protection, void **virtualAddress) -{ - fArea = map_physical_memory(name, physicalAddress, numBytes, spec, - protection, virtualAddress); - return fArea; -} - - -void -PhysicalMemoryMapper::Keep() -{ - fArea = -1; -} - - -// #pragma mark - - - static uint32 get_color_space_for_depth(uint32 depth) { @@ -89,6 +42,57 @@ get_color_space_for_depth(uint32 depth) } +static status_t +vbe_get_mode_info(struct vm86_state *vmState, uint16 mode, + struct vbe_mode_info *modeInfo) +{ + struct vbe_mode_info *vbeModeInfo = (struct vbe_mode_info *)0x1000; + + memset(vbeModeInfo, 0, sizeof(vbe_mode_info)); + vmState->regs.eax = 0x4f01; + vmState->regs.ecx = mode; + vmState->regs.es = 0x1000 >> 4; + vmState->regs.edi = 0x0000; + + status_t status = vm86_do_int(vmState, 0x10); + if (status != B_OK) { + dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): vm86 failed\n", mode); + return status; + } + + if ((vmState->regs.eax & 0xffff) != 0x4f) { + dprintf(DEVICE_NAME ": vbe_get_mode_info(): BIOS returned 0x%04lx\n", + vmState->regs.eax & 0xffff); + return B_ENTRY_NOT_FOUND; + } + + memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info)); + return B_OK; +} + + +static status_t +vbe_set_mode(struct vm86_state *vmState, uint16 mode) +{ + vmState->regs.eax = 0x4f02; + vmState->regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER; + + status_t status = vm86_do_int(vmState, 0x10); + if (status != B_OK) { + dprintf(DEVICE_NAME ": vbe_set_mode(%u): vm86 failed\n", mode); + return status; + } + + if ((vmState->regs.eax & 0xffff) != 0x4f) { + dprintf(DEVICE_NAME ": vbe_set_mode(): BIOS returned 0x%04lx\n", + vmState->regs.eax & 0xffff); + return B_ERROR; + } + + return B_OK; +} + + // #pragma mark - @@ -103,6 +107,7 @@ vesa_init(vesa_info &info) size_t modesSize = 0; vesa_mode *modes = (vesa_mode *)get_boot_item(VESA_MODES_BOOT_INFO, &modesSize); + info.modes = modes; size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7; @@ -115,7 +120,7 @@ vesa_init(vesa_info &info) vesa_shared_info &sharedInfo = *info.shared_info; - memset(/*(void *)*/&sharedInfo, 0, sizeof(vesa_shared_info)); + memset(&sharedInfo, 0, sizeof(vesa_shared_info)); if (modes != NULL) { sharedInfo.vesa_mode_offset = sharedSize; @@ -152,3 +157,73 @@ vesa_uninit(vesa_info &info) delete_area(info.shared_area); } + +status_t +vesa_set_display_mode(vesa_info &info, unsigned int mode) +{ + if (mode >= info.shared_info->vesa_mode_count) + return B_ENTRY_NOT_FOUND; + + // Prepare vm86 mode environment + struct vm86_state vmState; + status_t status = vm86_prepare(&vmState, 0x2000); + if (status != B_OK) { + dprintf(DEVICE_NAME": vesa_set_display_mode(): vm86_prepare failed\n"); + return status; + } + + area_id newFBArea; + frame_buffer_boot_info *bufferInfo; + struct vbe_mode_info modeInfo; + + // Get mode information + status = vbe_get_mode_info(&vmState, info.modes[mode].mode, &modeInfo); + if (status != B_OK) { + dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n"); + goto error; + } + + // Set mode + status = vbe_set_mode(&vmState, info.modes[mode].mode); + if (status != B_OK) { + dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n"); + goto error; + } + + // Map new frame buffer + void *frameBuffer; + newFBArea = map_physical_memory("vesa_fb", + (void *)modeInfo.physical_base, + modeInfo.bytes_per_row * modeInfo.height, B_ANY_KERNEL_ADDRESS, + B_READ_AREA | B_WRITE_AREA, &frameBuffer); + if (newFBArea < B_OK) { + status = (status_t)newFBArea; + goto error; + } + delete_area(info.shared_info->frame_buffer_area); + + // Update shared frame buffer information + info.shared_info->frame_buffer_area = newFBArea; + info.shared_info->frame_buffer = (uint8 *)frameBuffer; + info.shared_info->physical_frame_buffer = (uint8 *)modeInfo.physical_base; + info.shared_info->bytes_per_row = modeInfo.bytes_per_row; + info.shared_info->current_mode.virtual_width = modeInfo.width; + info.shared_info->current_mode.virtual_height = modeInfo.height; + info.shared_info->current_mode.space = get_color_space_for_depth( + modeInfo.bits_per_pixel); + + // Update boot item as it's used in vesa_init() + bufferInfo + = (frame_buffer_boot_info *)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL); + bufferInfo->area = newFBArea; + bufferInfo->frame_buffer = (addr_t)frameBuffer; + bufferInfo->width = modeInfo.width; + bufferInfo->height = modeInfo.height; + bufferInfo->depth = modeInfo.bits_per_pixel; + bufferInfo->bytes_per_row = modeInfo.bytes_per_row; + +error: + vm86_cleanup(&vmState); + return status; +} + diff --git a/src/add-ons/kernel/drivers/graphics/vesa/vesa_private.h b/src/add-ons/kernel/drivers/graphics/vesa/vesa_private.h index 15e6d48145..973a3b6b8c 100644 --- a/src/add-ons/kernel/drivers/graphics/vesa/vesa_private.h +++ b/src/add-ons/kernel/drivers/graphics/vesa/vesa_private.h @@ -16,6 +16,7 @@ struct vesa_get_supported_modes; +struct vesa_mode; struct vesa_info { uint32 cookie_magic; @@ -24,9 +25,11 @@ struct vesa_info { pci_info *pci; struct vesa_shared_info *shared_info; area_id shared_area; + vesa_mode *modes; }; extern status_t vesa_init(vesa_info &info); extern void vesa_uninit(vesa_info &info); +extern status_t vesa_set_display_mode(vesa_info &info, unsigned int mode); #endif /* VESA_PRIVATE_H */ diff --git a/src/system/boot/platform/bios_ia32/video.cpp b/src/system/boot/platform/bios_ia32/video.cpp index e24c62e146..230998952a 100644 --- a/src/system/boot/platform/bios_ia32/video.cpp +++ b/src/system/boot/platform/bios_ia32/video.cpp @@ -1007,6 +1007,7 @@ platform_init_video(void) uint32 i = 0; while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) { + modes[i].mode = mode->mode; modes[i].width = mode->width; modes[i].height = mode->height; modes[i].bits_per_pixel = mode->bits_per_pixel; diff --git a/src/system/kernel/debug/frame_buffer_console.cpp b/src/system/kernel/debug/frame_buffer_console.cpp index 8634f727f4..c5b7b889f7 100644 --- a/src/system/kernel/debug/frame_buffer_console.cpp +++ b/src/system/kernel/debug/frame_buffer_console.cpp @@ -410,6 +410,7 @@ frame_buffer_console_init(kernel_args *args) args->frame_buffer.height, args->frame_buffer.depth, args->frame_buffer.bytes_per_row); + sBootInfo.area = sConsole.area; sBootInfo.frame_buffer = (addr_t)frameBuffer; sBootInfo.width = args->frame_buffer.width; sBootInfo.height = args->frame_buffer.height;