[boot] Seperate video out from console

Separate the video mode setting out from the console code into video.*,
and remove the framebuffer from the kernel args, moving it to the new
init args format.
This commit is contained in:
Justin C. Miller
2021-07-27 19:45:34 -07:00
parent 269324c553
commit 1802c5ea2e
12 changed files with 256 additions and 194 deletions

View File

@@ -1,9 +1,6 @@
#include <stddef.h>
#include <stdint.h>
#include <uefi/boot_services.h>
#include <uefi/graphics.h>
#include <uefi/protos/graphics_output.h>
#include <uefi/protos/simple_text_output.h>
#include <uefi/types.h>
@@ -34,22 +31,23 @@ wstrlen(const wchar_t *s)
}
console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out) :
console::console(uefi::protos::simple_text_output *out) :
m_rows {0},
m_cols {0},
m_out {out},
m_fb {0, 0}
m_out {out}
{
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");
s_console = this;
}
void
console::announce()
{
m_out->set_attribute(uefi::attribute::light_cyan);
m_out->output_string(L"jsix loader ");
@@ -58,97 +56,6 @@ console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out)
m_out->set_attribute(uefi::attribute::light_gray);
m_out->output_string(L" booting...\r\n");
if (m_fb.type != kernel::init::fb_type::none) {
wchar_t const * type = nullptr;
switch (m_fb.type) {
case kernel::init::fb_type::rgb8:
type = L"rgb8";
break;
case kernel::init::fb_type::bgr8:
type = L"bgr8";
break;
default:
type = L"unknown";
}
printf(L"Found framebuffer: %dx%d[%d] type %s @0x%x\r\n",
m_fb.horizontal, m_fb.vertical, m_fb.scanline, type, m_fb.phys_addr);
} else {
printf(L"No framebuffer found.\r\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;
m_fb.type = kernel::init::fb_type::none;
uefi::status has_gop = bs->locate_protocol(&guid, nullptr,
(void **)&gfx_out_proto);
if (has_gop != uefi::status::success)
// No video output found, skip it
return;
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;
switch (gfx_out_proto->mode->info->pixel_format) {
case uefi::pixel_format::rgb8:
m_fb.type = kernel::init::fb_type::rgb8;
break;
case uefi::pixel_format::bgr8:
m_fb.type = kernel::init::fb_type::bgr8;
break;
default:
m_fb.type = kernel::init::fb_type::none;
}
}
}
size_t

View File

