Create a new framebuffer driver. Also hackily passing frame buffer size in the list of init handles to all processes and mapping the framebuffer into all processes. Changed bootloader passing frame buffer as a module to its own struct.
402 lines
8.0 KiB
C++
402 lines
8.0 KiB
C++
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include <uefi/types.h>
|
|
#include <uefi/graphics.h>
|
|
#include <uefi/protos/graphics_output.h>
|
|
|
|
#include "console.h"
|
|
#include "error.h"
|
|
|
|
#ifndef GIT_VERSION_WIDE
|
|
#define GIT_VERSION_WIDE L"no version"
|
|
#endif
|
|
|
|
namespace boot {
|
|
|
|
size_t ROWS = 0;
|
|
size_t COLS = 0;
|
|
|
|
static constexpr int level_ok = 0;
|
|
static constexpr int level_warn = 1;
|
|
static constexpr int level_fail = 2;
|
|
|
|
static const wchar_t *level_tags[] = {
|
|
L" ok ",
|
|
L" warn ",
|
|
L"failed"
|
|
};
|
|
static const uefi::attribute level_colors[] = {
|
|
uefi::attribute::green,
|
|
uefi::attribute::brown,
|
|
uefi::attribute::light_red
|
|
};
|
|
|
|
console *console::s_console = nullptr;
|
|
status_line *status_line::s_current = nullptr;
|
|
|
|
static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5',
|
|
u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
|
|
|
|
|
|
static size_t
|
|
wstrlen(const wchar_t *s)
|
|
{
|
|
size_t count = 0;
|
|
while (s && *s++) count++;
|
|
return count;
|
|
}
|
|
|
|
|
|
console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out) :
|
|
m_rows {0},
|
|
m_cols {0},
|
|
m_out {out},
|
|
m_fb {0, 0}
|
|
{
|
|
pick_mode(bs);
|
|
|
|
try_or_raise(
|
|
m_out->query_mode(m_out->mode->mode, &m_cols, &m_rows),
|
|
L"Failed to get text output mode.");
|
|
|
|
try_or_raise(
|
|
m_out->clear_screen(),
|
|
L"Failed to clear screen");
|
|
|
|
m_out->set_attribute(uefi::attribute::light_cyan);
|
|
m_out->output_string(L"jsix loader ");
|
|
|
|
m_out->set_attribute(uefi::attribute::light_magenta);
|
|
m_out->output_string(GIT_VERSION_WIDE);
|
|
|
|
m_out->set_attribute(uefi::attribute::light_gray);
|
|
m_out->output_string(L" booting...\r\n\n");
|
|
|
|
s_console = this;
|
|
}
|
|
|
|
void
|
|
console::pick_mode(uefi::boot_services *bs)
|
|
{
|
|
uefi::status status;
|
|
uefi::protos::graphics_output *gfx_out_proto;
|
|
uefi::guid guid = uefi::protos::graphics_output::guid;
|
|
|
|
try_or_raise(
|
|
bs->locate_protocol(&guid, nullptr, (void **)&gfx_out_proto),
|
|
L"Failed to find a Graphics Output Protocol handle");
|
|
|
|
const uint32_t modes = gfx_out_proto->mode->max_mode;
|
|
uint32_t best = gfx_out_proto->mode->mode;
|
|
|
|
uefi::graphics_output_mode_info *info =
|
|
(uefi::graphics_output_mode_info *)gfx_out_proto->mode;
|
|
|
|
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
|
|
int pixmode = static_cast<int>(info->pixel_format);
|
|
|
|
for (uint32_t i = 0; i < modes; ++i) {
|
|
size_t size = 0;
|
|
|
|
try_or_raise(
|
|
gfx_out_proto->query_mode(i, &size, &info),
|
|
L"Failed to find a graphics mode the driver claimed to support");
|
|
|
|
#ifdef MAX_HRES
|
|
if (info->horizontal_resolution > MAX_HRES) continue;
|
|
#endif
|
|
|
|
const uint32_t new_res = info->horizontal_resolution * info->vertical_resolution;
|
|
int new_pixmode = static_cast<int>(info->pixel_format);
|
|
|
|
if (new_pixmode <= pixmode && new_res >= res) {
|
|
best = i;
|
|
res = new_res;
|
|
pixmode = new_pixmode;
|
|
}
|
|
}
|
|
|
|
try_or_raise(
|
|
gfx_out_proto->set_mode(best),
|
|
L"Failed to set graphics mode");
|
|
|
|
if (pixmode <= static_cast<int>(uefi::pixel_format::bgr8)) {
|
|
m_fb.phys_addr = gfx_out_proto->mode->frame_buffer_base;
|
|
m_fb.size = gfx_out_proto->mode->frame_buffer_size;
|
|
m_fb.vertical = gfx_out_proto->mode->info->vertical_resolution;
|
|
m_fb.horizontal = gfx_out_proto->mode->info->horizontal_resolution;
|
|
m_fb.scanline = gfx_out_proto->mode->info->pixels_per_scanline;
|
|
m_fb.type = static_cast<kernel::args::fb_type>(pixmode);
|
|
}
|
|
}
|
|
|
|
size_t
|
|
console::print_hex(uint32_t n) const
|
|
{
|
|
wchar_t buffer[9];
|
|
wchar_t *p = buffer;
|
|
for (int i = 7; i >= 0; --i) {
|
|
uint8_t nibble = (n >> (i*4)) & 0xf;
|
|
*p++ = digits[nibble];
|
|
}
|
|
*p = 0;
|
|
m_out->output_string(buffer);
|
|
return 8;
|
|
}
|
|
|
|
size_t
|
|
console::print_long_hex(uint64_t n) const
|
|
{
|
|
wchar_t buffer[17];
|
|
wchar_t *p = buffer;
|
|
for (int i = 15; i >= 0; --i) {
|
|
uint8_t nibble = (n >> (i*4)) & 0xf;
|
|
*p++ = digits[nibble];
|
|
}
|
|
*p = 0;
|
|
m_out->output_string(buffer);
|
|
return 16;
|
|
}
|
|
|
|
size_t
|
|
console::print_dec(uint32_t n) const
|
|
{
|
|
wchar_t buffer[11];
|
|
wchar_t *p = buffer + 10;
|
|
*p-- = 0;
|
|
do {
|
|
*p-- = digits[n % 10];
|
|
n /= 10;
|
|
} while (n != 0);
|
|
|
|
m_out->output_string(++p);
|
|
return 10 - (p - buffer);
|
|
}
|
|
|
|
size_t
|
|
console::print_long_dec(uint64_t n) const
|
|
{
|
|
wchar_t buffer[21];
|
|
wchar_t *p = buffer + 20;
|
|
*p-- = 0;
|
|
do {
|
|
*p-- = digits[n % 10];
|
|
n /= 10;
|
|
} while (n != 0);
|
|
|
|
m_out->output_string(++p);
|
|
return 20 - (p - buffer);
|
|
}
|
|
|
|
size_t
|
|
console::vprintf(const wchar_t *fmt, va_list args) const
|
|
{
|
|
wchar_t buffer[256];
|
|
const wchar_t *r = fmt;
|
|
wchar_t *w = buffer;
|
|
size_t count = 0;
|
|
|
|
while (r && *r) {
|
|
if (*r != L'%') {
|
|
count++;
|
|
*w++ = *r++;
|
|
continue;
|
|
}
|
|
|
|
*w = 0;
|
|
m_out->output_string(buffer);
|
|
w = buffer;
|
|
|
|
r++; // chomp the %
|
|
|
|
switch (*r++) {
|
|
case L'%':
|
|
m_out->output_string(const_cast<wchar_t*>(L"%"));
|
|
count++;
|
|
break;
|
|
|
|
case L'x':
|
|
count += print_hex(va_arg(args, uint32_t));
|
|
break;
|
|
|
|
case L'd':
|
|
case L'u':
|
|
count += print_dec(va_arg(args, uint32_t));
|
|
break;
|
|
|
|
case L's':
|
|
{
|
|
wchar_t *s = va_arg(args, wchar_t*);
|
|
count += wstrlen(s);
|
|
m_out->output_string(s);
|
|
}
|
|
break;
|
|
|
|
case L'l':
|
|
switch (*r++) {
|
|
case L'x':
|
|
count += print_long_hex(va_arg(args, uint64_t));
|
|
break;
|
|
|
|
case L'd':
|
|
case L'u':
|
|
count += print_long_dec(va_arg(args, uint64_t));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
*w = 0;
|
|
m_out->output_string(buffer);
|
|
return count;
|
|
}
|
|
|
|
size_t
|
|
console::printf(const wchar_t *fmt, ...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
size_t result = vprintf(fmt, args);
|
|
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
size_t
|
|
console::print(const wchar_t *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
size_t result = get().vprintf(fmt, args);
|
|
|
|
va_end(args);
|
|
return result;
|
|
}
|
|
|
|
status_line::status_line(const wchar_t *message, const wchar_t *context) :
|
|
m_level(level_ok)
|
|
{
|
|
auto out = console::get().m_out;
|
|
m_line = out->mode->cursor_row;
|
|
m_depth = (s_current ? 1 + s_current->m_depth : 0);
|
|
|
|
int indent = 2 * m_depth;
|
|
out->set_cursor_position(indent, m_line);
|
|
out->set_attribute(uefi::attribute::light_gray);
|
|
out->output_string(message);
|
|
|
|
if (context) {
|
|
out->output_string(L": ");
|
|
out->output_string(context);
|
|
}
|
|
|
|
out->output_string(L"\r\n");
|
|
|
|
m_next = s_current;
|
|
s_current = this;
|
|
}
|
|
|
|
status_line::~status_line()
|
|
{
|
|
if (s_current != this)
|
|
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
|
|
|
|
finish();
|
|
if (m_next && m_level > m_next->m_level) {
|
|
m_next->m_level = m_level;
|
|
m_next->print_status_tag();
|
|
}
|
|
s_current = m_next;
|
|
}
|
|
|
|
void
|
|
status_line::print_status_tag()
|
|
{
|
|
auto out = console::get().m_out;
|
|
int row = out->mode->cursor_row;
|
|
int col = out->mode->cursor_column;
|
|
|
|
uefi::attribute color = level_colors[m_level];
|
|
const wchar_t *tag = level_tags[m_level];
|
|
|
|
out->set_cursor_position(50, m_line);
|
|
|
|
out->set_attribute(uefi::attribute::light_gray);
|
|
out->output_string(L"[");
|
|
out->set_attribute(color);
|
|
out->output_string(tag);
|
|
out->set_attribute(uefi::attribute::light_gray);
|
|
out->output_string(L"]\r\n");
|
|
|
|
out->set_cursor_position(col, row);
|
|
}
|
|
|
|
void
|
|
status_line::do_warn(const wchar_t *message, const wchar_t *error)
|
|
{
|
|
auto out = console::get().m_out;
|
|
int row = out->mode->cursor_row;
|
|
|
|
if (m_level < level_warn) {
|
|
m_level = level_warn;
|
|
print_status_tag();
|
|
}
|
|
|
|
int indent = 2 + 2 * m_depth;
|
|
out->set_cursor_position(indent, row);
|
|
out->set_attribute(uefi::attribute::yellow);
|
|
out->output_string(message);
|
|
|
|
if (error) {
|
|
out->output_string(L": ");
|
|
out->output_string(error);
|
|
}
|
|
|
|
out->set_attribute(uefi::attribute::light_gray);
|
|
out->output_string(L"\r\n");
|
|
}
|
|
|
|
void
|
|
status_line::do_fail(const wchar_t *message, const wchar_t *error)
|
|
{
|
|
auto out = console::get().m_out;
|
|
int row = out->mode->cursor_row;
|
|
|
|
if (s_current->m_level < level_fail) {
|
|
m_level = level_fail;
|
|
print_status_tag();
|
|
}
|
|
|
|
int indent = 2 + 2 * m_depth;
|
|
out->set_cursor_position(indent, row);
|
|
out->set_attribute(uefi::attribute::red);
|
|
out->output_string(message);
|
|
|
|
if (error) {
|
|
out->output_string(L": ");
|
|
out->output_string(error);
|
|
}
|
|
|
|
out->set_attribute(uefi::attribute::light_gray);
|
|
out->output_string(L"\r\n");
|
|
}
|
|
|
|
void
|
|
status_line::finish()
|
|
{
|
|
if (m_level <= level_ok)
|
|
print_status_tag();
|
|
}
|
|
|
|
} // namespace boot
|