#include <sys/types.h>
#include <sys/systm.h>
#include <sys/multiboot2.h>
#include <sys/framebuffer.h>
#include <sys/bootinfo.h>
#include <sys/boot_console.h>
#include <sys/bootconf.h>
#include <sys/rgb.h>
#include "boot_console_impl.h"
#define P2ROUNDUP(x, align) (-(-(x) & -(align)))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define nitems(x) (sizeof ((x)) / sizeof ((x)[0]))
struct vis_consdisplay {
uint16_t row;
uint16_t col;
uint16_t width;
uint16_t height;
uint8_t *data;
};
struct vis_conscopy {
uint16_t s_row;
uint16_t s_col;
uint16_t e_row;
uint16_t e_col;
uint16_t t_row;
uint16_t t_col;
};
#define MAX_GLYPH (16 * 32 * 4)
struct fontlist cf_fontlist;
static bitmap_data_t cf_data;
static struct font cf_font;
static struct font boot_fb_font;
static uint8_t glyph[MAX_GLYPH];
static void boot_fb_putchar(int);
static void boot_fb_eraseline(void);
static void boot_fb_setpos(int, int);
static void boot_fb_shiftline(int);
static void boot_fb_eraseline_impl(uint16_t, uint16_t);
static void
xbi_init_font(struct xboot_info *xbi)
{
uint32_t i, checksum = 0;
struct boot_modules *modules;
struct font_info *fi;
uintptr_t ptr;
modules = (struct boot_modules *)(uintptr_t)xbi->bi_modules;
for (i = 0; i < xbi->bi_module_cnt; i++) {
if (modules[i].bm_type == BMT_FONT)
break;
}
if (i == xbi->bi_module_cnt)
return;
ptr = (uintptr_t)modules[i].bm_addr;
fi = (struct font_info *)ptr;
checksum += fi->fi_width;
checksum += fi->fi_height;
checksum += fi->fi_bitmap_size;
for (i = 0; i < VFNT_MAPS; i++)
checksum += fi->fi_map_count[i];
if (checksum + fi->fi_checksum != 0)
return;
cf_data.width = fi->fi_width;
cf_data.height = fi->fi_height;
cf_data.uncompressed_size = fi->fi_bitmap_size;
cf_data.font = &cf_font;
ptr += sizeof (struct font_info);
ptr = P2ROUNDUP(ptr, 8);
cf_font.vf_width = fi->fi_width;
cf_font.vf_height = fi->fi_height;
for (i = 0; i < VFNT_MAPS; i++) {
if (fi->fi_map_count[i] == 0)
continue;
cf_font.vf_map_count[i] = fi->fi_map_count[i];
cf_font.vf_map[i] = (struct font_map *)ptr;
ptr += (fi->fi_map_count[i] * sizeof (struct font_map));
ptr = P2ROUNDUP(ptr, 8);
}
cf_font.vf_bytes = (uint8_t *)ptr;
cf_fontlist.font_name = NULL;
cf_fontlist.font_flags = FONT_BOOT;
cf_fontlist.font_data = &cf_data;
cf_fontlist.font_load = NULL;
STAILQ_INSERT_HEAD(&fonts, &cf_fontlist, font_next);
}
boolean_t
xbi_fb_init(struct xboot_info *xbi, bcons_dev_t *bcons_dev)
{
multiboot_tag_framebuffer_t *tag;
boot_framebuffer_t *xbi_fb;
xbi_fb = (boot_framebuffer_t *)(uintptr_t)xbi->bi_framebuffer;
if (xbi_fb == NULL)
return (B_FALSE);
#if !defined(_BOOT)
fb_info.cursor.origin.x = xbi_fb->cursor.origin.x;
fb_info.cursor.origin.y = xbi_fb->cursor.origin.y;
fb_info.cursor.pos.x = xbi_fb->cursor.pos.x;
fb_info.cursor.pos.y = xbi_fb->cursor.pos.y;
fb_info.cursor.visible = xbi_fb->cursor.visible;
#endif
xbi_init_font(xbi);
tag = (multiboot_tag_framebuffer_t *)(uintptr_t)xbi_fb->framebuffer;
if (tag == NULL) {
return (B_FALSE);
}
fb_info.paddr = tag->framebuffer_common.framebuffer_addr;
fb_info.pitch = tag->framebuffer_common.framebuffer_pitch;
fb_info.depth = tag->framebuffer_common.framebuffer_bpp;
fb_info.bpp = P2ROUNDUP(fb_info.depth, 8) >> 3;
fb_info.screen.x = tag->framebuffer_common.framebuffer_width;
fb_info.screen.y = tag->framebuffer_common.framebuffer_height;
fb_info.fb_size = fb_info.screen.y * fb_info.pitch;
bcons_dev->bd_putchar = boot_fb_putchar;
bcons_dev->bd_eraseline = boot_fb_eraseline;
bcons_dev->bd_cursor = boot_fb_cursor;
bcons_dev->bd_setpos = boot_fb_setpos;
bcons_dev->bd_shift = boot_fb_shiftline;
if (fb_info.paddr == 0)
fb_info.fb_type = FB_TYPE_UNKNOWN;
switch (tag->framebuffer_common.framebuffer_type) {
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
fb_info.fb_type = FB_TYPE_EGA_TEXT;
return (B_FALSE);
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
if (fb_info.paddr != 0)
fb_info.fb_type = FB_TYPE_INDEXED;
return (B_TRUE);
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
if (fb_info.paddr != 0)
fb_info.fb_type = FB_TYPE_RGB;
break;
default:
return (B_FALSE);
}
fb_info.rgb.red.size = tag->u.fb2.framebuffer_red_mask_size;
fb_info.rgb.red.pos = tag->u.fb2.framebuffer_red_field_position;
fb_info.rgb.green.size = tag->u.fb2.framebuffer_green_mask_size;
fb_info.rgb.green.pos = tag->u.fb2.framebuffer_green_field_position;
fb_info.rgb.blue.size = tag->u.fb2.framebuffer_blue_mask_size;
fb_info.rgb.blue.pos = tag->u.fb2.framebuffer_blue_field_position;
rgb_info = fb_info.rgb;
return (B_TRUE);
}
static void
boot_fb_set_font(uint16_t height, uint16_t width)
{
short h, w;
bitmap_data_t *bp;
int i;
h = MIN(height, 4096);
w = MIN(width, 4096);
bp = set_font((short *)&fb_info.terminal.y,
(short *)&fb_info.terminal.x, h, w);
boot_fb_font.vf_bytes = bp->font->vf_bytes;
boot_fb_font.vf_width = bp->font->vf_width;
boot_fb_font.vf_height = bp->font->vf_height;
for (i = 0; i < VFNT_MAPS; i++) {
boot_fb_font.vf_map[i] = bp->font->vf_map[i];
boot_fb_font.vf_map_count[i] = bp->font->vf_map_count[i];
}
fb_info.font_width = boot_fb_font.vf_width;
fb_info.font_height = boot_fb_font.vf_height;
}
static void
boot_fb_fill(uint8_t *dst, uint32_t data, uint32_t len)
{
uint16_t *dst16;
uint32_t *dst32;
uint32_t i;
switch (fb_info.depth) {
case 24:
case 8:
for (i = 0; i < len; i++)
dst[i] = (uint8_t)data;
break;
case 15:
case 16:
dst16 = (uint16_t *)dst;
len /= 2;
for (i = 0; i < len; i++)
dst16[i] = (uint16_t)data;
break;
case 32:
dst32 = (uint32_t *)dst;
len /= 4;
for (i = 0; i < len; i++)
dst32[i] = data;
break;
}
}
static void
boot_fb_cpy(uint8_t *dst, uint8_t *src, uint32_t len)
{
uint16_t *dst16, *src16;
uint32_t *dst32, *src32;
switch (fb_info.depth) {
case 24:
case 8:
default:
if (dst <= src) {
do {
*dst++ = *src++;
} while (--len != 0);
} else {
dst += len;
src += len;
do {
*--dst = *--src;
} while (--len != 0);
}
break;
case 15:
case 16:
dst16 = (uint16_t *)dst;
src16 = (uint16_t *)src;
len /= 2;
if (dst16 <= src16) {
do {
*dst16++ = *src16++;
} while (--len != 0);
} else {
dst16 += len;
src16 += len;
do {
*--dst16 = *--src16;
} while (--len != 0);
}
break;
case 32:
dst32 = (uint32_t *)dst;
src32 = (uint32_t *)src;
len /= 4;
if (dst32 <= src32) {
do {
*dst32++ = *src32++;
} while (--len != 0);
} else {
dst32 += len;
src32 += len;
do {
*--dst32 = *--src32;
} while (--len != 0);
}
break;
}
}
void
boot_fb_shadow_init(bootops_t *bops)
{
if (boot_console_type(NULL) != CONS_FRAMEBUFFER)
return;
fb_info.shadow_fb = (uint8_t *)bops->bsys_alloc(NULL, NULL,
fb_info.fb_size, MMU_PAGESIZE);
if (fb_info.shadow_fb == NULL)
return;
boot_fb_cpy(fb_info.shadow_fb, fb_info.fb, fb_info.fb_size);
}
void
boot_get_color(uint32_t *fg, uint32_t *bg)
{
if (fb_info.inverse == B_TRUE ||
fb_info.inverse_screen == B_TRUE) {
if (fb_info.fg_color < XLATE_NCOLORS) {
if (fb_info.fg_color == pc_white)
*bg = brt_xlate[fb_info.fg_color];
else
*bg = dim_xlate[fb_info.fg_color];
} else {
*bg = fb_info.fg_color;
}
if (fb_info.bg_color < XLATE_NCOLORS) {
if (fb_info.bg_color == pc_white)
*fg = brt_xlate[fb_info.bg_color];
else
*fg = dim_xlate[fb_info.bg_color];
} else {
*fg = fb_info.bg_color;
}
} else {
if (fb_info.fg_color < XLATE_NCOLORS) {
if (fb_info.fg_color == pc_white)
*fg = brt_xlate[fb_info.fg_color];
else
*fg = dim_xlate[fb_info.fg_color];
} else {
*fg = fb_info.fg_color;
}
if (fb_info.bg_color < XLATE_NCOLORS) {
if (fb_info.bg_color == pc_white)
*bg = brt_xlate[fb_info.bg_color];
else
*bg = dim_xlate[fb_info.bg_color];
} else {
*bg = fb_info.bg_color;
}
}
}
uint32_t
boot_color_map(uint8_t index)
{
if (fb_info.fb_type != FB_TYPE_RGB) {
if (index < nitems(solaris_color_to_pc_color))
return (solaris_color_to_pc_color[index]);
else
return (index);
}
return (rgb_color_map(&fb_info.rgb, index, 0));
}
void
boot_fb_init(int console)
{
fb_info_pixel_coord_t window;
fb_info.fb = (uint8_t *)(uintptr_t)fb_info.paddr;
boot_fb_set_font(fb_info.screen.y, fb_info.screen.x);
window.x = (fb_info.screen.x -
fb_info.terminal.x * boot_fb_font.vf_width) / 2;
window.y = (fb_info.screen.y -
fb_info.terminal.y * boot_fb_font.vf_height) / 2;
fb_info.terminal_origin.x = window.x;
fb_info.terminal_origin.y = window.y;
#if defined(_BOOT)
if (fb_info.cursor.pos.x != 0 || fb_info.cursor.pos.y != 0) {
fb_info.cursor.origin.x = window.x +
fb_info.cursor.pos.x * boot_fb_font.vf_width;
fb_info.cursor.origin.y = window.y +
fb_info.cursor.pos.y * boot_fb_font.vf_height;
}
#endif
if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) {
fb_info.cursor.origin.x = window.x;
fb_info.cursor.origin.y = window.y;
}
if (fb_info.cursor.pos.x > fb_info.terminal.x ||
fb_info.cursor.pos.y > fb_info.terminal.y ||
fb_info.cursor.origin.x > fb_info.screen.x ||
fb_info.cursor.origin.y > fb_info.screen.y) {
fb_info.cursor.origin.x = window.x;
fb_info.cursor.origin.y = window.y;
fb_info.cursor.pos.x = 0;
fb_info.cursor.pos.y = 0;
}
#if defined(_BOOT)
if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) {
uint32_t fg, bg, toffset;
uint16_t y;
boot_get_color(&fg, &bg);
bg = boot_color_map(bg);
toffset = 0;
for (y = 0; y < fb_info.screen.y; y++) {
uint8_t *dest = fb_info.fb + toffset;
boot_fb_fill(dest, bg, fb_info.pitch);
toffset += fb_info.pitch;
}
}
#endif
}
static void
boot_fb_blit(struct vis_consdisplay *rect)
{
uint32_t offset, size;
uint8_t *fbp, *sfbp = NULL;
int i;
if (rect->col >= fb_info.screen.x ||
rect->row >= fb_info.screen.y ||
rect->col + rect->width >= fb_info.screen.x ||
rect->row + rect->height >= fb_info.screen.y)
return;
size = rect->width * fb_info.bpp;
offset = rect->col * fb_info.bpp + rect->row * fb_info.pitch;
fbp = fb_info.fb + offset;
if (fb_info.shadow_fb != NULL)
sfbp = fb_info.shadow_fb + offset;
for (i = 0; i < rect->height; i++) {
uint8_t *dest = fbp + i * fb_info.pitch;
uint8_t *src = rect->data + i * size;
boot_fb_cpy(dest, src, size);
if (sfbp != NULL) {
dest = sfbp + i * fb_info.pitch;
boot_fb_cpy(dest, src, size);
}
}
}
static void
bit_to_pix(uchar_t c)
{
uint32_t fg, bg;
boot_get_color(&fg, &bg);
fg = boot_color_map(fg);
bg = boot_color_map(bg);
switch (fb_info.depth) {
case 8:
font_bit_to_pix8(&boot_fb_font, (uint8_t *)glyph, c, fg, bg);
break;
case 15:
case 16:
font_bit_to_pix16(&boot_fb_font, (uint16_t *)glyph, c,
(uint16_t)fg, (uint16_t)bg);
break;
case 24:
font_bit_to_pix24(&boot_fb_font, (uint8_t *)glyph, c, fg, bg);
break;
case 32:
font_bit_to_pix32(&boot_fb_font, (uint32_t *)glyph, c, fg, bg);
break;
}
}
static void
boot_fb_eraseline_impl(uint16_t x, uint16_t y)
{
uint32_t toffset, size;
uint32_t fg, bg;
uint8_t *dst, *sdst;
int i;
boot_get_color(&fg, &bg);
bg = boot_color_map(bg);
size = fb_info.terminal.x * boot_fb_font.vf_width * fb_info.bpp;
toffset = x * fb_info.bpp + y * fb_info.pitch;
dst = fb_info.fb + toffset;
sdst = fb_info.shadow_fb + toffset;
for (i = 0; i < boot_fb_font.vf_height; i++) {
uint8_t *dest = dst + i * fb_info.pitch;
if (fb_info.fb + fb_info.fb_size >= dest + size)
boot_fb_fill(dest, bg, size);
if (fb_info.shadow_fb != NULL) {
dest = sdst + i * fb_info.pitch;
if (fb_info.shadow_fb + fb_info.fb_size >=
dest + size) {
boot_fb_fill(dest, bg, size);
}
}
}
}
static void
boot_fb_eraseline(void)
{
boot_fb_eraseline_impl(fb_info.cursor.origin.x,
fb_info.cursor.origin.y);
}
static void
boot_fb_conscopy(struct vis_conscopy *c_copy)
{
uint32_t soffset, toffset;
uint32_t width, height, increment;
uint8_t *src, *dst, *sdst = NULL;
int i;
soffset = c_copy->s_col * fb_info.bpp + c_copy->s_row * fb_info.pitch;
toffset = c_copy->t_col * fb_info.bpp + c_copy->t_row * fb_info.pitch;
src = fb_info.fb + soffset;
dst = fb_info.fb + toffset;
if (fb_info.shadow_fb != NULL) {
src = fb_info.shadow_fb + soffset;
sdst = fb_info.shadow_fb + toffset;
}
width = (c_copy->e_col - c_copy->s_col + 1) * fb_info.bpp;
height = c_copy->e_row - c_copy->s_row + 1;
for (i = 0; i < height; i++) {
increment = i * fb_info.pitch;
if (soffset + increment + width >= fb_info.fb_size ||
toffset + increment + width >= fb_info.fb_size)
break;
boot_fb_cpy(dst + increment, src + increment, width);
if (sdst != NULL)
boot_fb_cpy(sdst + increment, src + increment, width);
}
}
static void
boot_fb_shiftline(int chars)
{
struct vis_conscopy c_copy;
c_copy.s_col = fb_info.cursor.origin.x;
c_copy.s_row = fb_info.cursor.origin.y;
c_copy.e_col = (fb_info.terminal.x - chars) * boot_fb_font.vf_width;
c_copy.e_col += fb_info.terminal_origin.x;
c_copy.e_row = c_copy.s_row + boot_fb_font.vf_height;
c_copy.t_col = fb_info.cursor.origin.x + chars * boot_fb_font.vf_width;
c_copy.t_row = fb_info.cursor.origin.y;
boot_fb_conscopy(&c_copy);
}
static void
boot_fb_scroll(void)
{
struct vis_conscopy c_copy;
c_copy.s_row = fb_info.terminal_origin.y + boot_fb_font.vf_height;
c_copy.s_col = fb_info.terminal_origin.x;
c_copy.e_row = fb_info.screen.y - fb_info.terminal_origin.y;
c_copy.e_col = fb_info.screen.x - fb_info.terminal_origin.x;
c_copy.t_row = fb_info.terminal_origin.y;
c_copy.t_col = fb_info.terminal_origin.x;
boot_fb_conscopy(&c_copy);
boot_fb_eraseline_impl(fb_info.terminal_origin.x,
fb_info.terminal_origin.y +
(fb_info.terminal.y - 1) * boot_fb_font.vf_height);
}
void
boot_fb_cursor(boolean_t visible)
{
uint32_t offset, size, j;
uint32_t *fb32, *sfb32 = NULL;
uint32_t fg, bg;
uint16_t *fb16, *sfb16 = NULL;
uint8_t *fb8, *sfb8 = NULL;
int i, pitch;
if (fb_info.cursor.visible == visible)
return;
boot_get_color(&fg, &bg);
fg = boot_color_map(fg);
bg = boot_color_map(bg);
fb_info.cursor.visible = visible;
pitch = fb_info.pitch;
size = boot_fb_font.vf_width * fb_info.bpp;
offset = fb_info.cursor.origin.x * fb_info.bpp +
fb_info.cursor.origin.y * pitch;
switch (fb_info.depth) {
case 8:
for (i = 0; i < boot_fb_font.vf_height; i++) {
fb8 = fb_info.fb + offset + i * pitch;
if (fb_info.shadow_fb != NULL)
sfb8 = fb_info.shadow_fb + offset + i * pitch;
for (j = 0; j < size; j += 1) {
fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
if (sfb8 == NULL)
continue;
sfb8[j] = (sfb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
}
}
break;
case 15:
case 16:
for (i = 0; i < boot_fb_font.vf_height; i++) {
fb16 = (uint16_t *)(fb_info.fb + offset + i * pitch);
if (fb_info.shadow_fb != NULL)
sfb16 = (uint16_t *)
(fb_info.shadow_fb + offset + i * pitch);
for (j = 0; j < boot_fb_font.vf_width; j++) {
fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^
(bg & 0xffff);
if (sfb16 == NULL)
continue;
sfb16[j] = (sfb16[j] ^ (fg & 0xffff)) ^
(bg & 0xffff);
}
}
break;
case 24:
for (i = 0; i < boot_fb_font.vf_height; i++) {
fb8 = fb_info.fb + offset + i * pitch;
if (fb_info.shadow_fb != NULL)
sfb8 = fb_info.shadow_fb + offset + i * pitch;
for (j = 0; j < size; j += 3) {
fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^
((bg >> 16) & 0xff);
fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^
((bg >> 8) & 0xff);
fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^
(bg & 0xff);
if (sfb8 == NULL)
continue;
sfb8[j] = (sfb8[j] ^ ((fg >> 16) & 0xff)) ^
((bg >> 16) & 0xff);
sfb8[j+1] = (sfb8[j+1] ^ ((fg >> 8) & 0xff)) ^
((bg >> 8) & 0xff);
sfb8[j+2] = (sfb8[j+2] ^ (fg & 0xff)) ^
(bg & 0xff);
}
}
break;
case 32:
for (i = 0; i < boot_fb_font.vf_height; i++) {
fb32 = (uint32_t *)(fb_info.fb + offset + i * pitch);
if (fb_info.shadow_fb != NULL) {
sfb32 = (uint32_t *)
(fb_info.shadow_fb + offset + i * pitch);
}
for (j = 0; j < boot_fb_font.vf_width; j++) {
fb32[j] = (fb32[j] ^ fg) ^ bg;
if (sfb32 == NULL)
continue;
sfb32[j] = (sfb32[j] ^ fg) ^ bg;
}
}
break;
}
}
static void
boot_fb_setpos(int row, int col)
{
if (row < 0)
row = 0;
if (row >= fb_info.terminal.y)
row = fb_info.terminal.y - 1;
if (col < 0)
col = 0;
if (col >= fb_info.terminal.x)
col = fb_info.terminal.x - 1;
fb_info.cursor.pos.x = col;
fb_info.cursor.pos.y = row;
fb_info.cursor.origin.x = fb_info.terminal_origin.x;
fb_info.cursor.origin.x += col * boot_fb_font.vf_width;
fb_info.cursor.origin.y = fb_info.terminal_origin.y;
fb_info.cursor.origin.y += row * boot_fb_font.vf_height;
}
static void
boot_fb_putchar(int c)
{
struct vis_consdisplay display;
int rows, cols;
rows = fb_info.cursor.pos.y;
cols = fb_info.cursor.pos.x;
if (c == '\n') {
if (rows < fb_info.terminal.y - 1)
boot_fb_setpos(rows + 1, cols);
else
boot_fb_scroll();
return;
}
bit_to_pix(c);
display.col = fb_info.cursor.origin.x;
display.row = fb_info.cursor.origin.y;
display.width = boot_fb_font.vf_width;
display.height = boot_fb_font.vf_height;
display.data = glyph;
boot_fb_blit(&display);
if (cols < fb_info.terminal.x - 1)
boot_fb_setpos(rows, cols + 1);
else if (rows < fb_info.terminal.y - 1)
boot_fb_setpos(rows + 1, 0);
else {
boot_fb_setpos(rows, 0);
boot_fb_scroll();
}
}