/** Set the display mode. * @param _dev Device to set mode of. * @param mode Mode structure for mode to set. * @return Status code describing result of the operation. */ static status_t vbe_display_set_mode(display_device_t *_device, display_mode_t *mode) { bios_regs_t regs; if(mode) { /* Check if anything needs to be done. */ bios_regs_init(®s); regs.eax = VBE_FUNCTION_GET_MODE; bios_interrupt(0x10, ®s); if((regs.eax & 0xFF00) != 0) { kprintf(LOG_DEBUG, "vbe: call failed with code 0x%x\n", regs.eax & 0xFFFF); return STATUS_DEVICE_ERROR; } if((regs.ebx & ~((1<<14) | (1<<15))) == mode->id) { return STATUS_SUCCESS; } kprintf(LOG_DEBUG, "vbe: switching to mode 0x%" PRIx32 " (mode: %p)\n", mode->id, mode); } /* Set bit 14 in the mode register to use linear framebuffer model. */ bios_regs_init(®s); regs.eax = VBE_FUNCTION_SET_MODE; regs.ebx = (mode) ? (mode->id | (1<<14)) : 3; bios_interrupt(0x10, ®s); if((regs.eax & 0xFF00) != 0) { kprintf(LOG_DEBUG, "vbe: call failed with code 0x%x\n", regs.eax & 0xFFFF); return STATUS_DEVICE_ERROR; } return STATUS_SUCCESS; }
/** * Get the PXE entry point address. * * @return Whether the entry point could be found. */ static bool get_entry_point(void) { bios_regs_t regs; pxenv_t *pxenv; pxe_t *pxe; /* Use the PXE installation check function. */ bios_regs_init(®s); regs.eax = INT1A_PXE_INSTALL_CHECK; bios_call(0x1a, ®s); if (regs.eax != INT1A_PXE_INSTALL_CHECK_RET || regs.eflags & X86_FLAGS_CF) { dprintf("pxe: loaded via PXE but PXE not available\n"); return false; } /* Get the PXENV+ structure. */ pxenv = (pxenv_t *)segoff_to_linear((regs.es << 16) | regs.bx); if (memcmp(pxenv->signature, PXENV_SIGNATURE, sizeof(pxenv->signature)) != 0) { boot_error("PXENV+ structure has incorrect signature"); } else if (!checksum_range(pxenv, pxenv->length)) { boot_error("PXENV+ structure is corrupt"); } /* Get the !PXE structure. */ pxe = (pxe_t *)segoff_to_linear(pxenv->pxe_ptr); if (memcmp(pxe->signature, PXE_SIGNATURE, sizeof(pxe->signature)) != 0) { boot_error("!PXE structure has incorrect signature"); } else if (!checksum_range(pxe, pxe->length)) { boot_error("!PXE structure is corrupt"); } pxe_entry_point = pxe->entry_point_16; return true; }
/** Initialisation function for the VBE driver. * @return Status code describing result of the operation. */ static status_t vbe_init(void) { phys_ptr_t mem_phys = ~((phys_ptr_t)0); size_t count = 0, i, mem_size = 0; vbe_mode_info_t *minfo = NULL; display_mode_t *modes = NULL; vbe_info_t *info = NULL; uint16_t *location; bios_regs_t regs; status_t ret; /* Detect VBE presence by trying to get controller information. */ info = bios_mem_alloc(sizeof(vbe_info_t), MM_WAIT); strncpy(info->vbe_signature, "VBE2", 4); bios_regs_init(®s); regs.eax = VBE_FUNCTION_CONTROLLER_INFO; regs.edi = bios_mem_virt2phys(info); bios_interrupt(0x10, ®s); if((regs.eax & 0x00FF) != 0x4F) { kprintf(LOG_DEBUG, "vbe: VBE is not supported!\n"); ret = STATUS_NOT_SUPPORTED; goto out; } else if((regs.eax & 0xFF00) != 0) { kprintf(LOG_DEBUG, "vbe: call failed with code 0x%x\n", regs.eax & 0xFFFF); ret = STATUS_DEVICE_ERROR; goto out; } kprintf(LOG_NOTICE, "vbe: vbe presence was detected:\n"); kprintf(LOG_NOTICE, " signature: %s\n", info->vbe_signature); kprintf(LOG_NOTICE, " version: 0x%" PRIx16 "\n", info->vbe_version); kprintf(LOG_NOTICE, " capabilities: 0x%" PRIx32 "\n", info->capabilities); kprintf(LOG_NOTICE, " mode pointer: 0x%" PRIx32 "\n", info->video_mode_ptr); kprintf(LOG_NOTICE, " total memory: %" PRIu16 "KB\n", info->total_memory * 64); if(info->vbe_version >= 0x0200) { kprintf(LOG_NOTICE, " OEM revision: 0x%" PRIx16 "\n", info->oem_software_rev); } mem_size = (info->total_memory * 64) * 1024; location = bios_mem_phys2virt(SEGOFF2LIN(info->video_mode_ptr)); if(!location) { ret = STATUS_DEVICE_ERROR; goto out; } /* Allocate a region to store the mode information structure in. */ minfo = bios_mem_alloc(sizeof(vbe_mode_info_t), MM_WAIT); /* Iterate through all the modes available. An ID of 0xFFFF indicates * the end of the mode list. */ for(i = 0; location[i] != 0xFFFF; i++) { /* Get information on the mode. */ bios_regs_init(®s); regs.eax = VBE_FUNCTION_MODE_INFO; regs.ecx = location[i]; regs.edi = bios_mem_virt2phys(minfo); bios_interrupt(0x10, ®s); if((regs.eax & 0xFF00) != 0) { kprintf(LOG_DEBUG, "vbe: call failed with code 0x%x\n", regs.eax & 0xFFFF); ret = STATUS_DEVICE_ERROR; goto out; } /* Check if the mode is suitable. */ if(minfo->memory_model != 4 && minfo->memory_model != 6) { /* Not packed-pixel or direct colour. */ continue; } else if(minfo->phys_base_ptr == 0) { /* Borked. */ continue; } else if((minfo->mode_attributes & (1<<0)) == 0) { /* Not supported. */ continue; } else if((minfo->mode_attributes & (1<<3)) == 0) { /* Not colour. */ continue; } else if((minfo->mode_attributes & (1<<4)) == 0) { /* Not a graphics mode. */ continue; } else if((minfo->mode_attributes & (1<<7)) == 0) { /* Not usable in linear mode. */ continue; } else if(minfo->bits_per_pixel != 8 && minfo->bits_per_pixel != 16 && minfo->bits_per_pixel != 24 && minfo->bits_per_pixel != 32) { continue; } /* Add the mode to the mode array. To begin with, set offset to * the full physical address. */ modes = krealloc(modes, sizeof(display_mode_t) * (count + 1), MM_WAIT); modes[count].id = location[i]; modes[count].width = minfo->x_resolution; modes[count].height = minfo->y_resolution; modes[count].format = depth_to_format(minfo->bits_per_pixel); modes[count].offset = minfo->phys_base_ptr; count++; /* Try to guess the memory address. */ if(minfo->phys_base_ptr < mem_phys) { mem_phys = minfo->phys_base_ptr; } } /* Now fix up mode offsets. */ for(i = 0; i < count; i++) { modes[i].offset = modes[i].offset - mem_phys; } /* Set the cache mode on the framebuffer to WC. */ phys_set_memory_type(mem_phys, ROUND_UP(mem_size, PAGE_SIZE), MEMORY_TYPE_WC); /* Add the display device. */ ret = display_device_create(NULL, NULL, &vbe_display_ops, NULL, modes, count, mem_phys, mem_size, &vbe_display_device); if(ret != STATUS_SUCCESS) { return ret; } out: if(minfo) { bios_mem_free(minfo, sizeof(vbe_mode_info_t)); } if(info) { bios_mem_free(info, sizeof(vbe_info_t)); } return ret; }