@@ -1,12 +1,11 @@
#pragma once
/// \file console.h
/// Text output handler
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include "kernel_args.h"
namespace uefi {
struct boot_services;
namespace protos {
struct simple_text_output;
}}
@@ -17,9 +16,9 @@ namespace boot {
class console
{
public:
using framebuffer = kernel::init::framebuffer;
console(uefi::protos::simple_text_output *out);
console(uefi::boot_services *bs, uefi::protos::simple_text_output *out);
void announce();
size_t print_hex(uint32_t n) const;
size_t print_dec(uint32_t n) const;
@@ -27,20 +26,16 @@ public:
size_t print_long_dec(uint64_t n) const;
size_t printf(const wchar_t *fmt, ...) const;
const framebuffer & fb() const { return m_fb; };
static console & get() { return *s_console; }
static size_t print(const wchar_t *fmt, ...);
private:
friend class status_line;
void pick_mode(uefi::boot_services *bs);
size_t vprintf(const wchar_t *fmt, va_list args) const;
size_t m_rows, m_cols;
uefi::protos::simple_text_output *m_out;
framebuffer m_fb;
static console *s_console;
};

View File

@@ -18,6 +18,7 @@
#include "memory_map.h"
#include "paging.h"
#include "status.h"
#include "video.h"
#include "kernel_args.h"
@@ -92,12 +93,12 @@ check_cpu_supported()
init::args *
uefi_preboot(uefi::handle image, uefi::system_table *st)
{
status_line status {L"Performing UEFI pre-boot"};
uefi::boot_services *bs = st->boot_services;
uefi::runtime_services *rs = st->runtime_services;
memory::init_allocator(bs);
status_line status {L"Performing UEFI pre-boot"};
check_cpu_supported();
memory::init_pointer_fixup(bs, rs);
init::args *args = new init::args;
@@ -157,14 +158,18 @@ extern "C" uefi::status
efi_main(uefi::handle image, uefi::system_table *st)
{
using namespace boot;
console con(st->boot_services, st->con_out);
check_cpu_supported();
uefi::boot_services *bs = st->boot_services;
console con(st->con_out);
memory::init_allocator(bs);
video::screen *screen = video::pick_mode(bs);
con.announce();
init::args *args = uefi_preboot(image, st);
memory::efi_mem_map map = uefi_exit(args, image, st->boot_services);
args->video = con.fb();
status_bar status {con.fb()}; // Switch to fb status display
status_bar status {screen}; // Switch to fb status display
// Map the kernel to the appropriate address
init::program &kernel = args->programs[0];

View File

@@ -16,4 +16,5 @@ sources = [
"paging.cpp",
"status.cpp",
"support.cpp",
"video.cpp",
]

View File

@@ -4,8 +4,9 @@
#include "console.h"
#include "error.h"
#include "kernel_args.h"
#include "init_args.h"
#include "status.h"
#include "video.h"
constexpr int num_boxes = 30;
@@ -149,15 +150,15 @@ status_line::do_fail(const wchar_t *message, uefi::status status)
status_bar::status_bar(kernel::init::framebuffer const &fb) :
status(fb.size),
status_bar::status_bar(video::screen *screen) :
status(),
m_outer(nullptr)
{
m_size = (fb.vertical / num_boxes) - 1;
m_top = fb.vertical - m_size;
m_horiz = fb.horizontal;
m_fb = reinterpret_cast<uint32_t*>(fb.phys_addr);
m_type = static_cast<uint16_t>(fb.type);
m_size = (screen->mode.vertical / num_boxes) - 1;
m_top = screen->mode.vertical - m_size;
m_horiz = screen->mode.horizontal;
m_fb = reinterpret_cast<uint32_t*>(screen->framebuffer.pointer);
m_type = static_cast<uint16_t>(screen->mode.layout);
next();
if (status::s_current_type == status_bar::type)
@@ -197,14 +198,14 @@ status_bar::do_fail(const wchar_t *message, uefi::status status)
static uint32_t
make_color(uint8_t r, uint8_t g, uint8_t b, uint16_t type)
{
switch (static_cast<kernel::init::fb_type>(type)) {
case kernel::init::fb_type::bgr8:
switch (static_cast<video::layout>(type)) {
case video::layout::bgr8:
return
(static_cast<uint32_t>(b) << 0) |
(static_cast<uint32_t>(g) << 8) |
(static_cast<uint32_t>(r) << 16);
case kernel::init::fb_type::rgb8:
case video::layout::rgb8:
return
(static_cast<uint32_t>(r) << 0) |
(static_cast<uint32_t>(g) << 8) |

View File

@@ -5,14 +5,16 @@
#include <stdint.h>
#include <uefi/types.h>
namespace kernel {
namespace init {
class framebuffer;
}
namespace uefi {
struct boot_services;
}
namespace boot {
namespace video {
struct screen;
}
// Abstract base class for status reporters.
class status
{
@@ -92,11 +94,8 @@ class status_bar :
public:
constexpr static unsigned type = 2;
using framebuffer = kernel::init::framebuffer;
/// Constructor.
/// \arg fb The framebuffer descriptor to draw to
status_bar(kernel::init::framebuffer const &fb);
status_bar(video::screen *screen);
~status_bar();
virtual void do_warn(const wchar_t *message, uefi::status status) override;

121
src/boot/video.cpp Normal file
View File

@@ -0,0 +1,121 @@
#include <uefi/boot_services.h>
#include <uefi/graphics.h>
#include <uefi/protos/graphics_output.h>
#include "console.h"
#include "error.h"
#include "init_args.h"
#include "video.h"
namespace boot {
namespace video {
using kernel::init::fb_layout;
using kernel::init::fb_type;
using kernel::init::module_flags;
using kernel::init::module_framebuffer;
using kernel::init::module_type;
static uefi::protos::graphics_output *
get_gop(uefi::boot_services *bs)
{
uefi::protos::graphics_output *gop = nullptr;
uefi::guid guid = uefi::protos::graphics_output::guid;
uefi::status has_gop = bs->locate_protocol(&guid, nullptr,
(void **)&gop);
if (has_gop != uefi::status::success)
return nullptr;
return gop;
}
screen *
pick_mode(uefi::boot_services *bs)
{
uefi::protos::graphics_output *gop = get_gop(bs);
if (!gop) {
console::print(L"No framebuffer found.\r\n");
return nullptr;
}
uefi::graphics_output_mode_info *info = gop->mode->info;
uint32_t best = gop->mode->mode;
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
int pixmode = static_cast<int>(info->pixel_format);
const uint32_t modes = gop->mode->max_mode;
for (uint32_t i = 0; i < modes; ++i) {
size_t size = 0;
uefi::graphics_output_mode_info *new_info = nullptr;
try_or_raise(
gop->query_mode(i, &size, &new_info),
L"Failed to find a graphics mode the driver claimed to support");
const uint32_t new_res = new_info->horizontal_resolution * new_info->vertical_resolution;
int new_pixmode = static_cast<int>(new_info->pixel_format);
if (new_pixmode <= pixmode && new_res >= res) {
best = i;
res = new_res;
pixmode = new_pixmode;
}
}
screen *s = new screen;
s->mode = {
.vertical = gop->mode->info->vertical_resolution,
.horizontal = gop->mode->info->horizontal_resolution,
.scanline = gop->mode->info->pixels_per_scanline,
.layout = layout::unknown,
};
s->framebuffer = {
.pointer = reinterpret_cast<void*>(gop->mode->frame_buffer_base),
.count = gop->mode->frame_buffer_size
};
wchar_t const * type = nullptr;
switch (info->pixel_format) {
case uefi::pixel_format::rgb8:
type = L"rgb8";
s->mode.layout = layout::rgb8;
break;
case uefi::pixel_format::bgr8:
type = L"bgr8";
s->mode.layout = layout::bgr8;
break;
default:
type = L"unknown";
}
console::print(L"Found framebuffer: %dx%d[%d] type %s @0x%x\r\n",
info->horizontal_resolution, info->vertical_resolution,
info->pixels_per_scanline, type, gop->mode->frame_buffer_base);
try_or_raise(
gop->set_mode(best),
L"Failed to set graphics mode");
return s;
}
void
make_module(screen *s, module_framebuffer *mod)
{
mod->mod_type = module_type::framebuffer;
mod->mod_flags = module_flags::none;
mod->mod_length = sizeof(module_framebuffer);
mod->type = fb_type::uefi;
mod->framebuffer = s->framebuffer;
mod->mode = s->mode;
}
} // namespace video
} // namespace boot

32
src/boot/video.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
/// \file video.h
/// Video mode handling
#include <stdarg.h>
#include <stddef.h>
#include "init_args.h"
namespace uefi {
struct boot_services;
}
namespace boot {
namespace video {
using kernel::init::video_mode;
using layout = kernel::init::fb_layout;
struct screen {
buffer framebuffer;
video_mode mode;
};
/// Pick the best video mode and set up the screen
screen * pick_mode(uefi::boot_services *bs);
/// Make an init arg module from the video mode
void make_module(screen *s, kernel::init::module_framebuffer *mod);
} // namespace video
} // namespace boot