#include #include #include #include #include #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(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(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(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(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(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