mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-27 10:00:17 -06:00
9785d8f8b9
This ensures they are accessible when the test is running, and if they are physically mapped above 4GB.
352 lines
11 KiB
C
352 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2020-2021 Martin Whitaker
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "boot.h"
|
|
#include "bootparams.h"
|
|
|
|
#include "font.h"
|
|
#include "vmem.h"
|
|
|
|
#include "screen.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Types
|
|
//------------------------------------------------------------------------------
|
|
|
|
typedef struct {
|
|
uint8_t r;
|
|
uint8_t g;
|
|
uint8_t b;
|
|
} __attribute__((packed)) rgb_value_t;
|
|
|
|
typedef union {
|
|
struct {
|
|
uint8_t ch;
|
|
uint8_t attr;
|
|
};
|
|
struct {
|
|
uint16_t value;
|
|
};
|
|
} vga_char_t;
|
|
|
|
typedef vga_char_t vga_buffer_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Variables
|
|
//------------------------------------------------------------------------------
|
|
|
|
static const rgb_value_t vga_pallete[16] = {
|
|
// R G B
|
|
{ 0, 0, 0 }, // BLACK
|
|
{ 0, 0, 170 }, // BLUE
|
|
{ 0, 170, 0 }, // GREEN
|
|
{ 0, 170, 170 }, // CYAN
|
|
{ 170, 0, 0 }, // RED
|
|
{ 170, 0, 170 }, // MAUVE
|
|
{ 170, 85, 0 }, // YELLOW (brown really)
|
|
{ 170, 170, 170 }, // WHITE
|
|
{ 85, 85, 85 }, // BOLD+BLACK
|
|
{ 85, 85, 255 }, // BOLD+BLUE
|
|
{ 85, 255, 85 }, // BOLD+GREEN
|
|
{ 85, 255, 255 }, // BOLD+CYAN
|
|
{ 255, 85, 85 }, // BOLD+RED
|
|
{ 255, 85, 255 }, // BOLD+MAUVE
|
|
{ 255, 255, 85 }, // BOLD+YELLOW
|
|
{ 255, 255, 255 } // BOLD+WHITE
|
|
};
|
|
|
|
static vga_buffer_t *vga_buffer = (vga_buffer_t *)(0xb8000);
|
|
|
|
static vga_buffer_t shadow_buffer;
|
|
|
|
static int lfb_bytes_per_pixel = 0;
|
|
|
|
static uintptr_t lfb_base;
|
|
static uintptr_t lfb_stride;
|
|
|
|
static uint32_t lfb_pallete[16];
|
|
|
|
static uint8_t current_attr = WHITE | BLUE << 4;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void vga_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
|
{
|
|
shadow_buffer[row][col].ch = ch;
|
|
shadow_buffer[row][col].attr = attr;
|
|
|
|
(*vga_buffer)[row][col].value = shadow_buffer[row][col].value;
|
|
}
|
|
|
|
static void lfb8_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
|
{
|
|
shadow_buffer[row][col].ch = ch;
|
|
shadow_buffer[row][col].attr = attr;
|
|
|
|
uint8_t fg_colour = attr % 16;
|
|
uint8_t bg_colour = attr / 16;
|
|
|
|
uint8_t *pixel_row = (uint8_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH;
|
|
for (int y = 0; y < FONT_HEIGHT; y++) {
|
|
uint8_t font_row = font_data[ch][y];
|
|
for (int x = 0; x < FONT_WIDTH; x++) {
|
|
pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour;
|
|
font_row <<= 1;
|
|
}
|
|
pixel_row += lfb_stride;
|
|
}
|
|
}
|
|
|
|
static void lfb16_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
|
{
|
|
shadow_buffer[row][col].ch = ch;
|
|
shadow_buffer[row][col].attr = attr;
|
|
|
|
uint16_t fg_colour = lfb_pallete[attr % 16];
|
|
uint16_t bg_colour = lfb_pallete[attr / 16];
|
|
|
|
uint16_t *pixel_row = (uint16_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH;
|
|
for (int y = 0; y < FONT_HEIGHT; y++) {
|
|
uint8_t font_row = font_data[ch][y];
|
|
for (int x = 0; x < FONT_WIDTH; x++) {
|
|
pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour;
|
|
font_row <<= 1;
|
|
}
|
|
pixel_row += lfb_stride;
|
|
}
|
|
}
|
|
|
|
static void lfb24_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
|
{
|
|
shadow_buffer[row][col].ch = ch;
|
|
shadow_buffer[row][col].attr = attr;
|
|
|
|
uint32_t fg_colour = lfb_pallete[attr % 16];
|
|
uint32_t bg_colour = lfb_pallete[attr / 16];
|
|
|
|
uint8_t *pixel_row = (uint8_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH * 3;
|
|
for (int y = 0; y < FONT_HEIGHT; y++) {
|
|
uint8_t font_row = font_data[ch][y];
|
|
for (int x = 0; x < FONT_WIDTH * 3; x += 3) {
|
|
uint32_t colour = font_row & 0x80 ? fg_colour : bg_colour;
|
|
pixel_row[x+0] = colour & 0xff; colour >>= 8;
|
|
pixel_row[x+1] = colour & 0xff; colour >>= 8;
|
|
pixel_row[x+2] = colour & 0xff;
|
|
font_row <<= 1;
|
|
}
|
|
pixel_row += lfb_stride;
|
|
}
|
|
}
|
|
|
|
static void lfb32_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
|
{
|
|
shadow_buffer[row][col].ch = ch;
|
|
shadow_buffer[row][col].attr = attr;
|
|
|
|
uint32_t fg_colour = lfb_pallete[attr % 16];
|
|
uint32_t bg_colour = lfb_pallete[attr / 16];
|
|
|
|
uint32_t *pixel_row = (uint32_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH;
|
|
for (int y = 0; y < FONT_HEIGHT; y++) {
|
|
uint8_t font_row = font_data[ch][y];
|
|
for (int x = 0; x < FONT_WIDTH; x++) {
|
|
pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour;
|
|
font_row <<= 1;
|
|
}
|
|
pixel_row += lfb_stride;
|
|
}
|
|
}
|
|
|
|
static void (*put_char)(int, int, uint8_t, uint8_t) = vga_put_char;
|
|
|
|
static void put_value(int row, int col, uint16_t value)
|
|
{
|
|
put_char(row, col, value % 256, value / 256);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void screen_init(void)
|
|
{
|
|
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
|
|
|
|
const screen_info_t *screen_info = &boot_params->screen_info;
|
|
|
|
bool use_lfb = screen_info->orig_video_isVGA == VIDEO_TYPE_VLFB
|
|
|| screen_info->orig_video_isVGA == VIDEO_TYPE_EFI;
|
|
|
|
if (use_lfb) {
|
|
int lfb_width = screen_info->lfb_width;
|
|
int lfb_height = screen_info->lfb_height;
|
|
int lfb_depth = screen_info->lfb_depth;
|
|
|
|
if (lfb_depth <= 8) {
|
|
lfb_bytes_per_pixel = 1;
|
|
put_char = lfb8_put_char;
|
|
} else if (lfb_depth <= 16) {
|
|
lfb_bytes_per_pixel = 2;
|
|
put_char = lfb16_put_char;
|
|
} else if (lfb_depth <= 24) {
|
|
lfb_bytes_per_pixel = 3;
|
|
put_char = lfb24_put_char;
|
|
} else {
|
|
lfb_bytes_per_pixel = 4;
|
|
put_char = lfb32_put_char;
|
|
}
|
|
|
|
lfb_base = screen_info->lfb_base;
|
|
#ifdef __x86_64__
|
|
if (LFB_CAPABILITY_64BIT_BASE & screen_info->capabilities) {
|
|
lfb_base |= (uintptr_t)screen_info->ext_lfb_base << 32;
|
|
}
|
|
#endif
|
|
lfb_stride = screen_info->lfb_linelength;
|
|
|
|
// Clip the framebuffer size to make sure we can map it into the 1GB device region.
|
|
// This will produce a garbled display, but that's better than nothing.
|
|
if (lfb_width > 16384) lfb_width = 16384;
|
|
if (lfb_height > 16384) lfb_height = 16384;
|
|
|
|
// This is the first device we map, so the above clipping should guarantee it never fails.
|
|
lfb_base = map_device(lfb_base, lfb_height * lfb_width * lfb_bytes_per_pixel);
|
|
|
|
// Blank the whole framebuffer.
|
|
int pixels_per_word = sizeof(uint32_t) / lfb_bytes_per_pixel;
|
|
uint32_t *line = (uint32_t *)lfb_base;
|
|
for (int y = 0; y < lfb_height; y++) {
|
|
for (int x = 0; x < (lfb_width / pixels_per_word); x++) {
|
|
line[x] = 0;
|
|
}
|
|
line += lfb_stride / sizeof(uint32_t);
|
|
}
|
|
|
|
int excess_width = lfb_width - (SCREEN_WIDTH * FONT_WIDTH);
|
|
if (excess_width > 0) {
|
|
lfb_base += (excess_width / 2) * lfb_bytes_per_pixel;
|
|
}
|
|
int excess_height = lfb_height - (SCREEN_HEIGHT * FONT_HEIGHT);
|
|
if (excess_height > 0) {
|
|
lfb_base += (excess_height / 2) * lfb_stride;
|
|
}
|
|
|
|
if (lfb_bytes_per_pixel != 3) {
|
|
lfb_stride /= lfb_bytes_per_pixel;
|
|
}
|
|
|
|
// Initialise the pallete.
|
|
uint32_t r_max = (1 << screen_info->red_size ) - 1;
|
|
uint32_t g_max = (1 << screen_info->green_size) - 1;
|
|
uint32_t b_max = (1 << screen_info->blue_size ) - 1;
|
|
for (int i = 0; i < 16; i++) {
|
|
uint32_t r = ((vga_pallete[i].r * r_max) / 255) << screen_info->red_pos;
|
|
uint32_t g = ((vga_pallete[i].g * g_max) / 255) << screen_info->green_pos;
|
|
uint32_t b = ((vga_pallete[i].b * b_max) / 255) << screen_info->blue_pos;
|
|
lfb_pallete[i] = r | g | b;
|
|
}
|
|
}
|
|
}
|
|
|
|
void set_foreground_colour(screen_colour_t colour)
|
|
{
|
|
current_attr = (current_attr & 0xf0) | (colour & 0x0f);
|
|
}
|
|
|
|
void set_background_colour(screen_colour_t colour)
|
|
{
|
|
current_attr = (current_attr & 0x8f) | ((colour << 4) & 0x70);
|
|
}
|
|
|
|
void clear_screen(void)
|
|
{
|
|
for (int row = 0; row < SCREEN_HEIGHT; row++) {
|
|
for (int col = 0; col < SCREEN_WIDTH; col++) {
|
|
put_char(row, col, ' ', current_attr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void clear_screen_region(int start_row, int start_col, int end_row, int end_col)
|
|
{
|
|
if (start_row < 0) start_row = 0;
|
|
if (start_col < 0) start_col = 0;
|
|
|
|
if (end_row >= SCREEN_HEIGHT) end_row = SCREEN_HEIGHT - 1;
|
|
if (end_col >= SCREEN_WIDTH) end_col = SCREEN_WIDTH - 1;
|
|
|
|
if (start_row > end_row) return;
|
|
if (start_col > end_col) return;
|
|
|
|
for (int row = start_row; row <= end_row; row++) {
|
|
for (int col = start_col; col <= end_col; col++) {
|
|
put_char(row, col, ' ', current_attr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void scroll_screen_region(int start_row, int start_col, int end_row, int end_col)
|
|
{
|
|
if (start_row < 0) start_row = 0;
|
|
if (start_col < 0) start_col = 0;
|
|
|
|
if (end_row >= SCREEN_HEIGHT) end_row = SCREEN_HEIGHT - 1;
|
|
if (end_col >= SCREEN_WIDTH) end_col = SCREEN_WIDTH - 1;
|
|
|
|
if (start_row > end_row) return;
|
|
if (start_col > end_col) return;
|
|
|
|
for (int row = start_row; row <= end_row; row++) {
|
|
for (int col = start_col; col <= end_col; col++) {
|
|
if (row < end_row) {
|
|
put_value(row, col, shadow_buffer[row + 1][col].value);
|
|
} else {
|
|
put_char(row, col, ' ', current_attr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void save_screen_region(int start_row, int start_col, int end_row, int end_col, uint16_t buffer[])
|
|
{
|
|
if (start_row < 0) start_row = 0;
|
|
if (start_col < 0) start_col = 0;
|
|
|
|
uint16_t *dst = &buffer[0];
|
|
for (int row = start_row; row <= end_row; row++) {
|
|
if (row >= SCREEN_HEIGHT) break;
|
|
for (int col = start_col; col <= end_col; col++) {
|
|
if (col >= SCREEN_WIDTH) break;
|
|
*dst++ = shadow_buffer[row][col].value;
|
|
}
|
|
}
|
|
}
|
|
|
|
void restore_screen_region(int start_row, int start_col, int end_row, int end_col, const uint16_t buffer[])
|
|
{
|
|
if (start_row < 0) start_row = 0;
|
|
if (start_col < 0) start_col = 0;
|
|
|
|
const uint16_t *src = &buffer[0];
|
|
for (int row = start_row; row <= end_row; row++) {
|
|
if (row >= SCREEN_HEIGHT) break;
|
|
for (int col = start_col; col <= end_col; col++) {
|
|
if (col >= SCREEN_WIDTH) break;
|
|
put_value(row, col, *src++);
|
|
}
|
|
}
|
|
}
|
|
|
|
void print_char(int row, int col, char ch)
|
|
{
|
|
if (row < 0 || row >= SCREEN_HEIGHT) return;
|
|
if (col < 0 || col >= SCREEN_WIDTH) return;
|
|
|
|
put_char(row, col, ch, (current_attr & 0x0f) | (shadow_buffer[row][col].attr & 0xf0));
|
|
}
|