blob: 31649c13d6e59b7c16c11abd87e93b6f82cf55ca [file] [log] [blame]
/*
* Copyright (C) 2008 Advanced Micro Devices, Inc.
* Copyright (C) 2010 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "base/xalloc.h"
#include "base/container_of.h"
#include <coreboot_tables.h>
#include <endian.h>
#include <pci.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysinfo.h>
#include <video_console.h>
#include "font8x16.h"
// Color definitions for the 16 standard colors.
typedef struct
{
uint8_t blue;
uint8_t green;
uint8_t red;
} VgaColor;
static const VgaColor vga_colors[] = {
{ .blue = 0x00, .green = 0x00, .red = 0x00 }, // Black.
{ .blue = 0xAA, .green = 0x00, .red = 0x00 }, // Blue.
{ .blue = 0x00, .green = 0xAA, .red = 0x00 }, // Green.
{ .blue = 0xAA, .green = 0x55, .red = 0x00 }, // Cyan.
{ .blue = 0x00, .green = 0x00, .red = 0xAA }, // Red.
{ .blue = 0xAA, .green = 0x00, .red = 0xAA }, // Magenta.
{ .blue = 0x00, .green = 0xAA, .red = 0xAA }, // Brown.
{ .blue = 0xAA, .green = 0xAA, .red = 0xAA }, // Gray.
{ .blue = 0x55, .green = 0x55, .red = 0x55 }, // Dark gray.
{ .blue = 0xFF, .green = 0x55, .red = 0x55 }, // Bright blue.
{ .blue = 0x55, .green = 0xFF, .red = 0x55 }, // Bright green.
{ .blue = 0xFF, .green = 0xFF, .red = 0x55 }, // Bright cyan.
{ .blue = 0x55, .green = 0x55, .red = 0xFF }, // Bright red.
{ .blue = 0xFF, .green = 0x55, .red = 0xFF }, // Bright magenta.
{ .blue = 0x55, .green = 0xFF, .red = 0xFF }, // Yellow.
{ .blue = 0xFF, .green = 0xFF, .red = 0xFF }, // White.
};
#define VGA_COLOR_DEFAULT 7
typedef struct {
VideoConsole vc;
struct cb_framebuffer *info;
uint8_t *buf;
uint16_t *chars;
int char_count;
uint32_t cursor_x;
uint32_t cursor_y;
} CorebootFb;
static inline uint32_t pixel_val(const CorebootFb *fb, const VgaColor *color)
{
uint32_t blue = color->blue >> (8 - fb->info->blue_mask_size);
uint32_t green = color->green >> (8 - fb->info->green_mask_size);
uint32_t red = color->red >> (8 - fb->info->red_mask_size);
return (blue << fb->info->blue_mask_pos) |
(green << fb->info->green_mask_pos) |
(red << fb->info->red_mask_pos);
}
static void corebootfb_scroll_up(VideoConsole *me)
{
CorebootFb *fb = container_of(me, CorebootFb, vc);
int bytes_per_row = fb->info->bytes_per_line * FONT_HEIGHT;
int fb_size = fb->info->bytes_per_line * fb->info->y_resolution;
// Scroll the text up.
memmove(fb->buf, fb->buf + bytes_per_row, fb_size - bytes_per_row);
// Erase the last line.
memset(fb->buf + fb_size - bytes_per_row, 0, bytes_per_row);
// Update the char buffer.
memmove(fb->chars, &fb->chars[me->columns],
(fb->char_count - me->columns) * sizeof(*fb->chars));
uint16_t *last_row = &fb->chars[(me->rows - 1) * me->columns];
for (int column = 0; column < me->columns; column++)
last_row[column] = VGA_COLOR_DEFAULT << 8;
fb->cursor_y--;
}
static void corebootfb_clear(VideoConsole *me)
{
CorebootFb *fb = container_of(me, CorebootFb, vc);
// Clear the screen.
memset(fb->buf, 0, fb->info->bytes_per_line * fb->info->y_resolution);
// And update the char buffer.
for (int i = 0; i < fb->char_count; i++)
fb->chars[i] = VGA_COLOR_DEFAULT << 8;
}
static void corebootfb_putc(VideoConsole *me,
uint32_t row, uint32_t col, uint16_t ch)
{
static int putting_char = 0;
if (putting_char)
return;
putting_char = 1;
CorebootFb *fb = container_of(me, CorebootFb, vc);
fb->chars[row * me->columns + col] = ch;
uint8_t *glyph = font8x16 + ((ch & 0xFF) * FONT_HEIGHT);
uint32_t bgval = (ch >> 12) & 0xf;
uint32_t fgval = (ch >> 8) & 0xf;
if (fb->info->bits_per_pixel > 8) {
bgval = pixel_val(fb, &vga_colors[bgval]);
fgval = pixel_val(fb, &vga_colors[fgval]);
}
int bits_per_pixel = fb->info->bits_per_pixel;
int bytes_per_pixel = bits_per_pixel / 8;
int bytes_per_line = fb->info->bytes_per_line;
uint8_t *line = fb->buf + (row * FONT_HEIGHT) * bytes_per_line;
for (int y = 0; y < FONT_HEIGHT; y++) {
uint8_t *pixel = line + col * FONT_WIDTH * bytes_per_pixel;
for (int x = FONT_WIDTH - 1; x >= 0; x--) {
uint32_t val = *glyph & (1 << x) ? fgval : bgval;
switch (bits_per_pixel) {
case 8: // Indexed.
*pixel = val;
break;
case 16:
*(uint16_t *)pixel = htole16(val);
break;
case 24:
pixel[0] = val & 0xff;
pixel[1] = (val >> 8) & 0xff;
pixel[2] = (val >> 16) & 0xff;
break;
case 32:
*(uint32_t *)pixel = htole32(val);
break;
}
pixel += bytes_per_pixel;
}
line += bytes_per_line;
glyph++;
}
putting_char = 0;
}
static void corebootfb_get_cursor(VideoConsole *me, uint32_t *x, uint32_t *y)
{
CorebootFb *fb = container_of(me, CorebootFb, vc);
*x = fb->cursor_x;
*y = fb->cursor_y;
}
static void corebootfb_set_cursor(VideoConsole *me, uint32_t x, uint32_t y)
{
CorebootFb *fb = container_of(me, CorebootFb, vc);
fb->cursor_x = x;
fb->cursor_y = y;
}
static int corebootfb_init(VideoConsole *me)
{
CorebootFb *fb = container_of(me, CorebootFb, vc);
if (lib_sysinfo.framebuffer == NULL)
return -1;
fb->info = lib_sysinfo.framebuffer;
fb->buf = (uint8_t *)(uintptr_t)fb->info->physical_address;
me->columns = fb->info->x_resolution / FONT_WIDTH;
me->rows = fb->info->y_resolution / FONT_HEIGHT;
if (me->columns > (1ULL << 16) || me->rows > (1ULL << 16))
return -1;
fb->char_count = me->rows * me->columns;
fb->chars = xmalloc(fb->char_count * sizeof(*fb->chars));
// Clear boot splash screen if there is one.
corebootfb_clear(me);
return 0;
}
static CorebootFb coreboot_fb = {
.vc = {
.init = &corebootfb_init,
.putc = &corebootfb_putc,
.clear = &corebootfb_clear,
.scroll_up = &corebootfb_scroll_up,
.get_cursor = &corebootfb_get_cursor,
.set_cursor = &corebootfb_set_cursor,
},
};
VideoConsole *coreboot_video_console = &coreboot_fb.vc;