[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 <stddef.h>
#include <stdint.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/protos/simple_text_output.h>
#include <uefi/types.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_rows {0},
m_cols {0}, m_cols {0},
m_out {out}, m_out {out}
m_fb {0, 0}
{ {
pick_mode(bs);
try_or_raise( try_or_raise(
m_out->query_mode(m_out->mode->mode, &m_cols, &m_rows), m_out->query_mode(m_out->mode->mode, &m_cols, &m_rows),
L"Failed to get text output mode."); L"Failed to get text output mode.");
try_or_raise( try_or_raise(
m_out->clear_screen(), m_out->clear_screen(),
L"Failed to clear screen"); L"Failed to clear screen");
s_console = this;
}
void
console::announce()
{
m_out->set_attribute(uefi::attribute::light_cyan); m_out->set_attribute(uefi::attribute::light_cyan);
m_out->output_string(L"jsix loader "); 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->set_attribute(uefi::attribute::light_gray);
m_out->output_string(L" booting...\r\n"); 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 size_t

View File

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

View File

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

View File

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

View File

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

View File

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

59
src/include/init_args.h Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "counted.h"
namespace kernel {
namespace init {
enum class module_type : uint8_t {
program,
framebuffer,
};
enum class module_flags : uint8_t { none = 0 };
struct module
{
module_type mod_type;
module_flags mod_flags;
uint32_t mod_length;
};
struct module_program :
public module
{
uintptr_t base_address;
char filename[];
};
enum class fb_layout : uint8_t { rgb8, bgr8, unknown = 0xff };
enum class fb_type : uint8_t { uefi };
struct video_mode
{
uint32_t vertical;
uint32_t horizontal;
uint32_t scanline;
fb_layout layout;
};
struct module_framebuffer :
public module
{
buffer framebuffer;
video_mode mode;
fb_type type;
};
struct modules_page
{
uint8_t count;
module *modules;
modules_page *next;
};
} // namespace init
} // namespace kernel

View File

@@ -25,21 +25,6 @@ struct module {
mod_type type; mod_type type;
}; };
enum class fb_type : uint16_t {
none,
rgb8,
bgr8
};
struct framebuffer {
uintptr_t phys_addr;
size_t size;
uint32_t vertical;
uint32_t horizontal;
uint16_t scanline;
fb_type type;
};
enum class section_flags : uint32_t { enum class section_flags : uint32_t {
none = 0, none = 0,
execute = 1, execute = 1,
@@ -156,8 +141,6 @@ struct args
void *runtime_services; void *runtime_services;
void *acpi_table; void *acpi_table;
framebuffer video;
} }
__attribute__((aligned(alignof(max_align_t)))); __attribute__((aligned(alignof(max_align_t))));

View File

@@ -57,9 +57,6 @@ process * load_simple_process(init::program &program);
unsigned start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4); unsigned start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4);
/// TODO: not this. this is awful.
init::framebuffer *fb = nullptr;
void void
init_console() init_console()
{ {
@@ -107,22 +104,6 @@ kernel_main(init::args *args)
uint64_t efer = rdmsr(msr::ia32_efer); uint64_t efer = rdmsr(msr::ia32_efer);
log::debug(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0, cr4, efer); log::debug(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0, cr4, efer);
bool has_video = false;
if (args->video.size > 0) {
has_video = true;
fb = &args->video;
const init::framebuffer &video = args->video;
log::debug(logs::boot, "Framebuffer: %dx%d[%d] type %d @ %llx size %llx",
video.horizontal,
video.vertical,
video.scanline,
video.type,
video.phys_addr,
video.size);
logger_clear_immediate();
}
extern IDT &g_bsp_idt; extern IDT &g_bsp_idt;
extern TSS &g_bsp_tss; extern TSS &g_bsp_tss;
extern GDT &g_bsp_gdt; extern GDT &g_bsp_gdt;
@@ -216,9 +197,8 @@ kernel_main(init::args *args)
for (unsigned i = 1; i < args->programs.count; ++i) for (unsigned i = 1; i < args->programs.count; ++i)
load_simple_process(args->programs[i]); load_simple_process(args->programs[i]);
if (!has_video)
sched->create_kernel_task(logger_task, scheduler::max_priority/2, true);
sched->create_kernel_task(logger_task, scheduler::max_priority/2, true);
sched->start(); sched->start();
} }

View File

@@ -228,27 +228,6 @@ initialize_main_user_stack()
j6_init_value *initv = nullptr; j6_init_value *initv = nullptr;
unsigned n = 0; unsigned n = 0;
extern init::framebuffer *fb;
if (fb) {
j6_init_framebuffer *fb_desc = push<j6_init_framebuffer>(tcb->rsp3);
kutil::memset(fb_desc, 0, sizeof(j6_init_framebuffer));
fb_desc->addr = fb->phys_addr;
fb_desc->size = fb->size;
fb_desc->vertical = fb->vertical;
fb_desc->horizontal = fb->horizontal;
fb_desc->scanline = fb->scanline;
if (fb->type == kernel::init::fb_type::bgr8)
fb_desc->flags |= 1;
initv = push<j6_init_value>(tcb->rsp3);
initv->type = j6_init_desc_framebuffer;
initv->data = fb_desc;
++n;
}
initv = push<j6_init_value>(tcb->rsp3); initv = push<j6_init_value>(tcb->rsp3);
initv->type = j6_init_handle_other; initv->type = j6_init_handle_other;
initv->handle.type = j6_object_type_system; initv->handle.type = j6_object_type_system;