#include "./graphics.h" #include "../boot/multiboot/data.h" #include "./debug/output.h" #include "./utils/string.h" /// Replace size with `Graphics::SCREEN_BUF_SIZE` /// We don't do this here, as this drastically increases the size of the kernel /// image. char frontbuffer[1] __attribute__((aligned(8))); char backbuffer[1] __attribute__((aligned(8))); /*! \brief Mode Information of the *Vesa BIOS Extension* * * \see Vesa BIOS Extension [ModeInfoBlock struc](vbe3.pdf#page=38) */ struct VbeModeInfo { enum ModeAttributes : uint16_t { SUPPORTED = 1 << 0, ///< Mode supported by hardware configuration TTY = 1 << 2, ///< TTY Output functions supported by BIOS COLOR = 1 << 3, ///< Color mode (otherwise monochrome) GRAPHICS = 1 << 4, ///< Graphic mode (otherwise text) VGA = 1 << 5, ///< VGA compatible VGA_PAGED = 1 << 6, ///< VGA compatible windowed memory mode is available LFB = 1 << 7, ///< Linear frame buffer mode is available }; uint16_t mode_attributes; // Window functions (used by all VBE revisions, but ignored here) struct __attribute__((packed)) { uint8_t attrib_a; uint8_t attrib_b; uint16_t granularity; uint16_t size; uint16_t segment_a; uint16_t segment_b; uint32_t func_ptr; } win; uint16_t pitch; ///< Bytes per scan line uint16_t width; ///< Horizontal resolution in pixels (GRAPHICS) or characters uint16_t height; ///< Vertical resolution in pixels (GRAPHICS) or characters uint8_t char_width; ///< Character cell width in pixels (deprecated) uint8_t char_height; ///< Character cell height in pixels (deprecated) uint8_t planes; ///< Number of memory planes uint8_t bpp; ///< Bits per pixel uint8_t banks; ///< Number of banks enum MemoryModel : uint8_t { TEXT_MODE = 0, ///< Text mode CGA = 1, ///< CGA graphics HERCULES = 2, ///< Hercules graphics PLANAR = 3, ///< Planar PACKED = 4, ///< Packed pixel NON_CHAIN_4 = 5, ///< Non-chain 4, 256 color DIRECT_COLOR = 6, ///< Direct Color YUV = 7 ///< YUV } memory_model; ///< Memory model type uint8_t bank_size; ///< Bank size in KB uint8_t image_pages; ///< Number of images uint8_t reserved; ///< Reserved for page function // Direct Color fields (required for DIRECT_COLOR and YUV memory models) uint8_t bits_red; ///< Size of direct color red mask in bits uint8_t offset_red; ///< Bit position of lsb of red mask uint8_t bits_green; ///< Size of direct color green mask in bits uint8_t offset_green; ///< Bit position of lsb of green mask uint8_t bits_blue; ///< Size of direct color blue mask in bits uint8_t offset_blue; ///< Bit position of lsb of blue mask uint8_t bits_rsv; ///< Size of direct color reserved mask in bits uint8_t offset_rsv; ///< Bit position of lsb of reserved mask enum DirectColorAttributes : uint8_t { DYNAMIC_COLOR_RAMP = 1 << 0, ///< Programmable (otherwise fixed) color ramp USABLE_BITS = 1 << 1 ///< Bits in reserved mask are usable (otherwise reserved) }; uint8_t directcolor_attributes; ///< direct color mode attributes // Mandatory information for VBE 2.0 and above uint32_t address; ///< physical address for flat memory frame buffer uint32_t offscreen_memory_offset; ///< reserved uint16_t offscreen_memory_size; ///< reserved } __attribute__((packed)); Graphics::Graphics() : buffer_size(SCREEN_BUF_SIZE), buffer{frontbuffer, backbuffer}, scanout_buffer(0), refresh(false) {} bool Graphics::init(bool force) { assert(sizeof(frontbuffer) >= buffer_size && sizeof(backbuffer) >= buffer_size && "Buffers are too small, adjust them!!!"); // Most boot loaders support the Vesa BIOS Extension (VBE) information quite // well Multiboot::VBE *vbe = Multiboot::getVesaBiosExtensionInfo(); if (vbe != nullptr) { // However, you have to manually parse the required data out of all the // outdated crap struct VbeModeInfo *vbe_info = reinterpret_cast( static_cast(vbe->mode_info)); // Is there linear frame buffer (or just ignore the check with `force` // due to a misleading information)? if (force || (vbe_info->mode_attributes & VbeModeInfo::ModeAttributes::LFB) != 0) { // Is there a suitable precompiled graphic mode printer = AbstractGraphicsPrinter::getMode( vbe_info->bpp, vbe_info->offset_red, vbe_info->offset_green, vbe_info->offset_blue, vbe_info->bits_red, vbe_info->bits_green, vbe_info->bits_blue); if (printer != nullptr) { address = reinterpret_cast( static_cast(vbe_info->address)); size = vbe_info->height * vbe_info->pitch; if (size > buffer_size) { DBG_VERBOSE << "The current graphic buffer (" << buffer_size << " bytes) is too small (at least " << size << " bytes required)!" << endl; return false; } else { printer->init(vbe_info->width, vbe_info->height, vbe_info->pitch); printer->buffer(buffer[1 - scanout_buffer]); return true; } } } else { DBG_VERBOSE << "Unsupported graphic mode" << endl; } } // Actually, \ref Multiboot::Framebuffer has everything we need in a real // smart structure, however boot loader like PXE don't support them (yet -- // quite new, added in the last Multiboot revision). Multiboot::Framebuffer *fb = Multiboot::getFramebufferInfo(); if (fb != nullptr) { if (force || fb->type == Multiboot::Framebuffer::RGB) { printer = AbstractGraphicsPrinter::getMode( fb->bpp, fb->offset_red, fb->offset_green, fb->offset_blue, fb->bits_red, fb->bits_green, fb->bits_blue); if (printer != nullptr) { address = reinterpret_cast( static_cast(fb->address)); size = fb->height * fb->pitch; if (size > buffer_size) { DBG_VERBOSE << "The current graphic buffer (" << buffer_size << " bytes) is too small (at least " << size << " bytes required)!" << endl; return false; } else { printer->init(fb->width, fb->height, fb->pitch); printer->buffer(buffer[1 - scanout_buffer]); return true; } } } else { DBG_VERBOSE << "Unsupported graphic mode" << endl; } } // Unable to initialize mode return false; } bool Graphics::switchBuffers() { if (!refresh) { printer->buffer(buffer[scanout_buffer]); scanout_buffer = 1 - scanout_buffer; refresh = true; return true; } else { return false; } } void Graphics::scanoutFrontbuffer() { if (refresh) { memcpy(address, buffer[scanout_buffer], size); refresh = false; } }