| /* |
| * 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; |