[project] Lose the battle between tabs & spaces
I'm a tabs guy. I like tabs, it's an elegant way to represent indentation instead of brute-forcing it. But I have to admit that the world seems to be going towards spaces, and tooling tends not to play nice with tabs. So here we go, changing the whole repo to spaces since I'm getting tired of all the inconsistent formatting.
This commit is contained in:
committed by
Justin C. Miller
parent
d36b2d8057
commit
8f529046a9
@@ -24,139 +24,139 @@ static_assert(sizeof(allocation_register) == page_size);
|
|||||||
|
|
||||||
void
|
void
|
||||||
allocator::init(
|
allocator::init(
|
||||||
allocation_register *&allocs,
|
allocation_register *&allocs,
|
||||||
modules_page *&modules,
|
modules_page *&modules,
|
||||||
uefi::boot_services *bs)
|
uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
new (&g_alloc) allocator(*bs);
|
new (&g_alloc) allocator(*bs);
|
||||||
allocs = g_alloc.m_register;
|
allocs = g_alloc.m_register;
|
||||||
modules = g_alloc.m_modules;
|
modules = g_alloc.m_modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
allocator::allocator(uefi::boot_services &bs) :
|
allocator::allocator(uefi::boot_services &bs) :
|
||||||
m_bs(bs),
|
m_bs(bs),
|
||||||
m_register(nullptr),
|
m_register(nullptr),
|
||||||
m_modules(nullptr)
|
m_modules(nullptr)
|
||||||
{
|
{
|
||||||
add_register();
|
add_register();
|
||||||
add_modules();
|
add_modules();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
allocator::add_register()
|
allocator::add_register()
|
||||||
{
|
{
|
||||||
allocation_register *reg = nullptr;
|
allocation_register *reg = nullptr;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_bs.allocate_pages(uefi::allocate_type::any_pages,
|
m_bs.allocate_pages(uefi::allocate_type::any_pages,
|
||||||
uefi::memory_type::loader_data, 1, reinterpret_cast<void**>(®)),
|
uefi::memory_type::loader_data, 1, reinterpret_cast<void**>(®)),
|
||||||
L"Failed allocating allocation register page");
|
L"Failed allocating allocation register page");
|
||||||
|
|
||||||
m_bs.set_mem(reg, sizeof(allocation_register), 0);
|
m_bs.set_mem(reg, sizeof(allocation_register), 0);
|
||||||
|
|
||||||
if (m_register)
|
if (m_register)
|
||||||
m_register->next = reg;
|
m_register->next = reg;
|
||||||
|
|
||||||
m_register = reg;
|
m_register = reg;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
allocator::add_modules()
|
allocator::add_modules()
|
||||||
{
|
{
|
||||||
modules_page *mods = reinterpret_cast<modules_page*>(
|
modules_page *mods = reinterpret_cast<modules_page*>(
|
||||||
allocate_pages(1, alloc_type::init_args, true));
|
allocate_pages(1, alloc_type::init_args, true));
|
||||||
|
|
||||||
if (m_modules)
|
if (m_modules)
|
||||||
m_modules->next = reinterpret_cast<uintptr_t>(mods);
|
m_modules->next = reinterpret_cast<uintptr_t>(mods);
|
||||||
|
|
||||||
mods->modules = reinterpret_cast<module*>(mods + 1);
|
mods->modules = reinterpret_cast<module*>(mods + 1);
|
||||||
m_modules = mods;
|
m_modules = mods;
|
||||||
m_next_mod = mods->modules;
|
m_next_mod = mods->modules;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
allocator::allocate_pages(size_t count, alloc_type type, bool zero)
|
allocator::allocate_pages(size_t count, alloc_type type, bool zero)
|
||||||
{
|
{
|
||||||
if (count & ~0xffffffffull) {
|
if (count & ~0xffffffffull) {
|
||||||
error::raise(uefi::status::unsupported,
|
error::raise(uefi::status::unsupported,
|
||||||
L"Cannot allocate more than 16TiB in pages at once.",
|
L"Cannot allocate more than 16TiB in pages at once.",
|
||||||
__LINE__);
|
__LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_register || m_register->count == 0xff)
|
if (!m_register || m_register->count == 0xff)
|
||||||
add_register();
|
add_register();
|
||||||
|
|
||||||
void *pages = nullptr;
|
void *pages = nullptr;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_bs.allocate_pages(uefi::allocate_type::any_pages,
|
m_bs.allocate_pages(uefi::allocate_type::any_pages,
|
||||||
uefi::memory_type::loader_data, count, &pages),
|
uefi::memory_type::loader_data, count, &pages),
|
||||||
L"Failed allocating usable pages");
|
L"Failed allocating usable pages");
|
||||||
|
|
||||||
page_allocation &ent = m_register->entries[m_register->count++];
|
page_allocation &ent = m_register->entries[m_register->count++];
|
||||||
ent.address = reinterpret_cast<uintptr_t>(pages);
|
ent.address = reinterpret_cast<uintptr_t>(pages);
|
||||||
ent.count = count;
|
ent.count = count;
|
||||||
ent.type = type;
|
ent.type = type;
|
||||||
|
|
||||||
if (zero)
|
if (zero)
|
||||||
m_bs.set_mem(pages, count * page_size, 0);
|
m_bs.set_mem(pages, count * page_size, 0);
|
||||||
|
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
module *
|
module *
|
||||||
allocator::allocate_module_untyped(size_t size)
|
allocator::allocate_module_untyped(size_t size)
|
||||||
{
|
{
|
||||||
size_t remaining =
|
size_t remaining =
|
||||||
reinterpret_cast<uintptr_t>(m_modules) + page_size
|
reinterpret_cast<uintptr_t>(m_modules) + page_size
|
||||||
- reinterpret_cast<uintptr_t>(m_next_mod);
|
- reinterpret_cast<uintptr_t>(m_next_mod);
|
||||||
|
|
||||||
if (size > remaining)
|
if (size > remaining)
|
||||||
add_modules();
|
add_modules();
|
||||||
|
|
||||||
++m_modules->count;
|
++m_modules->count;
|
||||||
module *m = m_next_mod;
|
module *m = m_next_mod;
|
||||||
m_next_mod = offset_ptr<module>(m_next_mod, size);
|
m_next_mod = offset_ptr<module>(m_next_mod, size);
|
||||||
|
|
||||||
m->mod_length = size;
|
m->mod_length = size;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
allocator::allocate(size_t size, bool zero)
|
allocator::allocate(size_t size, bool zero)
|
||||||
{
|
{
|
||||||
void *p = nullptr;
|
void *p = nullptr;
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_bs.allocate_pool(uefi::memory_type::loader_data, size, &p),
|
m_bs.allocate_pool(uefi::memory_type::loader_data, size, &p),
|
||||||
L"Could not allocate pool memory");
|
L"Could not allocate pool memory");
|
||||||
|
|
||||||
if (zero)
|
if (zero)
|
||||||
m_bs.set_mem(p, size, 0);
|
m_bs.set_mem(p, size, 0);
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
allocator::free(void *p)
|
allocator::free(void *p)
|
||||||
{
|
{
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_bs.free_pool(p),
|
m_bs.free_pool(p),
|
||||||
L"Freeing pool memory");
|
L"Freeing pool memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
allocator::memset(void *start, size_t size, uint8_t value)
|
allocator::memset(void *start, size_t size, uint8_t value)
|
||||||
{
|
{
|
||||||
m_bs.set_mem(start, size, value);
|
m_bs.set_mem(start, size, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
allocator::copy(void *to, void *from, size_t size)
|
allocator::copy(void *to, void *from, size_t size)
|
||||||
{
|
{
|
||||||
m_bs.copy_mem(to, from, size);
|
m_bs.copy_mem(to, from, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
/// Page allocator class definition
|
/// Page allocator class definition
|
||||||
|
|
||||||
namespace uefi {
|
namespace uefi {
|
||||||
class boot_services;
|
class boot_services;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace init {
|
namespace init {
|
||||||
enum class allocation_type : uint8_t;
|
enum class allocation_type : uint8_t;
|
||||||
struct allocation_register;
|
struct allocation_register;
|
||||||
struct module;
|
struct module;
|
||||||
struct modules_page;
|
struct modules_page;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
@@ -22,46 +22,46 @@ using alloc_type = kernel::init::allocation_type;
|
|||||||
class allocator
|
class allocator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using allocation_register = kernel::init::allocation_register;
|
using allocation_register = kernel::init::allocation_register;
|
||||||
using module = kernel::init::module;
|
using module = kernel::init::module;
|
||||||
using modules_page = kernel::init::modules_page;
|
using modules_page = kernel::init::modules_page;
|
||||||
|
|
||||||
allocator(uefi::boot_services &bs);
|
allocator(uefi::boot_services &bs);
|
||||||
|
|
||||||
void * allocate(size_t size, bool zero = false);
|
void * allocate(size_t size, bool zero = false);
|
||||||
void free(void *p);
|
void free(void *p);
|
||||||
|
|
||||||
void * allocate_pages(size_t count, alloc_type type, bool zero = false);
|
void * allocate_pages(size_t count, alloc_type type, bool zero = false);
|
||||||
|
|
||||||
template <typename M>
|
template <typename M>
|
||||||
M * allocate_module(size_t extra = 0) {
|
M * allocate_module(size_t extra = 0) {
|
||||||
return static_cast<M*>(allocate_module_untyped(sizeof(M) + extra));
|
return static_cast<M*>(allocate_module_untyped(sizeof(M) + extra));
|
||||||
}
|
}
|
||||||
|
|
||||||
void memset(void *start, size_t size, uint8_t value);
|
void memset(void *start, size_t size, uint8_t value);
|
||||||
void copy(void *to, void *from, size_t size);
|
void copy(void *to, void *from, size_t size);
|
||||||
|
|
||||||
inline void zero(void *start, size_t size) { memset(start, size, 0); }
|
inline void zero(void *start, size_t size) { memset(start, size, 0); }
|
||||||
|
|
||||||
/// Initialize the global allocator
|
/// Initialize the global allocator
|
||||||
/// \arg allocs [out] Poiinter to the initial allocation register
|
/// \arg allocs [out] Poiinter to the initial allocation register
|
||||||
/// \arg modules [out] Pointer to the initial modules_page
|
/// \arg modules [out] Pointer to the initial modules_page
|
||||||
/// \arg bs UEFI boot services
|
/// \arg bs UEFI boot services
|
||||||
static void init(
|
static void init(
|
||||||
allocation_register *&allocs,
|
allocation_register *&allocs,
|
||||||
modules_page *&modules,
|
modules_page *&modules,
|
||||||
uefi::boot_services *bs);
|
uefi::boot_services *bs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void add_register();
|
void add_register();
|
||||||
void add_modules();
|
void add_modules();
|
||||||
module * allocate_module_untyped(size_t size);
|
module * allocate_module_untyped(size_t size);
|
||||||
|
|
||||||
uefi::boot_services &m_bs;
|
uefi::boot_services &m_bs;
|
||||||
|
|
||||||
allocation_register *m_register;
|
allocation_register *m_register;
|
||||||
modules_page *m_modules;
|
modules_page *m_modules;
|
||||||
module *m_next_mod;
|
module *m_next_mod;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
|
|||||||
@@ -19,195 +19,195 @@ size_t COLS = 0;
|
|||||||
console *console::s_console = nullptr;
|
console *console::s_console = nullptr;
|
||||||
|
|
||||||
static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5',
|
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'};
|
u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
|
||||||
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
wstrlen(const wchar_t *s)
|
wstrlen(const wchar_t *s)
|
||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
while (s && *s++) count++;
|
while (s && *s++) count++;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
console::console(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}
|
||||||
{
|
{
|
||||||
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;
|
s_console = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
console::announce()
|
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 ");
|
||||||
|
|
||||||
m_out->set_attribute(uefi::attribute::light_magenta);
|
m_out->set_attribute(uefi::attribute::light_magenta);
|
||||||
m_out->output_string(GIT_VERSION_WIDE);
|
m_out->output_string(GIT_VERSION_WIDE);
|
||||||
|
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::print_hex(uint32_t n) const
|
console::print_hex(uint32_t n) const
|
||||||
{
|
{
|
||||||
wchar_t buffer[9];
|
wchar_t buffer[9];
|
||||||
wchar_t *p = buffer;
|
wchar_t *p = buffer;
|
||||||
for (int i = 7; i >= 0; --i) {
|
for (int i = 7; i >= 0; --i) {
|
||||||
uint8_t nibble = (n >> (i*4)) & 0xf;
|
uint8_t nibble = (n >> (i*4)) & 0xf;
|
||||||
*p++ = digits[nibble];
|
*p++ = digits[nibble];
|
||||||
}
|
}
|
||||||
*p = 0;
|
*p = 0;
|
||||||
m_out->output_string(buffer);
|
m_out->output_string(buffer);
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::print_long_hex(uint64_t n) const
|
console::print_long_hex(uint64_t n) const
|
||||||
{
|
{
|
||||||
wchar_t buffer[17];
|
wchar_t buffer[17];
|
||||||
wchar_t *p = buffer;
|
wchar_t *p = buffer;
|
||||||
for (int i = 15; i >= 0; --i) {
|
for (int i = 15; i >= 0; --i) {
|
||||||
uint8_t nibble = (n >> (i*4)) & 0xf;
|
uint8_t nibble = (n >> (i*4)) & 0xf;
|
||||||
*p++ = digits[nibble];
|
*p++ = digits[nibble];
|
||||||
}
|
}
|
||||||
*p = 0;
|
*p = 0;
|
||||||
m_out->output_string(buffer);
|
m_out->output_string(buffer);
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::print_dec(uint32_t n) const
|
console::print_dec(uint32_t n) const
|
||||||
{
|
{
|
||||||
wchar_t buffer[11];
|
wchar_t buffer[11];
|
||||||
wchar_t *p = buffer + 10;
|
wchar_t *p = buffer + 10;
|
||||||
*p-- = 0;
|
*p-- = 0;
|
||||||
do {
|
do {
|
||||||
*p-- = digits[n % 10];
|
*p-- = digits[n % 10];
|
||||||
n /= 10;
|
n /= 10;
|
||||||
} while (n != 0);
|
} while (n != 0);
|
||||||
|
|
||||||
m_out->output_string(++p);
|
m_out->output_string(++p);
|
||||||
return 10 - (p - buffer);
|
return 10 - (p - buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::print_long_dec(uint64_t n) const
|
console::print_long_dec(uint64_t n) const
|
||||||
{
|
{
|
||||||
wchar_t buffer[21];
|
wchar_t buffer[21];
|
||||||
wchar_t *p = buffer + 20;
|
wchar_t *p = buffer + 20;
|
||||||
*p-- = 0;
|
*p-- = 0;
|
||||||
do {
|
do {
|
||||||
*p-- = digits[n % 10];
|
*p-- = digits[n % 10];
|
||||||
n /= 10;
|
n /= 10;
|
||||||
} while (n != 0);
|
} while (n != 0);
|
||||||
|
|
||||||
m_out->output_string(++p);
|
m_out->output_string(++p);
|
||||||
return 20 - (p - buffer);
|
return 20 - (p - buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::vprintf(const wchar_t *fmt, va_list args) const
|
console::vprintf(const wchar_t *fmt, va_list args) const
|
||||||
{
|
{
|
||||||
wchar_t buffer[256];
|
wchar_t buffer[256];
|
||||||
const wchar_t *r = fmt;
|
const wchar_t *r = fmt;
|
||||||
wchar_t *w = buffer;
|
wchar_t *w = buffer;
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|
||||||
while (r && *r) {
|
while (r && *r) {
|
||||||
if (*r != L'%') {
|
if (*r != L'%') {
|
||||||
count++;
|
count++;
|
||||||
*w++ = *r++;
|
*w++ = *r++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
*w = 0;
|
*w = 0;
|
||||||
m_out->output_string(buffer);
|
m_out->output_string(buffer);
|
||||||
w = buffer;
|
w = buffer;
|
||||||
|
|
||||||
r++; // chomp the %
|
r++; // chomp the %
|
||||||
|
|
||||||
switch (*r++) {
|
switch (*r++) {
|
||||||
case L'%':
|
case L'%':
|
||||||
m_out->output_string(const_cast<wchar_t*>(L"%"));
|
m_out->output_string(const_cast<wchar_t*>(L"%"));
|
||||||
count++;
|
count++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L'x':
|
case L'x':
|
||||||
count += print_hex(va_arg(args, uint32_t));
|
count += print_hex(va_arg(args, uint32_t));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L'd':
|
case L'd':
|
||||||
case L'u':
|
case L'u':
|
||||||
count += print_dec(va_arg(args, uint32_t));
|
count += print_dec(va_arg(args, uint32_t));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L's':
|
case L's':
|
||||||
{
|
{
|
||||||
wchar_t *s = va_arg(args, wchar_t*);
|
wchar_t *s = va_arg(args, wchar_t*);
|
||||||
count += wstrlen(s);
|
count += wstrlen(s);
|
||||||
m_out->output_string(s);
|
m_out->output_string(s);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L'l':
|
case L'l':
|
||||||
switch (*r++) {
|
switch (*r++) {
|
||||||
case L'x':
|
case L'x':
|
||||||
count += print_long_hex(va_arg(args, uint64_t));
|
count += print_long_hex(va_arg(args, uint64_t));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L'd':
|
case L'd':
|
||||||
case L'u':
|
case L'u':
|
||||||
count += print_long_dec(va_arg(args, uint64_t));
|
count += print_long_dec(va_arg(args, uint64_t));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*w = 0;
|
*w = 0;
|
||||||
m_out->output_string(buffer);
|
m_out->output_string(buffer);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::printf(const wchar_t *fmt, ...) const
|
console::printf(const wchar_t *fmt, ...) const
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
|
||||||
size_t result = vprintf(fmt, args);
|
size_t result = vprintf(fmt, args);
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::print(const wchar_t *fmt, ...)
|
console::print(const wchar_t *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
|
||||||
size_t result = get().vprintf(fmt, args);
|
size_t result = get().vprintf(fmt, args);
|
||||||
|
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
namespace uefi {
|
namespace uefi {
|
||||||
namespace protos {
|
namespace protos {
|
||||||
struct simple_text_output;
|
struct simple_text_output;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
@@ -17,28 +17,28 @@ namespace boot {
|
|||||||
class console
|
class console
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
console(uefi::protos::simple_text_output *out);
|
console(uefi::protos::simple_text_output *out);
|
||||||
|
|
||||||
void announce();
|
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;
|
||||||
size_t print_long_hex(uint64_t n) const;
|
size_t print_long_hex(uint64_t n) const;
|
||||||
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;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
static console *s_console;
|
static console *s_console;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t wstrlen(const wchar_t *s);
|
size_t wstrlen(const wchar_t *s);
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ namespace boot {
|
|||||||
namespace error {
|
namespace error {
|
||||||
|
|
||||||
struct error_code_desc {
|
struct error_code_desc {
|
||||||
uefi::status code;
|
uefi::status code;
|
||||||
const wchar_t *name;
|
const wchar_t *name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct error_code_desc error_table[] = {
|
struct error_code_desc error_table[] = {
|
||||||
@@ -17,46 +17,46 @@ struct error_code_desc error_table[] = {
|
|||||||
#include "uefi/errors.inc"
|
#include "uefi/errors.inc"
|
||||||
#undef STATUS_ERROR
|
#undef STATUS_ERROR
|
||||||
#undef STATUS_WARNING
|
#undef STATUS_WARNING
|
||||||
{ uefi::status::success, nullptr }
|
{ uefi::status::success, nullptr }
|
||||||
};
|
};
|
||||||
|
|
||||||
const wchar_t *
|
const wchar_t *
|
||||||
message(uefi::status status)
|
message(uefi::status status)
|
||||||
{
|
{
|
||||||
int32_t i = -1;
|
int32_t i = -1;
|
||||||
while (error_table[++i].name != nullptr) {
|
while (error_table[++i].name != nullptr) {
|
||||||
if (error_table[i].code == status) return error_table[i].name;
|
if (error_table[i].code == status) return error_table[i].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uefi::is_error(status))
|
if (uefi::is_error(status))
|
||||||
return L"Unknown Error";
|
return L"Unknown Error";
|
||||||
else
|
else
|
||||||
return L"Unknown Warning";
|
return L"Unknown Warning";
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ noreturn ]] static void
|
[[ noreturn ]] static void
|
||||||
cpu_assert(uefi::status s, const wchar_t *message, size_t line)
|
cpu_assert(uefi::status s, const wchar_t *message, size_t line)
|
||||||
{
|
{
|
||||||
asm volatile (
|
asm volatile (
|
||||||
"movq $0xeeeeeeebadbadbad, %%r8;"
|
"movq $0xeeeeeeebadbadbad, %%r8;"
|
||||||
"movq %0, %%r9;"
|
"movq %0, %%r9;"
|
||||||
"movq %1, %%r10;"
|
"movq %1, %%r10;"
|
||||||
"movq %2, %%r11;"
|
"movq %2, %%r11;"
|
||||||
"movq $0, %%rdx;"
|
"movq $0, %%rdx;"
|
||||||
"divq %%rdx;"
|
"divq %%rdx;"
|
||||||
:
|
:
|
||||||
: "r"((uint64_t)s), "r"(message), "r"(line)
|
: "r"((uint64_t)s), "r"(message), "r"(line)
|
||||||
: "rax", "rdx", "r8", "r9", "r10");
|
: "rax", "rdx", "r8", "r9", "r10");
|
||||||
while (1) asm("hlt");
|
while (1) asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ noreturn ]] void
|
[[ noreturn ]] void
|
||||||
raise(uefi::status status, const wchar_t *message, size_t line)
|
raise(uefi::status status, const wchar_t *message, size_t line)
|
||||||
{
|
{
|
||||||
if(status_line::fail(message, status))
|
if(status_line::fail(message, status))
|
||||||
while (1) asm("hlt");
|
while (1) asm("hlt");
|
||||||
else
|
else
|
||||||
cpu_assert(status, message, line);
|
cpu_assert(status, message, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -65,6 +65,6 @@ raise(uefi::status status, const wchar_t *message, size_t line)
|
|||||||
|
|
||||||
void debug_break()
|
void debug_break()
|
||||||
{
|
{
|
||||||
volatile int go = 0;
|
volatile int go = 0;
|
||||||
while (!go);
|
while (!go);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ void debug_break();
|
|||||||
/// \arg s An expression evaluating to a UEFI status
|
/// \arg s An expression evaluating to a UEFI status
|
||||||
/// \arg m The error message to use on failure
|
/// \arg m The error message to use on failure
|
||||||
#define try_or_raise(s, m) \
|
#define try_or_raise(s, m) \
|
||||||
do { \
|
do { \
|
||||||
uefi::status _s = (s); \
|
uefi::status _s = (s); \
|
||||||
if (uefi::is_error(_s)) ::boot::error::raise(_s, (m), __LINE__); \
|
if (uefi::is_error(_s)) ::boot::error::raise(_s, (m), __LINE__); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|||||||
@@ -19,91 +19,91 @@ namespace fs {
|
|||||||
using memory::alloc_type;
|
using memory::alloc_type;
|
||||||
|
|
||||||
file::file(uefi::protos::file *f) :
|
file::file(uefi::protos::file *f) :
|
||||||
m_file(f)
|
m_file(f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
file::file(file &o) :
|
file::file(file &o) :
|
||||||
m_file(o.m_file)
|
m_file(o.m_file)
|
||||||
{
|
{
|
||||||
o.m_file = nullptr;
|
o.m_file = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
file::file(file &&o) :
|
file::file(file &&o) :
|
||||||
m_file(o.m_file)
|
m_file(o.m_file)
|
||||||
{
|
{
|
||||||
o.m_file = nullptr;
|
o.m_file = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
file::~file()
|
file::~file()
|
||||||
{
|
{
|
||||||
if (m_file)
|
if (m_file)
|
||||||
m_file->close();
|
m_file->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
file
|
file
|
||||||
file::open(const wchar_t *path)
|
file::open(const wchar_t *path)
|
||||||
{
|
{
|
||||||
uefi::protos::file *fh = nullptr;
|
uefi::protos::file *fh = nullptr;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_file->open(&fh, path, uefi::file_mode::read, uefi::file_attr::none),
|
m_file->open(&fh, path, uefi::file_mode::read, uefi::file_attr::none),
|
||||||
L"Could not open relative path to file");
|
L"Could not open relative path to file");
|
||||||
|
|
||||||
return file(fh);
|
return file(fh);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
file::load()
|
file::load()
|
||||||
{
|
{
|
||||||
uint8_t info_buf[sizeof(uefi::protos::file_info) + 100];
|
uint8_t info_buf[sizeof(uefi::protos::file_info) + 100];
|
||||||
size_t size = sizeof(info_buf);
|
size_t size = sizeof(info_buf);
|
||||||
uefi::guid info_guid = uefi::protos::file_info::guid;
|
uefi::guid info_guid = uefi::protos::file_info::guid;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_file->get_info(&info_guid, &size, &info_buf),
|
m_file->get_info(&info_guid, &size, &info_buf),
|
||||||
L"Could not get file info");
|
L"Could not get file info");
|
||||||
|
|
||||||
uefi::protos::file_info *info =
|
uefi::protos::file_info *info =
|
||||||
reinterpret_cast<uefi::protos::file_info*>(&info_buf);
|
reinterpret_cast<uefi::protos::file_info*>(&info_buf);
|
||||||
|
|
||||||
size_t pages = memory::bytes_to_pages(info->file_size);
|
size_t pages = memory::bytes_to_pages(info->file_size);
|
||||||
void *data = g_alloc.allocate_pages(pages, alloc_type::file);
|
void *data = g_alloc.allocate_pages(pages, alloc_type::file);
|
||||||
|
|
||||||
size = info->file_size;
|
size = info->file_size;
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_file->read(&size, data),
|
m_file->read(&size, data),
|
||||||
L"Could not read from file");
|
L"Could not read from file");
|
||||||
|
|
||||||
return { .pointer = data, .count = size };
|
return { .pointer = data, .count = size };
|
||||||
}
|
}
|
||||||
|
|
||||||
file
|
file
|
||||||
get_boot_volume(uefi::handle image, uefi::boot_services *bs)
|
get_boot_volume(uefi::handle image, uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line status(L"Looking up boot volume");
|
status_line status(L"Looking up boot volume");
|
||||||
|
|
||||||
const uefi::guid le_guid = uefi::protos::loaded_image::guid;
|
const uefi::guid le_guid = uefi::protos::loaded_image::guid;
|
||||||
uefi::protos::loaded_image *loaded_image = nullptr;
|
uefi::protos::loaded_image *loaded_image = nullptr;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->handle_protocol(image, &le_guid,
|
bs->handle_protocol(image, &le_guid,
|
||||||
reinterpret_cast<void**>(&loaded_image)),
|
reinterpret_cast<void**>(&loaded_image)),
|
||||||
L"Could not find currently running UEFI loaded image");
|
L"Could not find currently running UEFI loaded image");
|
||||||
|
|
||||||
const uefi::guid sfs_guid = uefi::protos::simple_file_system::guid;
|
const uefi::guid sfs_guid = uefi::protos::simple_file_system::guid;
|
||||||
uefi::protos::simple_file_system *fs;
|
uefi::protos::simple_file_system *fs;
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->handle_protocol(loaded_image->device_handle, &sfs_guid,
|
bs->handle_protocol(loaded_image->device_handle, &sfs_guid,
|
||||||
reinterpret_cast<void**>(&fs)),
|
reinterpret_cast<void**>(&fs)),
|
||||||
L"Could not find filesystem protocol for boot volume");
|
L"Could not find filesystem protocol for boot volume");
|
||||||
|
|
||||||
uefi::protos::file *f;
|
uefi::protos::file *f;
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
fs->open_volume(&f),
|
fs->open_volume(&f),
|
||||||
L"Could not open the boot volume");
|
L"Could not open the boot volume");
|
||||||
|
|
||||||
return file(f);
|
return file(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace fs
|
} // namespace fs
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
#include "counted.h"
|
#include "counted.h"
|
||||||
|
|
||||||
namespace uefi {
|
namespace uefi {
|
||||||
struct boot_services;
|
struct boot_services;
|
||||||
namespace protos {
|
namespace protos {
|
||||||
struct file;
|
struct file;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
@@ -18,26 +18,26 @@ namespace fs {
|
|||||||
class file
|
class file
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
file(file &&o);
|
file(file &&o);
|
||||||
file(file &o);
|
file(file &o);
|
||||||
~file();
|
~file();
|
||||||
|
|
||||||
/// Open another file or directory, relative to this one.
|
/// Open another file or directory, relative to this one.
|
||||||
/// \arg path Relative path to the target file from this one
|
/// \arg path Relative path to the target file from this one
|
||||||
file open(const wchar_t *path);
|
file open(const wchar_t *path);
|
||||||
|
|
||||||
/// Load the contents of this file into memory.
|
/// Load the contents of this file into memory.
|
||||||
/// \returns A buffer describing the loaded memory. The
|
/// \returns A buffer describing the loaded memory. The
|
||||||
/// memory will be page-aligned.
|
/// memory will be page-aligned.
|
||||||
buffer load();
|
buffer load();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend file get_boot_volume(uefi::handle, uefi::boot_services*);
|
friend file get_boot_volume(uefi::handle, uefi::boot_services*);
|
||||||
|
|
||||||
file(uefi::protos::file *f);
|
file(uefi::protos::file *f);
|
||||||
|
|
||||||
uefi::protos::file *m_file;
|
uefi::protos::file *m_file;
|
||||||
uefi::boot_services *m_bs;
|
uefi::boot_services *m_bs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get the filesystem this loader was loaded from.
|
/// Get the filesystem this loader was loaded from.
|
||||||
|
|||||||
@@ -10,92 +10,92 @@ namespace hw {
|
|||||||
void *
|
void *
|
||||||
find_acpi_table(uefi::system_table *st)
|
find_acpi_table(uefi::system_table *st)
|
||||||
{
|
{
|
||||||
status_line status(L"Searching for ACPI table");
|
status_line status(L"Searching for ACPI table");
|
||||||
|
|
||||||
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
|
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
|
||||||
uintptr_t acpi1_table = 0;
|
uintptr_t acpi1_table = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < st->number_of_table_entries; ++i) {
|
for (size_t i = 0; i < st->number_of_table_entries; ++i) {
|
||||||
uefi::configuration_table *table = &st->configuration_table[i];
|
uefi::configuration_table *table = &st->configuration_table[i];
|
||||||
|
|
||||||
// If we find an ACPI 2.0 table, return it immediately
|
// If we find an ACPI 2.0 table, return it immediately
|
||||||
if (table->vendor_guid == uefi::vendor_guids::acpi2)
|
if (table->vendor_guid == uefi::vendor_guids::acpi2)
|
||||||
return table->vendor_table;
|
return table->vendor_table;
|
||||||
|
|
||||||
if (table->vendor_guid == uefi::vendor_guids::acpi1) {
|
if (table->vendor_guid == uefi::vendor_guids::acpi1) {
|
||||||
// Mark a v1 table with the LSB high
|
// Mark a v1 table with the LSB high
|
||||||
acpi1_table = reinterpret_cast<uintptr_t>(table->vendor_table);
|
acpi1_table = reinterpret_cast<uintptr_t>(table->vendor_table);
|
||||||
acpi1_table |= 1;
|
acpi1_table |= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!acpi1_table) {
|
if (!acpi1_table) {
|
||||||
error::raise(uefi::status::not_found, L"Could not find ACPI table");
|
error::raise(uefi::status::not_found, L"Could not find ACPI table");
|
||||||
} else if (acpi1_table & 1) {
|
} else if (acpi1_table & 1) {
|
||||||
status_line::warn(L"Only found ACPI 1.0 table");
|
status_line::warn(L"Only found ACPI 1.0 table");
|
||||||
}
|
}
|
||||||
|
|
||||||
return reinterpret_cast<void*>(acpi1_table);
|
return reinterpret_cast<void*>(acpi1_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t
|
static uint64_t
|
||||||
rdmsr(uint32_t addr)
|
rdmsr(uint32_t addr)
|
||||||
{
|
{
|
||||||
uint32_t low, high;
|
uint32_t low, high;
|
||||||
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
|
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
|
||||||
return (static_cast<uint64_t>(high) << 32) | low;
|
return (static_cast<uint64_t>(high) << 32) | low;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wrmsr(uint32_t addr, uint64_t value)
|
wrmsr(uint32_t addr, uint64_t value)
|
||||||
{
|
{
|
||||||
uint32_t low = value & 0xffffffff;
|
uint32_t low = value & 0xffffffff;
|
||||||
uint32_t high = value >> 32;
|
uint32_t high = value >> 32;
|
||||||
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
|
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
setup_control_regs()
|
setup_control_regs()
|
||||||
{
|
{
|
||||||
uint64_t cr4 = 0;
|
uint64_t cr4 = 0;
|
||||||
asm volatile ( "mov %%cr4, %0" : "=r" (cr4) );
|
asm volatile ( "mov %%cr4, %0" : "=r" (cr4) );
|
||||||
cr4 |=
|
cr4 |=
|
||||||
0x000080 | // Enable global pages
|
0x000080 | // Enable global pages
|
||||||
0x000200 | // Enable FXSAVE/FXRSTOR
|
0x000200 | // Enable FXSAVE/FXRSTOR
|
||||||
0x010000 | // Enable FSGSBASE
|
0x010000 | // Enable FSGSBASE
|
||||||
0x020000 | // Enable PCIDs
|
0x020000 | // Enable PCIDs
|
||||||
0;
|
0;
|
||||||
asm volatile ( "mov %0, %%cr4" :: "r" (cr4) );
|
asm volatile ( "mov %0, %%cr4" :: "r" (cr4) );
|
||||||
|
|
||||||
// Set up IA32_EFER
|
// Set up IA32_EFER
|
||||||
constexpr uint32_t IA32_EFER = 0xC0000080;
|
constexpr uint32_t IA32_EFER = 0xC0000080;
|
||||||
uint64_t efer = rdmsr(IA32_EFER);
|
uint64_t efer = rdmsr(IA32_EFER);
|
||||||
efer |=
|
efer |=
|
||||||
0x0001 | // Enable SYSCALL
|
0x0001 | // Enable SYSCALL
|
||||||
0x0800 | // Enable NX bit
|
0x0800 | // Enable NX bit
|
||||||
0;
|
0;
|
||||||
wrmsr(IA32_EFER, efer);
|
wrmsr(IA32_EFER, efer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
check_cpu_supported()
|
check_cpu_supported()
|
||||||
{
|
{
|
||||||
status_line status {L"Checking CPU features"};
|
status_line status {L"Checking CPU features"};
|
||||||
|
|
||||||
cpu::cpu_id cpu;
|
cpu::cpu_id cpu;
|
||||||
uint64_t missing = cpu.missing();
|
uint64_t missing = cpu.missing();
|
||||||
if (missing) {
|
if (missing) {
|
||||||
#define CPU_FEATURE_OPT(...)
|
#define CPU_FEATURE_OPT(...)
|
||||||
#define CPU_FEATURE_REQ(name, ...) \
|
#define CPU_FEATURE_REQ(name, ...) \
|
||||||
if (!cpu.has_feature(cpu::feature::name)) { \
|
if (!cpu.has_feature(cpu::feature::name)) { \
|
||||||
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
|
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
|
||||||
}
|
}
|
||||||
#include "cpu/features.inc"
|
#include "cpu/features.inc"
|
||||||
#undef CPU_FEATURE_REQ
|
#undef CPU_FEATURE_REQ
|
||||||
#undef CPU_FEATURE_OPT
|
#undef CPU_FEATURE_OPT
|
||||||
|
|
||||||
error::raise(uefi::status::unsupported, L"CPU not supported");
|
error::raise(uefi::status::unsupported, L"CPU not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,108 +23,108 @@ using memory::alloc_type;
|
|||||||
|
|
||||||
buffer
|
buffer
|
||||||
load_file(
|
load_file(
|
||||||
fs::file &disk,
|
fs::file &disk,
|
||||||
const program_desc &desc)
|
const program_desc &desc)
|
||||||
{
|
{
|
||||||
status_line status(L"Loading file", desc.path);
|
status_line status(L"Loading file", desc.path);
|
||||||
|
|
||||||
fs::file file = disk.open(desc.path);
|
fs::file file = disk.open(desc.path);
|
||||||
buffer b = file.load();
|
buffer b = file.load();
|
||||||
|
|
||||||
//console::print(L" Loaded at: 0x%lx, %d bytes\r\n", b.data, b.size);
|
//console::print(L" Loaded at: 0x%lx, %d bytes\r\n", b.data, b.size);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
create_module(buffer data, const program_desc &desc, bool loaded)
|
create_module(buffer data, const program_desc &desc, bool loaded)
|
||||||
{
|
{
|
||||||
size_t path_len = wstrlen(desc.path);
|
size_t path_len = wstrlen(desc.path);
|
||||||
init::module_program *mod = g_alloc.allocate_module<init::module_program>(path_len);
|
init::module_program *mod = g_alloc.allocate_module<init::module_program>(path_len);
|
||||||
mod->mod_type = init::module_type::program;
|
mod->mod_type = init::module_type::program;
|
||||||
mod->base_address = reinterpret_cast<uintptr_t>(data.pointer);
|
mod->base_address = reinterpret_cast<uintptr_t>(data.pointer);
|
||||||
if (loaded)
|
if (loaded)
|
||||||
mod->mod_flags = static_cast<init::module_flags>(
|
mod->mod_flags = static_cast<init::module_flags>(
|
||||||
static_cast<uint8_t>(mod->mod_flags) |
|
static_cast<uint8_t>(mod->mod_flags) |
|
||||||
static_cast<uint8_t>(init::module_flags::no_load));
|
static_cast<uint8_t>(init::module_flags::no_load));
|
||||||
|
|
||||||
// TODO: support non-ascii path characters and do real utf-16 to utf-8
|
// TODO: support non-ascii path characters and do real utf-16 to utf-8
|
||||||
// conversion
|
// conversion
|
||||||
for (int i = 0; i < path_len; ++i)
|
for (int i = 0; i < path_len; ++i)
|
||||||
mod->filename[i] = desc.path[i];
|
mod->filename[i] = desc.path[i];
|
||||||
mod->filename[path_len] = 0;
|
mod->filename[path_len] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
init::program *
|
init::program *
|
||||||
load_program(
|
load_program(
|
||||||
fs::file &disk,
|
fs::file &disk,
|
||||||
const program_desc &desc,
|
const program_desc &desc,
|
||||||
bool add_module)
|
bool add_module)
|
||||||
{
|
{
|
||||||
status_line status(L"Loading program", desc.name);
|
status_line status(L"Loading program", desc.name);
|
||||||
|
|
||||||
buffer data = load_file(disk, desc);
|
buffer data = load_file(disk, desc);
|
||||||
|
|
||||||
if (add_module)
|
if (add_module)
|
||||||
create_module(data, desc, true);
|
create_module(data, desc, true);
|
||||||
|
|
||||||
elf::file program(data.pointer, data.count);
|
elf::file program(data.pointer, data.count);
|
||||||
if (!program.valid())
|
if (!program.valid())
|
||||||
error::raise(uefi::status::load_error, L"ELF file not valid");
|
error::raise(uefi::status::load_error, L"ELF file not valid");
|
||||||
|
|
||||||
size_t num_sections = 0;
|
size_t num_sections = 0;
|
||||||
for (auto &seg : program.programs()) {
|
for (auto &seg : program.programs()) {
|
||||||
if (seg.type == elf::segment_type::load)
|
if (seg.type == elf::segment_type::load)
|
||||||
++num_sections;
|
++num_sections;
|
||||||
}
|
}
|
||||||
|
|
||||||
init::program_section *sections = new init::program_section [num_sections];
|
init::program_section *sections = new init::program_section [num_sections];
|
||||||
|
|
||||||
size_t next_section = 0;
|
size_t next_section = 0;
|
||||||
for (auto &seg : program.programs()) {
|
for (auto &seg : program.programs()) {
|
||||||
if (seg.type != elf::segment_type::load)
|
if (seg.type != elf::segment_type::load)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
init::program_section §ion = sections[next_section++];
|
init::program_section §ion = sections[next_section++];
|
||||||
|
|
||||||
size_t page_count = memory::bytes_to_pages(seg.mem_size);
|
size_t page_count = memory::bytes_to_pages(seg.mem_size);
|
||||||
|
|
||||||
if (seg.mem_size > seg.file_size) {
|
if (seg.mem_size > seg.file_size) {
|
||||||
void *pages = g_alloc.allocate_pages(page_count, alloc_type::program, true);
|
void *pages = g_alloc.allocate_pages(page_count, alloc_type::program, true);
|
||||||
void *source = offset_ptr<void>(data.pointer, seg.offset);
|
void *source = offset_ptr<void>(data.pointer, seg.offset);
|
||||||
g_alloc.copy(pages, source, seg.file_size);
|
g_alloc.copy(pages, source, seg.file_size);
|
||||||
section.phys_addr = reinterpret_cast<uintptr_t>(pages);
|
section.phys_addr = reinterpret_cast<uintptr_t>(pages);
|
||||||
} else {
|
} else {
|
||||||
section.phys_addr = program.base() + seg.offset;
|
section.phys_addr = program.base() + seg.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.virt_addr = seg.vaddr;
|
section.virt_addr = seg.vaddr;
|
||||||
section.size = seg.mem_size;
|
section.size = seg.mem_size;
|
||||||
section.type = static_cast<init::section_flags>(seg.flags);
|
section.type = static_cast<init::section_flags>(seg.flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
init::program *prog = new init::program;
|
init::program *prog = new init::program;
|
||||||
prog->sections = { .pointer = sections, .count = num_sections };
|
prog->sections = { .pointer = sections, .count = num_sections };
|
||||||
prog->phys_base = program.base();
|
prog->phys_base = program.base();
|
||||||
prog->entrypoint = program.entrypoint();
|
prog->entrypoint = program.entrypoint();
|
||||||
return prog;
|
return prog;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
load_module(
|
load_module(
|
||||||
fs::file &disk,
|
fs::file &disk,
|
||||||
const program_desc &desc)
|
const program_desc &desc)
|
||||||
{
|
{
|
||||||
status_line status(L"Loading module", desc.name);
|
status_line status(L"Loading module", desc.name);
|
||||||
|
|
||||||
buffer data = load_file(disk, desc);
|
buffer data = load_file(disk, desc);
|
||||||
create_module(data, desc, false);
|
create_module(data, desc, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
verify_kernel_header(init::program &program)
|
verify_kernel_header(init::program &program)
|
||||||
{
|
{
|
||||||
status_line status(L"Verifying kernel header");
|
status_line status(L"Verifying kernel header");
|
||||||
|
|
||||||
const init::header *header =
|
const init::header *header =
|
||||||
reinterpret_cast<const init::header *>(program.sections[0].phys_addr);
|
reinterpret_cast<const init::header *>(program.sections[0].phys_addr);
|
||||||
@@ -138,15 +138,15 @@ verify_kernel_header(init::program &program)
|
|||||||
if (header->version < init::min_header_version)
|
if (header->version < init::min_header_version)
|
||||||
error::raise(uefi::status::unsupported, L"Kernel header version not supported");
|
error::raise(uefi::status::unsupported, L"Kernel header version not supported");
|
||||||
|
|
||||||
console::print(L" Loaded kernel vserion: %d.%d.%d %x\r\n",
|
console::print(L" Loaded kernel vserion: %d.%d.%d %x\r\n",
|
||||||
header->version_major, header->version_minor, header->version_patch,
|
header->version_major, header->version_minor, header->version_patch,
|
||||||
header->version_gitsha);
|
header->version_gitsha);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
for (auto §ion : program.sections)
|
for (auto §ion : program.sections)
|
||||||
console::print(L" Section: p:0x%lx v:0x%lx fs:0x%x ms:0x%x\r\n",
|
console::print(L" Section: p:0x%lx v:0x%lx fs:0x%x ms:0x%x\r\n",
|
||||||
section.phys_addr, section.virt_addr, section.file_size, section.mem_size);
|
section.phys_addr, section.virt_addr, section.file_size, section.mem_size);
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace loader
|
} // namespace loader
|
||||||
|
|||||||
@@ -6,22 +6,22 @@
|
|||||||
|
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace init {
|
namespace init {
|
||||||
struct program;
|
struct program;
|
||||||
struct module;
|
struct module;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
|
|
||||||
namespace fs {
|
namespace fs {
|
||||||
class file;
|
class file;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace loader {
|
namespace loader {
|
||||||
|
|
||||||
struct program_desc
|
struct program_desc
|
||||||
{
|
{
|
||||||
const wchar_t *name;
|
const wchar_t *name;
|
||||||
const wchar_t *path;
|
const wchar_t *path;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Load a file from disk into memory.
|
/// Load a file from disk into memory.
|
||||||
@@ -29,8 +29,8 @@ struct program_desc
|
|||||||
/// \arg desc The program descriptor identifying the file
|
/// \arg desc The program descriptor identifying the file
|
||||||
buffer
|
buffer
|
||||||
load_file(
|
load_file(
|
||||||
fs::file &disk,
|
fs::file &disk,
|
||||||
const program_desc &desc);
|
const program_desc &desc);
|
||||||
|
|
||||||
/// Parse and load an ELF file in memory into a loaded image.
|
/// Parse and load an ELF file in memory into a loaded image.
|
||||||
/// \arg disk The opened UEFI filesystem to load from
|
/// \arg disk The opened UEFI filesystem to load from
|
||||||
@@ -38,17 +38,17 @@ load_file(
|
|||||||
/// \arg add_module Also create a module for this loaded program
|
/// \arg add_module Also create a module for this loaded program
|
||||||
kernel::init::program *
|
kernel::init::program *
|
||||||
load_program(
|
load_program(
|
||||||
fs::file &disk,
|
fs::file &disk,
|
||||||
const program_desc &desc,
|
const program_desc &desc,
|
||||||
bool add_module = false);
|
bool add_module = false);
|
||||||
|
|
||||||
/// Load a file from disk into memory, creating an init args module
|
/// Load a file from disk into memory, creating an init args module
|
||||||
/// \arg disk The opened UEFI filesystem to load from
|
/// \arg disk The opened UEFI filesystem to load from
|
||||||
/// \arg desc The program descriptor identifying the file
|
/// \arg desc The program descriptor identifying the file
|
||||||
void
|
void
|
||||||
load_module(
|
load_module(
|
||||||
fs::file &disk,
|
fs::file &disk,
|
||||||
const program_desc &desc);
|
const program_desc &desc);
|
||||||
|
|
||||||
/// Verify that a loaded ELF has the j6 kernel header
|
/// Verify that a loaded ELF has the j6 kernel header
|
||||||
/// \arg program The program to check for a header
|
/// \arg program The program to check for a header
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const loader::program_desc fb_driver = {L"UEFI framebuffer driver", L"drv.uefi_f
|
|||||||
const loader::program_desc panic_handler = {L"Serial panic handler", L"panic.serial.elf"};
|
const loader::program_desc panic_handler = {L"Serial panic handler", L"panic.serial.elf"};
|
||||||
|
|
||||||
const loader::program_desc extra_programs[] = {
|
const loader::program_desc extra_programs[] = {
|
||||||
{L"test application", L"testapp.elf"},
|
{L"test application", L"testapp.elf"},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Change a pointer to point to the higher-half linear-offset version
|
/// Change a pointer to point to the higher-half linear-offset version
|
||||||
@@ -44,7 +44,7 @@ const loader::program_desc extra_programs[] = {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void change_pointer(T *&pointer)
|
void change_pointer(T *&pointer)
|
||||||
{
|
{
|
||||||
pointer = offset_ptr<T>(pointer, kernel::memory::page_offset);
|
pointer = offset_ptr<T>(pointer, kernel::memory::page_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main procedure for the portion of the loader that runs while
|
/// The main procedure for the portion of the loader that runs while
|
||||||
@@ -53,50 +53,50 @@ void change_pointer(T *&pointer)
|
|||||||
init::args *
|
init::args *
|
||||||
uefi_preboot(uefi::handle image, uefi::system_table *st)
|
uefi_preboot(uefi::handle image, uefi::system_table *st)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|
||||||
status_line status {L"Performing UEFI pre-boot"};
|
status_line status {L"Performing UEFI pre-boot"};
|
||||||
|
|
||||||
hw::check_cpu_supported();
|
hw::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;
|
||||||
g_alloc.zero(args, sizeof(init::args));
|
g_alloc.zero(args, sizeof(init::args));
|
||||||
|
|
||||||
args->magic = init::args_magic;
|
args->magic = init::args_magic;
|
||||||
args->version = init::args_version;
|
args->version = init::args_version;
|
||||||
args->runtime_services = rs;
|
args->runtime_services = rs;
|
||||||
args->acpi_table = hw::find_acpi_table(st);
|
args->acpi_table = hw::find_acpi_table(st);
|
||||||
memory::mark_pointer_fixup(&args->runtime_services);
|
memory::mark_pointer_fixup(&args->runtime_services);
|
||||||
|
|
||||||
paging::allocate_tables(args);
|
paging::allocate_tables(args);
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the kernel and other programs from disk
|
/// Load the kernel and other programs from disk
|
||||||
void
|
void
|
||||||
load_resources(init::args *args, video::screen *screen, uefi::handle image, uefi::boot_services *bs)
|
load_resources(init::args *args, video::screen *screen, uefi::handle image, uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line status {L"Loading programs"};
|
status_line status {L"Loading programs"};
|
||||||
|
|
||||||
fs::file disk = fs::get_boot_volume(image, bs);
|
fs::file disk = fs::get_boot_volume(image, bs);
|
||||||
|
|
||||||
if (screen) {
|
if (screen) {
|
||||||
video::make_module(screen);
|
video::make_module(screen);
|
||||||
loader::load_module(disk, fb_driver);
|
loader::load_module(disk, fb_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer symbol_table = loader::load_file(disk, {L"symbol table", L"symbol_table.dat"});
|
buffer symbol_table = loader::load_file(disk, {L"symbol table", L"symbol_table.dat"});
|
||||||
args->symbol_table = reinterpret_cast<uintptr_t>(symbol_table.pointer);
|
args->symbol_table = reinterpret_cast<uintptr_t>(symbol_table.pointer);
|
||||||
|
|
||||||
args->kernel = loader::load_program(disk, kern_desc, true);
|
args->kernel = loader::load_program(disk, kern_desc, true);
|
||||||
args->init = loader::load_program(disk, init_desc);
|
args->init = loader::load_program(disk, init_desc);
|
||||||
args->panic = loader::load_program(disk, panic_handler);
|
args->panic = loader::load_program(disk, panic_handler);
|
||||||
|
|
||||||
for (auto &desc : extra_programs)
|
for (auto &desc : extra_programs)
|
||||||
loader::load_module(disk, desc);
|
loader::load_module(disk, desc);
|
||||||
|
|
||||||
loader::verify_kernel_header(*args->kernel);
|
loader::verify_kernel_header(*args->kernel);
|
||||||
}
|
}
|
||||||
@@ -104,20 +104,20 @@ load_resources(init::args *args, video::screen *screen, uefi::handle image, uefi
|
|||||||
memory::efi_mem_map
|
memory::efi_mem_map
|
||||||
uefi_exit(init::args *args, uefi::handle image, uefi::boot_services *bs)
|
uefi_exit(init::args *args, uefi::handle image, uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line status {L"Exiting UEFI", nullptr, false};
|
status_line status {L"Exiting UEFI", nullptr, false};
|
||||||
|
|
||||||
memory::efi_mem_map map;
|
memory::efi_mem_map map;
|
||||||
map.update(*bs);
|
map.update(*bs);
|
||||||
|
|
||||||
args->mem_map = memory::build_kernel_map(map);
|
args->mem_map = memory::build_kernel_map(map);
|
||||||
args->frame_blocks = memory::build_frame_blocks(args->mem_map);
|
args->frame_blocks = memory::build_frame_blocks(args->mem_map);
|
||||||
|
|
||||||
map.update(*bs);
|
map.update(*bs);
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->exit_boot_services(image, map.key),
|
bs->exit_boot_services(image, map.key),
|
||||||
L"Failed to exit boot services");
|
L"Failed to exit boot services");
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
@@ -126,53 +126,53 @@ uefi_exit(init::args *args, uefi::handle image, uefi::boot_services *bs)
|
|||||||
extern "C" uefi::status
|
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;
|
||||||
|
|
||||||
uefi::boot_services *bs = st->boot_services;
|
uefi::boot_services *bs = st->boot_services;
|
||||||
console con(st->con_out);
|
console con(st->con_out);
|
||||||
|
|
||||||
init::allocation_register *allocs = nullptr;
|
init::allocation_register *allocs = nullptr;
|
||||||
init::modules_page *modules = nullptr;
|
init::modules_page *modules = nullptr;
|
||||||
memory::allocator::init(allocs, modules, bs);
|
memory::allocator::init(allocs, modules, bs);
|
||||||
|
|
||||||
video::screen *screen = video::pick_mode(bs);
|
video::screen *screen = video::pick_mode(bs);
|
||||||
con.announce();
|
con.announce();
|
||||||
|
|
||||||
init::args *args = uefi_preboot(image, st);
|
init::args *args = uefi_preboot(image, st);
|
||||||
load_resources(args, screen, image, bs);
|
load_resources(args, screen, image, bs);
|
||||||
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->allocations = allocs;
|
args->allocations = allocs;
|
||||||
args->modules = reinterpret_cast<uintptr_t>(modules);
|
args->modules = reinterpret_cast<uintptr_t>(modules);
|
||||||
|
|
||||||
status_bar status {screen}; // Switch to fb status display
|
status_bar status {screen}; // Switch to fb status display
|
||||||
|
|
||||||
// Map the kernel and panic handler to the appropriate addresses
|
// Map the kernel and panic handler to the appropriate addresses
|
||||||
paging::map_program(args, *args->kernel);
|
paging::map_program(args, *args->kernel);
|
||||||
paging::map_program(args, *args->panic);
|
paging::map_program(args, *args->panic);
|
||||||
|
|
||||||
memory::fix_frame_blocks(args);
|
memory::fix_frame_blocks(args);
|
||||||
|
|
||||||
init::entrypoint kentry =
|
init::entrypoint kentry =
|
||||||
reinterpret_cast<init::entrypoint>(args->kernel->entrypoint);
|
reinterpret_cast<init::entrypoint>(args->kernel->entrypoint);
|
||||||
//status.next();
|
//status.next();
|
||||||
|
|
||||||
hw::setup_control_regs();
|
hw::setup_control_regs();
|
||||||
memory::virtualize(args->pml4, map, st->runtime_services);
|
memory::virtualize(args->pml4, map, st->runtime_services);
|
||||||
//status.next();
|
//status.next();
|
||||||
|
|
||||||
change_pointer(args);
|
change_pointer(args);
|
||||||
change_pointer(args->pml4);
|
change_pointer(args->pml4);
|
||||||
|
|
||||||
change_pointer(args->kernel);
|
change_pointer(args->kernel);
|
||||||
change_pointer(args->kernel->sections.pointer);
|
change_pointer(args->kernel->sections.pointer);
|
||||||
change_pointer(args->init);
|
change_pointer(args->init);
|
||||||
change_pointer(args->init->sections.pointer);
|
change_pointer(args->init->sections.pointer);
|
||||||
|
|
||||||
//status.next();
|
//status.next();
|
||||||
|
|
||||||
kentry(args);
|
kentry(args);
|
||||||
debug_break();
|
debug_break();
|
||||||
return uefi::status::unsupported;
|
return uefi::status::unsupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,56 +22,56 @@ void **fixup_pointers[64];
|
|||||||
void
|
void
|
||||||
update_marked_addresses(uefi::event, void *context)
|
update_marked_addresses(uefi::event, void *context)
|
||||||
{
|
{
|
||||||
uefi::runtime_services *rs =
|
uefi::runtime_services *rs =
|
||||||
reinterpret_cast<uefi::runtime_services*>(context);
|
reinterpret_cast<uefi::runtime_services*>(context);
|
||||||
|
|
||||||
for (size_t i = 0; i < fixup_pointer_index; ++i) {
|
for (size_t i = 0; i < fixup_pointer_index; ++i) {
|
||||||
if (fixup_pointers[i])
|
if (fixup_pointers[i])
|
||||||
rs->convert_pointer(0, fixup_pointers[i]);
|
rs->convert_pointer(0, fixup_pointers[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs)
|
init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs)
|
||||||
{
|
{
|
||||||
status_line status(L"Initializing pointer virtualization event");
|
status_line status(L"Initializing pointer virtualization event");
|
||||||
|
|
||||||
uefi::event event;
|
uefi::event event;
|
||||||
bs->set_mem(&fixup_pointers, sizeof(fixup_pointers), 0);
|
bs->set_mem(&fixup_pointers, sizeof(fixup_pointers), 0);
|
||||||
fixup_pointer_index = 0;
|
fixup_pointer_index = 0;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->create_event(
|
bs->create_event(
|
||||||
uefi::evt::signal_virtual_address_change,
|
uefi::evt::signal_virtual_address_change,
|
||||||
uefi::tpl::callback,
|
uefi::tpl::callback,
|
||||||
(uefi::event_notify)&update_marked_addresses,
|
(uefi::event_notify)&update_marked_addresses,
|
||||||
rs,
|
rs,
|
||||||
&event),
|
&event),
|
||||||
L"Error creating memory virtualization event");
|
L"Error creating memory virtualization event");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
mark_pointer_fixup(void **p)
|
mark_pointer_fixup(void **p)
|
||||||
{
|
{
|
||||||
fixup_pointers[fixup_pointer_index++] = p;
|
fixup_pointers[fixup_pointer_index++] = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
virtualize(void *pml4, efi_mem_map &map, uefi::runtime_services *rs)
|
virtualize(void *pml4, efi_mem_map &map, uefi::runtime_services *rs)
|
||||||
{
|
{
|
||||||
paging::add_current_mappings(reinterpret_cast<paging::page_table*>(pml4));
|
paging::add_current_mappings(reinterpret_cast<paging::page_table*>(pml4));
|
||||||
|
|
||||||
for (auto &desc : map)
|
for (auto &desc : map)
|
||||||
desc.virtual_start = desc.physical_start + ::memory::page_offset;
|
desc.virtual_start = desc.physical_start + ::memory::page_offset;
|
||||||
|
|
||||||
// Write our new PML4 pointer to CR3
|
// Write our new PML4 pointer to CR3
|
||||||
asm volatile ( "mov %0, %%cr3" :: "r" (pml4) );
|
asm volatile ( "mov %0, %%cr3" :: "r" (pml4) );
|
||||||
__sync_synchronize();
|
__sync_synchronize();
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
rs->set_virtual_address_map(
|
rs->set_virtual_address_map(
|
||||||
map.length, map.size, map.version, map.entries),
|
map.length, map.size, map.version, map.entries),
|
||||||
L"Error setting virtual address map");
|
L"Error setting virtual address map");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace uefi {
|
namespace uefi {
|
||||||
struct boot_services;
|
struct boot_services;
|
||||||
struct runtime_services;
|
struct runtime_services;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
@@ -19,7 +19,7 @@ constexpr size_t page_size = 0x1000;
|
|||||||
|
|
||||||
/// Get the number of pages needed to hold `bytes` bytes
|
/// Get the number of pages needed to hold `bytes` bytes
|
||||||
inline constexpr size_t bytes_to_pages(size_t bytes) {
|
inline constexpr size_t bytes_to_pages(size_t bytes) {
|
||||||
return ((bytes - 1) / page_size) + 1;
|
return ((bytes - 1) / page_size) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \defgroup pointer_fixup
|
/// \defgroup pointer_fixup
|
||||||
@@ -42,9 +42,9 @@ void mark_pointer_fixup(void **p);
|
|||||||
/// \arg pml4 The root page table for the new mappings
|
/// \arg pml4 The root page table for the new mappings
|
||||||
/// \arg map The UEFI memory map, used to update runtime services
|
/// \arg map The UEFI memory map, used to update runtime services
|
||||||
void virtualize(
|
void virtualize(
|
||||||
void *pml4,
|
void *pml4,
|
||||||
efi_mem_map &map,
|
efi_mem_map &map,
|
||||||
uefi::runtime_services *rs);
|
uefi::runtime_services *rs);
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
|
|||||||
@@ -21,180 +21,180 @@ using kernel::init::mem_type;
|
|||||||
void
|
void
|
||||||
efi_mem_map::update(uefi::boot_services &bs)
|
efi_mem_map::update(uefi::boot_services &bs)
|
||||||
{
|
{
|
||||||
size_t l = total;
|
size_t l = total;
|
||||||
uefi::status status = bs.get_memory_map(
|
uefi::status status = bs.get_memory_map(
|
||||||
&l, entries, &key, &size, &version);
|
&l, entries, &key, &size, &version);
|
||||||
length = l;
|
length = l;
|
||||||
|
|
||||||
if (status == uefi::status::success)
|
if (status == uefi::status::success)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (status != uefi::status::buffer_too_small)
|
if (status != uefi::status::buffer_too_small)
|
||||||
error::raise(status, L"Error getting memory map size");
|
error::raise(status, L"Error getting memory map size");
|
||||||
|
|
||||||
if (entries) {
|
if (entries) {
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs.free_pool(reinterpret_cast<void*>(entries)),
|
bs.free_pool(reinterpret_cast<void*>(entries)),
|
||||||
L"Freeing previous memory map space");
|
L"Freeing previous memory map space");
|
||||||
}
|
}
|
||||||
|
|
||||||
total = length + 10 * size;
|
total = length + 10 * size;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs.allocate_pool(
|
bs.allocate_pool(
|
||||||
uefi::memory_type::loader_data, total,
|
uefi::memory_type::loader_data, total,
|
||||||
reinterpret_cast<void**>(&entries)),
|
reinterpret_cast<void**>(&entries)),
|
||||||
L"Allocating space for memory map");
|
L"Allocating space for memory map");
|
||||||
|
|
||||||
length = total;
|
length = total;
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs.get_memory_map(&length, entries, &key, &size, &version),
|
bs.get_memory_map(&length, entries, &key, &size, &version),
|
||||||
L"Getting UEFI memory map");
|
L"Getting UEFI memory map");
|
||||||
}
|
}
|
||||||
|
|
||||||
static const wchar_t *memory_type_names[] = {
|
static const wchar_t *memory_type_names[] = {
|
||||||
L"reserved memory type",
|
L"reserved memory type",
|
||||||
L"loader code",
|
L"loader code",
|
||||||
L"loader data",
|
L"loader data",
|
||||||
L"boot services code",
|
L"boot services code",
|
||||||
L"boot services data",
|
L"boot services data",
|
||||||
L"runtime services code",
|
L"runtime services code",
|
||||||
L"runtime services data",
|
L"runtime services data",
|
||||||
L"conventional memory",
|
L"conventional memory",
|
||||||
L"unusable memory",
|
L"unusable memory",
|
||||||
L"acpi reclaim memory",
|
L"acpi reclaim memory",
|
||||||
L"acpi memory nvs",
|
L"acpi memory nvs",
|
||||||
L"memory mapped io",
|
L"memory mapped io",
|
||||||
L"memory mapped io port space",
|
L"memory mapped io port space",
|
||||||
L"pal code",
|
L"pal code",
|
||||||
L"persistent memory"
|
L"persistent memory"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const wchar_t *kernel_memory_type_names[] = {
|
static const wchar_t *kernel_memory_type_names[] = {
|
||||||
L"free",
|
L"free",
|
||||||
L"pending",
|
L"pending",
|
||||||
L"acpi",
|
L"acpi",
|
||||||
L"uefi_runtime",
|
L"uefi_runtime",
|
||||||
L"mmio",
|
L"mmio",
|
||||||
L"persistent"
|
L"persistent"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const wchar_t *
|
static const wchar_t *
|
||||||
memory_type_name(uefi::memory_type t)
|
memory_type_name(uefi::memory_type t)
|
||||||
{
|
{
|
||||||
if (t < uefi::memory_type::max_memory_type)
|
if (t < uefi::memory_type::max_memory_type)
|
||||||
return memory_type_names[static_cast<uint32_t>(t)];
|
return memory_type_names[static_cast<uint32_t>(t)];
|
||||||
|
|
||||||
return L"Bad Type Value";
|
return L"Bad Type Value";
|
||||||
}
|
}
|
||||||
|
|
||||||
static const wchar_t *
|
static const wchar_t *
|
||||||
kernel_memory_type_name(kernel::init::mem_type t)
|
kernel_memory_type_name(kernel::init::mem_type t)
|
||||||
{
|
{
|
||||||
return kernel_memory_type_names[static_cast<uint32_t>(t)];
|
return kernel_memory_type_names[static_cast<uint32_t>(t)];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor &next)
|
can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor &next)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
prev.type == type &&
|
prev.type == type &&
|
||||||
prev.start + (page_size * prev.pages) == next.physical_start &&
|
prev.start + (page_size * prev.pages) == next.physical_start &&
|
||||||
prev.attr == (next.attribute & 0xffffffff);
|
prev.attr == (next.attribute & 0xffffffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
counted<mem_entry>
|
counted<mem_entry>
|
||||||
build_kernel_map(efi_mem_map &map)
|
build_kernel_map(efi_mem_map &map)
|
||||||
{
|
{
|
||||||
status_line status {L"Creating kernel memory map"};
|
status_line status {L"Creating kernel memory map"};
|
||||||
|
|
||||||
size_t map_size = map.num_entries() * sizeof(mem_entry);
|
size_t map_size = map.num_entries() * sizeof(mem_entry);
|
||||||
size_t num_pages = bytes_to_pages(map_size);
|
size_t num_pages = bytes_to_pages(map_size);
|
||||||
mem_entry *kernel_map = reinterpret_cast<mem_entry*>(
|
mem_entry *kernel_map = reinterpret_cast<mem_entry*>(
|
||||||
g_alloc.allocate_pages(num_pages, alloc_type::mem_map, true));
|
g_alloc.allocate_pages(num_pages, alloc_type::mem_map, true));
|
||||||
|
|
||||||
size_t nent = 0;
|
size_t nent = 0;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto &desc : map) {
|
for (auto &desc : map) {
|
||||||
/*
|
/*
|
||||||
// EFI map dump
|
// EFI map dump
|
||||||
console::print(L" eRange %lx (%lx) %x(%s) [%lu]\r\n",
|
console::print(L" eRange %lx (%lx) %x(%s) [%lu]\r\n",
|
||||||
desc.physical_start, desc.attribute, desc.type, memory_type_name(desc.type), desc.number_of_pages);
|
desc.physical_start, desc.attribute, desc.type, memory_type_name(desc.type), desc.number_of_pages);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mem_type type;
|
mem_type type;
|
||||||
switch (desc.type) {
|
switch (desc.type) {
|
||||||
case uefi::memory_type::reserved:
|
case uefi::memory_type::reserved:
|
||||||
case uefi::memory_type::unusable_memory:
|
case uefi::memory_type::unusable_memory:
|
||||||
case uefi::memory_type::acpi_memory_nvs:
|
case uefi::memory_type::acpi_memory_nvs:
|
||||||
case uefi::memory_type::pal_code:
|
case uefi::memory_type::pal_code:
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case uefi::memory_type::loader_code:
|
case uefi::memory_type::loader_code:
|
||||||
case uefi::memory_type::boot_services_code:
|
case uefi::memory_type::boot_services_code:
|
||||||
case uefi::memory_type::boot_services_data:
|
case uefi::memory_type::boot_services_data:
|
||||||
case uefi::memory_type::conventional_memory:
|
case uefi::memory_type::conventional_memory:
|
||||||
case uefi::memory_type::loader_data:
|
case uefi::memory_type::loader_data:
|
||||||
type = mem_type::free;
|
type = mem_type::free;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case uefi::memory_type::runtime_services_code:
|
case uefi::memory_type::runtime_services_code:
|
||||||
case uefi::memory_type::runtime_services_data:
|
case uefi::memory_type::runtime_services_data:
|
||||||
type = mem_type::uefi_runtime;
|
type = mem_type::uefi_runtime;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case uefi::memory_type::acpi_reclaim_memory:
|
case uefi::memory_type::acpi_reclaim_memory:
|
||||||
type = mem_type::acpi;
|
type = mem_type::acpi;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case uefi::memory_type::memory_mapped_io:
|
case uefi::memory_type::memory_mapped_io:
|
||||||
case uefi::memory_type::memory_mapped_io_port_space:
|
case uefi::memory_type::memory_mapped_io_port_space:
|
||||||
type = mem_type::mmio;
|
type = mem_type::mmio;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case uefi::memory_type::persistent_memory:
|
case uefi::memory_type::persistent_memory:
|
||||||
type = mem_type::persistent;
|
type = mem_type::persistent;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error::raise(
|
error::raise(
|
||||||
uefi::status::invalid_parameter,
|
uefi::status::invalid_parameter,
|
||||||
L"Got an unexpected memory type from UEFI memory map");
|
L"Got an unexpected memory type from UEFI memory map");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: validate uefi's map is sorted
|
// TODO: validate uefi's map is sorted
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
mem_entry &ent = kernel_map[nent++];
|
mem_entry &ent = kernel_map[nent++];
|
||||||
ent.start = desc.physical_start;
|
ent.start = desc.physical_start;
|
||||||
ent.pages = desc.number_of_pages;
|
ent.pages = desc.number_of_pages;
|
||||||
ent.type = type;
|
ent.type = type;
|
||||||
ent.attr = (desc.attribute & 0xffffffff);
|
ent.attr = (desc.attribute & 0xffffffff);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_entry &prev = kernel_map[nent - 1];
|
mem_entry &prev = kernel_map[nent - 1];
|
||||||
if (can_merge(prev, type, desc)) {
|
if (can_merge(prev, type, desc)) {
|
||||||
prev.pages += desc.number_of_pages;
|
prev.pages += desc.number_of_pages;
|
||||||
} else {
|
} else {
|
||||||
mem_entry &next = kernel_map[nent++];
|
mem_entry &next = kernel_map[nent++];
|
||||||
next.start = desc.physical_start;
|
next.start = desc.physical_start;
|
||||||
next.pages = desc.number_of_pages;
|
next.pages = desc.number_of_pages;
|
||||||
next.type = type;
|
next.type = type;
|
||||||
next.attr = (desc.attribute & 0xffffffff);
|
next.attr = (desc.attribute & 0xffffffff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// kernel map dump
|
// kernel map dump
|
||||||
for (unsigned i = 0; i < nent; ++i) {
|
for (unsigned i = 0; i < nent; ++i) {
|
||||||
const mem_entry &e = kernel_map[i];
|
const mem_entry &e = kernel_map[i];
|
||||||
console::print(L" kRange %lx (%lx) %x(%s) [%lu]\r\n",
|
console::print(L" kRange %lx (%lx) %x(%s) [%lu]\r\n",
|
||||||
e.start, e.attr, e.type, kernel_memory_type_name(e.type), e.pages);
|
e.start, e.attr, e.type, kernel_memory_type_name(e.type), e.pages);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return { .pointer = kernel_map, .count = nent };
|
return { .pointer = kernel_map, .count = nent };
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t bitmap_size(size_t frames) { return (frames + 63) / 64; }
|
inline size_t bitmap_size(size_t frames) { return (frames + 63) / 64; }
|
||||||
@@ -203,99 +203,99 @@ inline size_t num_blocks(size_t frames) { return (frames + (frames_per_block-1))
|
|||||||
counted<kernel::init::frame_block>
|
counted<kernel::init::frame_block>
|
||||||
build_frame_blocks(const counted<kernel::init::mem_entry> &kmap)
|
build_frame_blocks(const counted<kernel::init::mem_entry> &kmap)
|
||||||
{
|
{
|
||||||
status_line status {L"Creating kernel frame accounting map"};
|
status_line status {L"Creating kernel frame accounting map"};
|
||||||
|
|
||||||
size_t block_count = 0;
|
size_t block_count = 0;
|
||||||
size_t total_bitmap_size = 0;
|
size_t total_bitmap_size = 0;
|
||||||
for (size_t i = 0; i < kmap.count; ++i) {
|
for (size_t i = 0; i < kmap.count; ++i) {
|
||||||
const mem_entry &ent = kmap[i];
|
const mem_entry &ent = kmap[i];
|
||||||
if (ent.type != mem_type::free)
|
if (ent.type != mem_type::free)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
block_count += num_blocks(ent.pages);
|
block_count += num_blocks(ent.pages);
|
||||||
total_bitmap_size += bitmap_size(ent.pages) * sizeof(uint64_t);
|
total_bitmap_size += bitmap_size(ent.pages) * sizeof(uint64_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t total_size = block_count * sizeof(frame_block) + total_bitmap_size;
|
size_t total_size = block_count * sizeof(frame_block) + total_bitmap_size;
|
||||||
|
|
||||||
frame_block *blocks = reinterpret_cast<frame_block*>(
|
frame_block *blocks = reinterpret_cast<frame_block*>(
|
||||||
g_alloc.allocate_pages(bytes_to_pages(total_size), alloc_type::frame_map, true));
|
g_alloc.allocate_pages(bytes_to_pages(total_size), alloc_type::frame_map, true));
|
||||||
|
|
||||||
frame_block *next_block = blocks;
|
frame_block *next_block = blocks;
|
||||||
for (size_t i = 0; i < kmap.count; ++i) {
|
for (size_t i = 0; i < kmap.count; ++i) {
|
||||||
const mem_entry &ent = kmap[i];
|
const mem_entry &ent = kmap[i];
|
||||||
if (ent.type != mem_type::free)
|
if (ent.type != mem_type::free)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
size_t page_count = ent.pages;
|
size_t page_count = ent.pages;
|
||||||
uintptr_t base_addr = ent.start;
|
uintptr_t base_addr = ent.start;
|
||||||
while (page_count) {
|
while (page_count) {
|
||||||
frame_block *blk = next_block++;
|
frame_block *blk = next_block++;
|
||||||
|
|
||||||
blk->flags = static_cast<kernel::init::frame_flags>(ent.attr);
|
blk->flags = static_cast<kernel::init::frame_flags>(ent.attr);
|
||||||
blk->base = base_addr;
|
blk->base = base_addr;
|
||||||
base_addr += frames_per_block * page_size;
|
base_addr += frames_per_block * page_size;
|
||||||
|
|
||||||
if (page_count >= frames_per_block) {
|
if (page_count >= frames_per_block) {
|
||||||
page_count -= frames_per_block;
|
page_count -= frames_per_block;
|
||||||
blk->count = frames_per_block;
|
blk->count = frames_per_block;
|
||||||
blk->map1 = ~0ull;
|
blk->map1 = ~0ull;
|
||||||
g_alloc.memset(blk->map2, sizeof(blk->map2), 0xff);
|
g_alloc.memset(blk->map2, sizeof(blk->map2), 0xff);
|
||||||
} else {
|
} else {
|
||||||
blk->count = page_count;
|
blk->count = page_count;
|
||||||
unsigned i = 0;
|
unsigned i = 0;
|
||||||
|
|
||||||
uint64_t b1 = (page_count + 4095) / 4096;
|
uint64_t b1 = (page_count + 4095) / 4096;
|
||||||
blk->map1 = (1 << b1) - 1;
|
blk->map1 = (1 << b1) - 1;
|
||||||
|
|
||||||
uint64_t b2 = (page_count + 63) / 64;
|
uint64_t b2 = (page_count + 63) / 64;
|
||||||
uint64_t b2q = b2 / 64;
|
uint64_t b2q = b2 / 64;
|
||||||
uint64_t b2r = b2 % 64;
|
uint64_t b2r = b2 % 64;
|
||||||
g_alloc.memset(blk->map2, b2q, 0xff);
|
g_alloc.memset(blk->map2, b2q, 0xff);
|
||||||
blk->map2[b2q] = (1 << b2r) - 1;
|
blk->map2[b2q] = (1 << b2r) - 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t *bitmap = reinterpret_cast<uint64_t*>(next_block);
|
uint64_t *bitmap = reinterpret_cast<uint64_t*>(next_block);
|
||||||
for (unsigned i = 0; i < block_count; ++i) {
|
for (unsigned i = 0; i < block_count; ++i) {
|
||||||
frame_block &blk = blocks[i];
|
frame_block &blk = blocks[i];
|
||||||
blk.bitmap = bitmap;
|
blk.bitmap = bitmap;
|
||||||
|
|
||||||
size_t b = blk.count / 64;
|
size_t b = blk.count / 64;
|
||||||
size_t r = blk.count % 64;
|
size_t r = blk.count % 64;
|
||||||
g_alloc.memset(blk.bitmap, b*8, 0xff);
|
g_alloc.memset(blk.bitmap, b*8, 0xff);
|
||||||
blk.bitmap[b] = (1 << r) - 1;
|
blk.bitmap[b] = (1 << r) - 1;
|
||||||
|
|
||||||
bitmap += bitmap_size(blk.count);
|
bitmap += bitmap_size(blk.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { .pointer = blocks, .count = block_count };
|
return { .pointer = blocks, .count = block_count };
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
fix_frame_blocks(kernel::init::args *args)
|
fix_frame_blocks(kernel::init::args *args)
|
||||||
{
|
{
|
||||||
counted<frame_block> &blocks = args->frame_blocks;
|
counted<frame_block> &blocks = args->frame_blocks;
|
||||||
|
|
||||||
size_t size = blocks.count * sizeof(frame_block);
|
size_t size = blocks.count * sizeof(frame_block);
|
||||||
for (unsigned i = 0; i < blocks.count; ++i)
|
for (unsigned i = 0; i < blocks.count; ++i)
|
||||||
size += bitmap_size(blocks[i].count) * sizeof(uint64_t);
|
size += bitmap_size(blocks[i].count) * sizeof(uint64_t);
|
||||||
|
|
||||||
size_t pages = bytes_to_pages(size);
|
size_t pages = bytes_to_pages(size);
|
||||||
uintptr_t addr = reinterpret_cast<uintptr_t>(blocks.pointer);
|
uintptr_t addr = reinterpret_cast<uintptr_t>(blocks.pointer);
|
||||||
|
|
||||||
// Map the frame blocks to the appropriate address
|
// Map the frame blocks to the appropriate address
|
||||||
paging::map_pages(args, addr,
|
paging::map_pages(args, addr,
|
||||||
::memory::bitmap_start, pages, true, false);
|
::memory::bitmap_start, pages, true, false);
|
||||||
|
|
||||||
uintptr_t offset = ::memory::bitmap_start - addr;
|
uintptr_t offset = ::memory::bitmap_start - addr;
|
||||||
|
|
||||||
for (unsigned i = 0; i < blocks.count; ++i) {
|
for (unsigned i = 0; i < blocks.count; ++i) {
|
||||||
frame_block &blk = blocks[i];
|
frame_block &blk = blocks[i];
|
||||||
blk.bitmap = offset_ptr<uint64_t>(blk.bitmap, offset);
|
blk.bitmap = offset_ptr<uint64_t>(blk.bitmap, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,15 @@
|
|||||||
#include "pointer_manipulation.h"
|
#include "pointer_manipulation.h"
|
||||||
|
|
||||||
namespace uefi {
|
namespace uefi {
|
||||||
struct boot_services;
|
struct boot_services;
|
||||||
struct memory_descriptor;
|
struct memory_descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace init {
|
namespace init {
|
||||||
struct args;
|
struct args;
|
||||||
struct frame_block;
|
struct frame_block;
|
||||||
struct mem_entry;
|
struct mem_entry;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
@@ -24,29 +24,29 @@ namespace memory {
|
|||||||
/// as well as the data on how to read it.
|
/// as well as the data on how to read it.
|
||||||
struct efi_mem_map
|
struct efi_mem_map
|
||||||
{
|
{
|
||||||
using desc = uefi::memory_descriptor;
|
using desc = uefi::memory_descriptor;
|
||||||
using iterator = offset_iterator<desc>;
|
using iterator = offset_iterator<desc>;
|
||||||
|
|
||||||
size_t length; ///< Total length of the map data
|
size_t length; ///< Total length of the map data
|
||||||
size_t total; ///< Total allocated space for map data
|
size_t total; ///< Total allocated space for map data
|
||||||
size_t size; ///< Size of an entry in the array
|
size_t size; ///< Size of an entry in the array
|
||||||
size_t key; ///< Key for detecting changes
|
size_t key; ///< Key for detecting changes
|
||||||
uint32_t version; ///< Version of the `memory_descriptor` struct
|
uint32_t version; ///< Version of the `memory_descriptor` struct
|
||||||
desc *entries; ///< The array of UEFI descriptors
|
desc *entries; ///< The array of UEFI descriptors
|
||||||
|
|
||||||
efi_mem_map() : length(0), total(0), size(0), key(0), version(0), entries(nullptr) {}
|
efi_mem_map() : length(0), total(0), size(0), key(0), version(0), entries(nullptr) {}
|
||||||
|
|
||||||
/// Update the map from UEFI
|
/// Update the map from UEFI
|
||||||
void update(uefi::boot_services &bs);
|
void update(uefi::boot_services &bs);
|
||||||
|
|
||||||
/// Get the count of entries in the array
|
/// Get the count of entries in the array
|
||||||
inline size_t num_entries() const { return length / size; }
|
inline size_t num_entries() const { return length / size; }
|
||||||
|
|
||||||
/// Return an iterator to the beginning of the array
|
/// Return an iterator to the beginning of the array
|
||||||
inline iterator begin() { return iterator(entries, size); }
|
inline iterator begin() { return iterator(entries, size); }
|
||||||
|
|
||||||
/// Return an iterator to the end of the array
|
/// Return an iterator to the end of the array
|
||||||
inline iterator end() { return offset_ptr<desc>(entries, length); }
|
inline iterator end() { return offset_ptr<desc>(entries, length); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Add the kernel's memory map as a module to the kernel args.
|
/// Add the kernel's memory map as a module to the kernel args.
|
||||||
|
|||||||
@@ -60,13 +60,13 @@ constexpr uint64_t table_flags = 0x003;
|
|||||||
inline void *
|
inline void *
|
||||||
pop_pages(counted<void> &pages, size_t count)
|
pop_pages(counted<void> &pages, size_t count)
|
||||||
{
|
{
|
||||||
if (count > pages.count)
|
if (count > pages.count)
|
||||||
error::raise(uefi::status::out_of_resources, L"Page table cache empty", 0x7ab1e5);
|
error::raise(uefi::status::out_of_resources, L"Page table cache empty", 0x7ab1e5);
|
||||||
|
|
||||||
void *next = pages.pointer;
|
void *next = pages.pointer;
|
||||||
pages.pointer = offset_ptr<void>(pages.pointer, count*page_size);
|
pages.pointer = offset_ptr<void>(pages.pointer, count*page_size);
|
||||||
pages.count -= count;
|
pages.count -= count;
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator over page table entries.
|
/// Iterator over page table entries.
|
||||||
@@ -74,204 +74,204 @@ template <unsigned D = 4>
|
|||||||
class page_entry_iterator
|
class page_entry_iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg virt Virtual address this iterator is starting at
|
/// \arg virt Virtual address this iterator is starting at
|
||||||
/// \arg pml4 Root of the page tables to iterate
|
/// \arg pml4 Root of the page tables to iterate
|
||||||
/// \arg pages Cache of usable table pages
|
/// \arg pages Cache of usable table pages
|
||||||
page_entry_iterator(
|
page_entry_iterator(
|
||||||
uintptr_t virt,
|
uintptr_t virt,
|
||||||
page_table *pml4,
|
page_table *pml4,
|
||||||
counted<void> &pages) :
|
counted<void> &pages) :
|
||||||
m_pages(pages)
|
m_pages(pages)
|
||||||
{
|
{
|
||||||
m_table[0] = pml4;
|
m_table[0] = pml4;
|
||||||
for (unsigned i = 0; i < D; ++i) {
|
for (unsigned i = 0; i < D; ++i) {
|
||||||
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
|
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
|
||||||
ensure_table(i);
|
ensure_table(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t vaddress() const {
|
uintptr_t vaddress() const {
|
||||||
uintptr_t address = 0;
|
uintptr_t address = 0;
|
||||||
for (unsigned i = 0; i < D; ++i)
|
for (unsigned i = 0; i < D; ++i)
|
||||||
address |= static_cast<uintptr_t>(m_index[i]) << (12 + 9*(3-i));
|
address |= static_cast<uintptr_t>(m_index[i]) << (12 + 9*(3-i));
|
||||||
if (address & (1ull<<47)) // canonicalize the address
|
if (address & (1ull<<47)) // canonicalize the address
|
||||||
address |= (0xffffull<<48);
|
address |= (0xffffull<<48);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
void increment()
|
void increment()
|
||||||
{
|
{
|
||||||
for (unsigned i = D - 1; i >= 0; --i) {
|
for (unsigned i = D - 1; i >= 0; --i) {
|
||||||
if (++m_index[i] <= 511) {
|
if (++m_index[i] <= 511) {
|
||||||
for (unsigned j = i + 1; j < D; ++j)
|
for (unsigned j = i + 1; j < D; ++j)
|
||||||
ensure_table(j);
|
ensure_table(j);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_index[i] = 0;
|
m_index[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t & operator*() { return entry(D-1); }
|
uint64_t & operator*() { return entry(D-1); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline uint64_t & entry(unsigned level) { return m_table[level]->entries[m_index[level]]; }
|
inline uint64_t & entry(unsigned level) { return m_table[level]->entries[m_index[level]]; }
|
||||||
|
|
||||||
void ensure_table(unsigned level)
|
void ensure_table(unsigned level)
|
||||||
{
|
{
|
||||||
// We're only dealing with D levels of paging, and
|
// We're only dealing with D levels of paging, and
|
||||||
// there must always be a PML4.
|
// there must always be a PML4.
|
||||||
if (level < 1 || level >= D)
|
if (level < 1 || level >= D)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Entry in the parent that points to the table we want
|
// Entry in the parent that points to the table we want
|
||||||
uint64_t & parent_ent = entry(level - 1);
|
uint64_t & parent_ent = entry(level - 1);
|
||||||
|
|
||||||
if (!(parent_ent & 1)) {
|
if (!(parent_ent & 1)) {
|
||||||
page_table *table = reinterpret_cast<page_table*>(pop_pages(m_pages, 1));
|
page_table *table = reinterpret_cast<page_table*>(pop_pages(m_pages, 1));
|
||||||
parent_ent = (reinterpret_cast<uintptr_t>(table) & ~0xfffull) | table_flags;
|
parent_ent = (reinterpret_cast<uintptr_t>(table) & ~0xfffull) | table_flags;
|
||||||
m_table[level] = table;
|
m_table[level] = table;
|
||||||
} else {
|
} else {
|
||||||
m_table[level] = reinterpret_cast<page_table*>(parent_ent & ~0xfffull);
|
m_table[level] = reinterpret_cast<page_table*>(parent_ent & ~0xfffull);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
counted<void> &m_pages;
|
counted<void> &m_pages;
|
||||||
page_table *m_table[D];
|
page_table *m_table[D];
|
||||||
uint16_t m_index[D];
|
uint16_t m_index[D];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_offset_mappings(page_table *pml4, counted<void> &pages)
|
add_offset_mappings(page_table *pml4, counted<void> &pages)
|
||||||
{
|
{
|
||||||
uintptr_t phys = 0;
|
uintptr_t phys = 0;
|
||||||
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
|
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
|
||||||
size_t page_count = 64 * 1024; // 64 TiB of 1 GiB pages
|
size_t page_count = 64 * 1024; // 64 TiB of 1 GiB pages
|
||||||
constexpr size_t GiB = 0x40000000ull;
|
constexpr size_t GiB = 0x40000000ull;
|
||||||
|
|
||||||
page_entry_iterator<2> iterator{virt, pml4, pages};
|
page_entry_iterator<2> iterator{virt, pml4, pages};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
*iterator = phys | huge_page_flags;
|
*iterator = phys | huge_page_flags;
|
||||||
if (--page_count == 0)
|
if (--page_count == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
iterator.increment();
|
iterator.increment();
|
||||||
phys += GiB;
|
phys += GiB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_kernel_pds(page_table *pml4, counted<void> &pages)
|
add_kernel_pds(page_table *pml4, counted<void> &pages)
|
||||||
{
|
{
|
||||||
for (unsigned i = pml4e_kernel; i < table_entries; ++i)
|
for (unsigned i = pml4e_kernel; i < table_entries; ++i)
|
||||||
pml4->set(i, pop_pages(pages, 1), table_flags);
|
pml4->set(i, pop_pages(pages, 1), table_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
add_current_mappings(page_table *new_pml4)
|
add_current_mappings(page_table *new_pml4)
|
||||||
{
|
{
|
||||||
// Get the pointer to the current PML4
|
// Get the pointer to the current PML4
|
||||||
page_table *old_pml4 = 0;
|
page_table *old_pml4 = 0;
|
||||||
asm volatile ( "mov %%cr3, %0" : "=r" (old_pml4) );
|
asm volatile ( "mov %%cr3, %0" : "=r" (old_pml4) );
|
||||||
|
|
||||||
// Only copy mappings in the lower half
|
// Only copy mappings in the lower half
|
||||||
for (int i = 0; i < ::memory::pml4e_kernel; ++i) {
|
for (int i = 0; i < ::memory::pml4e_kernel; ++i) {
|
||||||
uint64_t entry = old_pml4->entries[i];
|
uint64_t entry = old_pml4->entries[i];
|
||||||
if (entry & 1)
|
if (entry & 1)
|
||||||
new_pml4->entries[i] = entry;
|
new_pml4->entries[i] = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
allocate_tables(kernel::init::args *args)
|
allocate_tables(kernel::init::args *args)
|
||||||
{
|
{
|
||||||
status_line status(L"Allocating initial page tables");
|
status_line status(L"Allocating initial page tables");
|
||||||
|
|
||||||
static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs
|
static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs
|
||||||
static constexpr size_t extra_tables = 64; // number of extra pages
|
static constexpr size_t extra_tables = 64; // number of extra pages
|
||||||
|
|
||||||
// number of pages for kernelspace PDs + PML4
|
// number of pages for kernelspace PDs + PML4
|
||||||
static constexpr size_t kernel_tables = pd_tables + 1;
|
static constexpr size_t kernel_tables = pd_tables + 1;
|
||||||
|
|
||||||
static constexpr size_t tables_needed = kernel_tables + extra_tables;
|
static constexpr size_t tables_needed = kernel_tables + extra_tables;
|
||||||
|
|
||||||
void *addr = g_alloc.allocate_pages(tables_needed, alloc_type::page_table, true);
|
void *addr = g_alloc.allocate_pages(tables_needed, alloc_type::page_table, true);
|
||||||
page_table *pml4 = reinterpret_cast<page_table*>(addr);
|
page_table *pml4 = reinterpret_cast<page_table*>(addr);
|
||||||
|
|
||||||
args->pml4 = pml4;
|
args->pml4 = pml4;
|
||||||
args->page_tables = { .pointer = pml4 + 1, .count = tables_needed - 1 };
|
args->page_tables = { .pointer = pml4 + 1, .count = tables_needed - 1 };
|
||||||
|
|
||||||
console::print(L" First page (pml4) at: 0x%lx\r\n", pml4);
|
console::print(L" First page (pml4) at: 0x%lx\r\n", pml4);
|
||||||
|
|
||||||
add_kernel_pds(pml4, args->page_tables);
|
add_kernel_pds(pml4, args->page_tables);
|
||||||
add_offset_mappings(pml4, args->page_tables);
|
add_offset_mappings(pml4, args->page_tables);
|
||||||
|
|
||||||
//console::print(L" Set up initial mappings, %d spare tables.\r\n", args->table_count);
|
//console::print(L" Set up initial mappings, %d spare tables.\r\n", args->table_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
constexpr bool has_flag(E set, E flag) {
|
constexpr bool has_flag(E set, E flag) {
|
||||||
return
|
return
|
||||||
(static_cast<uint64_t>(set) & static_cast<uint64_t>(flag)) ==
|
(static_cast<uint64_t>(set) & static_cast<uint64_t>(flag)) ==
|
||||||
static_cast<uint64_t>(flag);
|
static_cast<uint64_t>(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
map_pages(
|
map_pages(
|
||||||
kernel::init::args *args,
|
kernel::init::args *args,
|
||||||
uintptr_t phys, uintptr_t virt,
|
uintptr_t phys, uintptr_t virt,
|
||||||
size_t count, bool write_flag, bool exe_flag)
|
size_t count, bool write_flag, bool exe_flag)
|
||||||
{
|
{
|
||||||
if (!count)
|
if (!count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
paging::page_table *pml4 =
|
paging::page_table *pml4 =
|
||||||
reinterpret_cast<paging::page_table*>(args->pml4);
|
reinterpret_cast<paging::page_table*>(args->pml4);
|
||||||
|
|
||||||
page_entry_iterator<4> iterator{virt, pml4, args->page_tables};
|
page_entry_iterator<4> iterator{virt, pml4, args->page_tables};
|
||||||
|
|
||||||
uint64_t flags = page_flags;
|
uint64_t flags = page_flags;
|
||||||
if (!exe_flag)
|
if (!exe_flag)
|
||||||
flags |= (1ull << 63); // set NX bit
|
flags |= (1ull << 63); // set NX bit
|
||||||
if (write_flag)
|
if (write_flag)
|
||||||
flags |= 2;
|
flags |= 2;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
*iterator = phys | flags;
|
*iterator = phys | flags;
|
||||||
if (--count == 0)
|
if (--count == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
iterator.increment();
|
iterator.increment();
|
||||||
phys += page_size;
|
phys += page_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
map_section(
|
map_section(
|
||||||
kernel::init::args *args,
|
kernel::init::args *args,
|
||||||
const kernel::init::program_section §ion)
|
const kernel::init::program_section §ion)
|
||||||
{
|
{
|
||||||
using kernel::init::section_flags;
|
using kernel::init::section_flags;
|
||||||
|
|
||||||
map_pages(
|
map_pages(
|
||||||
args,
|
args,
|
||||||
section.phys_addr,
|
section.phys_addr,
|
||||||
section.virt_addr,
|
section.virt_addr,
|
||||||
memory::bytes_to_pages(section.size),
|
memory::bytes_to_pages(section.size),
|
||||||
has_flag(section.type, section_flags::write),
|
has_flag(section.type, section_flags::write),
|
||||||
has_flag(section.type, section_flags::execute));
|
has_flag(section.type, section_flags::execute));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
map_program(
|
map_program(
|
||||||
kernel::init::args *args,
|
kernel::init::args *args,
|
||||||
kernel::init::program &program)
|
kernel::init::program &program)
|
||||||
{
|
{
|
||||||
for (auto §ion : program.sections)
|
for (auto §ion : program.sections)
|
||||||
paging::map_section(args, section);
|
paging::map_section(args, section);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace paging
|
} // namespace paging
|
||||||
|
|||||||
@@ -11,18 +11,18 @@ namespace paging {
|
|||||||
/// Struct to allow easy accessing of a memory page being used as a page table.
|
/// Struct to allow easy accessing of a memory page being used as a page table.
|
||||||
struct page_table
|
struct page_table
|
||||||
{
|
{
|
||||||
uint64_t entries[512];
|
uint64_t entries[512];
|
||||||
|
|
||||||
inline page_table * get(int i, uint16_t *flags = nullptr) const {
|
inline page_table * get(int i, uint16_t *flags = nullptr) const {
|
||||||
uint64_t entry = entries[i];
|
uint64_t entry = entries[i];
|
||||||
if ((entry & 1) == 0) return nullptr;
|
if ((entry & 1) == 0) return nullptr;
|
||||||
if (flags) *flags = entry & 0xfff;
|
if (flags) *flags = entry & 0xfff;
|
||||||
return reinterpret_cast<page_table *>(entry & ~0xfffull);
|
return reinterpret_cast<page_table *>(entry & ~0xfffull);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set(int i, void *p, uint16_t flags) {
|
inline void set(int i, void *p, uint16_t flags) {
|
||||||
entries[i] = reinterpret_cast<uint64_t>(p) | (flags & 0xfff);
|
entries[i] = reinterpret_cast<uint64_t>(p) | (flags & 0xfff);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Allocate memory to be used for initial page tables. Initial offset-mapped
|
/// Allocate memory to be used for initial page tables. Initial offset-mapped
|
||||||
@@ -46,16 +46,16 @@ void add_current_mappings(page_table *new_pml4);
|
|||||||
/// \arg write_flag If true, mark the pages writeable
|
/// \arg write_flag If true, mark the pages writeable
|
||||||
/// \arg exe_flag If true, mark the pages executable
|
/// \arg exe_flag If true, mark the pages executable
|
||||||
void map_pages(
|
void map_pages(
|
||||||
kernel::init::args *args,
|
kernel::init::args *args,
|
||||||
uintptr_t phys, uintptr_t virt,
|
uintptr_t phys, uintptr_t virt,
|
||||||
size_t count, bool write_flag, bool exe_flag);
|
size_t count, bool write_flag, bool exe_flag);
|
||||||
|
|
||||||
/// Map the sections of a program in physical memory to their virtual memory
|
/// Map the sections of a program in physical memory to their virtual memory
|
||||||
/// addresses in the given page tables.
|
/// addresses in the given page tables.
|
||||||
/// \arg args The kernel args struct, used for the page table cache and pml4
|
/// \arg args The kernel args struct, used for the page table cache and pml4
|
||||||
/// \arg program The program to load
|
/// \arg program The program to load
|
||||||
void map_program(
|
void map_program(
|
||||||
kernel::init::args *args,
|
kernel::init::args *args,
|
||||||
kernel::init::program &program);
|
kernel::init::program &program);
|
||||||
|
|
||||||
} // namespace paging
|
} // namespace paging
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ static constexpr int level_warn = 1;
|
|||||||
static constexpr int level_fail = 2;
|
static constexpr int level_fail = 2;
|
||||||
|
|
||||||
static const wchar_t *level_tags[] = {
|
static const wchar_t *level_tags[] = {
|
||||||
L" ok ",
|
L" ok ",
|
||||||
L" warn ",
|
L" warn ",
|
||||||
L"failed"
|
L"failed"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uefi::attribute level_colors[] = {
|
static const uefi::attribute level_colors[] = {
|
||||||
uefi::attribute::green,
|
uefi::attribute::green,
|
||||||
uefi::attribute::brown,
|
uefi::attribute::brown,
|
||||||
uefi::attribute::light_red
|
uefi::attribute::light_red
|
||||||
};
|
};
|
||||||
|
|
||||||
status *status::s_current = nullptr;
|
status *status::s_current = nullptr;
|
||||||
@@ -33,235 +33,235 @@ unsigned status::s_current_type = 0;
|
|||||||
unsigned status_bar::s_count = 0;
|
unsigned status_bar::s_count = 0;
|
||||||
|
|
||||||
status_line::status_line(const wchar_t *message, const wchar_t *context, bool fails_clean) :
|
status_line::status_line(const wchar_t *message, const wchar_t *context, bool fails_clean) :
|
||||||
status(fails_clean),
|
status(fails_clean),
|
||||||
m_level(level_ok),
|
m_level(level_ok),
|
||||||
m_depth(0),
|
m_depth(0),
|
||||||
m_outer(nullptr)
|
m_outer(nullptr)
|
||||||
{
|
{
|
||||||
if (status::s_current_type == status_line::type) {
|
if (status::s_current_type == status_line::type) {
|
||||||
m_outer = static_cast<status_line*>(s_current);
|
m_outer = static_cast<status_line*>(s_current);
|
||||||
m_depth = (m_outer ? 1 + m_outer->m_depth : 0);
|
m_depth = (m_outer ? 1 + m_outer->m_depth : 0);
|
||||||
}
|
}
|
||||||
s_current = this;
|
s_current = this;
|
||||||
s_current_type = status_line::type;
|
s_current_type = status_line::type;
|
||||||
|
|
||||||
auto out = console::get().m_out;
|
auto out = console::get().m_out;
|
||||||
m_line = out->mode->cursor_row;
|
m_line = out->mode->cursor_row;
|
||||||
|
|
||||||
int indent = 2 * m_depth;
|
int indent = 2 * m_depth;
|
||||||
out->set_cursor_position(indent, m_line);
|
out->set_cursor_position(indent, m_line);
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
out->output_string(message);
|
out->output_string(message);
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
out->output_string(L": ");
|
out->output_string(L": ");
|
||||||
out->output_string(context);
|
out->output_string(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
out->output_string(L"\r\n");
|
out->output_string(L"\r\n");
|
||||||
print_status_tag();
|
print_status_tag();
|
||||||
}
|
}
|
||||||
|
|
||||||
status_line::~status_line()
|
status_line::~status_line()
|
||||||
{
|
{
|
||||||
if (s_current != this)
|
if (s_current != this)
|
||||||
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
|
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
|
||||||
|
|
||||||
if (m_outer && m_level > m_outer->m_level) {
|
if (m_outer && m_level > m_outer->m_level) {
|
||||||
m_outer->m_level = m_level;
|
m_outer->m_level = m_level;
|
||||||
m_outer->print_status_tag();
|
m_outer->print_status_tag();
|
||||||
}
|
}
|
||||||
s_current = m_outer;
|
s_current = m_outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
status_line::print_status_tag()
|
status_line::print_status_tag()
|
||||||
{
|
{
|
||||||
auto out = console::get().m_out;
|
auto out = console::get().m_out;
|
||||||
int row = out->mode->cursor_row;
|
int row = out->mode->cursor_row;
|
||||||
int col = out->mode->cursor_column;
|
int col = out->mode->cursor_column;
|
||||||
|
|
||||||
uefi::attribute color = level_colors[m_level];
|
uefi::attribute color = level_colors[m_level];
|
||||||
const wchar_t *tag = level_tags[m_level];
|
const wchar_t *tag = level_tags[m_level];
|
||||||
|
|
||||||
out->set_cursor_position(50, m_line);
|
out->set_cursor_position(50, m_line);
|
||||||
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
out->output_string(L"[");
|
out->output_string(L"[");
|
||||||
out->set_attribute(color);
|
out->set_attribute(color);
|
||||||
out->output_string(tag);
|
out->output_string(tag);
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
out->output_string(L"]\r\n");
|
out->output_string(L"]\r\n");
|
||||||
|
|
||||||
out->set_cursor_position(col, row);
|
out->set_cursor_position(col, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
status_line::do_warn(const wchar_t *message, uefi::status status)
|
status_line::do_warn(const wchar_t *message, uefi::status status)
|
||||||
{
|
{
|
||||||
auto out = console::get().m_out;
|
auto out = console::get().m_out;
|
||||||
int row = out->mode->cursor_row;
|
int row = out->mode->cursor_row;
|
||||||
|
|
||||||
if (m_level < level_warn) {
|
if (m_level < level_warn) {
|
||||||
m_level = level_warn;
|
m_level = level_warn;
|
||||||
print_status_tag();
|
print_status_tag();
|
||||||
}
|
}
|
||||||
|
|
||||||
int indent = 2 + 2 * m_depth;
|
int indent = 2 + 2 * m_depth;
|
||||||
out->set_cursor_position(indent, row);
|
out->set_cursor_position(indent, row);
|
||||||
out->set_attribute(uefi::attribute::yellow);
|
out->set_attribute(uefi::attribute::yellow);
|
||||||
out->output_string(message);
|
out->output_string(message);
|
||||||
|
|
||||||
const wchar_t *error = error::message(status);
|
const wchar_t *error = error::message(status);
|
||||||
if (error) {
|
if (error) {
|
||||||
out->output_string(L": ");
|
out->output_string(L": ");
|
||||||
out->output_string(error);
|
out->output_string(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
out->output_string(L"\r\n");
|
out->output_string(L"\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
status_line::do_fail(const wchar_t *message, uefi::status status)
|
status_line::do_fail(const wchar_t *message, uefi::status status)
|
||||||
{
|
{
|
||||||
auto out = console::get().m_out;
|
auto out = console::get().m_out;
|
||||||
int row = out->mode->cursor_row;
|
int row = out->mode->cursor_row;
|
||||||
|
|
||||||
if (m_level < level_fail) {
|
if (m_level < level_fail) {
|
||||||
m_level = level_fail;
|
m_level = level_fail;
|
||||||
print_status_tag();
|
print_status_tag();
|
||||||
}
|
}
|
||||||
|
|
||||||
int indent = 2 + 2 * m_depth;
|
int indent = 2 + 2 * m_depth;
|
||||||
out->set_cursor_position(indent, row);
|
out->set_cursor_position(indent, row);
|
||||||
out->set_attribute(uefi::attribute::red);
|
out->set_attribute(uefi::attribute::red);
|
||||||
out->output_string(message);
|
out->output_string(message);
|
||||||
|
|
||||||
const wchar_t *error = error::message(status);
|
const wchar_t *error = error::message(status);
|
||||||
if (error) {
|
if (error) {
|
||||||
out->output_string(L": ");
|
out->output_string(L": ");
|
||||||
out->output_string(error);
|
out->output_string(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
out->output_string(L"\r\n");
|
out->output_string(L"\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
status_bar::status_bar(video::screen *screen) :
|
status_bar::status_bar(video::screen *screen) :
|
||||||
status(),
|
status(),
|
||||||
m_outer(nullptr)
|
m_outer(nullptr)
|
||||||
{
|
{
|
||||||
m_size = (screen->mode.vertical / num_boxes) - 1;
|
m_size = (screen->mode.vertical / num_boxes) - 1;
|
||||||
m_top = screen->mode.vertical - m_size;
|
m_top = screen->mode.vertical - m_size;
|
||||||
m_horiz = screen->mode.horizontal;
|
m_horiz = screen->mode.horizontal;
|
||||||
m_fb = reinterpret_cast<uint32_t*>(screen->framebuffer.pointer);
|
m_fb = reinterpret_cast<uint32_t*>(screen->framebuffer.pointer);
|
||||||
m_type = static_cast<uint16_t>(screen->mode.layout);
|
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)
|
||||||
m_outer = static_cast<status_bar*>(s_current);
|
m_outer = static_cast<status_bar*>(s_current);
|
||||||
s_current = this;
|
s_current = this;
|
||||||
s_current_type = status_bar::type;
|
s_current_type = status_bar::type;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_bar::~status_bar()
|
status_bar::~status_bar()
|
||||||
{
|
{
|
||||||
if (s_current != this)
|
if (s_current != this)
|
||||||
error::raise(uefi::status::unsupported, L"Destroying non-current status_bar");
|
error::raise(uefi::status::unsupported, L"Destroying non-current status_bar");
|
||||||
draw_box();
|
draw_box();
|
||||||
s_current = m_outer;
|
s_current = m_outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
status_bar::do_warn(const wchar_t *message, uefi::status status)
|
status_bar::do_warn(const wchar_t *message, uefi::status status)
|
||||||
{
|
{
|
||||||
m_status = status;
|
m_status = status;
|
||||||
if (m_level < level_warn) {
|
if (m_level < level_warn) {
|
||||||
m_level = level_warn;
|
m_level = level_warn;
|
||||||
draw_box();
|
draw_box();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
status_bar::do_fail(const wchar_t *message, uefi::status status)
|
status_bar::do_fail(const wchar_t *message, uefi::status status)
|
||||||
{
|
{
|
||||||
m_status = status;
|
m_status = status;
|
||||||
if (m_level < level_fail) {
|
if (m_level < level_fail) {
|
||||||
m_level = level_fail;
|
m_level = level_fail;
|
||||||
draw_box();
|
draw_box();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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<video::layout>(type)) {
|
switch (static_cast<video::layout>(type)) {
|
||||||
case video::layout::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 video::layout::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) |
|
||||||
(static_cast<uint32_t>(b) << 16);
|
(static_cast<uint32_t>(b) << 16);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
status_bar::draw_box()
|
status_bar::draw_box()
|
||||||
{
|
{
|
||||||
static const uint32_t colors[] = {0x909090, 0xf0f0f0};
|
static const uint32_t colors[] = {0x909090, 0xf0f0f0};
|
||||||
constexpr unsigned ncolors = sizeof(colors) / sizeof(uint32_t);
|
constexpr unsigned ncolors = sizeof(colors) / sizeof(uint32_t);
|
||||||
|
|
||||||
if (m_fb == nullptr)
|
if (m_fb == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unsigned x0 = m_current * m_size;
|
unsigned x0 = m_current * m_size;
|
||||||
unsigned x1 = x0 + m_size - 3;
|
unsigned x1 = x0 + m_size - 3;
|
||||||
unsigned y0 = m_top;
|
unsigned y0 = m_top;
|
||||||
unsigned y1 = m_top + m_size - 3;
|
unsigned y1 = m_top + m_size - 3;
|
||||||
|
|
||||||
uint32_t color = 0;
|
uint32_t color = 0;
|
||||||
switch (m_level) {
|
switch (m_level) {
|
||||||
case level_ok:
|
case level_ok:
|
||||||
color = colors[m_current % ncolors];
|
color = colors[m_current % ncolors];
|
||||||
break;
|
break;
|
||||||
case level_warn:
|
case level_warn:
|
||||||
color = make_color(0xff, 0xb2, 0x34, m_type);
|
color = make_color(0xff, 0xb2, 0x34, m_type);
|
||||||
break;
|
break;
|
||||||
case level_fail:
|
case level_fail:
|
||||||
color = make_color(0xfb, 0x0a, 0x1e, m_type);
|
color = make_color(0xfb, 0x0a, 0x1e, m_type);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
color = 0;
|
color = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned y = y0; y < y1; ++y)
|
for (unsigned y = y0; y < y1; ++y)
|
||||||
for (unsigned x = x0; x < x1; ++x)
|
for (unsigned x = x0; x < x1; ++x)
|
||||||
m_fb[y * m_horiz + x] = color;
|
m_fb[y * m_horiz + x] = color;
|
||||||
|
|
||||||
if (m_level > level_ok) {
|
if (m_level > level_ok) {
|
||||||
unsigned nbars = static_cast<uint64_t>(m_status) & 0xffff;
|
unsigned nbars = static_cast<uint64_t>(m_status) & 0xffff;
|
||||||
constexpr unsigned bar_height = 4;
|
constexpr unsigned bar_height = 4;
|
||||||
|
|
||||||
for (unsigned i = 1; i <= nbars; ++i) {
|
for (unsigned i = 1; i <= nbars; ++i) {
|
||||||
y0 = m_top - 2 * i * bar_height;
|
y0 = m_top - 2 * i * bar_height;
|
||||||
y1 = y0 + bar_height;
|
y1 = y0 + bar_height;
|
||||||
|
|
||||||
for (unsigned y = y0; y < y1; ++y)
|
for (unsigned y = y0; y < y1; ++y)
|
||||||
for (unsigned x = x0; x < x1; ++x)
|
for (unsigned x = x0; x < x1; ++x)
|
||||||
m_fb[y * m_horiz + x] = color;
|
m_fb[y * m_horiz + x] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|||||||
@@ -6,125 +6,125 @@
|
|||||||
#include <uefi/types.h>
|
#include <uefi/types.h>
|
||||||
|
|
||||||
namespace uefi {
|
namespace uefi {
|
||||||
struct boot_services;
|
struct boot_services;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
|
|
||||||
namespace video {
|
namespace video {
|
||||||
struct screen;
|
struct screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abstract base class for status reporters.
|
// Abstract base class for status reporters.
|
||||||
class status
|
class status
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
status(bool fails_clean = true) : m_fails_clean(fails_clean) {}
|
status(bool fails_clean = true) : m_fails_clean(fails_clean) {}
|
||||||
|
|
||||||
virtual void do_warn(const wchar_t *message, uefi::status status) = 0;
|
virtual void do_warn(const wchar_t *message, uefi::status status) = 0;
|
||||||
virtual void do_fail(const wchar_t *message, uefi::status status) = 0;
|
virtual void do_fail(const wchar_t *message, uefi::status status) = 0;
|
||||||
|
|
||||||
/// Set the state to warning, and print a message. If the state is already at
|
/// Set the state to warning, and print a message. If the state is already at
|
||||||
/// warning or error, the state is unchanged but the message is still printed.
|
/// warning or error, the state is unchanged but the message is still printed.
|
||||||
/// \arg message The warning message to print, if text is supported
|
/// \arg message The warning message to print, if text is supported
|
||||||
/// \arg status If set, the error or warning code that should be represented
|
/// \arg status If set, the error or warning code that should be represented
|
||||||
/// \returns True if there was a status handler to display the warning
|
/// \returns True if there was a status handler to display the warning
|
||||||
inline static bool warn(const wchar_t *message, uefi::status status = uefi::status::success) {
|
inline static bool warn(const wchar_t *message, uefi::status status = uefi::status::success) {
|
||||||
if (!s_current) return false;
|
if (!s_current) return false;
|
||||||
s_current->do_warn(message, status);
|
s_current->do_warn(message, status);
|
||||||
return s_current->m_fails_clean;
|
return s_current->m_fails_clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the state to error, and print a message. If the state is already at
|
/// Set the state to error, and print a message. If the state is already at
|
||||||
/// error, the state is unchanged but the message is still printed.
|
/// error, the state is unchanged but the message is still printed.
|
||||||
/// \arg message The error message to print, if text is supported
|
/// \arg message The error message to print, if text is supported
|
||||||
/// \arg status The error or warning code that should be represented
|
/// \arg status The error or warning code that should be represented
|
||||||
/// \returns True if there was a status handler to display the failure
|
/// \returns True if there was a status handler to display the failure
|
||||||
inline static bool fail(const wchar_t *message, uefi::status status) {
|
inline static bool fail(const wchar_t *message, uefi::status status) {
|
||||||
if (!s_current) return false;
|
if (!s_current) return false;
|
||||||
s_current->do_fail(message, status);
|
s_current->do_fail(message, status);
|
||||||
return s_current->m_fails_clean;
|
return s_current->m_fails_clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static status *s_current;
|
static status *s_current;
|
||||||
static unsigned s_current_type;
|
static unsigned s_current_type;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_fails_clean;
|
bool m_fails_clean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Scoped status line reporter. Prints a message and an "OK" if no errors
|
/// Scoped status line reporter. Prints a message and an "OK" if no errors
|
||||||
/// or warnings were reported before destruction, otherwise reports the
|
/// or warnings were reported before destruction, otherwise reports the
|
||||||
/// error or warning.
|
/// error or warning.
|
||||||
class status_line :
|
class status_line :
|
||||||
public status
|
public status
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr static unsigned type = 1;
|
constexpr static unsigned type = 1;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg message Description of the operation in progress
|
/// \arg message Description of the operation in progress
|
||||||
/// \arg context If non-null, printed after `message` and a colon
|
/// \arg context If non-null, printed after `message` and a colon
|
||||||
/// \arg fails_clean If true, this object can handle printing failure
|
/// \arg fails_clean If true, this object can handle printing failure
|
||||||
status_line(
|
status_line(
|
||||||
const wchar_t *message,
|
const wchar_t *message,
|
||||||
const wchar_t *context = nullptr,
|
const wchar_t *context = nullptr,
|
||||||
bool fails_clean = true);
|
bool fails_clean = true);
|
||||||
~status_line();
|
~status_line();
|
||||||
|
|
||||||
virtual void do_warn(const wchar_t *message, uefi::status status) override;
|
virtual void do_warn(const wchar_t *message, uefi::status status) override;
|
||||||
virtual void do_fail(const wchar_t *message, uefi::status status) override;
|
virtual void do_fail(const wchar_t *message, uefi::status status) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void print_status_tag();
|
void print_status_tag();
|
||||||
|
|
||||||
size_t m_line;
|
size_t m_line;
|
||||||
int m_level;
|
int m_level;
|
||||||
int m_depth;
|
int m_depth;
|
||||||
|
|
||||||
status_line *m_outer;
|
status_line *m_outer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Scoped status bar reporter. Draws a row of boxes along the bottom of
|
/// Scoped status bar reporter. Draws a row of boxes along the bottom of
|
||||||
/// the screen, turning one red if there's an error in that step.
|
/// the screen, turning one red if there's an error in that step.
|
||||||
class status_bar :
|
class status_bar :
|
||||||
public status
|
public status
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr static unsigned type = 2;
|
constexpr static unsigned type = 2;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
status_bar(video::screen *screen);
|
status_bar(video::screen *screen);
|
||||||
~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;
|
||||||
virtual void do_fail(const wchar_t *message, uefi::status status) override;
|
virtual void do_fail(const wchar_t *message, uefi::status status) override;
|
||||||
|
|
||||||
inline void next() {
|
inline void next() {
|
||||||
m_current = s_count++;
|
m_current = s_count++;
|
||||||
m_level = 0;
|
m_level = 0;
|
||||||
m_status = uefi::status::success;
|
m_status = uefi::status::success;
|
||||||
draw_box();
|
draw_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void draw_box();
|
void draw_box();
|
||||||
|
|
||||||
uint32_t *m_fb;
|
uint32_t *m_fb;
|
||||||
uint32_t m_size;
|
uint32_t m_size;
|
||||||
uint32_t m_top;
|
uint32_t m_top;
|
||||||
uint32_t m_horiz;
|
uint32_t m_horiz;
|
||||||
|
|
||||||
int m_level;
|
int m_level;
|
||||||
uefi::status m_status;
|
uefi::status m_status;
|
||||||
|
|
||||||
uint16_t m_type;
|
uint16_t m_type;
|
||||||
uint16_t m_current;
|
uint16_t m_current;
|
||||||
|
|
||||||
status_bar *m_outer;
|
status_bar *m_outer;
|
||||||
|
|
||||||
static unsigned s_count;
|
static unsigned s_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ extern "C" {
|
|||||||
__attribute__ ((__weak__))
|
__attribute__ ((__weak__))
|
||||||
void *memcpy(void *dest, const void *src, size_t n)
|
void *memcpy(void *dest, const void *src, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
|
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
|
||||||
const uint8_t *csrc = reinterpret_cast<const uint8_t*>(src);
|
const uint8_t *csrc = reinterpret_cast<const uint8_t*>(src);
|
||||||
for (size_t i = 0; i < n; ++i)
|
for (size_t i = 0; i < n; ++i)
|
||||||
cdest[i] = csrc[i];
|
cdest[i] = csrc[i];
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Basic memset() implementation for clang. Clang requires freestanding code
|
/// Basic memset() implementation for clang. Clang requires freestanding code
|
||||||
@@ -23,15 +23,15 @@ void *memcpy(void *dest, const void *src, size_t n)
|
|||||||
__attribute__ ((__weak__))
|
__attribute__ ((__weak__))
|
||||||
void *memset(void *dest, int c, size_t n)
|
void *memset(void *dest, int c, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
|
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
|
||||||
for (size_t i = 0; i < n; ++i)
|
for (size_t i = 0; i < n; ++i)
|
||||||
cdest[i] = static_cast<uint8_t>(c);
|
cdest[i] = static_cast<uint8_t>(c);
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _purecall()
|
int _purecall()
|
||||||
{
|
{
|
||||||
::boot::error::raise(uefi::status::unsupported, L"Pure virtual call");
|
::boot::error::raise(uefi::status::unsupported, L"Pure virtual call");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clang can emit calls to atexit() in constructors or destructors, but
|
/// Clang can emit calls to atexit() in constructors or destructors, but
|
||||||
|
|||||||
@@ -20,102 +20,102 @@ using kernel::init::module_type;
|
|||||||
static uefi::protos::graphics_output *
|
static uefi::protos::graphics_output *
|
||||||
get_gop(uefi::boot_services *bs)
|
get_gop(uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
uefi::protos::graphics_output *gop = nullptr;
|
uefi::protos::graphics_output *gop = nullptr;
|
||||||
uefi::guid guid = uefi::protos::graphics_output::guid;
|
uefi::guid guid = uefi::protos::graphics_output::guid;
|
||||||
|
|
||||||
uefi::status has_gop = bs->locate_protocol(&guid, nullptr,
|
uefi::status has_gop = bs->locate_protocol(&guid, nullptr,
|
||||||
(void **)&gop);
|
(void **)&gop);
|
||||||
|
|
||||||
if (has_gop != uefi::status::success)
|
if (has_gop != uefi::status::success)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return gop;
|
return gop;
|
||||||
}
|
}
|
||||||
|
|
||||||
screen *
|
screen *
|
||||||
pick_mode(uefi::boot_services *bs)
|
pick_mode(uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
uefi::protos::graphics_output *gop = get_gop(bs);
|
uefi::protos::graphics_output *gop = get_gop(bs);
|
||||||
if (!gop) {
|
if (!gop) {
|
||||||
console::print(L"No framebuffer found.\r\n");
|
console::print(L"No framebuffer found.\r\n");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
uefi::graphics_output_mode_info *info = gop->mode->info;
|
uefi::graphics_output_mode_info *info = gop->mode->info;
|
||||||
|
|
||||||
uint32_t best = gop->mode->mode;
|
uint32_t best = gop->mode->mode;
|
||||||
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
|
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
|
||||||
int pixmode = static_cast<int>(info->pixel_format);
|
int pixmode = static_cast<int>(info->pixel_format);
|
||||||
|
|
||||||
const uint32_t modes = gop->mode->max_mode;
|
const uint32_t modes = gop->mode->max_mode;
|
||||||
for (uint32_t i = 0; i < modes; ++i) {
|
for (uint32_t i = 0; i < modes; ++i) {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
uefi::graphics_output_mode_info *new_info = nullptr;
|
uefi::graphics_output_mode_info *new_info = nullptr;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
gop->query_mode(i, &size, &new_info),
|
gop->query_mode(i, &size, &new_info),
|
||||||
L"Failed to find a graphics mode the driver claimed to support");
|
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;
|
const uint32_t new_res = new_info->horizontal_resolution * new_info->vertical_resolution;
|
||||||
int new_pixmode = static_cast<int>(new_info->pixel_format);
|
int new_pixmode = static_cast<int>(new_info->pixel_format);
|
||||||
|
|
||||||
if (new_pixmode <= pixmode && new_res >= res) {
|
if (new_pixmode <= pixmode && new_res >= res) {
|
||||||
best = i;
|
best = i;
|
||||||
res = new_res;
|
res = new_res;
|
||||||
pixmode = new_pixmode;
|
pixmode = new_pixmode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
screen *s = new screen;
|
screen *s = new screen;
|
||||||
s->mode = {
|
s->mode = {
|
||||||
.vertical = gop->mode->info->vertical_resolution,
|
.vertical = gop->mode->info->vertical_resolution,
|
||||||
.horizontal = gop->mode->info->horizontal_resolution,
|
.horizontal = gop->mode->info->horizontal_resolution,
|
||||||
.scanline = gop->mode->info->pixels_per_scanline,
|
.scanline = gop->mode->info->pixels_per_scanline,
|
||||||
.layout = layout::unknown,
|
.layout = layout::unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
s->framebuffer = {
|
s->framebuffer = {
|
||||||
.pointer = reinterpret_cast<void*>(gop->mode->frame_buffer_base),
|
.pointer = reinterpret_cast<void*>(gop->mode->frame_buffer_base),
|
||||||
.count = gop->mode->frame_buffer_size
|
.count = gop->mode->frame_buffer_size
|
||||||
};
|
};
|
||||||
|
|
||||||
wchar_t const * type = nullptr;
|
wchar_t const * type = nullptr;
|
||||||
switch (info->pixel_format) {
|
switch (info->pixel_format) {
|
||||||
case uefi::pixel_format::rgb8:
|
case uefi::pixel_format::rgb8:
|
||||||
type = L"rgb8";
|
type = L"rgb8";
|
||||||
s->mode.layout = layout::rgb8;
|
s->mode.layout = layout::rgb8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case uefi::pixel_format::bgr8:
|
case uefi::pixel_format::bgr8:
|
||||||
type = L"bgr8";
|
type = L"bgr8";
|
||||||
s->mode.layout = layout::bgr8;
|
s->mode.layout = layout::bgr8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
type = L"unknown";
|
type = L"unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
console::print(L"Found framebuffer: %dx%d[%d] type %s @0x%x\r\n",
|
console::print(L"Found framebuffer: %dx%d[%d] type %s @0x%x\r\n",
|
||||||
info->horizontal_resolution, info->vertical_resolution,
|
info->horizontal_resolution, info->vertical_resolution,
|
||||||
info->pixels_per_scanline, type, gop->mode->frame_buffer_base);
|
info->pixels_per_scanline, type, gop->mode->frame_buffer_base);
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
gop->set_mode(best),
|
gop->set_mode(best),
|
||||||
L"Failed to set graphics mode");
|
L"Failed to set graphics mode");
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
make_module(screen *s)
|
make_module(screen *s)
|
||||||
{
|
{
|
||||||
using kernel::init::module_framebuffer;
|
using kernel::init::module_framebuffer;
|
||||||
module_framebuffer *modfb = g_alloc.allocate_module<module_framebuffer>();
|
module_framebuffer *modfb = g_alloc.allocate_module<module_framebuffer>();
|
||||||
modfb->mod_type = module_type::framebuffer;
|
modfb->mod_type = module_type::framebuffer;
|
||||||
modfb->type = fb_type::uefi;
|
modfb->type = fb_type::uefi;
|
||||||
|
|
||||||
modfb->framebuffer = s->framebuffer;
|
modfb->framebuffer = s->framebuffer;
|
||||||
modfb->mode = s->mode;
|
modfb->mode = s->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace video
|
} // namespace video
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "init_args.h"
|
#include "init_args.h"
|
||||||
|
|
||||||
namespace uefi {
|
namespace uefi {
|
||||||
struct boot_services;
|
struct boot_services;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
@@ -18,8 +18,8 @@ using kernel::init::video_mode;
|
|||||||
using layout = kernel::init::fb_layout;
|
using layout = kernel::init::fb_layout;
|
||||||
|
|
||||||
struct screen {
|
struct screen {
|
||||||
buffer framebuffer;
|
buffer framebuffer;
|
||||||
video_mode mode;
|
video_mode mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Pick the best video mode and set up the screen
|
/// Pick the best video mode and set up the screen
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ template <> struct integral<unsigned long long> { using type = unsigned long lon
|
|||||||
template <typename T, T v>
|
template <typename T, T v>
|
||||||
struct integral_constant
|
struct integral_constant
|
||||||
{
|
{
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using type = integral_constant;
|
using type = integral_constant;
|
||||||
static constexpr value_type value = v;
|
static constexpr value_type value = v;
|
||||||
constexpr operator value_type() const noexcept { return value; }
|
constexpr operator value_type() const noexcept { return value; }
|
||||||
constexpr value_type operator()() const noexcept { return value; }
|
constexpr value_type operator()() const noexcept { return value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
using true_type = integral_constant<bool, true>;
|
using true_type = integral_constant<bool, true>;
|
||||||
|
|||||||
@@ -10,37 +10,37 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct counted
|
struct counted
|
||||||
{
|
{
|
||||||
T *pointer;
|
T *pointer;
|
||||||
size_t count;
|
size_t count;
|
||||||
|
|
||||||
/// Index this object as an array of type T
|
/// Index this object as an array of type T
|
||||||
inline T & operator [] (int i) { return pointer[i]; }
|
inline T & operator [] (int i) { return pointer[i]; }
|
||||||
|
|
||||||
/// Index this object as a const array of type T
|
/// Index this object as a const array of type T
|
||||||
inline const T & operator [] (int i) const { return pointer[i]; }
|
inline const T & operator [] (int i) const { return pointer[i]; }
|
||||||
|
|
||||||
using iterator = offset_iterator<T>;
|
using iterator = offset_iterator<T>;
|
||||||
using const_iterator = const_offset_iterator<T>;
|
using const_iterator = const_offset_iterator<T>;
|
||||||
|
|
||||||
/// Return an iterator to the beginning of the array
|
/// Return an iterator to the beginning of the array
|
||||||
inline iterator begin() { return iterator(pointer, sizeof(T)); }
|
inline iterator begin() { return iterator(pointer, sizeof(T)); }
|
||||||
|
|
||||||
/// Return an iterator to the end of the array
|
/// Return an iterator to the end of the array
|
||||||
inline iterator end() { return offset_ptr<T>(pointer, sizeof(T)*count); }
|
inline iterator end() { return offset_ptr<T>(pointer, sizeof(T)*count); }
|
||||||
|
|
||||||
/// Return an iterator to the beginning of the array
|
/// Return an iterator to the beginning of the array
|
||||||
inline const_iterator begin() const { return const_iterator(pointer, sizeof(T)); }
|
inline const_iterator begin() const { return const_iterator(pointer, sizeof(T)); }
|
||||||
|
|
||||||
/// Return an iterator to the end of the array
|
/// Return an iterator to the end of the array
|
||||||
inline const_iterator end() const { return offset_ptr<const T>(pointer, sizeof(T)*count); }
|
inline const_iterator end() const { return offset_ptr<const T>(pointer, sizeof(T)*count); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Specialize for `void` which cannot be indexed or iterated
|
/// Specialize for `void` which cannot be indexed or iterated
|
||||||
template <>
|
template <>
|
||||||
struct counted<void>
|
struct counted<void>
|
||||||
{
|
{
|
||||||
void *pointer;
|
void *pointer;
|
||||||
size_t count;
|
size_t count;
|
||||||
};
|
};
|
||||||
|
|
||||||
using buffer = counted<void>;
|
using buffer = counted<void>;
|
||||||
|
|||||||
@@ -9,41 +9,41 @@ constexpr bool is_enum_bitfield(E) { return false; }
|
|||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
struct enum_or_int {
|
struct enum_or_int {
|
||||||
static constexpr bool value =
|
static constexpr bool value =
|
||||||
is_enum_bitfield(E{}) || types::is_integral<E>::value;
|
is_enum_bitfield(E{}) || types::is_integral<E>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename E, typename F>
|
template <typename E, typename F>
|
||||||
struct both_enum_or_int {
|
struct both_enum_or_int {
|
||||||
static constexpr bool value =
|
static constexpr bool value =
|
||||||
types::conjunction< enum_or_int<E>, enum_or_int<F> >::value;
|
types::conjunction< enum_or_int<E>, enum_or_int<F> >::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename E, typename F>
|
template <typename E, typename F>
|
||||||
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
|
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
|
||||||
operator |= (E &lhs, F rhs)
|
operator |= (E &lhs, F rhs)
|
||||||
{
|
{
|
||||||
return lhs = static_cast<E>(
|
return lhs = static_cast<E>(
|
||||||
static_cast<typename types::integral<E>::type>(lhs) |
|
static_cast<typename types::integral<E>::type>(lhs) |
|
||||||
static_cast<typename types::integral<F>::type>(rhs));
|
static_cast<typename types::integral<F>::type>(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E, typename F>
|
template <typename E, typename F>
|
||||||
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
|
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
|
||||||
operator &= (E &lhs, F rhs)
|
operator &= (E &lhs, F rhs)
|
||||||
{
|
{
|
||||||
return lhs = static_cast<E>(
|
return lhs = static_cast<E>(
|
||||||
static_cast<typename types::integral<E>::type>(lhs) &
|
static_cast<typename types::integral<E>::type>(lhs) &
|
||||||
static_cast<typename types::integral<F>::type>(rhs));
|
static_cast<typename types::integral<F>::type>(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E, typename F>
|
template <typename E, typename F>
|
||||||
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
|
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
|
||||||
operator ^= (E &lhs, F rhs)
|
operator ^= (E &lhs, F rhs)
|
||||||
{
|
{
|
||||||
return lhs = static_cast<E>(
|
return lhs = static_cast<E>(
|
||||||
static_cast<typename types::integral<E>::type>(lhs) ^
|
static_cast<typename types::integral<E>::type>(lhs) ^
|
||||||
static_cast<typename types::integral<F>::type>(rhs));
|
static_cast<typename types::integral<F>::type>(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E, typename F>
|
template <typename E, typename F>
|
||||||
@@ -75,15 +75,15 @@ operator && (E rhs, E lhs) { return (rhs & lhs) == lhs; }
|
|||||||
template <typename E, typename F>
|
template <typename E, typename F>
|
||||||
constexpr bool has(E set, F flags)
|
constexpr bool has(E set, F flags)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
(static_cast<typename types::integral<E>::type>(set) &
|
(static_cast<typename types::integral<E>::type>(set) &
|
||||||
static_cast<typename types::integral<F>::type>(flags)) ==
|
static_cast<typename types::integral<F>::type>(flags)) ==
|
||||||
static_cast<typename types::integral<F>::type>(flags);
|
static_cast<typename types::integral<F>::type>(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace bitfields
|
} // namespace bitfields
|
||||||
|
|
||||||
#define is_bitfield(name) \
|
#define is_bitfield(name) \
|
||||||
constexpr bool is_enum_bitfield(name) { return true; } \
|
constexpr bool is_enum_bitfield(name) { return true; } \
|
||||||
using namespace ::bitfields;
|
using namespace ::bitfields;
|
||||||
|
|
||||||
|
|||||||
@@ -9,32 +9,32 @@ namespace kernel {
|
|||||||
namespace init {
|
namespace init {
|
||||||
|
|
||||||
enum class module_type : uint8_t {
|
enum class module_type : uint8_t {
|
||||||
none,
|
none,
|
||||||
program,
|
program,
|
||||||
framebuffer,
|
framebuffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class module_flags : uint8_t {
|
enum class module_flags : uint8_t {
|
||||||
none = 0x00,
|
none = 0x00,
|
||||||
|
|
||||||
/// This module was already handled by the bootloader,
|
/// This module was already handled by the bootloader,
|
||||||
/// no action is needed. The module is included for
|
/// no action is needed. The module is included for
|
||||||
/// informational purposes only.
|
/// informational purposes only.
|
||||||
no_load = 0x01,
|
no_load = 0x01,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct module
|
struct module
|
||||||
{
|
{
|
||||||
module_type mod_type;
|
module_type mod_type;
|
||||||
module_flags mod_flags;
|
module_flags mod_flags;
|
||||||
uint32_t mod_length;
|
uint32_t mod_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct module_program :
|
struct module_program :
|
||||||
public module
|
public module
|
||||||
{
|
{
|
||||||
uintptr_t base_address;
|
uintptr_t base_address;
|
||||||
char filename[];
|
char filename[];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class fb_layout : uint8_t { rgb8, bgr8, unknown = 0xff };
|
enum class fb_layout : uint8_t { rgb8, bgr8, unknown = 0xff };
|
||||||
@@ -42,25 +42,25 @@ enum class fb_type : uint8_t { uefi };
|
|||||||
|
|
||||||
struct video_mode
|
struct video_mode
|
||||||
{
|
{
|
||||||
uint32_t vertical;
|
uint32_t vertical;
|
||||||
uint32_t horizontal;
|
uint32_t horizontal;
|
||||||
uint32_t scanline;
|
uint32_t scanline;
|
||||||
fb_layout layout;
|
fb_layout layout;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct module_framebuffer :
|
struct module_framebuffer :
|
||||||
public module
|
public module
|
||||||
{
|
{
|
||||||
buffer framebuffer;
|
buffer framebuffer;
|
||||||
video_mode mode;
|
video_mode mode;
|
||||||
fb_type type;
|
fb_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct modules_page
|
struct modules_page
|
||||||
{
|
{
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
module *modules;
|
module *modules;
|
||||||
uintptr_t next;
|
uintptr_t next;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace init
|
} // namespace init
|
||||||
|
|||||||
@@ -2,19 +2,19 @@
|
|||||||
/// \file errors.h
|
/// \file errors.h
|
||||||
/// Collection of constants for the j6_status_t type
|
/// Collection of constants for the j6_status_t type
|
||||||
|
|
||||||
#define j6_status_error 0x8000000000000000
|
#define j6_status_error 0x8000000000000000
|
||||||
#define j6_err(x) ((x) | j6_status_error)
|
#define j6_err(x) ((x) | j6_status_error)
|
||||||
#define j6_is_err(x) (((x) & j6_status_error) == j6_status_error)
|
#define j6_is_err(x) (((x) & j6_status_error) == j6_status_error)
|
||||||
|
|
||||||
#define j6_status_ok 0x0000
|
#define j6_status_ok 0x0000
|
||||||
|
|
||||||
#define j6_status_closed 0x1000
|
#define j6_status_closed 0x1000
|
||||||
#define j6_status_destroyed 0x1001
|
#define j6_status_destroyed 0x1001
|
||||||
#define j6_status_exists 0x1002
|
#define j6_status_exists 0x1002
|
||||||
|
|
||||||
#define j6_err_nyi j6_err(0x0001)
|
#define j6_err_nyi j6_err(0x0001)
|
||||||
#define j6_err_unexpected j6_err(0x0002)
|
#define j6_err_unexpected j6_err(0x0002)
|
||||||
#define j6_err_invalid_arg j6_err(0x0003)
|
#define j6_err_invalid_arg j6_err(0x0003)
|
||||||
#define j6_err_not_ready j6_err(0x0004)
|
#define j6_err_not_ready j6_err(0x0004)
|
||||||
#define j6_err_insufficient j6_err(0x0005)
|
#define j6_err_insufficient j6_err(0x0005)
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ enum j6_vm_flags {
|
|||||||
#define VM_FLAG(name, v) j6_vm_flag_ ## name = v,
|
#define VM_FLAG(name, v) j6_vm_flag_ ## name = v,
|
||||||
#include "j6/tables/vm_flags.inc"
|
#include "j6/tables/vm_flags.inc"
|
||||||
#undef VM_FLAG
|
#undef VM_FLAG
|
||||||
j6_vm_flags_MAX
|
j6_vm_flags_MAX
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,33 +5,33 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
|
|
||||||
enum j6_init_type { // `value` is a:
|
enum j6_init_type { // `value` is a:
|
||||||
j6_init_handle_self, // Handle to the system
|
j6_init_handle_self, // Handle to the system
|
||||||
j6_init_handle_other, // Handle to this process
|
j6_init_handle_other, // Handle to this process
|
||||||
j6_init_desc_framebuffer // Pointer to a j6_init_framebuffer descriptor
|
j6_init_desc_framebuffer // Pointer to a j6_init_framebuffer descriptor
|
||||||
};
|
};
|
||||||
|
|
||||||
struct j6_typed_handle {
|
struct j6_typed_handle {
|
||||||
enum j6_object_type type;
|
enum j6_object_type type;
|
||||||
j6_handle_t handle;
|
j6_handle_t handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct j6_init_value {
|
struct j6_init_value {
|
||||||
enum j6_init_type type;
|
enum j6_init_type type;
|
||||||
union {
|
union {
|
||||||
struct j6_typed_handle handle;
|
struct j6_typed_handle handle;
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Structure defining a framebuffer.
|
/// Structure defining a framebuffer.
|
||||||
/// `flags` has the following bits:
|
/// `flags` has the following bits:
|
||||||
/// 0-3: Pixel layout. 0000: rgb8, 0001: bgr8
|
/// 0-3: Pixel layout. 0000: rgb8, 0001: bgr8
|
||||||
struct j6_init_framebuffer {
|
struct j6_init_framebuffer {
|
||||||
uintptr_t addr;
|
uintptr_t addr;
|
||||||
size_t size;
|
size_t size;
|
||||||
uint32_t vertical;
|
uint32_t vertical;
|
||||||
uint32_t horizontal;
|
uint32_t horizontal;
|
||||||
uint32_t scanline;
|
uint32_t scanline;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
/// Collection of constants for the j6_signal_t type
|
/// Collection of constants for the j6_signal_t type
|
||||||
|
|
||||||
// Signals 0-15 are common to all types
|
// Signals 0-15 are common to all types
|
||||||
#define j6_signal_no_handles (1ull << 0)
|
#define j6_signal_no_handles (1ull << 0)
|
||||||
#define j6_signal_closed (1ull << 1)
|
#define j6_signal_closed (1ull << 1)
|
||||||
|
|
||||||
// Signals 16-47 are defined per-object-type
|
// Signals 16-47 are defined per-object-type
|
||||||
|
|
||||||
@@ -43,32 +43,32 @@
|
|||||||
#define j7_signal_event31 (1ull << 47)
|
#define j7_signal_event31 (1ull << 47)
|
||||||
|
|
||||||
// System signals
|
// System signals
|
||||||
#define j6_signal_system_has_log (1ull << 16)
|
#define j6_signal_system_has_log (1ull << 16)
|
||||||
|
|
||||||
// Channel signals
|
// Channel signals
|
||||||
#define j6_signal_channel_can_send (1ull << 16)
|
#define j6_signal_channel_can_send (1ull << 16)
|
||||||
#define j6_signal_channel_can_recv (1ull << 17)
|
#define j6_signal_channel_can_recv (1ull << 17)
|
||||||
|
|
||||||
// Endpoint signals
|
// Endpoint signals
|
||||||
#define j6_signal_endpoint_can_send (1ull << 16)
|
#define j6_signal_endpoint_can_send (1ull << 16)
|
||||||
#define j6_signal_endpoint_can_recv (1ull << 17)
|
#define j6_signal_endpoint_can_recv (1ull << 17)
|
||||||
|
|
||||||
// Signals 48-63 are user-defined signals
|
// Signals 48-63 are user-defined signals
|
||||||
#define j6_signal_user0 (1ull << 48)
|
#define j6_signal_user0 (1ull << 48)
|
||||||
#define j6_signal_user1 (1ull << 49)
|
#define j6_signal_user1 (1ull << 49)
|
||||||
#define j6_signal_user2 (1ull << 50)
|
#define j6_signal_user2 (1ull << 50)
|
||||||
#define j6_signal_user3 (1ull << 51)
|
#define j6_signal_user3 (1ull << 51)
|
||||||
#define j6_signal_user4 (1ull << 52)
|
#define j6_signal_user4 (1ull << 52)
|
||||||
#define j6_signal_user5 (1ull << 53)
|
#define j6_signal_user5 (1ull << 53)
|
||||||
#define j6_signal_user6 (1ull << 54)
|
#define j6_signal_user6 (1ull << 54)
|
||||||
#define j6_signal_user7 (1ull << 55)
|
#define j6_signal_user7 (1ull << 55)
|
||||||
#define j6_signal_user8 (1ull << 56)
|
#define j6_signal_user8 (1ull << 56)
|
||||||
#define j6_signal_user9 (1ull << 57)
|
#define j6_signal_user9 (1ull << 57)
|
||||||
#define j6_signal_user10 (1ull << 58)
|
#define j6_signal_user10 (1ull << 58)
|
||||||
#define j6_signal_user11 (1ull << 59)
|
#define j6_signal_user11 (1ull << 59)
|
||||||
#define j6_signal_user12 (1ull << 60)
|
#define j6_signal_user12 (1ull << 60)
|
||||||
#define j6_signal_user13 (1ull << 61)
|
#define j6_signal_user13 (1ull << 61)
|
||||||
#define j6_signal_user14 (1ull << 62)
|
#define j6_signal_user14 (1ull << 62)
|
||||||
#define j6_signal_user15 (1ull << 63)
|
#define j6_signal_user15 (1ull << 63)
|
||||||
|
|
||||||
#define j6_signal_user_mask (0xffffull << 48)
|
#define j6_signal_user_mask (0xffffull << 48)
|
||||||
|
|||||||
@@ -41,5 +41,5 @@ enum j6_object_type {
|
|||||||
#include "j6/tables/object_types.inc"
|
#include "j6/tables/object_types.inc"
|
||||||
#undef OBJECT_TYPE
|
#undef OBJECT_TYPE
|
||||||
|
|
||||||
j6_object_type_max
|
j6_object_type_max
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,83 +16,83 @@ constexpr uint16_t header_version = 2;
|
|||||||
constexpr uint16_t min_header_version = 2;
|
constexpr uint16_t min_header_version = 2;
|
||||||
|
|
||||||
enum class section_flags : uint32_t {
|
enum class section_flags : uint32_t {
|
||||||
none = 0,
|
none = 0,
|
||||||
execute = 1,
|
execute = 1,
|
||||||
write = 2,
|
write = 2,
|
||||||
read = 4,
|
read = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct program_section {
|
struct program_section {
|
||||||
uintptr_t phys_addr;
|
uintptr_t phys_addr;
|
||||||
uintptr_t virt_addr;
|
uintptr_t virt_addr;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
section_flags type;
|
section_flags type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct program {
|
struct program {
|
||||||
uintptr_t entrypoint;
|
uintptr_t entrypoint;
|
||||||
uintptr_t phys_base;
|
uintptr_t phys_base;
|
||||||
counted<program_section> sections;
|
counted<program_section> sections;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class mem_type : uint32_t {
|
enum class mem_type : uint32_t {
|
||||||
free,
|
free,
|
||||||
pending,
|
pending,
|
||||||
acpi,
|
acpi,
|
||||||
uefi_runtime,
|
uefi_runtime,
|
||||||
mmio,
|
mmio,
|
||||||
persistent
|
persistent
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Structure to hold an entry in the memory map.
|
/// Structure to hold an entry in the memory map.
|
||||||
struct mem_entry
|
struct mem_entry
|
||||||
{
|
{
|
||||||
uintptr_t start;
|
uintptr_t start;
|
||||||
size_t pages;
|
size_t pages;
|
||||||
mem_type type;
|
mem_type type;
|
||||||
uint32_t attr;
|
uint32_t attr;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class allocation_type : uint8_t {
|
enum class allocation_type : uint8_t {
|
||||||
none, page_table, mem_map, frame_map, file, program, init_args,
|
none, page_table, mem_map, frame_map, file, program, init_args,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A single contiguous allocation of pages
|
/// A single contiguous allocation of pages
|
||||||
struct page_allocation
|
struct page_allocation
|
||||||
{
|
{
|
||||||
uintptr_t address;
|
uintptr_t address;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
allocation_type type;
|
allocation_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A page-sized register of page_allocation entries
|
/// A page-sized register of page_allocation entries
|
||||||
struct allocation_register
|
struct allocation_register
|
||||||
{
|
{
|
||||||
allocation_register *next;
|
allocation_register *next;
|
||||||
uint8_t count;
|
uint8_t count;
|
||||||
|
|
||||||
uint8_t reserved0;
|
uint8_t reserved0;
|
||||||
uint16_t reserved1;
|
uint16_t reserved1;
|
||||||
uint32_t reserved2;
|
uint32_t reserved2;
|
||||||
|
|
||||||
page_allocation entries[255];
|
page_allocation entries[255];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class frame_flags : uint32_t {
|
enum class frame_flags : uint32_t {
|
||||||
uncacheable = 0x00000001,
|
uncacheable = 0x00000001,
|
||||||
write_combining = 0x00000002,
|
write_combining = 0x00000002,
|
||||||
write_through = 0x00000004,
|
write_through = 0x00000004,
|
||||||
write_back = 0x00000008,
|
write_back = 0x00000008,
|
||||||
uncache_exported = 0x00000010,
|
uncache_exported = 0x00000010,
|
||||||
|
|
||||||
write_protect = 0x00001000,
|
write_protect = 0x00001000,
|
||||||
read_protect = 0x00002000,
|
read_protect = 0x00002000,
|
||||||
exec_protect = 0x00004000,
|
exec_protect = 0x00004000,
|
||||||
non_volatile = 0x00008000,
|
non_volatile = 0x00008000,
|
||||||
|
|
||||||
read_only = 0x00020000,
|
read_only = 0x00020000,
|
||||||
earmarked = 0x00040000,
|
earmarked = 0x00040000,
|
||||||
hw_crypto = 0x00080000,
|
hw_crypto = 0x00080000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -100,39 +100,39 @@ constexpr size_t frames_per_block = 64 * 64 * 64;
|
|||||||
|
|
||||||
struct frame_block
|
struct frame_block
|
||||||
{
|
{
|
||||||
uintptr_t base;
|
uintptr_t base;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
frame_flags flags;
|
frame_flags flags;
|
||||||
uint64_t map1;
|
uint64_t map1;
|
||||||
uint64_t map2[64];
|
uint64_t map2[64];
|
||||||
uint64_t *bitmap;
|
uint64_t *bitmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class boot_flags : uint16_t {
|
enum class boot_flags : uint16_t {
|
||||||
none = 0x0000,
|
none = 0x0000,
|
||||||
debug = 0x0001
|
debug = 0x0001
|
||||||
};
|
};
|
||||||
|
|
||||||
struct args
|
struct args
|
||||||
{
|
{
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
boot_flags flags;
|
boot_flags flags;
|
||||||
|
|
||||||
void *pml4;
|
void *pml4;
|
||||||
counted<void> page_tables;
|
counted<void> page_tables;
|
||||||
counted<mem_entry> mem_map;
|
counted<mem_entry> mem_map;
|
||||||
counted<frame_block> frame_blocks;
|
counted<frame_block> frame_blocks;
|
||||||
|
|
||||||
program *kernel;
|
program *kernel;
|
||||||
program *init;
|
program *init;
|
||||||
program *panic;
|
program *panic;
|
||||||
allocation_register *allocations;
|
allocation_register *allocations;
|
||||||
uintptr_t modules;
|
uintptr_t modules;
|
||||||
uintptr_t symbol_table;
|
uintptr_t symbol_table;
|
||||||
|
|
||||||
void *runtime_services;
|
void *runtime_services;
|
||||||
void *acpi_table;
|
void *acpi_table;
|
||||||
}
|
}
|
||||||
__attribute__((aligned(alignof(max_align_t))));
|
__attribute__((aligned(alignof(max_align_t))));
|
||||||
|
|
||||||
|
|||||||
@@ -7,79 +7,79 @@
|
|||||||
|
|
||||||
namespace memory {
|
namespace memory {
|
||||||
|
|
||||||
/// Size of a single page frame.
|
/// Size of a single page frame.
|
||||||
constexpr size_t frame_size = 0x1000;
|
constexpr size_t frame_size = 0x1000;
|
||||||
|
|
||||||
/// Start of kernel memory.
|
/// Start of kernel memory.
|
||||||
constexpr uintptr_t kernel_offset = 0xffff800000000000ull;
|
constexpr uintptr_t kernel_offset = 0xffff800000000000ull;
|
||||||
|
|
||||||
/// Offset from physical where page tables are mapped.
|
/// Offset from physical where page tables are mapped.
|
||||||
constexpr uintptr_t page_offset = 0xffffc00000000000ull;
|
constexpr uintptr_t page_offset = 0xffffc00000000000ull;
|
||||||
|
|
||||||
/// Max number of pages for a kernel stack
|
/// Max number of pages for a kernel stack
|
||||||
constexpr unsigned kernel_stack_pages = 2;
|
constexpr unsigned kernel_stack_pages = 2;
|
||||||
|
|
||||||
/// Max number of pages for a kernel buffer
|
/// Max number of pages for a kernel buffer
|
||||||
constexpr unsigned kernel_buffer_pages = 16;
|
constexpr unsigned kernel_buffer_pages = 16;
|
||||||
|
|
||||||
/// Max size of the kernel heap
|
/// Max size of the kernel heap
|
||||||
constexpr size_t kernel_max_heap = 0x8000000000ull; // 512GiB
|
constexpr size_t kernel_max_heap = 0x8000000000ull; // 512GiB
|
||||||
|
|
||||||
/// Start of the kernel heap
|
/// Start of the kernel heap
|
||||||
constexpr uintptr_t heap_start = page_offset - kernel_max_heap;
|
constexpr uintptr_t heap_start = page_offset - kernel_max_heap;
|
||||||
|
|
||||||
/// Max size of the kernel stacks area
|
/// Max size of the kernel stacks area
|
||||||
constexpr size_t kernel_max_stacks = 0x8000000000ull; // 512GiB
|
constexpr size_t kernel_max_stacks = 0x8000000000ull; // 512GiB
|
||||||
|
|
||||||
/// Start of the kernel stacks
|
/// Start of the kernel stacks
|
||||||
constexpr uintptr_t stacks_start = heap_start - kernel_max_stacks;
|
constexpr uintptr_t stacks_start = heap_start - kernel_max_stacks;
|
||||||
|
|
||||||
/// Max size of kernel buffers area
|
/// Max size of kernel buffers area
|
||||||
constexpr size_t kernel_max_buffers = 0x8000000000ull; // 512GiB
|
constexpr size_t kernel_max_buffers = 0x8000000000ull; // 512GiB
|
||||||
|
|
||||||
/// Start of kernel buffers
|
/// Start of kernel buffers
|
||||||
constexpr uintptr_t buffers_start = stacks_start - kernel_max_buffers;
|
constexpr uintptr_t buffers_start = stacks_start - kernel_max_buffers;
|
||||||
|
|
||||||
/// Max size of kernel bitmap area
|
/// Max size of kernel bitmap area
|
||||||
constexpr size_t kernel_max_bitmap = 0x8000000000ull; // 512GiB
|
constexpr size_t kernel_max_bitmap = 0x8000000000ull; // 512GiB
|
||||||
|
|
||||||
/// Start of kernel bitmap
|
/// Start of kernel bitmap
|
||||||
constexpr uintptr_t bitmap_start = buffers_start - kernel_max_bitmap;
|
constexpr uintptr_t bitmap_start = buffers_start - kernel_max_bitmap;
|
||||||
|
|
||||||
/// First kernel space PML4 entry
|
/// First kernel space PML4 entry
|
||||||
constexpr unsigned pml4e_kernel = 256;
|
constexpr unsigned pml4e_kernel = 256;
|
||||||
|
|
||||||
/// First offset-mapped space PML4 entry
|
/// First offset-mapped space PML4 entry
|
||||||
constexpr unsigned pml4e_offset = 384;
|
constexpr unsigned pml4e_offset = 384;
|
||||||
|
|
||||||
/// Number of page_table entries
|
/// Number of page_table entries
|
||||||
constexpr unsigned table_entries = 512;
|
constexpr unsigned table_entries = 512;
|
||||||
|
|
||||||
/// Helper to determine if a physical address can be accessed
|
/// Helper to determine if a physical address can be accessed
|
||||||
/// through the page_offset area.
|
/// through the page_offset area.
|
||||||
inline bool page_mappable(uintptr_t a) { return (a & page_offset) == 0; }
|
inline bool page_mappable(uintptr_t a) { return (a & page_offset) == 0; }
|
||||||
|
|
||||||
/// Convert a physical address to a virtual one (in the offset-mapped area)
|
/// Convert a physical address to a virtual one (in the offset-mapped area)
|
||||||
template <typename T> T * to_virtual(uintptr_t a) {
|
template <typename T> T * to_virtual(uintptr_t a) {
|
||||||
return reinterpret_cast<T*>(a|page_offset);
|
return reinterpret_cast<T*>(a|page_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a physical address to a virtual one (in the offset-mapped area)
|
/// Convert a physical address to a virtual one (in the offset-mapped area)
|
||||||
template <typename T> T * to_virtual(T *p) {
|
template <typename T> T * to_virtual(T *p) {
|
||||||
return to_virtual<T>(reinterpret_cast<uintptr_t>(p));
|
return to_virtual<T>(reinterpret_cast<uintptr_t>(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of pages needed for a given number of bytes.
|
/// Get the number of pages needed for a given number of bytes.
|
||||||
/// \arg bytes The number of bytes desired
|
/// \arg bytes The number of bytes desired
|
||||||
/// \returns The number of pages needed to contain the desired bytes
|
/// \returns The number of pages needed to contain the desired bytes
|
||||||
inline size_t page_count(size_t bytes) {
|
inline size_t page_count(size_t bytes) {
|
||||||
return ((bytes - 1) >> 12) + 1;
|
return ((bytes - 1) >> 12) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the given address, aligned to the next lowest page
|
/// Get the given address, aligned to the next lowest page
|
||||||
inline uintptr_t page_align_down(uintptr_t a) { return a & ~(frame_size-1); }
|
inline uintptr_t page_align_down(uintptr_t a) { return a & ~(frame_size-1); }
|
||||||
|
|
||||||
/// Get the given address, aligned to the next page
|
/// Get the given address, aligned to the next page
|
||||||
inline uintptr_t page_align_up(uintptr_t a) { return page_align_down(a-1) + frame_size; }
|
inline uintptr_t page_align_up(uintptr_t a) { return page_align_down(a-1) + frame_size; }
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
/// \tparam S The type pointed to by the `input` pointer
|
/// \tparam S The type pointed to by the `input` pointer
|
||||||
template <typename T, typename S>
|
template <typename T, typename S>
|
||||||
inline T* offset_ptr(S* input, ptrdiff_t offset) {
|
inline T* offset_ptr(S* input, ptrdiff_t offset) {
|
||||||
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
|
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterator for an array of `const T` whose size is known at runtime
|
/// Iterator for an array of `const T` whose size is known at runtime
|
||||||
@@ -19,27 +19,27 @@ template <typename T>
|
|||||||
class const_offset_iterator
|
class const_offset_iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg t Pointer to the first item in the array
|
/// \arg t Pointer to the first item in the array
|
||||||
/// \arg off Offset applied to reach successive items. Default is 0,
|
/// \arg off Offset applied to reach successive items. Default is 0,
|
||||||
/// which creates an effectively constant iterator.
|
/// which creates an effectively constant iterator.
|
||||||
const_offset_iterator(T const *t, size_t off=0) : m_t(t), m_off(off) {}
|
const_offset_iterator(T const *t, size_t off=0) : m_t(t), m_off(off) {}
|
||||||
|
|
||||||
const T * operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
|
const T * operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
|
||||||
const T * operator++(int) { T* tmp = m_t; operator++(); return tmp; }
|
const T * operator++(int) { T* tmp = m_t; operator++(); return tmp; }
|
||||||
|
|
||||||
bool operator==(T* p) const { return p == m_t; }
|
bool operator==(T* p) const { return p == m_t; }
|
||||||
bool operator!=(T* p) const { return p != m_t; }
|
bool operator!=(T* p) const { return p != m_t; }
|
||||||
bool operator==(const_offset_iterator<T> &i) const { return i.m_t == m_t; }
|
bool operator==(const_offset_iterator<T> &i) const { return i.m_t == m_t; }
|
||||||
bool operator!=(const_offset_iterator<T> &i) const { return i.m_t != m_t; }
|
bool operator!=(const_offset_iterator<T> &i) const { return i.m_t != m_t; }
|
||||||
|
|
||||||
const T& operator*() const { return *m_t; }
|
const T& operator*() const { return *m_t; }
|
||||||
operator const T& () const { return *m_t; }
|
operator const T& () const { return *m_t; }
|
||||||
const T* operator->() const { return m_t; }
|
const T* operator->() const { return m_t; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T const *m_t;
|
T const *m_t;
|
||||||
size_t m_off;
|
size_t m_off;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// iterator for an array of `const T` whose size is known at runtime
|
/// iterator for an array of `const T` whose size is known at runtime
|
||||||
@@ -49,26 +49,26 @@ template <typename T>
|
|||||||
class offset_iterator
|
class offset_iterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// constructor.
|
/// constructor.
|
||||||
/// \arg t pointer to the first item in the array
|
/// \arg t pointer to the first item in the array
|
||||||
/// \arg off offset applied to reach successive items. default is 0,
|
/// \arg off offset applied to reach successive items. default is 0,
|
||||||
/// which creates an effectively constant iterator.
|
/// which creates an effectively constant iterator.
|
||||||
offset_iterator(T *t, size_t off=0) : m_t(t), m_off(off) {}
|
offset_iterator(T *t, size_t off=0) : m_t(t), m_off(off) {}
|
||||||
|
|
||||||
T * operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
|
T * operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
|
||||||
T * operator++(int) { T* tmp = m_t; operator++(); return tmp; }
|
T * operator++(int) { T* tmp = m_t; operator++(); return tmp; }
|
||||||
|
|
||||||
bool operator==(T *p) const { return p == m_t; }
|
bool operator==(T *p) const { return p == m_t; }
|
||||||
bool operator!=(T *p) const { return p != m_t; }
|
bool operator!=(T *p) const { return p != m_t; }
|
||||||
bool operator==(offset_iterator<T> &i) const { return i.m_t == m_t; }
|
bool operator==(offset_iterator<T> &i) const { return i.m_t == m_t; }
|
||||||
bool operator!=(offset_iterator<T> &i) const { return i.m_t != m_t; }
|
bool operator!=(offset_iterator<T> &i) const { return i.m_t != m_t; }
|
||||||
|
|
||||||
T & operator*() const { return *m_t; }
|
T & operator*() const { return *m_t; }
|
||||||
operator T & () const { return *m_t; }
|
operator T & () const { return *m_t; }
|
||||||
T * operator->() const { return m_t; }
|
T * operator->() const { return m_t; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T *m_t;
|
T *m_t;
|
||||||
size_t m_off;
|
size_t m_off;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,22 +9,22 @@
|
|||||||
|
|
||||||
struct acpi_table_header
|
struct acpi_table_header
|
||||||
{
|
{
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
uint8_t revision;
|
uint8_t revision;
|
||||||
uint8_t checksum;
|
uint8_t checksum;
|
||||||
char oem_id[6];
|
char oem_id[6];
|
||||||
char oem_table[8];
|
char oem_table[8];
|
||||||
uint32_t oem_revision;
|
uint32_t oem_revision;
|
||||||
uint32_t creator_id;
|
uint32_t creator_id;
|
||||||
uint32_t creator_revision;
|
uint32_t creator_revision;
|
||||||
|
|
||||||
bool validate(uint32_t expected_type = 0) const;
|
bool validate(uint32_t expected_type = 0) const;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
#define TABLE_HEADER(signature) \
|
#define TABLE_HEADER(signature) \
|
||||||
static constexpr uint32_t type_id = kutil::byteswap(signature); \
|
static constexpr uint32_t type_id = kutil::byteswap(signature); \
|
||||||
acpi_table_header header;
|
acpi_table_header header;
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -33,62 +33,62 @@ bool acpi_validate(const T *t) { return t->header.validate(T::type_id); }
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
size_t acpi_table_entries(const T *t, size_t size)
|
size_t acpi_table_entries(const T *t, size_t size)
|
||||||
{
|
{
|
||||||
return (t->header.length - sizeof(T)) / size;
|
return (t->header.length - sizeof(T)) / size;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class acpi_gas_type : uint8_t
|
enum class acpi_gas_type : uint8_t
|
||||||
{
|
{
|
||||||
system_memory,
|
system_memory,
|
||||||
system_io,
|
system_io,
|
||||||
pci_config,
|
pci_config,
|
||||||
embedded,
|
embedded,
|
||||||
smbus,
|
smbus,
|
||||||
platform_channel = 0x0a,
|
platform_channel = 0x0a,
|
||||||
functional_fixed = 0x7f
|
functional_fixed = 0x7f
|
||||||
};
|
};
|
||||||
|
|
||||||
struct acpi_gas
|
struct acpi_gas
|
||||||
{
|
{
|
||||||
acpi_gas_type type;
|
acpi_gas_type type;
|
||||||
|
|
||||||
uint8_t reg_bits;
|
uint8_t reg_bits;
|
||||||
uint8_t reg_offset;
|
uint8_t reg_offset;
|
||||||
uint8_t access_size;
|
uint8_t access_size;
|
||||||
|
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
enum class acpi_fadt_flags : uint32_t
|
enum class acpi_fadt_flags : uint32_t
|
||||||
{
|
{
|
||||||
wbinvd = 0x00000001,
|
wbinvd = 0x00000001,
|
||||||
wbinvd_flush = 0x00000002,
|
wbinvd_flush = 0x00000002,
|
||||||
proc_c1 = 0x00000004,
|
proc_c1 = 0x00000004,
|
||||||
p_lvl2_up = 0x00000008,
|
p_lvl2_up = 0x00000008,
|
||||||
pwr_button = 0x00000010,
|
pwr_button = 0x00000010,
|
||||||
slp_button = 0x00000020,
|
slp_button = 0x00000020,
|
||||||
fix_rtc = 0x00000040,
|
fix_rtc = 0x00000040,
|
||||||
rtc_s4 = 0x00000080,
|
rtc_s4 = 0x00000080,
|
||||||
tmr_val_ext = 0x00000100,
|
tmr_val_ext = 0x00000100,
|
||||||
dck_cap = 0x00000200,
|
dck_cap = 0x00000200,
|
||||||
reset_reg_sup = 0x00000400,
|
reset_reg_sup = 0x00000400,
|
||||||
sealed_case = 0x00000800,
|
sealed_case = 0x00000800,
|
||||||
headless = 0x00001000,
|
headless = 0x00001000,
|
||||||
cpu_sw_slp = 0x00002000,
|
cpu_sw_slp = 0x00002000,
|
||||||
pci_exp_wak = 0x00004000,
|
pci_exp_wak = 0x00004000,
|
||||||
use_plat_clock = 0x00008000,
|
use_plat_clock = 0x00008000,
|
||||||
s4_rtc_sts_val = 0x00010000,
|
s4_rtc_sts_val = 0x00010000,
|
||||||
remote_pwr_cap = 0x00020000,
|
remote_pwr_cap = 0x00020000,
|
||||||
apic_cluster = 0x00040000,
|
apic_cluster = 0x00040000,
|
||||||
apic_physical = 0x00080000,
|
apic_physical = 0x00080000,
|
||||||
hw_reduced_acpi = 0x00100000,
|
hw_reduced_acpi = 0x00100000,
|
||||||
low_pwr_s0_idle = 0x00200000
|
low_pwr_s0_idle = 0x00200000
|
||||||
};
|
};
|
||||||
is_bitfield(acpi_fadt_flags);
|
is_bitfield(acpi_fadt_flags);
|
||||||
|
|
||||||
struct acpi_fadt
|
struct acpi_fadt
|
||||||
{
|
{
|
||||||
TABLE_HEADER('FACP');
|
TABLE_HEADER('FACP');
|
||||||
|
|
||||||
uint32_t facs;
|
uint32_t facs;
|
||||||
uint32_t dsdt;
|
uint32_t dsdt;
|
||||||
@@ -136,7 +136,7 @@ struct acpi_fadt
|
|||||||
acpi_gas reset_reg;
|
acpi_gas reset_reg;
|
||||||
uint8_t reset_value;
|
uint8_t reset_value;
|
||||||
|
|
||||||
uint16_t arm_boot_arch;
|
uint16_t arm_boot_arch;
|
||||||
|
|
||||||
uint8_t fadt_minor_version;
|
uint8_t fadt_minor_version;
|
||||||
|
|
||||||
@@ -152,60 +152,60 @@ struct acpi_fadt
|
|||||||
acpi_gas x_gpe0_block;
|
acpi_gas x_gpe0_block;
|
||||||
acpi_gas x_gpe1_block;
|
acpi_gas x_gpe1_block;
|
||||||
|
|
||||||
acpi_gas sleep_control_reg;
|
acpi_gas sleep_control_reg;
|
||||||
acpi_gas sleep_status_reg;
|
acpi_gas sleep_status_reg;
|
||||||
|
|
||||||
uint64_t hypervisor_vendor_id;
|
uint64_t hypervisor_vendor_id;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct acpi_xsdt
|
struct acpi_xsdt
|
||||||
{
|
{
|
||||||
TABLE_HEADER('XSDT');
|
TABLE_HEADER('XSDT');
|
||||||
acpi_table_header *headers[0];
|
acpi_table_header *headers[0];
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct acpi_apic
|
struct acpi_apic
|
||||||
{
|
{
|
||||||
TABLE_HEADER('APIC');
|
TABLE_HEADER('APIC');
|
||||||
uint32_t local_address;
|
uint32_t local_address;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint8_t controller_data[0];
|
uint8_t controller_data[0];
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct acpi_mcfg_entry
|
struct acpi_mcfg_entry
|
||||||
{
|
{
|
||||||
uint64_t base;
|
uint64_t base;
|
||||||
uint16_t group;
|
uint16_t group;
|
||||||
uint8_t bus_start;
|
uint8_t bus_start;
|
||||||
uint8_t bus_end;
|
uint8_t bus_end;
|
||||||
uint32_t reserved;
|
uint32_t reserved;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct acpi_mcfg
|
struct acpi_mcfg
|
||||||
{
|
{
|
||||||
TABLE_HEADER('MCFG');
|
TABLE_HEADER('MCFG');
|
||||||
uint64_t reserved;
|
uint64_t reserved;
|
||||||
acpi_mcfg_entry entries[0];
|
acpi_mcfg_entry entries[0];
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct acpi_hpet
|
struct acpi_hpet
|
||||||
{
|
{
|
||||||
TABLE_HEADER('HPET');
|
TABLE_HEADER('HPET');
|
||||||
uint32_t hardware_id;
|
uint32_t hardware_id;
|
||||||
acpi_gas base_address;
|
acpi_gas base_address;
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
uint16_t periodic_min;
|
uint16_t periodic_min;
|
||||||
uint8_t attributes;
|
uint8_t attributes;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct acpi_bgrt
|
struct acpi_bgrt
|
||||||
{
|
{
|
||||||
TABLE_HEADER('BGRT');
|
TABLE_HEADER('BGRT');
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uintptr_t address;
|
uintptr_t address;
|
||||||
uint32_t offset_x;
|
uint32_t offset_x;
|
||||||
uint32_t offset_y;
|
uint32_t offset_y;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|||||||
@@ -26,286 +26,286 @@ static constexpr uint16_t lapic_timer_div = 0x03e0;
|
|||||||
static uint32_t
|
static uint32_t
|
||||||
apic_read(uint32_t volatile *apic, uint16_t offset)
|
apic_read(uint32_t volatile *apic, uint16_t offset)
|
||||||
{
|
{
|
||||||
return *(apic + offset/sizeof(uint32_t));
|
return *(apic + offset/sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
apic_write(uint32_t volatile *apic, uint16_t offset, uint32_t value)
|
apic_write(uint32_t volatile *apic, uint16_t offset, uint32_t value)
|
||||||
{
|
{
|
||||||
log::debug(logs::apic, "LAPIC write: %x = %08lx", offset, value);
|
log::debug(logs::apic, "LAPIC write: %x = %08lx", offset, value);
|
||||||
*(apic + offset/sizeof(uint32_t)) = value;
|
*(apic + offset/sizeof(uint32_t)) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
ioapic_read(uint32_t volatile *base, uint8_t reg)
|
ioapic_read(uint32_t volatile *base, uint8_t reg)
|
||||||
{
|
{
|
||||||
*base = reg;
|
*base = reg;
|
||||||
return *(base + 4);
|
return *(base + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ioapic_write(uint32_t volatile *base, uint8_t reg, uint32_t value)
|
ioapic_write(uint32_t volatile *base, uint8_t reg, uint32_t value)
|
||||||
{
|
{
|
||||||
*base = reg;
|
*base = reg;
|
||||||
*(base + 4) = value;
|
*(base + 4) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
apic::apic(uintptr_t base) :
|
apic::apic(uintptr_t base) :
|
||||||
m_base(memory::to_virtual<uint32_t>(base))
|
m_base(memory::to_virtual<uint32_t>(base))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lapic::lapic(uintptr_t base) :
|
lapic::lapic(uintptr_t base) :
|
||||||
apic(base),
|
apic(base),
|
||||||
m_divisor(0)
|
m_divisor(0)
|
||||||
{
|
{
|
||||||
apic_write(m_base, lapic_lvt_error, static_cast<uint32_t>(isr::isrAPICError));
|
apic_write(m_base, lapic_lvt_error, static_cast<uint32_t>(isr::isrAPICError));
|
||||||
apic_write(m_base, lapic_spurious, static_cast<uint32_t>(isr::isrSpurious));
|
apic_write(m_base, lapic_spurious, static_cast<uint32_t>(isr::isrSpurious));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t
|
uint8_t
|
||||||
lapic::get_id()
|
lapic::get_id()
|
||||||
{
|
{
|
||||||
return static_cast<uint8_t>(apic_read(m_base, lapic_id) >> 24);
|
return static_cast<uint8_t>(apic_read(m_base, lapic_id) >> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::send_ipi(ipi mode, uint8_t vector, uint8_t dest)
|
lapic::send_ipi(ipi mode, uint8_t vector, uint8_t dest)
|
||||||
{
|
{
|
||||||
// Wait until the APIC is ready to send
|
// Wait until the APIC is ready to send
|
||||||
ipi_wait();
|
ipi_wait();
|
||||||
|
|
||||||
uint32_t command =
|
uint32_t command =
|
||||||
static_cast<uint32_t>(vector) |
|
static_cast<uint32_t>(vector) |
|
||||||
static_cast<uint32_t>(mode);
|
static_cast<uint32_t>(mode);
|
||||||
|
|
||||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
||||||
apic_write(m_base, lapic_icr_low, command);
|
apic_write(m_base, lapic_icr_low, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::send_ipi_broadcast(ipi mode, bool self, uint8_t vector)
|
lapic::send_ipi_broadcast(ipi mode, bool self, uint8_t vector)
|
||||||
{
|
{
|
||||||
// Wait until the APIC is ready to send
|
// Wait until the APIC is ready to send
|
||||||
ipi_wait();
|
ipi_wait();
|
||||||
|
|
||||||
uint32_t command =
|
uint32_t command =
|
||||||
static_cast<uint32_t>(vector) |
|
static_cast<uint32_t>(vector) |
|
||||||
static_cast<uint32_t>(mode) |
|
static_cast<uint32_t>(mode) |
|
||||||
(self ? 0 : (1 << 18)) |
|
(self ? 0 : (1 << 18)) |
|
||||||
(1 << 19);
|
(1 << 19);
|
||||||
|
|
||||||
apic_write(m_base, lapic_icr_high, 0);
|
apic_write(m_base, lapic_icr_high, 0);
|
||||||
apic_write(m_base, lapic_icr_low, command);
|
apic_write(m_base, lapic_icr_low, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::ipi_wait()
|
lapic::ipi_wait()
|
||||||
{
|
{
|
||||||
while (apic_read(m_base, lapic_icr_low) & (1<<12))
|
while (apic_read(m_base, lapic_icr_low) & (1<<12))
|
||||||
asm volatile ("pause" : : : "memory");
|
asm volatile ("pause" : : : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::calibrate_timer()
|
lapic::calibrate_timer()
|
||||||
{
|
{
|
||||||
interrupts_disable();
|
interrupts_disable();
|
||||||
|
|
||||||
log::info(logs::apic, "Calibrating APIC timer...");
|
log::info(logs::apic, "Calibrating APIC timer...");
|
||||||
|
|
||||||
const uint32_t initial = -1u;
|
const uint32_t initial = -1u;
|
||||||
enable_timer(isr::isrSpurious);
|
enable_timer(isr::isrSpurious);
|
||||||
set_divisor(1);
|
set_divisor(1);
|
||||||
apic_write(m_base, lapic_timer_init, initial);
|
apic_write(m_base, lapic_timer_init, initial);
|
||||||
|
|
||||||
uint64_t us = 20000;
|
uint64_t us = 20000;
|
||||||
clock::get().spinwait(us);
|
clock::get().spinwait(us);
|
||||||
|
|
||||||
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
|
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
|
||||||
uint64_t ticks_total = initial - remaining;
|
uint64_t ticks_total = initial - remaining;
|
||||||
s_ticks_per_us = ticks_total / us;
|
s_ticks_per_us = ticks_total / us;
|
||||||
|
|
||||||
log::info(logs::apic, "APIC timer ticks %d times per microsecond.", s_ticks_per_us);
|
log::info(logs::apic, "APIC timer ticks %d times per microsecond.", s_ticks_per_us);
|
||||||
|
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::set_divisor(uint8_t divisor)
|
lapic::set_divisor(uint8_t divisor)
|
||||||
{
|
{
|
||||||
uint32_t divbits = 0;
|
uint32_t divbits = 0;
|
||||||
|
|
||||||
switch (divisor) {
|
switch (divisor) {
|
||||||
case 1: divbits = 0xb; break;
|
case 1: divbits = 0xb; break;
|
||||||
case 2: divbits = 0x0; break;
|
case 2: divbits = 0x0; break;
|
||||||
case 4: divbits = 0x1; break;
|
case 4: divbits = 0x1; break;
|
||||||
case 8: divbits = 0x2; break;
|
case 8: divbits = 0x2; break;
|
||||||
case 16: divbits = 0x3; break;
|
case 16: divbits = 0x3; break;
|
||||||
case 32: divbits = 0x8; break;
|
case 32: divbits = 0x8; break;
|
||||||
case 64: divbits = 0x9; break;
|
case 64: divbits = 0x9; break;
|
||||||
case 128: divbits = 0xa; break;
|
case 128: divbits = 0xa; break;
|
||||||
default:
|
default:
|
||||||
kassert(0, "Invalid divisor passed to lapic::set_divisor");
|
kassert(0, "Invalid divisor passed to lapic::set_divisor");
|
||||||
}
|
}
|
||||||
|
|
||||||
apic_write(m_base, lapic_timer_div, divbits);
|
apic_write(m_base, lapic_timer_div, divbits);
|
||||||
m_divisor = divisor;
|
m_divisor = divisor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::enable_timer(isr vector, bool repeat)
|
lapic::enable_timer(isr vector, bool repeat)
|
||||||
{
|
{
|
||||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||||
if (repeat)
|
if (repeat)
|
||||||
lvte |= 0x20000;
|
lvte |= 0x20000;
|
||||||
apic_write(m_base, lapic_lvt_timer, lvte);
|
apic_write(m_base, lapic_lvt_timer, lvte);
|
||||||
|
|
||||||
log::debug(logs::apic, "Enabling APIC timer at isr %02x", vector);
|
log::debug(logs::apic, "Enabling APIC timer at isr %02x", vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
lapic::reset_timer(uint64_t interval)
|
lapic::reset_timer(uint64_t interval)
|
||||||
{
|
{
|
||||||
uint64_t remaining = ticks_to_us(apic_read(m_base, lapic_timer_cur));
|
uint64_t remaining = ticks_to_us(apic_read(m_base, lapic_timer_cur));
|
||||||
uint64_t ticks = us_to_ticks(interval);
|
uint64_t ticks = us_to_ticks(interval);
|
||||||
|
|
||||||
int divisor = 1;
|
int divisor = 1;
|
||||||
while (ticks > 0xffffffffull) {
|
while (ticks > 0xffffffffull) {
|
||||||
ticks >>= 1;
|
ticks >>= 1;
|
||||||
divisor <<= 1;
|
divisor <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (divisor != m_divisor)
|
if (divisor != m_divisor)
|
||||||
set_divisor(divisor);
|
set_divisor(divisor);
|
||||||
|
|
||||||
apic_write(m_base, lapic_timer_init, ticks);
|
apic_write(m_base, lapic_timer_init, ticks);
|
||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
|
lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
|
||||||
{
|
{
|
||||||
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
|
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
|
||||||
|
|
||||||
uint16_t off = num ? lapic_lvt_lint1 : lapic_lvt_lint0;
|
uint16_t off = num ? lapic_lvt_lint1 : lapic_lvt_lint0;
|
||||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||||
|
|
||||||
uint16_t polarity = flags & 0x3;
|
uint16_t polarity = flags & 0x3;
|
||||||
if (polarity == 3)
|
if (polarity == 3)
|
||||||
lvte |= (1 << 13);
|
lvte |= (1 << 13);
|
||||||
|
|
||||||
uint16_t trigger = (flags >> 2) & 0x3;
|
uint16_t trigger = (flags >> 2) & 0x3;
|
||||||
if (trigger == 3)
|
if (trigger == 3)
|
||||||
lvte |= (1 << 15);
|
lvte |= (1 << 15);
|
||||||
|
|
||||||
apic_write(m_base, off, lvte);
|
apic_write(m_base, off, lvte);
|
||||||
log::debug(logs::apic, "APIC LINT%d enabled as %s %d %s-triggered, active %s.",
|
log::debug(logs::apic, "APIC LINT%d enabled as %s %d %s-triggered, active %s.",
|
||||||
num, nmi ? "NMI" : "ISR", vector,
|
num, nmi ? "NMI" : "ISR", vector,
|
||||||
polarity == 3 ? "level" : "edge",
|
polarity == 3 ? "level" : "edge",
|
||||||
trigger == 3 ? "low" : "high");
|
trigger == 3 ? "low" : "high");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::enable()
|
lapic::enable()
|
||||||
{
|
{
|
||||||
apic_write(m_base, lapic_spurious,
|
apic_write(m_base, lapic_spurious,
|
||||||
apic_read(m_base, lapic_spurious) | 0x100);
|
apic_read(m_base, lapic_spurious) | 0x100);
|
||||||
log::debug(logs::apic, "LAPIC enabled!");
|
log::debug(logs::apic, "LAPIC enabled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::disable()
|
lapic::disable()
|
||||||
{
|
{
|
||||||
apic_write(m_base, lapic_spurious,
|
apic_write(m_base, lapic_spurious,
|
||||||
apic_read(m_base, lapic_spurious) & ~0x100);
|
apic_read(m_base, lapic_spurious) & ~0x100);
|
||||||
log::debug(logs::apic, "LAPIC disabled.");
|
log::debug(logs::apic, "LAPIC disabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ioapic::ioapic(uintptr_t base, uint32_t base_gsi) :
|
ioapic::ioapic(uintptr_t base, uint32_t base_gsi) :
|
||||||
apic(base),
|
apic(base),
|
||||||
m_base_gsi(base_gsi)
|
m_base_gsi(base_gsi)
|
||||||
{
|
{
|
||||||
uint32_t id = ioapic_read(m_base, 0);
|
uint32_t id = ioapic_read(m_base, 0);
|
||||||
uint32_t version = ioapic_read(m_base, 1);
|
uint32_t version = ioapic_read(m_base, 1);
|
||||||
|
|
||||||
m_id = (id >> 24) & 0xff;
|
m_id = (id >> 24) & 0xff;
|
||||||
m_version = version & 0xff;
|
m_version = version & 0xff;
|
||||||
m_num_gsi = (version >> 16) & 0xff;
|
m_num_gsi = (version >> 16) & 0xff;
|
||||||
log::debug(logs::apic, "IOAPIC %d loaded, version %d, GSIs %d-%d",
|
log::debug(logs::apic, "IOAPIC %d loaded, version %d, GSIs %d-%d",
|
||||||
m_id, m_version, base_gsi, base_gsi + (m_num_gsi - 1));
|
m_id, m_version, base_gsi, base_gsi + (m_num_gsi - 1));
|
||||||
|
|
||||||
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
||||||
uint16_t flags = (i < 0x10) ? 0 : 0xf;
|
uint16_t flags = (i < 0x10) ? 0 : 0xf;
|
||||||
isr vector = isr::irq00 + i;
|
isr vector = isr::irq00 + i;
|
||||||
redirect(i, vector, flags, true);
|
redirect(i, vector, flags, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
|
ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
|
||||||
{
|
{
|
||||||
log::debug(logs::apic, "IOAPIC %d redirecting irq %3d to vector %3d [%04x]%s",
|
log::debug(logs::apic, "IOAPIC %d redirecting irq %3d to vector %3d [%04x]%s",
|
||||||
m_id, irq, vector, flags, masked ? " (masked)" : "");
|
m_id, irq, vector, flags, masked ? " (masked)" : "");
|
||||||
|
|
||||||
uint64_t entry = static_cast<uint64_t>(vector);
|
uint64_t entry = static_cast<uint64_t>(vector);
|
||||||
|
|
||||||
uint16_t polarity = flags & 0x3;
|
uint16_t polarity = flags & 0x3;
|
||||||
if (polarity == 3)
|
if (polarity == 3)
|
||||||
entry |= (1 << 13);
|
entry |= (1 << 13);
|
||||||
|
|
||||||
uint16_t trigger = (flags >> 2) & 0x3;
|
uint16_t trigger = (flags >> 2) & 0x3;
|
||||||
if (trigger == 3)
|
if (trigger == 3)
|
||||||
entry |= (1 << 15);
|
entry |= (1 << 15);
|
||||||
|
|
||||||
if (masked)
|
if (masked)
|
||||||
entry |= (1 << 16);
|
entry |= (1 << 16);
|
||||||
|
|
||||||
ioapic_write(m_base, (2 * irq) + 0x10, static_cast<uint32_t>(entry & 0xffffffff));
|
ioapic_write(m_base, (2 * irq) + 0x10, static_cast<uint32_t>(entry & 0xffffffff));
|
||||||
ioapic_write(m_base, (2 * irq) + 0x11, static_cast<uint32_t>(entry >> 32));
|
ioapic_write(m_base, (2 * irq) + 0x11, static_cast<uint32_t>(entry >> 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ioapic::mask(uint8_t irq, bool masked)
|
ioapic::mask(uint8_t irq, bool masked)
|
||||||
{
|
{
|
||||||
log::debug(logs::apic, "IOAPIC %d %smasking irq %3d",
|
log::debug(logs::apic, "IOAPIC %d %smasking irq %3d",
|
||||||
m_id, masked ? "" : "un", irq);
|
m_id, masked ? "" : "un", irq);
|
||||||
|
|
||||||
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
|
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
|
||||||
if (masked)
|
if (masked)
|
||||||
entry |= (1 << 16);
|
entry |= (1 << 16);
|
||||||
else
|
else
|
||||||
entry &= ~(1 << 16);
|
entry &= ~(1 << 16);
|
||||||
|
|
||||||
ioapic_write(m_base, (2 * irq) + 0x10, entry);
|
ioapic_write(m_base, (2 * irq) + 0x10, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ioapic::dump_redirs() const
|
ioapic::dump_redirs() const
|
||||||
{
|
{
|
||||||
log::debug(logs::apic, "IOAPIC %d redirections:", m_id);
|
log::debug(logs::apic, "IOAPIC %d redirections:", m_id);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
||||||
uint64_t low = ioapic_read(m_base, 0x10 + (2 *i));
|
uint64_t low = ioapic_read(m_base, 0x10 + (2 *i));
|
||||||
uint64_t high = ioapic_read(m_base, 0x10 + (2 *i));
|
uint64_t high = ioapic_read(m_base, 0x10 + (2 *i));
|
||||||
uint64_t redir = low | (high << 32);
|
uint64_t redir = low | (high << 32);
|
||||||
if (redir == 0) continue;
|
if (redir == 0) continue;
|
||||||
|
|
||||||
uint8_t vector = redir & 0xff;
|
uint8_t vector = redir & 0xff;
|
||||||
uint8_t mode = (redir >> 8) & 0x7;
|
uint8_t mode = (redir >> 8) & 0x7;
|
||||||
uint8_t dest_mode = (redir >> 11) & 0x1;
|
uint8_t dest_mode = (redir >> 11) & 0x1;
|
||||||
uint8_t polarity = (redir >> 13) & 0x1;
|
uint8_t polarity = (redir >> 13) & 0x1;
|
||||||
uint8_t trigger = (redir >> 15) & 0x1;
|
uint8_t trigger = (redir >> 15) & 0x1;
|
||||||
uint8_t mask = (redir >> 16) & 0x1;
|
uint8_t mask = (redir >> 16) & 0x1;
|
||||||
uint8_t dest = (redir >> 56) & 0xff;
|
uint8_t dest = (redir >> 56) & 0xff;
|
||||||
|
|
||||||
log::debug(logs::apic, " %2d: vec %3d %s active, %s-triggered %s dest %d: %x",
|
log::debug(logs::apic, " %2d: vec %3d %s active, %s-triggered %s dest %d: %x",
|
||||||
m_base_gsi + i, vector,
|
m_base_gsi + i, vector,
|
||||||
polarity ? "low" : "high",
|
polarity ? "low" : "high",
|
||||||
trigger ? "level" : "edge",
|
trigger ? "level" : "edge",
|
||||||
mask ? "masked" : "",
|
mask ? "masked" : "",
|
||||||
dest_mode,
|
dest_mode,
|
||||||
dest);
|
dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,137 +11,137 @@ enum class isr : uint8_t;
|
|||||||
class apic
|
class apic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg base Physical base address of the APIC's MMIO registers
|
/// \arg base Physical base address of the APIC's MMIO registers
|
||||||
apic(uintptr_t base);
|
apic(uintptr_t base);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t *m_base;
|
uint32_t *m_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Controller for processor-local APICs
|
/// Controller for processor-local APICs
|
||||||
class lapic :
|
class lapic :
|
||||||
public apic
|
public apic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg base Physicl base address of the APIC's MMIO registers
|
/// \arg base Physicl base address of the APIC's MMIO registers
|
||||||
lapic(uintptr_t base);
|
lapic(uintptr_t base);
|
||||||
|
|
||||||
/// Get the local APIC's ID
|
/// Get the local APIC's ID
|
||||||
uint8_t get_id();
|
uint8_t get_id();
|
||||||
|
|
||||||
enum class ipi : uint32_t
|
enum class ipi : uint32_t
|
||||||
{
|
{
|
||||||
// Delivery modes
|
// Delivery modes
|
||||||
fixed = 0x0000,
|
fixed = 0x0000,
|
||||||
smi = 0x0200,
|
smi = 0x0200,
|
||||||
nmi = 0x0400,
|
nmi = 0x0400,
|
||||||
init = 0x0500,
|
init = 0x0500,
|
||||||
startup = 0x0600,
|
startup = 0x0600,
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
deassert = 0x0000,
|
deassert = 0x0000,
|
||||||
assert = 0x4000,
|
assert = 0x4000,
|
||||||
edge = 0x0000, ///< edge-triggered
|
edge = 0x0000, ///< edge-triggered
|
||||||
level = 0x8000, ///< level-triggered
|
level = 0x8000, ///< level-triggered
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Send an inter-processor interrupt.
|
/// Send an inter-processor interrupt.
|
||||||
/// \arg mode The sending mode
|
/// \arg mode The sending mode
|
||||||
/// \arg vector The interrupt vector
|
/// \arg vector The interrupt vector
|
||||||
/// \arg dest The APIC ID of the destination
|
/// \arg dest The APIC ID of the destination
|
||||||
void send_ipi(ipi mode, uint8_t vector, uint8_t dest);
|
void send_ipi(ipi mode, uint8_t vector, uint8_t dest);
|
||||||
|
|
||||||
/// Send an inter-processor broadcast interrupt to all other CPUs
|
/// Send an inter-processor broadcast interrupt to all other CPUs
|
||||||
/// \arg mode The sending mode
|
/// \arg mode The sending mode
|
||||||
/// \arg self If true, include this CPU in the broadcast
|
/// \arg self If true, include this CPU in the broadcast
|
||||||
/// \arg vector The interrupt vector
|
/// \arg vector The interrupt vector
|
||||||
void send_ipi_broadcast(ipi mode, bool self, uint8_t vector);
|
void send_ipi_broadcast(ipi mode, bool self, uint8_t vector);
|
||||||
|
|
||||||
/// Wait for an IPI to finish sending. This is done automatically
|
/// Wait for an IPI to finish sending. This is done automatically
|
||||||
/// before sending another IPI with send_ipi().
|
/// before sending another IPI with send_ipi().
|
||||||
void ipi_wait();
|
void ipi_wait();
|
||||||
|
|
||||||
/// Enable interrupts for the LAPIC timer.
|
/// Enable interrupts for the LAPIC timer.
|
||||||
/// \arg vector Interrupt vector the timer should use
|
/// \arg vector Interrupt vector the timer should use
|
||||||
/// \arg repeat If false, this timer is one-off, otherwise repeating
|
/// \arg repeat If false, this timer is one-off, otherwise repeating
|
||||||
void enable_timer(isr vector, bool repeat = true);
|
void enable_timer(isr vector, bool repeat = true);
|
||||||
|
|
||||||
/// Reset the timer countdown.
|
/// Reset the timer countdown.
|
||||||
/// \arg interval The interval in us before an interrupt, or 0 to stop the timer
|
/// \arg interval The interval in us before an interrupt, or 0 to stop the timer
|
||||||
/// \returns The interval in us that was remaining before reset
|
/// \returns The interval in us that was remaining before reset
|
||||||
uint32_t reset_timer(uint64_t interval);
|
uint32_t reset_timer(uint64_t interval);
|
||||||
|
|
||||||
/// Stop the timer.
|
/// Stop the timer.
|
||||||
/// \returns The interval in us remaining before an interrupt was to happen
|
/// \returns The interval in us remaining before an interrupt was to happen
|
||||||
inline uint32_t stop_timer() { return reset_timer(0); }
|
inline uint32_t stop_timer() { return reset_timer(0); }
|
||||||
|
|
||||||
/// Enable interrupts for the LAPIC LINT0 pin.
|
/// Enable interrupts for the LAPIC LINT0 pin.
|
||||||
/// \arg num Local interrupt number (0 or 1)
|
/// \arg num Local interrupt number (0 or 1)
|
||||||
/// \arg vector Interrupt vector LINT0 should use
|
/// \arg vector Interrupt vector LINT0 should use
|
||||||
/// \arg nmi Whether this interrupt is NMI delivery mode
|
/// \arg nmi Whether this interrupt is NMI delivery mode
|
||||||
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
||||||
void enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags);
|
void enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags);
|
||||||
|
|
||||||
void enable(); ///< Enable servicing of interrupts
|
void enable(); ///< Enable servicing of interrupts
|
||||||
void disable(); ///< Disable (temporarily) servicing of interrupts
|
void disable(); ///< Disable (temporarily) servicing of interrupts
|
||||||
|
|
||||||
/// Calibrate the timer speed against the clock
|
/// Calibrate the timer speed against the clock
|
||||||
void calibrate_timer();
|
void calibrate_timer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline static uint64_t ticks_to_us(uint64_t ticks) { return ticks / s_ticks_per_us; }
|
inline static uint64_t ticks_to_us(uint64_t ticks) { return ticks / s_ticks_per_us; }
|
||||||
inline static uint64_t us_to_ticks(uint64_t interval) { return interval * s_ticks_per_us; }
|
inline static uint64_t us_to_ticks(uint64_t interval) { return interval * s_ticks_per_us; }
|
||||||
|
|
||||||
void set_divisor(uint8_t divisor);
|
void set_divisor(uint8_t divisor);
|
||||||
void set_repeat(bool repeat);
|
void set_repeat(bool repeat);
|
||||||
|
|
||||||
uint32_t m_divisor;
|
uint32_t m_divisor;
|
||||||
static uint64_t s_ticks_per_us;
|
static uint64_t s_ticks_per_us;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Controller for I/O APICs
|
/// Controller for I/O APICs
|
||||||
class ioapic :
|
class ioapic :
|
||||||
public apic
|
public apic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg base Physical base address of the APIC's MMIO registers
|
/// \arg base Physical base address of the APIC's MMIO registers
|
||||||
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
|
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
|
||||||
ioapic(uintptr_t base, uint32_t base_gsi);
|
ioapic(uintptr_t base, uint32_t base_gsi);
|
||||||
|
|
||||||
uint32_t get_base_gsi() const { return m_base_gsi; }
|
uint32_t get_base_gsi() const { return m_base_gsi; }
|
||||||
uint32_t get_num_gsi() const { return m_num_gsi; }
|
uint32_t get_num_gsi() const { return m_num_gsi; }
|
||||||
|
|
||||||
/// Set a redirection entry.
|
/// Set a redirection entry.
|
||||||
/// TODO: pick CPU
|
/// TODO: pick CPU
|
||||||
/// \arg source Source interrupt number
|
/// \arg source Source interrupt number
|
||||||
/// \arg vector Interrupt vector that should be used
|
/// \arg vector Interrupt vector that should be used
|
||||||
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
||||||
/// \arg masked Whether the iterrupt should be suppressed
|
/// \arg masked Whether the iterrupt should be suppressed
|
||||||
void redirect(uint8_t irq, isr vector, uint16_t flags, bool masked);
|
void redirect(uint8_t irq, isr vector, uint16_t flags, bool masked);
|
||||||
|
|
||||||
/// Mask or unmask an interrupt to stop/start having it sent to the CPU
|
/// Mask or unmask an interrupt to stop/start having it sent to the CPU
|
||||||
/// \arg irq The IOAPIC-local irq number
|
/// \arg irq The IOAPIC-local irq number
|
||||||
/// \arg masked Whether to suppress this interrupt
|
/// \arg masked Whether to suppress this interrupt
|
||||||
void mask(uint8_t irq, bool masked);
|
void mask(uint8_t irq, bool masked);
|
||||||
|
|
||||||
/// Mask all interrupts on this IOAPIC.
|
/// Mask all interrupts on this IOAPIC.
|
||||||
void mask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, true); }
|
void mask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, true); }
|
||||||
|
|
||||||
/// Unmask all interrupts on this IOAPIC.
|
/// Unmask all interrupts on this IOAPIC.
|
||||||
void unmask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, false); }
|
void unmask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, false); }
|
||||||
|
|
||||||
void dump_redirs() const;
|
void dump_redirs() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t m_base_gsi;
|
uint32_t m_base_gsi;
|
||||||
uint32_t m_num_gsi;
|
uint32_t m_num_gsi;
|
||||||
|
|
||||||
uint8_t m_id;
|
uint8_t m_id;
|
||||||
uint8_t m_version;
|
uint8_t m_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
is_bitfield(lapic::ipi);
|
is_bitfield(lapic::ipi);
|
||||||
|
|||||||
@@ -7,5 +7,5 @@
|
|||||||
class block_device
|
class block_device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual size_t read(size_t offset, size_t length, void *buffer) = 0;
|
virtual size_t read(size_t offset, size_t length, void *buffer) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,20 +3,20 @@
|
|||||||
clock * clock::s_instance = nullptr;
|
clock * clock::s_instance = nullptr;
|
||||||
|
|
||||||
clock::clock(uint64_t rate, clock::source source_func, void *data) :
|
clock::clock(uint64_t rate, clock::source source_func, void *data) :
|
||||||
m_rate(rate),
|
m_rate(rate),
|
||||||
m_data(data),
|
m_data(data),
|
||||||
m_source(source_func)
|
m_source(source_func)
|
||||||
{
|
{
|
||||||
// TODO: make this atomic
|
// TODO: make this atomic
|
||||||
if (s_instance == nullptr)
|
if (s_instance == nullptr)
|
||||||
s_instance = this;
|
s_instance = this;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clock::spinwait(uint64_t us) const
|
clock::spinwait(uint64_t us) const
|
||||||
{
|
{
|
||||||
uint64_t when = value() + us;
|
uint64_t when = value() + us;
|
||||||
while (value() < when) asm ("pause");
|
while (value() < when) asm ("pause");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,38 +7,38 @@
|
|||||||
class clock
|
class clock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// A source is a function that returns the current
|
/// A source is a function that returns the current
|
||||||
/// value of some clock source.
|
/// value of some clock source.
|
||||||
using source = uint64_t (*)(void*);
|
using source = uint64_t (*)(void*);
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg rate Number of source ticks per us
|
/// \arg rate Number of source ticks per us
|
||||||
/// \arg source Function for the clock source
|
/// \arg source Function for the clock source
|
||||||
/// \arg data Data to pass to the source function
|
/// \arg data Data to pass to the source function
|
||||||
clock(uint64_t rate, source source_func, void *data);
|
clock(uint64_t rate, source source_func, void *data);
|
||||||
|
|
||||||
/// Get the current value of the clock.
|
/// Get the current value of the clock.
|
||||||
/// \returns Current value of the source, in us
|
/// \returns Current value of the source, in us
|
||||||
/// TODO: optimize divison by finding a multiply and
|
/// TODO: optimize divison by finding a multiply and
|
||||||
/// shift value instead
|
/// shift value instead
|
||||||
inline uint64_t value() const { return m_source(m_data) / m_rate; }
|
inline uint64_t value() const { return m_source(m_data) / m_rate; }
|
||||||
|
|
||||||
/// Update the internal state via the source
|
/// Update the internal state via the source
|
||||||
/// \returns Current value of the clock
|
/// \returns Current value of the clock
|
||||||
inline void update() { m_current = value(); }
|
inline void update() { m_current = value(); }
|
||||||
|
|
||||||
/// Wait in a tight loop
|
/// Wait in a tight loop
|
||||||
/// \arg interval Time to wait, in us
|
/// \arg interval Time to wait, in us
|
||||||
void spinwait(uint64_t us) const;
|
void spinwait(uint64_t us) const;
|
||||||
|
|
||||||
/// Get the master clock
|
/// Get the master clock
|
||||||
static clock & get() { return *s_instance; }
|
static clock & get() { return *s_instance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t m_current; ///< current us count
|
uint64_t m_current; ///< current us count
|
||||||
uint64_t m_rate; ///< source ticks per us
|
uint64_t m_rate; ///< source ticks per us
|
||||||
void *m_data;
|
void *m_data;
|
||||||
source m_source;
|
source m_source;
|
||||||
|
|
||||||
static clock *s_instance;
|
static clock *s_instance;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,73 +14,73 @@ console &g_console = __g_console_storage.value;
|
|||||||
|
|
||||||
|
|
||||||
console::console() :
|
console::console() :
|
||||||
m_serial(nullptr)
|
m_serial(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
console::console(serial_port *serial) :
|
console::console(serial_port *serial) :
|
||||||
m_serial(serial)
|
m_serial(serial)
|
||||||
{
|
{
|
||||||
if (m_serial) {
|
if (m_serial) {
|
||||||
const char *fgseq = "\x1b[2J";
|
const char *fgseq = "\x1b[2J";
|
||||||
while (*fgseq)
|
while (*fgseq)
|
||||||
m_serial->write(*fgseq++);
|
m_serial->write(*fgseq++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
console::echo()
|
console::echo()
|
||||||
{
|
{
|
||||||
putc(m_serial->read());
|
putc(m_serial->read());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
console::set_color(uint8_t fg, uint8_t bg)
|
console::set_color(uint8_t fg, uint8_t bg)
|
||||||
{
|
{
|
||||||
if (m_serial) {
|
if (m_serial) {
|
||||||
const char *fgseq = "\x1b[38;5;";
|
const char *fgseq = "\x1b[38;5;";
|
||||||
while (*fgseq)
|
while (*fgseq)
|
||||||
m_serial->write(*fgseq++);
|
m_serial->write(*fgseq++);
|
||||||
if (fg >= 100) m_serial->write('0' + (fg/100));
|
if (fg >= 100) m_serial->write('0' + (fg/100));
|
||||||
if (fg >= 10) m_serial->write('0' + (fg/10) % 10);
|
if (fg >= 10) m_serial->write('0' + (fg/10) % 10);
|
||||||
m_serial->write('0' + fg % 10);
|
m_serial->write('0' + fg % 10);
|
||||||
m_serial->write('m');
|
m_serial->write('m');
|
||||||
|
|
||||||
const char *bgseq = "\x1b[48;5;";
|
const char *bgseq = "\x1b[48;5;";
|
||||||
while (*bgseq)
|
while (*bgseq)
|
||||||
m_serial->write(*bgseq++);
|
m_serial->write(*bgseq++);
|
||||||
if (bg >= 100) m_serial->write('0' + (bg/100));
|
if (bg >= 100) m_serial->write('0' + (bg/100));
|
||||||
if (bg >= 10) m_serial->write('0' + (bg/10) % 10);
|
if (bg >= 10) m_serial->write('0' + (bg/10) % 10);
|
||||||
m_serial->write('0' + bg % 10);
|
m_serial->write('0' + bg % 10);
|
||||||
m_serial->write('m');
|
m_serial->write('m');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
console::puts(const char *message)
|
console::puts(const char *message)
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
while (message && *message) {
|
while (message && *message) {
|
||||||
n++;
|
n++;
|
||||||
putc(*message++);
|
putc(*message++);
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
console::putc(char c)
|
console::putc(char c)
|
||||||
{
|
{
|
||||||
if (m_serial) {
|
if (m_serial) {
|
||||||
if (c == '\n') m_serial->write('\r');
|
if (c == '\n') m_serial->write('\r');
|
||||||
m_serial->write(c);
|
m_serial->write(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void console::vprintf(const char *fmt, va_list args)
|
void console::vprintf(const char *fmt, va_list args)
|
||||||
{
|
{
|
||||||
static const unsigned buf_size = 256;
|
static const unsigned buf_size = 256;
|
||||||
char buffer[buf_size];
|
char buffer[buf_size];
|
||||||
vsnprintf_(buffer, buf_size, fmt, args);
|
vsnprintf_(buffer, buf_size, fmt, args);
|
||||||
puts(buffer);
|
puts(buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,35 +8,35 @@ class serial_port;
|
|||||||
class console
|
class console
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
console();
|
console();
|
||||||
console(serial_port *serial);
|
console(serial_port *serial);
|
||||||
|
|
||||||
void set_color(uint8_t fg = 7, uint8_t bg = 0);
|
void set_color(uint8_t fg = 7, uint8_t bg = 0);
|
||||||
|
|
||||||
void putc(char c);
|
void putc(char c);
|
||||||
size_t puts(const char *message);
|
size_t puts(const char *message);
|
||||||
void vprintf(const char *fmt, va_list args);
|
void vprintf(const char *fmt, va_list args);
|
||||||
|
|
||||||
inline void printf(const char *fmt, ...)
|
inline void printf(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vprintf(fmt, args);
|
vprintf(fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void put_hex(T x, int width = 0, char pad = ' ');
|
void put_hex(T x, int width = 0, char pad = ' ');
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void put_dec(T x, int width = 0, char pad = ' ');
|
void put_dec(T x, int width = 0, char pad = ' ');
|
||||||
|
|
||||||
void echo();
|
void echo();
|
||||||
|
|
||||||
static console * get();
|
static console * get();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
serial_port *m_serial;
|
serial_port *m_serial;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern console &g_console;
|
extern console &g_console;
|
||||||
@@ -48,37 +48,37 @@ extern const char digits[];
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void console::put_hex(T x, int width, char pad)
|
void console::put_hex(T x, int width, char pad)
|
||||||
{
|
{
|
||||||
static const int chars = sizeof(x) * 2;
|
static const int chars = sizeof(x) * 2;
|
||||||
char message[chars + 1];
|
char message[chars + 1];
|
||||||
|
|
||||||
int len = 1;
|
int len = 1;
|
||||||
for (int i = chars - 1; i >= 0; --i) {
|
for (int i = chars - 1; i >= 0; --i) {
|
||||||
uint8_t nibble = (x >> (i*4)) & 0xf;
|
uint8_t nibble = (x >> (i*4)) & 0xf;
|
||||||
if (nibble) len = len > i + 1 ? len : i + 1;
|
if (nibble) len = len > i + 1 ? len : i + 1;
|
||||||
message[chars - i - 1] = digits[nibble];
|
message[chars - i - 1] = digits[nibble];
|
||||||
}
|
}
|
||||||
message[chars] = 0;
|
message[chars] = 0;
|
||||||
|
|
||||||
if (width > len) for(int i=0; i<(width-len); ++i) putc(pad);
|
if (width > len) for(int i=0; i<(width-len); ++i) putc(pad);
|
||||||
puts(message + (chars - len));
|
puts(message + (chars - len));
|
||||||
if (-width > len) for(int i=0; i<(-width-len); ++i) putc(' ');
|
if (-width > len) for(int i=0; i<(-width-len); ++i) putc(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void console::put_dec(T x, int width, char pad)
|
void console::put_dec(T x, int width, char pad)
|
||||||
{
|
{
|
||||||
static const int chars = sizeof(x) * 3;
|
static const int chars = sizeof(x) * 3;
|
||||||
char message[chars + 1];
|
char message[chars + 1];
|
||||||
char *p = message + chars;
|
char *p = message + chars;
|
||||||
int length = 0;
|
int length = 0;
|
||||||
*p-- = 0;
|
*p-- = 0;
|
||||||
do {
|
do {
|
||||||
*p-- = digits[x % 10];
|
*p-- = digits[x % 10];
|
||||||
x /= 10;
|
x /= 10;
|
||||||
length += 1;
|
length += 1;
|
||||||
} while (x != 0);
|
} while (x != 0);
|
||||||
|
|
||||||
if (width > length) for(int i=0; i<(width-length); ++i) putc(pad);
|
if (width > length) for(int i=0; i<(width-length); ++i) putc(pad);
|
||||||
puts(++p);
|
puts(++p);
|
||||||
if (-width > length) for(int i=0; i<(-width-length); ++i) putc(' ');
|
if (-width > length) for(int i=0; i<(-width-length); ++i) putc(' ');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
using __exit_func = void (*)(void *);
|
using __exit_func = void (*)(void *);
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void *__dso_handle __attribute__ ((__weak__));
|
void *__dso_handle __attribute__ ((__weak__));
|
||||||
int __cxa_atexit(__exit_func, void *, void *);
|
int __cxa_atexit(__exit_func, void *, void *);
|
||||||
void __cxa_pure_virtual();
|
void __cxa_pure_virtual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct __exit_func_entry
|
struct __exit_func_entry
|
||||||
{
|
{
|
||||||
__exit_func func;
|
__exit_func func;
|
||||||
void *obj;
|
void *obj;
|
||||||
void *dso;
|
void *dso;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __num_exit_funcs = 0;
|
static int __num_exit_funcs = 0;
|
||||||
@@ -23,15 +23,15 @@ __exit_func_entry __exit_funcs[__max_exit_funcs];
|
|||||||
int
|
int
|
||||||
__cxa_atexit(__exit_func f, void *o, void *dso)
|
__cxa_atexit(__exit_func f, void *o, void *dso)
|
||||||
{
|
{
|
||||||
int i = __num_exit_funcs++;
|
int i = __num_exit_funcs++;
|
||||||
if (i >= __max_exit_funcs) return -1;
|
if (i >= __max_exit_funcs) return -1;
|
||||||
__exit_funcs[i].func = f;
|
__exit_funcs[i].func = f;
|
||||||
__exit_funcs[i].obj = o;
|
__exit_funcs[i].obj = o;
|
||||||
__exit_funcs[i].dso = dso;
|
__exit_funcs[i].dso = dso;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __cxa_pure_virtual()
|
void __cxa_pure_virtual()
|
||||||
{
|
{
|
||||||
kassert(0, "Pure virtual function call");
|
kassert(0, "Pure virtual function call");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,20 +18,20 @@ cpu_data g_bsp_cpu_data;
|
|||||||
void
|
void
|
||||||
cpu_validate()
|
cpu_validate()
|
||||||
{
|
{
|
||||||
cpu::cpu_id cpu;
|
cpu::cpu_id cpu;
|
||||||
|
|
||||||
log::info(logs::boot, "CPU: %s", cpu.brand_name());
|
log::info(logs::boot, "CPU: %s", cpu.brand_name());
|
||||||
log::debug(logs::boot, " Vendor is %s", cpu.vendor_id());
|
log::debug(logs::boot, " Vendor is %s", cpu.vendor_id());
|
||||||
|
|
||||||
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic());
|
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic());
|
||||||
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
|
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
|
||||||
|
|
||||||
#define CPU_FEATURE_OPT(name, ...) \
|
#define CPU_FEATURE_OPT(name, ...) \
|
||||||
log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "yes" : "no");
|
log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "yes" : "no");
|
||||||
|
|
||||||
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
|
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
|
||||||
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
|
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
|
||||||
kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name );
|
kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name );
|
||||||
|
|
||||||
#include "cpu/features.inc"
|
#include "cpu/features.inc"
|
||||||
#undef CPU_FEATURE_OPT
|
#undef CPU_FEATURE_OPT
|
||||||
@@ -41,32 +41,32 @@ cpu_validate()
|
|||||||
void
|
void
|
||||||
cpu_early_init(cpu_data *cpu)
|
cpu_early_init(cpu_data *cpu)
|
||||||
{
|
{
|
||||||
cpu->idt->install();
|
cpu->idt->install();
|
||||||
cpu->gdt->install();
|
cpu->gdt->install();
|
||||||
|
|
||||||
// Install the GS base pointint to the cpu_data
|
// Install the GS base pointint to the cpu_data
|
||||||
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
|
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
|
||||||
|
|
||||||
// Set the initial process as the kernel "process"
|
// Set the initial process as the kernel "process"
|
||||||
extern process &g_kernel_process;
|
extern process &g_kernel_process;
|
||||||
cpu->process = &g_kernel_process;
|
cpu->process = &g_kernel_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cpu_init(cpu_data *cpu, bool bsp)
|
cpu_init(cpu_data *cpu, bool bsp)
|
||||||
{
|
{
|
||||||
if (!bsp) {
|
if (!bsp) {
|
||||||
// The BSP already called cpu_early_init
|
// The BSP already called cpu_early_init
|
||||||
cpu_early_init(cpu);
|
cpu_early_init(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the syscall MSRs
|
// Set up the syscall MSRs
|
||||||
syscall_enable();
|
syscall_enable();
|
||||||
|
|
||||||
// Set up the page attributes table
|
// Set up the page attributes table
|
||||||
uint64_t pat = rdmsr(msr::ia32_pat);
|
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||||
pat = (pat & 0x00ffffffffffffffull) | (0x01ull << 56); // set PAT 7 to WC
|
pat = (pat & 0x00ffffffffffffffull) | (0x01ull << 56); // set PAT 7 to WC
|
||||||
wrmsr(msr::ia32_pat, pat);
|
wrmsr(msr::ia32_pat, pat);
|
||||||
|
|
||||||
cpu->idt->add_ist_entries();
|
cpu->idt->add_ist_entries();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,32 +12,32 @@ class TSS;
|
|||||||
|
|
||||||
struct cpu_state
|
struct cpu_state
|
||||||
{
|
{
|
||||||
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
|
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
|
||||||
uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax;
|
uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax;
|
||||||
uint64_t interrupt, errorcode;
|
uint64_t interrupt, errorcode;
|
||||||
uint64_t rip, cs, rflags, rsp, ss;
|
uint64_t rip, cs, rflags, rsp, ss;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Per-cpu state data. If you change this, remember to update the assembly
|
/// Per-cpu state data. If you change this, remember to update the assembly
|
||||||
/// version in 'tasking.inc'
|
/// version in 'tasking.inc'
|
||||||
struct cpu_data
|
struct cpu_data
|
||||||
{
|
{
|
||||||
cpu_data *self;
|
cpu_data *self;
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
uint16_t index;
|
uint16_t index;
|
||||||
uint32_t reserved;
|
uint32_t reserved;
|
||||||
uintptr_t rsp0;
|
uintptr_t rsp0;
|
||||||
uintptr_t rsp3;
|
uintptr_t rsp3;
|
||||||
TCB *tcb;
|
TCB *tcb;
|
||||||
thread *thread;
|
thread *thread;
|
||||||
process *process;
|
process *process;
|
||||||
IDT *idt;
|
IDT *idt;
|
||||||
TSS *tss;
|
TSS *tss;
|
||||||
GDT *gdt;
|
GDT *gdt;
|
||||||
|
|
||||||
// Members beyond this point do not appear in
|
// Members beyond this point do not appear in
|
||||||
// the assembly version
|
// the assembly version
|
||||||
lapic *apic;
|
lapic *apic;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" cpu_data * _current_gsbase();
|
extern "C" cpu_data * _current_gsbase();
|
||||||
|
|||||||
@@ -11,63 +11,63 @@ size_t __counter_syscall_sysret = 0;
|
|||||||
void
|
void
|
||||||
print_regs(const cpu_state ®s)
|
print_regs(const cpu_state ®s)
|
||||||
{
|
{
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
cpu_data &cpu = current_cpu();
|
cpu_data &cpu = current_cpu();
|
||||||
|
|
||||||
uint64_t cr2 = 0;
|
uint64_t cr2 = 0;
|
||||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
|
||||||
uintptr_t cr3 = 0;
|
uintptr_t cr3 = 0;
|
||||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
||||||
|
|
||||||
cons->printf(" process: %llx", cpu.process->koid());
|
cons->printf(" process: %llx", cpu.process->koid());
|
||||||
cons->printf(" thread: %llx\n", cpu.thread->koid());
|
cons->printf(" thread: %llx\n", cpu.thread->koid());
|
||||||
|
|
||||||
print_regL("rax", regs.rax);
|
print_regL("rax", regs.rax);
|
||||||
print_regM("rbx", regs.rbx);
|
print_regM("rbx", regs.rbx);
|
||||||
print_regR("rcx", regs.rcx);
|
print_regR("rcx", regs.rcx);
|
||||||
print_regL("rdx", regs.rdx);
|
print_regL("rdx", regs.rdx);
|
||||||
print_regM("rdi", regs.rdi);
|
print_regM("rdi", regs.rdi);
|
||||||
print_regR("rsi", regs.rsi);
|
print_regR("rsi", regs.rsi);
|
||||||
|
|
||||||
cons->puts("\n");
|
cons->puts("\n");
|
||||||
print_regL(" r8", regs.r8);
|
print_regL(" r8", regs.r8);
|
||||||
print_regM(" r9", regs.r9);
|
print_regM(" r9", regs.r9);
|
||||||
print_regR("r10", regs.r10);
|
print_regR("r10", regs.r10);
|
||||||
print_regL("r11", regs.r11);
|
print_regL("r11", regs.r11);
|
||||||
print_regM("r12", regs.r12);
|
print_regM("r12", regs.r12);
|
||||||
print_regR("r13", regs.r13);
|
print_regR("r13", regs.r13);
|
||||||
print_regL("r14", regs.r14);
|
print_regL("r14", regs.r14);
|
||||||
print_regM("r15", regs.r15);
|
print_regM("r15", regs.r15);
|
||||||
|
|
||||||
cons->puts("\n\n");
|
cons->puts("\n\n");
|
||||||
print_regL("rbp", regs.rbp);
|
print_regL("rbp", regs.rbp);
|
||||||
print_regM("rsp", regs.rsp);
|
print_regM("rsp", regs.rsp);
|
||||||
print_regR("sp0", cpu.rsp0);
|
print_regR("sp0", cpu.rsp0);
|
||||||
|
|
||||||
print_regL("rip", regs.rip);
|
print_regL("rip", regs.rip);
|
||||||
print_regM("cr3", cr3);
|
print_regM("cr3", cr3);
|
||||||
print_regR("cr2", cr2);
|
print_regR("cr2", cr2);
|
||||||
|
|
||||||
cons->puts("\n");
|
cons->puts("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct frame
|
struct frame
|
||||||
{
|
{
|
||||||
frame *prev;
|
frame *prev;
|
||||||
uintptr_t return_addr;
|
uintptr_t return_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
print_stack(const cpu_state ®s)
|
print_stack(const cpu_state ®s)
|
||||||
{
|
{
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
|
|
||||||
cons->puts("\nStack:\n");
|
cons->puts("\nStack:\n");
|
||||||
uint64_t sp = regs.rsp;
|
uint64_t sp = regs.rsp;
|
||||||
while (sp <= regs.rbp) {
|
while (sp <= regs.rbp) {
|
||||||
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
|
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
|
||||||
sp += sizeof(uint64_t);
|
sp += sizeof(uint64_t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
struct cpu_state;
|
struct cpu_state;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
uintptr_t get_rsp();
|
uintptr_t get_rsp();
|
||||||
uintptr_t get_rip();
|
uintptr_t get_rip();
|
||||||
uintptr_t get_caller();
|
uintptr_t get_caller();
|
||||||
uintptr_t get_grandcaller();
|
uintptr_t get_grandcaller();
|
||||||
uintptr_t get_frame(int frame);
|
uintptr_t get_frame(int frame);
|
||||||
uintptr_t get_gsbase();
|
uintptr_t get_gsbase();
|
||||||
void _halt();
|
void _halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern size_t __counter_syscall_enter;
|
extern size_t __counter_syscall_enter;
|
||||||
|
|||||||
@@ -23,417 +23,417 @@ device_manager device_manager::s_instance;
|
|||||||
|
|
||||||
struct acpi1_rsdp
|
struct acpi1_rsdp
|
||||||
{
|
{
|
||||||
char signature[8];
|
char signature[8];
|
||||||
uint8_t checksum;
|
uint8_t checksum;
|
||||||
char oem_id[6];
|
char oem_id[6];
|
||||||
uint8_t revision;
|
uint8_t revision;
|
||||||
uint32_t rsdt_address;
|
uint32_t rsdt_address;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct acpi2_rsdp
|
struct acpi2_rsdp
|
||||||
{
|
{
|
||||||
char signature[8];
|
char signature[8];
|
||||||
uint8_t checksum10;
|
uint8_t checksum10;
|
||||||
char oem_id[6];
|
char oem_id[6];
|
||||||
uint8_t revision;
|
uint8_t revision;
|
||||||
uint32_t rsdt_address;
|
uint32_t rsdt_address;
|
||||||
|
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
acpi_table_header *xsdt_address;
|
acpi_table_header *xsdt_address;
|
||||||
uint8_t checksum20;
|
uint8_t checksum20;
|
||||||
uint8_t reserved[3];
|
uint8_t reserved[3];
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
bool
|
bool
|
||||||
acpi_table_header::validate(uint32_t expected_type) const
|
acpi_table_header::validate(uint32_t expected_type) const
|
||||||
{
|
{
|
||||||
if (kutil::checksum(this, length) != 0) return false;
|
if (kutil::checksum(this, length) != 0) return false;
|
||||||
return !expected_type || (expected_type == type);
|
return !expected_type || (expected_type == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
device_manager::device_manager() :
|
device_manager::device_manager() :
|
||||||
m_lapic_base(0)
|
m_lapic_base(0)
|
||||||
{
|
{
|
||||||
m_irqs.ensure_capacity(32);
|
m_irqs.ensure_capacity(32);
|
||||||
m_irqs.set_size(16);
|
m_irqs.set_size(16);
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = 0; i < 16; ++i)
|
||||||
m_irqs[i] = nullptr;
|
m_irqs[i] = nullptr;
|
||||||
|
|
||||||
m_irqs[2] = ignore_endpoint;
|
m_irqs[2] = ignore_endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> static const T *
|
template <typename T> static const T *
|
||||||
check_get_table(const acpi_table_header *header)
|
check_get_table(const acpi_table_header *header)
|
||||||
{
|
{
|
||||||
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
|
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
|
||||||
return reinterpret_cast<const T *>(header);
|
return reinterpret_cast<const T *>(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::parse_acpi(const void *root_table)
|
device_manager::parse_acpi(const void *root_table)
|
||||||
{
|
{
|
||||||
kassert(root_table != 0, "ACPI root table pointer is null.");
|
kassert(root_table != 0, "ACPI root table pointer is null.");
|
||||||
|
|
||||||
const acpi1_rsdp *acpi1 = memory::to_virtual(
|
const acpi1_rsdp *acpi1 = memory::to_virtual(
|
||||||
reinterpret_cast<const acpi1_rsdp *>(root_table));
|
reinterpret_cast<const acpi1_rsdp *>(root_table));
|
||||||
|
|
||||||
for (int i = 0; i < sizeof(acpi1->signature); ++i)
|
for (int i = 0; i < sizeof(acpi1->signature); ++i)
|
||||||
kassert(acpi1->signature[i] == expected_signature[i],
|
kassert(acpi1->signature[i] == expected_signature[i],
|
||||||
"ACPI RSDP table signature mismatch");
|
"ACPI RSDP table signature mismatch");
|
||||||
|
|
||||||
uint8_t sum = kutil::checksum(acpi1, sizeof(acpi1_rsdp), 0);
|
uint8_t sum = kutil::checksum(acpi1, sizeof(acpi1_rsdp), 0);
|
||||||
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
|
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
|
||||||
|
|
||||||
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
|
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
|
||||||
|
|
||||||
const acpi2_rsdp *acpi2 =
|
const acpi2_rsdp *acpi2 =
|
||||||
reinterpret_cast<const acpi2_rsdp *>(acpi1);
|
reinterpret_cast<const acpi2_rsdp *>(acpi1);
|
||||||
|
|
||||||
sum = kutil::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
|
sum = kutil::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
|
||||||
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
||||||
|
|
||||||
load_xsdt(memory::to_virtual(acpi2->xsdt_address));
|
load_xsdt(memory::to_virtual(acpi2->xsdt_address));
|
||||||
}
|
}
|
||||||
|
|
||||||
const device_manager::apic_nmi *
|
const device_manager::apic_nmi *
|
||||||
device_manager::get_lapic_nmi(uint8_t id) const
|
device_manager::get_lapic_nmi(uint8_t id) const
|
||||||
{
|
{
|
||||||
for (const auto &nmi : m_nmis) {
|
for (const auto &nmi : m_nmis) {
|
||||||
if (nmi.cpu == 0xff || nmi.cpu == id)
|
if (nmi.cpu == 0xff || nmi.cpu == id)
|
||||||
return &nmi;
|
return &nmi;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const device_manager::irq_override *
|
const device_manager::irq_override *
|
||||||
device_manager::get_irq_override(uint8_t irq) const
|
device_manager::get_irq_override(uint8_t irq) const
|
||||||
{
|
{
|
||||||
for (const auto &o : m_overrides)
|
for (const auto &o : m_overrides)
|
||||||
if (o.source == irq) return &o;
|
if (o.source == irq) return &o;
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ioapic *
|
ioapic *
|
||||||
device_manager::get_ioapic(int i)
|
device_manager::get_ioapic(int i)
|
||||||
{
|
{
|
||||||
return (i < m_ioapics.count()) ? &m_ioapics[i] : nullptr;
|
return (i < m_ioapics.count()) ? &m_ioapics[i] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
put_sig(char *into, uint32_t type)
|
put_sig(char *into, uint32_t type)
|
||||||
{
|
{
|
||||||
for (int j=0; j<4; ++j) into[j] = reinterpret_cast<char *>(&type)[j];
|
for (int j=0; j<4; ++j) into[j] = reinterpret_cast<char *>(&type)[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::load_xsdt(const acpi_table_header *header)
|
device_manager::load_xsdt(const acpi_table_header *header)
|
||||||
{
|
{
|
||||||
const auto *xsdt = check_get_table<acpi_xsdt>(header);
|
const auto *xsdt = check_get_table<acpi_xsdt>(header);
|
||||||
|
|
||||||
char sig[5] = {0,0,0,0,0};
|
char sig[5] = {0,0,0,0,0};
|
||||||
log::info(logs::device, "ACPI 2.0+ tables loading");
|
log::info(logs::device, "ACPI 2.0+ tables loading");
|
||||||
|
|
||||||
put_sig(sig, xsdt->header.type);
|
put_sig(sig, xsdt->header.type);
|
||||||
log::debug(logs::device, " Found table %s", sig);
|
log::debug(logs::device, " Found table %s", sig);
|
||||||
|
|
||||||
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
|
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
|
||||||
for (size_t i = 0; i < num_tables; ++i) {
|
for (size_t i = 0; i < num_tables; ++i) {
|
||||||
const acpi_table_header *header =
|
const acpi_table_header *header =
|
||||||
memory::to_virtual(xsdt->headers[i]);
|
memory::to_virtual(xsdt->headers[i]);
|
||||||
|
|
||||||
put_sig(sig, header->type);
|
put_sig(sig, header->type);
|
||||||
log::debug(logs::device, " Found table %s", sig);
|
log::debug(logs::device, " Found table %s", sig);
|
||||||
|
|
||||||
kassert(header->validate(), "Table failed validation.");
|
kassert(header->validate(), "Table failed validation.");
|
||||||
|
|
||||||
switch (header->type) {
|
switch (header->type) {
|
||||||
case acpi_apic::type_id:
|
case acpi_apic::type_id:
|
||||||
load_apic(header);
|
load_apic(header);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case acpi_mcfg::type_id:
|
case acpi_mcfg::type_id:
|
||||||
load_mcfg(header);
|
load_mcfg(header);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case acpi_hpet::type_id:
|
case acpi_hpet::type_id:
|
||||||
load_hpet(header);
|
load_hpet(header);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::load_apic(const acpi_table_header *header)
|
device_manager::load_apic(const acpi_table_header *header)
|
||||||
{
|
{
|
||||||
const auto *apic = check_get_table<acpi_apic>(header);
|
const auto *apic = check_get_table<acpi_apic>(header);
|
||||||
|
|
||||||
m_lapic_base = apic->local_address;
|
m_lapic_base = apic->local_address;
|
||||||
|
|
||||||
size_t count = acpi_table_entries(apic, 1);
|
size_t count = acpi_table_entries(apic, 1);
|
||||||
uint8_t const *p = apic->controller_data;
|
uint8_t const *p = apic->controller_data;
|
||||||
uint8_t const *end = p + count;
|
uint8_t const *end = p + count;
|
||||||
|
|
||||||
// Pass one: count objcts
|
// Pass one: count objcts
|
||||||
unsigned num_lapics = 0;
|
unsigned num_lapics = 0;
|
||||||
unsigned num_ioapics = 0;
|
unsigned num_ioapics = 0;
|
||||||
unsigned num_overrides = 0;
|
unsigned num_overrides = 0;
|
||||||
unsigned num_nmis = 0;
|
unsigned num_nmis = 0;
|
||||||
while (p < end) {
|
while (p < end) {
|
||||||
const uint8_t type = p[0];
|
const uint8_t type = p[0];
|
||||||
const uint8_t length = p[1];
|
const uint8_t length = p[1];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0: ++num_lapics; break;
|
case 0: ++num_lapics; break;
|
||||||
case 1: ++num_ioapics; break;
|
case 1: ++num_ioapics; break;
|
||||||
case 2: ++num_overrides; break;
|
case 2: ++num_overrides; break;
|
||||||
case 4: ++num_nmis; break;
|
case 4: ++num_nmis; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
p += length;
|
p += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_apic_ids.set_capacity(num_lapics);
|
m_apic_ids.set_capacity(num_lapics);
|
||||||
m_ioapics.set_capacity(num_ioapics);
|
m_ioapics.set_capacity(num_ioapics);
|
||||||
m_overrides.set_capacity(num_overrides);
|
m_overrides.set_capacity(num_overrides);
|
||||||
m_nmis.set_capacity(num_nmis);
|
m_nmis.set_capacity(num_nmis);
|
||||||
|
|
||||||
// Pass two: configure objects
|
// Pass two: configure objects
|
||||||
p = apic->controller_data;
|
p = apic->controller_data;
|
||||||
while (p < end) {
|
while (p < end) {
|
||||||
const uint8_t type = p[0];
|
const uint8_t type = p[0];
|
||||||
const uint8_t length = p[1];
|
const uint8_t length = p[1];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0: { // Local APIC
|
case 0: { // Local APIC
|
||||||
uint8_t uid = kutil::read_from<uint8_t>(p+2);
|
uint8_t uid = kutil::read_from<uint8_t>(p+2);
|
||||||
uint8_t id = kutil::read_from<uint8_t>(p+3);
|
uint8_t id = kutil::read_from<uint8_t>(p+3);
|
||||||
m_apic_ids.append(id);
|
m_apic_ids.append(id);
|
||||||
|
|
||||||
log::debug(logs::device, " Local APIC uid %x id %x", uid, id);
|
log::debug(logs::device, " Local APIC uid %x id %x", uid, id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: { // I/O APIC
|
case 1: { // I/O APIC
|
||||||
uintptr_t base = kutil::read_from<uint32_t>(p+4);
|
uintptr_t base = kutil::read_from<uint32_t>(p+4);
|
||||||
uint32_t base_gsi = kutil::read_from<uint32_t>(p+8);
|
uint32_t base_gsi = kutil::read_from<uint32_t>(p+8);
|
||||||
m_ioapics.emplace(base, base_gsi);
|
m_ioapics.emplace(base, base_gsi);
|
||||||
|
|
||||||
log::debug(logs::device, " IO APIC gsi %x base %x", base_gsi, base);
|
log::debug(logs::device, " IO APIC gsi %x base %x", base_gsi, base);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: { // Interrupt source override
|
case 2: { // Interrupt source override
|
||||||
irq_override o;
|
irq_override o;
|
||||||
o.source = kutil::read_from<uint8_t>(p+3);
|
o.source = kutil::read_from<uint8_t>(p+3);
|
||||||
o.gsi = kutil::read_from<uint32_t>(p+4);
|
o.gsi = kutil::read_from<uint32_t>(p+4);
|
||||||
o.flags = kutil::read_from<uint16_t>(p+8);
|
o.flags = kutil::read_from<uint16_t>(p+8);
|
||||||
m_overrides.append(o);
|
m_overrides.append(o);
|
||||||
|
|
||||||
log::debug(logs::device, " Intr source override IRQ %d -> %d Pol %d Tri %d",
|
log::debug(logs::device, " Intr source override IRQ %d -> %d Pol %d Tri %d",
|
||||||
o.source, o.gsi, (o.flags & 0x3), ((o.flags >> 2) & 0x3));
|
o.source, o.gsi, (o.flags & 0x3), ((o.flags >> 2) & 0x3));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: {// LAPIC NMI
|
case 4: {// LAPIC NMI
|
||||||
apic_nmi nmi;
|
apic_nmi nmi;
|
||||||
nmi.cpu = kutil::read_from<uint8_t>(p + 2);
|
nmi.cpu = kutil::read_from<uint8_t>(p + 2);
|
||||||
nmi.lint = kutil::read_from<uint8_t>(p + 5);
|
nmi.lint = kutil::read_from<uint8_t>(p + 5);
|
||||||
nmi.flags = kutil::read_from<uint16_t>(p + 3);
|
nmi.flags = kutil::read_from<uint16_t>(p + 3);
|
||||||
m_nmis.append(nmi);
|
m_nmis.append(nmi);
|
||||||
|
|
||||||
log::debug(logs::device, " LAPIC NMI Proc %02x LINT%d Pol %d Tri %d",
|
log::debug(logs::device, " LAPIC NMI Proc %02x LINT%d Pol %d Tri %d",
|
||||||
nmi.cpu, nmi.lint, nmi.flags & 0x3, (nmi.flags >> 2) & 0x3);
|
nmi.cpu, nmi.lint, nmi.flags & 0x3, (nmi.flags >> 2) & 0x3);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log::debug(logs::device, " APIC entry type %d", type);
|
log::debug(logs::device, " APIC entry type %d", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
p += length;
|
p += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ioapics[0].mask(3, false);
|
m_ioapics[0].mask(3, false);
|
||||||
m_ioapics[0].mask(4, false);
|
m_ioapics[0].mask(4, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::load_mcfg(const acpi_table_header *header)
|
device_manager::load_mcfg(const acpi_table_header *header)
|
||||||
{
|
{
|
||||||
const auto *mcfg = check_get_table<acpi_mcfg>(header);
|
const auto *mcfg = check_get_table<acpi_mcfg>(header);
|
||||||
|
|
||||||
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
|
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
|
||||||
m_pci.set_size(count);
|
m_pci.set_size(count);
|
||||||
m_devices.set_capacity(16);
|
m_devices.set_capacity(16);
|
||||||
|
|
||||||
for (unsigned i = 0; i < count; ++i) {
|
for (unsigned i = 0; i < count; ++i) {
|
||||||
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
|
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
|
||||||
|
|
||||||
m_pci[i].group = mcfge.group;
|
m_pci[i].group = mcfge.group;
|
||||||
m_pci[i].bus_start = mcfge.bus_start;
|
m_pci[i].bus_start = mcfge.bus_start;
|
||||||
m_pci[i].bus_end = mcfge.bus_end;
|
m_pci[i].bus_end = mcfge.bus_end;
|
||||||
m_pci[i].base = memory::to_virtual<uint32_t>(mcfge.base);
|
m_pci[i].base = memory::to_virtual<uint32_t>(mcfge.base);
|
||||||
|
|
||||||
log::debug(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
|
log::debug(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
|
||||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
probe_pci();
|
probe_pci();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::load_hpet(const acpi_table_header *header)
|
device_manager::load_hpet(const acpi_table_header *header)
|
||||||
{
|
{
|
||||||
const auto *hpet = check_get_table<acpi_hpet>(header);
|
const auto *hpet = check_get_table<acpi_hpet>(header);
|
||||||
|
|
||||||
log::debug(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
|
log::debug(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
|
||||||
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
|
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
|
||||||
|
|
||||||
uint32_t hwid = hpet->hardware_id;
|
uint32_t hwid = hpet->hardware_id;
|
||||||
uint8_t rev_id = hwid & 0xff;
|
uint8_t rev_id = hwid & 0xff;
|
||||||
uint8_t comparators = (hwid >> 8) & 0x1f;
|
uint8_t comparators = (hwid >> 8) & 0x1f;
|
||||||
uint8_t count_size_cap = (hwid >> 13) & 1;
|
uint8_t count_size_cap = (hwid >> 13) & 1;
|
||||||
uint8_t legacy_replacement = (hwid >> 15) & 1;
|
uint8_t legacy_replacement = (hwid >> 15) & 1;
|
||||||
uint32_t pci_vendor_id = (hwid >> 16);
|
uint32_t pci_vendor_id = (hwid >> 16);
|
||||||
|
|
||||||
log::debug(logs::device, " rev:%02d comparators:%02d count_size_cap:%1d legacy_repl:%1d",
|
log::debug(logs::device, " rev:%02d comparators:%02d count_size_cap:%1d legacy_repl:%1d",
|
||||||
rev_id, comparators, count_size_cap, legacy_replacement);
|
rev_id, comparators, count_size_cap, legacy_replacement);
|
||||||
log::debug(logs::device, " pci vendor id: %04x", pci_vendor_id);
|
log::debug(logs::device, " pci vendor id: %04x", pci_vendor_id);
|
||||||
|
|
||||||
m_hpets.emplace(hpet->index,
|
m_hpets.emplace(hpet->index,
|
||||||
reinterpret_cast<uint64_t*>(hpet->base_address.address + ::memory::page_offset));
|
reinterpret_cast<uint64_t*>(hpet->base_address.address + ::memory::page_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::probe_pci()
|
device_manager::probe_pci()
|
||||||
{
|
{
|
||||||
for (auto &pci : m_pci) {
|
for (auto &pci : m_pci) {
|
||||||
log::debug(logs::device, "Probing PCI group at base %016lx", pci.base);
|
log::debug(logs::device, "Probing PCI group at base %016lx", pci.base);
|
||||||
|
|
||||||
for (int bus = pci.bus_start; bus <= pci.bus_end; ++bus) {
|
for (int bus = pci.bus_start; bus <= pci.bus_end; ++bus) {
|
||||||
for (int dev = 0; dev < 32; ++dev) {
|
for (int dev = 0; dev < 32; ++dev) {
|
||||||
if (!pci.has_device(bus, dev, 0)) continue;
|
if (!pci.has_device(bus, dev, 0)) continue;
|
||||||
|
|
||||||
auto &d0 = m_devices.emplace(pci, bus, dev, 0);
|
auto &d0 = m_devices.emplace(pci, bus, dev, 0);
|
||||||
if (!d0.multi()) continue;
|
if (!d0.multi()) continue;
|
||||||
|
|
||||||
for (int i = 1; i < 8; ++i) {
|
for (int i = 1; i < 8; ++i) {
|
||||||
if (pci.has_device(bus, dev, i))
|
if (pci.has_device(bus, dev, i))
|
||||||
m_devices.emplace(pci, bus, dev, i);
|
m_devices.emplace(pci, bus, dev, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t
|
static uint64_t
|
||||||
fake_clock_source(void*)
|
fake_clock_source(void*)
|
||||||
{
|
{
|
||||||
static uint64_t value = 0;
|
static uint64_t value = 0;
|
||||||
return value++;
|
return value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::init_drivers()
|
device_manager::init_drivers()
|
||||||
{
|
{
|
||||||
// Eventually this should be e.g. a lookup into a loadable driver list
|
// Eventually this should be e.g. a lookup into a loadable driver list
|
||||||
// for now, just look for AHCI devices
|
// for now, just look for AHCI devices
|
||||||
/*
|
/*
|
||||||
for (auto &device : m_devices) {
|
for (auto &device : m_devices) {
|
||||||
if (device.devclass() != 1 || device.subclass() != 6)
|
if (device.devclass() != 1 || device.subclass() != 6)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (device.progif() != 1) {
|
if (device.progif() != 1) {
|
||||||
log::warn(logs::device, "Found SATA device %d:%d:%d, but not an AHCI interface.",
|
log::warn(logs::device, "Found SATA device %d:%d:%d, but not an AHCI interface.",
|
||||||
device.bus(), device.device(), device.function());
|
device.bus(), device.device(), device.function());
|
||||||
}
|
}
|
||||||
|
|
||||||
ahcid.register_device(&device);
|
ahcid.register_device(&device);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
clock *master_clock = nullptr;
|
clock *master_clock = nullptr;
|
||||||
if (m_hpets.count() > 0) {
|
if (m_hpets.count() > 0) {
|
||||||
hpet &h = m_hpets[0];
|
hpet &h = m_hpets[0];
|
||||||
h.enable();
|
h.enable();
|
||||||
|
|
||||||
// becomes the singleton
|
// becomes the singleton
|
||||||
master_clock = new clock(h.rate(), hpet_clock_source, &h);
|
master_clock = new clock(h.rate(), hpet_clock_source, &h);
|
||||||
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
||||||
} else {
|
} else {
|
||||||
//TODO: Other clocks, APIC clock?
|
//TODO: Other clocks, APIC clock?
|
||||||
master_clock = new clock(5000, fake_clock_source, nullptr);
|
master_clock = new clock(5000, fake_clock_source, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
kassert(master_clock, "Failed to allocate master clock");
|
kassert(master_clock, "Failed to allocate master clock");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_manager::dispatch_irq(unsigned irq)
|
device_manager::dispatch_irq(unsigned irq)
|
||||||
{
|
{
|
||||||
if (irq == 4) {
|
if (irq == 4) {
|
||||||
g_com1.handle_interrupt();
|
g_com1.handle_interrupt();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (irq >= m_irqs.count())
|
if (irq >= m_irqs.count())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
endpoint *e = m_irqs[irq];
|
endpoint *e = m_irqs[irq];
|
||||||
if (!e || e == ignore_endpoint)
|
if (!e || e == ignore_endpoint)
|
||||||
return e == ignore_endpoint;
|
return e == ignore_endpoint;
|
||||||
|
|
||||||
e->signal_irq(irq);
|
e->signal_irq(irq);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_manager::bind_irq(unsigned irq, endpoint *target)
|
device_manager::bind_irq(unsigned irq, endpoint *target)
|
||||||
{
|
{
|
||||||
// TODO: grow if under max size
|
// TODO: grow if under max size
|
||||||
if (irq >= m_irqs.count())
|
if (irq >= m_irqs.count())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_irqs[irq]= target;
|
m_irqs[irq]= target;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::unbind_irqs(endpoint *target)
|
device_manager::unbind_irqs(endpoint *target)
|
||||||
{
|
{
|
||||||
const size_t count = m_irqs.count();
|
const size_t count = m_irqs.count();
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
if (m_irqs[i] == target)
|
if (m_irqs[i] == target)
|
||||||
m_irqs[i] = nullptr;
|
m_irqs[i] = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
// TODO: find gaps to fill
|
// TODO: find gaps to fill
|
||||||
uint8_t irq = m_irqs.count();
|
uint8_t irq = m_irqs.count();
|
||||||
isr vector = isr::irq00 + irq;
|
isr vector = isr::irq00 + irq;
|
||||||
m_irqs.append({name, cb, data});
|
m_irqs.append({name, cb, data});
|
||||||
|
|
||||||
log::debug(logs::device, "Allocating IRQ %02x to %s.", irq, name);
|
log::debug(logs::device, "Allocating IRQ %02x to %s.", irq, name);
|
||||||
|
|
||||||
device.write_msi_regs(
|
device.write_msi_regs(
|
||||||
0xFEE00000,
|
0xFEE00000,
|
||||||
static_cast<uint16_t>(vector));
|
static_cast<uint16_t>(vector));
|
||||||
*/
|
*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::register_block_device(block_device *blockdev)
|
device_manager::register_block_device(block_device *blockdev)
|
||||||
{
|
{
|
||||||
m_blockdevs.append(blockdev);
|
m_blockdevs.append(blockdev);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,154 +17,154 @@ using irq_callback = void (*)(void *);
|
|||||||
class device_manager
|
class device_manager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
device_manager();
|
device_manager();
|
||||||
|
|
||||||
/// Get the system global device manager.
|
/// Get the system global device manager.
|
||||||
/// \returns A reference to the system device manager
|
/// \returns A reference to the system device manager
|
||||||
static device_manager & get() { return s_instance; }
|
static device_manager & get() { return s_instance; }
|
||||||
|
|
||||||
/// Get an IOAPIC
|
/// Get an IOAPIC
|
||||||
/// \arg i Index of the requested IOAPIC
|
/// \arg i Index of the requested IOAPIC
|
||||||
/// \returns An object representing the given IOAPIC if it exists,
|
/// \returns An object representing the given IOAPIC if it exists,
|
||||||
/// otherwise nullptr.
|
/// otherwise nullptr.
|
||||||
ioapic * get_ioapic(int i);
|
ioapic * get_ioapic(int i);
|
||||||
|
|
||||||
/// Parse ACPI tables.
|
/// Parse ACPI tables.
|
||||||
/// \arg root_table Pointer to the ACPI RSDP
|
/// \arg root_table Pointer to the ACPI RSDP
|
||||||
void parse_acpi(const void *root_table);
|
void parse_acpi(const void *root_table);
|
||||||
|
|
||||||
/// Intialize drivers for the current device list.
|
/// Intialize drivers for the current device list.
|
||||||
void init_drivers();
|
void init_drivers();
|
||||||
|
|
||||||
/// Bind an IRQ to an endpoint
|
/// Bind an IRQ to an endpoint
|
||||||
/// \arg irq The IRQ number to bind
|
/// \arg irq The IRQ number to bind
|
||||||
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
||||||
/// \returns True on success
|
/// \returns True on success
|
||||||
bool bind_irq(unsigned irq, endpoint *target);
|
bool bind_irq(unsigned irq, endpoint *target);
|
||||||
|
|
||||||
/// Remove IRQ bindings for an endpoint
|
/// Remove IRQ bindings for an endpoint
|
||||||
/// \arg target The endpoint to remove
|
/// \arg target The endpoint to remove
|
||||||
void unbind_irqs(endpoint *target);
|
void unbind_irqs(endpoint *target);
|
||||||
|
|
||||||
/// Allocate an MSI IRQ for a device
|
/// Allocate an MSI IRQ for a device
|
||||||
/// \arg name Name of the interrupt, for display to user
|
/// \arg name Name of the interrupt, for display to user
|
||||||
/// \arg device Device this MSI is being allocated for
|
/// \arg device Device this MSI is being allocated for
|
||||||
/// \arg cb Callback to call when the interrupt is received
|
/// \arg cb Callback to call when the interrupt is received
|
||||||
/// \arg data Data to pass to the callback
|
/// \arg data Data to pass to the callback
|
||||||
/// \returns True if an interrupt was allocated successfully
|
/// \returns True if an interrupt was allocated successfully
|
||||||
bool allocate_msi(
|
bool allocate_msi(
|
||||||
const char *name,
|
const char *name,
|
||||||
pci_device &device,
|
pci_device &device,
|
||||||
irq_callback cb,
|
irq_callback cb,
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
/// Dispatch an IRQ interrupt
|
/// Dispatch an IRQ interrupt
|
||||||
/// \arg irq The irq number of the interrupt
|
/// \arg irq The irq number of the interrupt
|
||||||
/// \returns True if the interrupt was handled
|
/// \returns True if the interrupt was handled
|
||||||
bool dispatch_irq(unsigned irq);
|
bool dispatch_irq(unsigned irq);
|
||||||
|
|
||||||
struct apic_nmi
|
struct apic_nmi
|
||||||
{
|
{
|
||||||
uint8_t cpu;
|
uint8_t cpu;
|
||||||
uint8_t lint;
|
uint8_t lint;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct irq_override
|
struct irq_override
|
||||||
{
|
{
|
||||||
uint8_t source;
|
uint8_t source;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint32_t gsi;
|
uint32_t gsi;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get the list of APIC ids for other CPUs
|
/// Get the list of APIC ids for other CPUs
|
||||||
inline const kutil::vector<uint8_t> & get_apic_ids() const { return m_apic_ids; }
|
inline const kutil::vector<uint8_t> & get_apic_ids() const { return m_apic_ids; }
|
||||||
|
|
||||||
/// Get the LAPIC base address
|
/// Get the LAPIC base address
|
||||||
/// \returns The physical base address of the local apic registers
|
/// \returns The physical base address of the local apic registers
|
||||||
uintptr_t get_lapic_base() const { return m_lapic_base; }
|
uintptr_t get_lapic_base() const { return m_lapic_base; }
|
||||||
|
|
||||||
/// Get the NMI mapping for the given local APIC
|
/// Get the NMI mapping for the given local APIC
|
||||||
/// \arg id ID of the local APIC
|
/// \arg id ID of the local APIC
|
||||||
/// \returns apic_nmi structure describing the NMI configuration,
|
/// \returns apic_nmi structure describing the NMI configuration,
|
||||||
/// or null if no configuration was provided
|
/// or null if no configuration was provided
|
||||||
const apic_nmi * get_lapic_nmi(uint8_t id) const;
|
const apic_nmi * get_lapic_nmi(uint8_t id) const;
|
||||||
|
|
||||||
/// Get the IRQ source override for the given IRQ
|
/// Get the IRQ source override for the given IRQ
|
||||||
/// \arg irq IRQ number (not isr vector)
|
/// \arg irq IRQ number (not isr vector)
|
||||||
/// \returns irq_override structure describing that IRQ's
|
/// \returns irq_override structure describing that IRQ's
|
||||||
/// configuration, or null if no configuration was provided
|
/// configuration, or null if no configuration was provided
|
||||||
const irq_override * get_irq_override(uint8_t irq) const;
|
const irq_override * get_irq_override(uint8_t irq) const;
|
||||||
|
|
||||||
/// Register the existance of a block device.
|
/// Register the existance of a block device.
|
||||||
/// \arg blockdev Pointer to the block device
|
/// \arg blockdev Pointer to the block device
|
||||||
void register_block_device(block_device *blockdev);
|
void register_block_device(block_device *blockdev);
|
||||||
|
|
||||||
/// Get the number of block devices in the system
|
/// Get the number of block devices in the system
|
||||||
/// \returns A count of devices
|
/// \returns A count of devices
|
||||||
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
|
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
|
||||||
|
|
||||||
/// Get a block device
|
/// Get a block device
|
||||||
/// \arg i Index of the device to get
|
/// \arg i Index of the device to get
|
||||||
/// \returns A pointer to the requested device, or nullptr
|
/// \returns A pointer to the requested device, or nullptr
|
||||||
inline block_device * get_block_device(unsigned i)
|
inline block_device * get_block_device(unsigned i)
|
||||||
{
|
{
|
||||||
return i < m_blockdevs.count() ?
|
return i < m_blockdevs.count() ?
|
||||||
m_blockdevs[i] : nullptr;
|
m_blockdevs[i] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an HPET device
|
/// Get an HPET device
|
||||||
/// \arg i Index of the device to get
|
/// \arg i Index of the device to get
|
||||||
/// \returns A pointer to the requested device, or nullptr
|
/// \returns A pointer to the requested device, or nullptr
|
||||||
inline hpet * get_hpet(unsigned i)
|
inline hpet * get_hpet(unsigned i)
|
||||||
{
|
{
|
||||||
return i < m_hpets.count() ?
|
return i < m_hpets.count() ?
|
||||||
&m_hpets[i] : nullptr;
|
&m_hpets[i] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Parse the ACPI XSDT and load relevant sub-tables.
|
/// Parse the ACPI XSDT and load relevant sub-tables.
|
||||||
/// \arg xsdt Pointer to the XSDT from the firmware
|
/// \arg xsdt Pointer to the XSDT from the firmware
|
||||||
void load_xsdt(const acpi_table_header *xsdt);
|
void load_xsdt(const acpi_table_header *xsdt);
|
||||||
|
|
||||||
/// Parse the ACPI MADT and initialize APICs from it.
|
/// Parse the ACPI MADT and initialize APICs from it.
|
||||||
/// \arg apic Pointer to the MADT from the XSDT
|
/// \arg apic Pointer to the MADT from the XSDT
|
||||||
void load_apic(const acpi_table_header *apic);
|
void load_apic(const acpi_table_header *apic);
|
||||||
|
|
||||||
/// Parse the ACPI MCFG and initialize PCIe from it.
|
/// Parse the ACPI MCFG and initialize PCIe from it.
|
||||||
/// \arg mcfg Pointer to the MCFG from the XSDT
|
/// \arg mcfg Pointer to the MCFG from the XSDT
|
||||||
void load_mcfg(const acpi_table_header *mcfg);
|
void load_mcfg(const acpi_table_header *mcfg);
|
||||||
|
|
||||||
/// Parse the ACPI HPET and initialize an HPET from it.
|
/// Parse the ACPI HPET and initialize an HPET from it.
|
||||||
/// \arg hpet Pointer to the HPET from the XSDT
|
/// \arg hpet Pointer to the HPET from the XSDT
|
||||||
void load_hpet(const acpi_table_header *hpet);
|
void load_hpet(const acpi_table_header *hpet);
|
||||||
|
|
||||||
/// Probe the PCIe busses and add found devices to our
|
/// Probe the PCIe busses and add found devices to our
|
||||||
/// device list. The device list is destroyed and rebuilt.
|
/// device list. The device list is destroyed and rebuilt.
|
||||||
void probe_pci();
|
void probe_pci();
|
||||||
|
|
||||||
/// Handle a bad IRQ. Called when an interrupt is dispatched
|
/// Handle a bad IRQ. Called when an interrupt is dispatched
|
||||||
/// that has no callback.
|
/// that has no callback.
|
||||||
void bad_irq(uint8_t irq);
|
void bad_irq(uint8_t irq);
|
||||||
|
|
||||||
uintptr_t m_lapic_base;
|
uintptr_t m_lapic_base;
|
||||||
|
|
||||||
kutil::vector<ioapic> m_ioapics;
|
kutil::vector<ioapic> m_ioapics;
|
||||||
kutil::vector<hpet> m_hpets;
|
kutil::vector<hpet> m_hpets;
|
||||||
kutil::vector<uint8_t> m_apic_ids;
|
kutil::vector<uint8_t> m_apic_ids;
|
||||||
kutil::vector<apic_nmi> m_nmis;
|
kutil::vector<apic_nmi> m_nmis;
|
||||||
kutil::vector<irq_override> m_overrides;
|
kutil::vector<irq_override> m_overrides;
|
||||||
|
|
||||||
kutil::vector<pci_group> m_pci;
|
kutil::vector<pci_group> m_pci;
|
||||||
kutil::vector<pci_device> m_devices;
|
kutil::vector<pci_device> m_devices;
|
||||||
|
|
||||||
kutil::vector<endpoint*> m_irqs;
|
kutil::vector<endpoint*> m_irqs;
|
||||||
|
|
||||||
kutil::vector<block_device *> m_blockdevs;
|
kutil::vector<block_device *> m_blockdevs;
|
||||||
|
|
||||||
static device_manager s_instance;
|
static device_manager s_instance;
|
||||||
|
|
||||||
device_manager(const device_manager &) = delete;
|
device_manager(const device_manager &) = delete;
|
||||||
device_manager operator=(const device_manager &) = delete;
|
device_manager operator=(const device_manager &) = delete;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,152 +12,152 @@ using memory::frame_size;
|
|||||||
frame_allocator &
|
frame_allocator &
|
||||||
frame_allocator::get()
|
frame_allocator::get()
|
||||||
{
|
{
|
||||||
extern frame_allocator &g_frame_allocator;
|
extern frame_allocator &g_frame_allocator;
|
||||||
return g_frame_allocator;
|
return g_frame_allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_allocator::frame_allocator(kernel::init::frame_block *frames, size_t count) :
|
frame_allocator::frame_allocator(kernel::init::frame_block *frames, size_t count) :
|
||||||
m_blocks {frames},
|
m_blocks {frames},
|
||||||
m_count {count}
|
m_count {count}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned
|
inline unsigned
|
||||||
bsf(uint64_t v)
|
bsf(uint64_t v)
|
||||||
{
|
{
|
||||||
asm ("tzcntq %q0, %q1" : "=r"(v) : "0"(v) : "cc");
|
asm ("tzcntq %q0, %q1" : "=r"(v) : "0"(v) : "cc");
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
frame_allocator::allocate(size_t count, uintptr_t *address)
|
frame_allocator::allocate(size_t count, uintptr_t *address)
|
||||||
{
|
{
|
||||||
kutil::scoped_lock lock {m_lock};
|
kutil::scoped_lock lock {m_lock};
|
||||||
|
|
||||||
for (long i = m_count - 1; i >= 0; --i) {
|
for (long i = m_count - 1; i >= 0; --i) {
|
||||||
frame_block &block = m_blocks[i];
|
frame_block &block = m_blocks[i];
|
||||||
|
|
||||||
if (!block.map1)
|
if (!block.map1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Tree walk to find the first available page
|
// Tree walk to find the first available page
|
||||||
unsigned o1 = bsf(block.map1);
|
unsigned o1 = bsf(block.map1);
|
||||||
|
|
||||||
uint64_t m2 = block.map2[o1];
|
uint64_t m2 = block.map2[o1];
|
||||||
unsigned o2 = bsf(m2);
|
unsigned o2 = bsf(m2);
|
||||||
|
|
||||||
uint64_t m3 = block.bitmap[(o1 << 6) + o2];
|
uint64_t m3 = block.bitmap[(o1 << 6) + o2];
|
||||||
unsigned o3 = bsf(m3);
|
unsigned o3 = bsf(m3);
|
||||||
|
|
||||||
unsigned frame = (o1 << 12) + (o2 << 6) + o3;
|
unsigned frame = (o1 << 12) + (o2 << 6) + o3;
|
||||||
|
|
||||||
// See how many contiguous pages are here
|
// See how many contiguous pages are here
|
||||||
unsigned n = bsf(~m3 >> o3);
|
unsigned n = bsf(~m3 >> o3);
|
||||||
if (n > count)
|
if (n > count)
|
||||||
n = count;
|
n = count;
|
||||||
|
|
||||||
*address = block.base + frame * frame_size;
|
*address = block.base + frame * frame_size;
|
||||||
|
|
||||||
// Clear the bits to mark these pages allocated
|
// Clear the bits to mark these pages allocated
|
||||||
m3 &= ~(((1 << n) - 1) << o3);
|
m3 &= ~(((1 << n) - 1) << o3);
|
||||||
block.bitmap[(o1 << 6) + o2] = m3;
|
block.bitmap[(o1 << 6) + o2] = m3;
|
||||||
if (!m3) {
|
if (!m3) {
|
||||||
// if that was it for this group, clear the next level bit
|
// if that was it for this group, clear the next level bit
|
||||||
m2 &= ~(1 << o2);
|
m2 &= ~(1 << o2);
|
||||||
block.map2[o1] = m2;
|
block.map2[o1] = m2;
|
||||||
|
|
||||||
if (!m2) {
|
if (!m2) {
|
||||||
// if that was cleared too, update the top level
|
// if that was cleared too, update the top level
|
||||||
block.map1 &= ~(1 << o1);
|
block.map1 &= ~(1 << o1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
kassert(false, "frame_allocator ran out of free frames!");
|
kassert(false, "frame_allocator ran out of free frames!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
frame_allocator::free(uintptr_t address, size_t count)
|
frame_allocator::free(uintptr_t address, size_t count)
|
||||||
{
|
{
|
||||||
kutil::scoped_lock lock {m_lock};
|
kutil::scoped_lock lock {m_lock};
|
||||||
|
|
||||||
kassert(address % frame_size == 0, "Trying to free a non page-aligned frame!");
|
kassert(address % frame_size == 0, "Trying to free a non page-aligned frame!");
|
||||||
|
|
||||||
if (!count)
|
if (!count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (long i = 0; i < m_count; ++i) {
|
for (long i = 0; i < m_count; ++i) {
|
||||||
frame_block &block = m_blocks[i];
|
frame_block &block = m_blocks[i];
|
||||||
uintptr_t end = block.base + block.count * frame_size;
|
uintptr_t end = block.base + block.count * frame_size;
|
||||||
|
|
||||||
if (address < block.base || address >= end)
|
if (address < block.base || address >= end)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
uint64_t frame = (address - block.base) >> 12;
|
uint64_t frame = (address - block.base) >> 12;
|
||||||
unsigned o1 = (frame >> 12) & 0x3f;
|
unsigned o1 = (frame >> 12) & 0x3f;
|
||||||
unsigned o2 = (frame >> 6) & 0x3f;
|
unsigned o2 = (frame >> 6) & 0x3f;
|
||||||
unsigned o3 = frame & 0x3f;
|
unsigned o3 = frame & 0x3f;
|
||||||
|
|
||||||
while (count--) {
|
while (count--) {
|
||||||
block.map1 |= (1 << o1);
|
block.map1 |= (1 << o1);
|
||||||
block.map2[o1] |= (1 << o2);
|
block.map2[o1] |= (1 << o2);
|
||||||
block.bitmap[o2] |= (1 << o3);
|
block.bitmap[o2] |= (1 << o3);
|
||||||
if (++o3 == 64) {
|
if (++o3 == 64) {
|
||||||
o3 = 0;
|
o3 = 0;
|
||||||
if (++o2 == 64) {
|
if (++o2 == 64) {
|
||||||
o2 = 0;
|
o2 = 0;
|
||||||
++o1;
|
++o1;
|
||||||
kassert(o1 < 64, "Tried to free pages past the end of a block");
|
kassert(o1 < 64, "Tried to free pages past the end of a block");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
frame_allocator::used(uintptr_t address, size_t count)
|
frame_allocator::used(uintptr_t address, size_t count)
|
||||||
{
|
{
|
||||||
kutil::scoped_lock lock {m_lock};
|
kutil::scoped_lock lock {m_lock};
|
||||||
|
|
||||||
kassert(address % frame_size == 0, "Trying to mark a non page-aligned frame!");
|
kassert(address % frame_size == 0, "Trying to mark a non page-aligned frame!");
|
||||||
|
|
||||||
if (!count)
|
if (!count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (long i = 0; i < m_count; ++i) {
|
for (long i = 0; i < m_count; ++i) {
|
||||||
frame_block &block = m_blocks[i];
|
frame_block &block = m_blocks[i];
|
||||||
uintptr_t end = block.base + block.count * frame_size;
|
uintptr_t end = block.base + block.count * frame_size;
|
||||||
|
|
||||||
if (address < block.base || address >= end)
|
if (address < block.base || address >= end)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
uint64_t frame = (address - block.base) >> 12;
|
uint64_t frame = (address - block.base) >> 12;
|
||||||
unsigned o1 = (frame >> 12) & 0x3f;
|
unsigned o1 = (frame >> 12) & 0x3f;
|
||||||
unsigned o2 = (frame >> 6) & 0x3f;
|
unsigned o2 = (frame >> 6) & 0x3f;
|
||||||
unsigned o3 = frame & 0x3f;
|
unsigned o3 = frame & 0x3f;
|
||||||
|
|
||||||
while (count--) {
|
while (count--) {
|
||||||
block.bitmap[o2] &= ~(1 << o3);
|
block.bitmap[o2] &= ~(1 << o3);
|
||||||
if (!block.bitmap[o2]) {
|
if (!block.bitmap[o2]) {
|
||||||
block.map2[o1] &= ~(1 << o2);
|
block.map2[o1] &= ~(1 << o2);
|
||||||
|
|
||||||
if (!block.map2[o1]) {
|
if (!block.map2[o1]) {
|
||||||
block.map1 &= ~(1 << o1);
|
block.map1 &= ~(1 << o1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++o3 == 64) {
|
if (++o3 == 64) {
|
||||||
o3 = 0;
|
o3 = 0;
|
||||||
if (++o2 == 64) {
|
if (++o2 == 64) {
|
||||||
o2 = 0;
|
o2 = 0;
|
||||||
++o1;
|
++o1;
|
||||||
kassert(o1 < 64, "Tried to mark pages past the end of a block");
|
kassert(o1 < 64, "Tried to mark pages past the end of a block");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,47 +7,47 @@
|
|||||||
|
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace init {
|
namespace init {
|
||||||
struct frame_block;
|
struct frame_block;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/// Allocator for physical memory frames
|
/// Allocator for physical memory frames
|
||||||
class frame_allocator
|
class frame_allocator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using frame_block = kernel::init::frame_block;
|
using frame_block = kernel::init::frame_block;
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg blocks The bootloader-supplied frame bitmap block list
|
/// \arg blocks The bootloader-supplied frame bitmap block list
|
||||||
/// \arg count Number of entries in the block list
|
/// \arg count Number of entries in the block list
|
||||||
frame_allocator(frame_block *frames, size_t count);
|
frame_allocator(frame_block *frames, size_t count);
|
||||||
|
|
||||||
/// Get free frames from the free list. Only frames from the first free block
|
/// Get free frames from the free list. Only frames from the first free block
|
||||||
/// are returned, so the number may be less than requested, but they will
|
/// are returned, so the number may be less than requested, but they will
|
||||||
/// be contiguous.
|
/// be contiguous.
|
||||||
/// \arg count The maximum number of frames to get
|
/// \arg count The maximum number of frames to get
|
||||||
/// \arg address [out] The physical address of the first frame
|
/// \arg address [out] The physical address of the first frame
|
||||||
/// \returns The number of frames retrieved
|
/// \returns The number of frames retrieved
|
||||||
size_t allocate(size_t count, uintptr_t *address);
|
size_t allocate(size_t count, uintptr_t *address);
|
||||||
|
|
||||||
/// Free previously allocated frames.
|
/// Free previously allocated frames.
|
||||||
/// \arg address The physical address of the first frame to free
|
/// \arg address The physical address of the first frame to free
|
||||||
/// \arg count The number of frames to be freed
|
/// \arg count The number of frames to be freed
|
||||||
void free(uintptr_t address, size_t count);
|
void free(uintptr_t address, size_t count);
|
||||||
|
|
||||||
/// Mark frames as used
|
/// Mark frames as used
|
||||||
/// \arg address The physical address of the first frame to free
|
/// \arg address The physical address of the first frame to free
|
||||||
/// \arg count The number of frames to be freed
|
/// \arg count The number of frames to be freed
|
||||||
void used(uintptr_t address, size_t count);
|
void used(uintptr_t address, size_t count);
|
||||||
|
|
||||||
/// Get the global frame allocator
|
/// Get the global frame allocator
|
||||||
static frame_allocator & get();
|
static frame_allocator & get();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
frame_block *m_blocks;
|
frame_block *m_blocks;
|
||||||
size_t m_count;
|
size_t m_count;
|
||||||
|
|
||||||
kutil::spinlock m_lock;
|
kutil::spinlock m_lock;
|
||||||
|
|
||||||
frame_allocator() = delete;
|
frame_allocator() = delete;
|
||||||
frame_allocator(const frame_allocator &) = delete;
|
frame_allocator(const frame_allocator &) = delete;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,143 +26,143 @@ GDT &g_bsp_gdt = __g_bsp_gdt_storage.value;
|
|||||||
|
|
||||||
|
|
||||||
GDT::GDT(TSS *tss) :
|
GDT::GDT(TSS *tss) :
|
||||||
m_tss(tss)
|
m_tss(tss)
|
||||||
{
|
{
|
||||||
kutil::memset(this, 0, sizeof(GDT));
|
kutil::memset(this, 0, sizeof(GDT));
|
||||||
|
|
||||||
m_ptr.limit = sizeof(m_entries) - 1;
|
m_ptr.limit = sizeof(m_entries) - 1;
|
||||||
m_ptr.base = &m_entries[0];
|
m_ptr.base = &m_entries[0];
|
||||||
|
|
||||||
// Kernel CS/SS - always 64bit
|
// Kernel CS/SS - always 64bit
|
||||||
set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute);
|
set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute);
|
||||||
set(kern_ss_index, 0, 0xfffff, true, type::read_write);
|
set(kern_ss_index, 0, 0xfffff, true, type::read_write);
|
||||||
|
|
||||||
// User CS32/SS/CS64 - layout expected by SYSRET
|
// User CS32/SS/CS64 - layout expected by SYSRET
|
||||||
set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute);
|
set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute);
|
||||||
set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write);
|
set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write);
|
||||||
set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute);
|
set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute);
|
||||||
|
|
||||||
set_tss(tss);
|
set_tss(tss);
|
||||||
}
|
}
|
||||||
|
|
||||||
GDT &
|
GDT &
|
||||||
GDT::current()
|
GDT::current()
|
||||||
{
|
{
|
||||||
cpu_data &cpu = current_cpu();
|
cpu_data &cpu = current_cpu();
|
||||||
return *cpu.gdt;
|
return *cpu.gdt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GDT::install() const
|
GDT::install() const
|
||||||
{
|
{
|
||||||
gdt_write(
|
gdt_write(
|
||||||
static_cast<const void*>(&m_ptr),
|
static_cast<const void*>(&m_ptr),
|
||||||
kern_cs_index << 3,
|
kern_cs_index << 3,
|
||||||
kern_ss_index << 3,
|
kern_ss_index << 3,
|
||||||
tss_index << 3);
|
tss_index << 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t)
|
GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t)
|
||||||
{
|
{
|
||||||
m_entries[i].limit_low = limit & 0xffff;
|
m_entries[i].limit_low = limit & 0xffff;
|
||||||
m_entries[i].size = (limit >> 16) & 0xf;
|
m_entries[i].size = (limit >> 16) & 0xf;
|
||||||
m_entries[i].size |= (is64 ? 0xa0 : 0xc0);
|
m_entries[i].size |= (is64 ? 0xa0 : 0xc0);
|
||||||
|
|
||||||
m_entries[i].base_low = base & 0xffff;
|
m_entries[i].base_low = base & 0xffff;
|
||||||
m_entries[i].base_mid = (base >> 16) & 0xff;
|
m_entries[i].base_mid = (base >> 16) & 0xff;
|
||||||
m_entries[i].base_high = (base >> 24) & 0xff;
|
m_entries[i].base_high = (base >> 24) & 0xff;
|
||||||
|
|
||||||
m_entries[i].type = t | type::system | type::present;
|
m_entries[i].type = t | type::system | type::present;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tss_descriptor
|
struct tss_descriptor
|
||||||
{
|
{
|
||||||
uint16_t limit_low;
|
uint16_t limit_low;
|
||||||
uint16_t base_00;
|
uint16_t base_00;
|
||||||
uint8_t base_16;
|
uint8_t base_16;
|
||||||
GDT::type type;
|
GDT::type type;
|
||||||
uint8_t size;
|
uint8_t size;
|
||||||
uint8_t base_24;
|
uint8_t base_24;
|
||||||
uint32_t base_32;
|
uint32_t base_32;
|
||||||
uint32_t reserved;
|
uint32_t reserved;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
void
|
void
|
||||||
GDT::set_tss(TSS *tss)
|
GDT::set_tss(TSS *tss)
|
||||||
{
|
{
|
||||||
tss_descriptor tssd;
|
tss_descriptor tssd;
|
||||||
|
|
||||||
size_t limit = sizeof(TSS);
|
size_t limit = sizeof(TSS);
|
||||||
tssd.limit_low = limit & 0xffff;
|
tssd.limit_low = limit & 0xffff;
|
||||||
tssd.size = (limit >> 16) & 0xf;
|
tssd.size = (limit >> 16) & 0xf;
|
||||||
|
|
||||||
uintptr_t base = reinterpret_cast<uintptr_t>(tss);
|
uintptr_t base = reinterpret_cast<uintptr_t>(tss);
|
||||||
tssd.base_00 = base & 0xffff;
|
tssd.base_00 = base & 0xffff;
|
||||||
tssd.base_16 = (base >> 16) & 0xff;
|
tssd.base_16 = (base >> 16) & 0xff;
|
||||||
tssd.base_24 = (base >> 24) & 0xff;
|
tssd.base_24 = (base >> 24) & 0xff;
|
||||||
tssd.base_32 = (base >> 32) & 0xffffffff;
|
tssd.base_32 = (base >> 32) & 0xffffffff;
|
||||||
tssd.reserved = 0;
|
tssd.reserved = 0;
|
||||||
|
|
||||||
tssd.type =
|
tssd.type =
|
||||||
type::accessed |
|
type::accessed |
|
||||||
type::execute |
|
type::execute |
|
||||||
type::ring3 |
|
type::ring3 |
|
||||||
type::present;
|
type::present;
|
||||||
|
|
||||||
kutil::memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
|
kutil::memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GDT::dump(unsigned index) const
|
GDT::dump(unsigned index) const
|
||||||
{
|
{
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
|
|
||||||
unsigned start = 0;
|
unsigned start = 0;
|
||||||
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
start = index;
|
start = index;
|
||||||
count = 1;
|
count = 1;
|
||||||
} else {
|
} else {
|
||||||
cons->printf(" GDT: loc:%lx size:%d\n", m_ptr.base, m_ptr.limit+1);
|
cons->printf(" GDT: loc:%lx size:%d\n", m_ptr.base, m_ptr.limit+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const descriptor *gdt =
|
const descriptor *gdt =
|
||||||
reinterpret_cast<const descriptor *>(m_ptr.base);
|
reinterpret_cast<const descriptor *>(m_ptr.base);
|
||||||
|
|
||||||
for (int i = start; i < start+count; ++i) {
|
for (int i = start; i < start+count; ++i) {
|
||||||
uint32_t base =
|
uint32_t base =
|
||||||
(gdt[i].base_high << 24) |
|
(gdt[i].base_high << 24) |
|
||||||
(gdt[i].base_mid << 16) |
|
(gdt[i].base_mid << 16) |
|
||||||
gdt[i].base_low;
|
gdt[i].base_low;
|
||||||
|
|
||||||
uint32_t limit =
|
uint32_t limit =
|
||||||
static_cast<uint32_t>(gdt[i].size & 0x0f) << 16 |
|
static_cast<uint32_t>(gdt[i].size & 0x0f) << 16 |
|
||||||
gdt[i].limit_low;
|
gdt[i].limit_low;
|
||||||
|
|
||||||
cons->printf(" %02d:", i);
|
cons->printf(" %02d:", i);
|
||||||
if (! (gdt[i].type && type::present)) {
|
if (! (gdt[i].type && type::present)) {
|
||||||
cons->puts(" Not Present\n");
|
cons->puts(" Not Present\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cons->printf(" Base %08x limit %05x ", base, limit);
|
cons->printf(" Base %08x limit %05x ", base, limit);
|
||||||
|
|
||||||
switch (gdt[i].type & type::ring3) {
|
switch (gdt[i].type & type::ring3) {
|
||||||
case type::ring3: cons->puts("ring3"); break;
|
case type::ring3: cons->puts("ring3"); break;
|
||||||
case type::ring2: cons->puts("ring2"); break;
|
case type::ring2: cons->puts("ring2"); break;
|
||||||
case type::ring1: cons->puts("ring1"); break;
|
case type::ring1: cons->puts("ring1"); break;
|
||||||
default: cons->puts("ring0"); break;
|
default: cons->puts("ring0"); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
cons->printf(" %s %s %s %s %s %s %s\n",
|
cons->printf(" %s %s %s %s %s %s %s\n",
|
||||||
(gdt[i].type && type::accessed) ? "A" : " ",
|
(gdt[i].type && type::accessed) ? "A" : " ",
|
||||||
(gdt[i].type && type::read_write) ? "RW" : " ",
|
(gdt[i].type && type::read_write) ? "RW" : " ",
|
||||||
(gdt[i].type && type::conforming) ? "C" : " ",
|
(gdt[i].type && type::conforming) ? "C" : " ",
|
||||||
(gdt[i].type && type::execute) ? "EX" : " ",
|
(gdt[i].type && type::execute) ? "EX" : " ",
|
||||||
(gdt[i].type && type::system) ? "S" : " ",
|
(gdt[i].type && type::system) ? "S" : " ",
|
||||||
(gdt[i].size & 0x80) ? "KB" : " B",
|
(gdt[i].size & 0x80) ? "KB" : " B",
|
||||||
(gdt[i].size & 0x60) == 0x20 ? "64" :
|
(gdt[i].size & 0x60) == 0x20 ? "64" :
|
||||||
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
|
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,58 +10,58 @@ class TSS;
|
|||||||
class GDT
|
class GDT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GDT(TSS *tss);
|
GDT(TSS *tss);
|
||||||
|
|
||||||
/// Get the currently running CPU's GDT
|
/// Get the currently running CPU's GDT
|
||||||
static GDT & current();
|
static GDT & current();
|
||||||
|
|
||||||
/// Install this GDT to the current CPU
|
/// Install this GDT to the current CPU
|
||||||
void install() const;
|
void install() const;
|
||||||
|
|
||||||
/// Get the addrss of the pointer
|
/// Get the addrss of the pointer
|
||||||
inline const void * pointer() const { return static_cast<const void*>(&m_ptr); }
|
inline const void * pointer() const { return static_cast<const void*>(&m_ptr); }
|
||||||
|
|
||||||
/// Dump debug information about the GDT to the console.
|
/// Dump debug information about the GDT to the console.
|
||||||
/// \arg index Which entry to print, or -1 for all entries
|
/// \arg index Which entry to print, or -1 for all entries
|
||||||
void dump(unsigned index = -1) const;
|
void dump(unsigned index = -1) const;
|
||||||
|
|
||||||
enum class type : uint8_t
|
enum class type : uint8_t
|
||||||
{
|
{
|
||||||
accessed = 0x01,
|
accessed = 0x01,
|
||||||
read_write = 0x02,
|
read_write = 0x02,
|
||||||
conforming = 0x04,
|
conforming = 0x04,
|
||||||
execute = 0x08,
|
execute = 0x08,
|
||||||
system = 0x10,
|
system = 0x10,
|
||||||
ring1 = 0x20,
|
ring1 = 0x20,
|
||||||
ring2 = 0x40,
|
ring2 = 0x40,
|
||||||
ring3 = 0x60,
|
ring3 = 0x60,
|
||||||
present = 0x80
|
present = 0x80
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t);
|
void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t);
|
||||||
void set_tss(TSS *tss);
|
void set_tss(TSS *tss);
|
||||||
|
|
||||||
struct descriptor
|
struct descriptor
|
||||||
{
|
{
|
||||||
uint16_t limit_low;
|
uint16_t limit_low;
|
||||||
uint16_t base_low;
|
uint16_t base_low;
|
||||||
uint8_t base_mid;
|
uint8_t base_mid;
|
||||||
type type;
|
type type;
|
||||||
uint8_t size;
|
uint8_t size;
|
||||||
uint8_t base_high;
|
uint8_t base_high;
|
||||||
} __attribute__ ((packed, align(8)));
|
} __attribute__ ((packed, align(8)));
|
||||||
|
|
||||||
struct ptr
|
struct ptr
|
||||||
{
|
{
|
||||||
uint16_t limit;
|
uint16_t limit;
|
||||||
descriptor *base;
|
descriptor *base;
|
||||||
} __attribute__ ((packed, align(4)));
|
} __attribute__ ((packed, align(4)));
|
||||||
|
|
||||||
descriptor m_entries[8];
|
descriptor m_entries[8];
|
||||||
TSS *m_tss;
|
TSS *m_tss;
|
||||||
|
|
||||||
ptr m_ptr;
|
ptr m_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
is_bitfield(GDT::type);
|
is_bitfield(GDT::type);
|
||||||
|
|||||||
@@ -6,114 +6,114 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
inline uint64_t volatile *capabilities(uint64_t volatile *base) { return base; }
|
inline uint64_t volatile *capabilities(uint64_t volatile *base) { return base; }
|
||||||
inline uint64_t volatile *configuration(uint64_t volatile *base) { return base+2; }
|
inline uint64_t volatile *configuration(uint64_t volatile *base) { return base+2; }
|
||||||
inline uint64_t volatile *interrupt_status(uint64_t volatile *base) { return base+4; }
|
inline uint64_t volatile *interrupt_status(uint64_t volatile *base) { return base+4; }
|
||||||
inline uint64_t volatile *counter_value(uint64_t volatile *base) { return base+30; }
|
inline uint64_t volatile *counter_value(uint64_t volatile *base) { return base+30; }
|
||||||
|
|
||||||
inline uint64_t volatile *timer_base(uint64_t volatile *base, unsigned i) { return base + 0x20 + (4*i); }
|
inline uint64_t volatile *timer_base(uint64_t volatile *base, unsigned i) { return base + 0x20 + (4*i); }
|
||||||
inline uint64_t volatile *timer_config(uint64_t volatile *base, unsigned i) { return timer_base(base, i); }
|
inline uint64_t volatile *timer_config(uint64_t volatile *base, unsigned i) { return timer_base(base, i); }
|
||||||
inline uint64_t volatile *timer_comparator(uint64_t volatile *base, unsigned i) { return timer_base(base, i) + 1; }
|
inline uint64_t volatile *timer_comparator(uint64_t volatile *base, unsigned i) { return timer_base(base, i) + 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
hpet_irq_callback(void *hpet_ptr)
|
hpet_irq_callback(void *hpet_ptr)
|
||||||
{
|
{
|
||||||
if (hpet_ptr)
|
if (hpet_ptr)
|
||||||
reinterpret_cast<hpet*>(hpet_ptr)->callback();
|
reinterpret_cast<hpet*>(hpet_ptr)->callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
hpet::hpet(uint8_t index, uint64_t *base) :
|
hpet::hpet(uint8_t index, uint64_t *base) :
|
||||||
m_index(index),
|
m_index(index),
|
||||||
m_base(base)
|
m_base(base)
|
||||||
{
|
{
|
||||||
*configuration(m_base) = 0;
|
*configuration(m_base) = 0;
|
||||||
*counter_value(m_base) = 0;
|
*counter_value(m_base) = 0;
|
||||||
|
|
||||||
uint64_t caps = *capabilities(base);
|
uint64_t caps = *capabilities(base);
|
||||||
uint64_t config = *configuration(base);
|
uint64_t config = *configuration(base);
|
||||||
m_timers = ((caps >> 8) & 0x1f) + 1;
|
m_timers = ((caps >> 8) & 0x1f) + 1;
|
||||||
m_period = (caps >> 32);
|
m_period = (caps >> 32);
|
||||||
|
|
||||||
setup_timer_interrupts(0, 2, 1000, true);
|
setup_timer_interrupts(0, 2, 1000, true);
|
||||||
// bool installed = device_manager::get()
|
// bool installed = device_manager::get()
|
||||||
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
|
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
|
||||||
// kassert(installed, "Installing HPET IRQ handler");
|
// kassert(installed, "Installing HPET IRQ handler");
|
||||||
|
|
||||||
log::debug(logs::timer, "HPET %d capabilities:", index);
|
log::debug(logs::timer, "HPET %d capabilities:", index);
|
||||||
log::debug(logs::timer, " revision: %d", caps & 0xff);
|
log::debug(logs::timer, " revision: %d", caps & 0xff);
|
||||||
log::debug(logs::timer, " timers: %d", m_timers);
|
log::debug(logs::timer, " timers: %d", m_timers);
|
||||||
log::debug(logs::timer, " bits: %d", ((caps >> 13) & 1) ? 64 : 32);
|
log::debug(logs::timer, " bits: %d", ((caps >> 13) & 1) ? 64 : 32);
|
||||||
log::debug(logs::timer, " LRR capable: %d", ((caps >> 15) & 1));
|
log::debug(logs::timer, " LRR capable: %d", ((caps >> 15) & 1));
|
||||||
log::debug(logs::timer, " period: %dns", m_period / 1000000);
|
log::debug(logs::timer, " period: %dns", m_period / 1000000);
|
||||||
log::debug(logs::timer, " global enabled: %d", config & 1);
|
log::debug(logs::timer, " global enabled: %d", config & 1);
|
||||||
log::debug(logs::timer, " LRR enable: %d", (config >> 1) & 1);
|
log::debug(logs::timer, " LRR enable: %d", (config >> 1) & 1);
|
||||||
|
|
||||||
for (unsigned i = 0; i < m_timers; ++i) {
|
for (unsigned i = 0; i < m_timers; ++i) {
|
||||||
disable_timer(i);
|
disable_timer(i);
|
||||||
uint64_t config = *timer_config(m_base, i);
|
uint64_t config = *timer_config(m_base, i);
|
||||||
|
|
||||||
log::debug(logs::timer, "HPET %d timer %d:", index, i);
|
log::debug(logs::timer, "HPET %d timer %d:", index, i);
|
||||||
log::debug(logs::timer, " int type: %d", (config >> 1) & 1);
|
log::debug(logs::timer, " int type: %d", (config >> 1) & 1);
|
||||||
log::debug(logs::timer, " int enable: %d", (config >> 2) & 1);
|
log::debug(logs::timer, " int enable: %d", (config >> 2) & 1);
|
||||||
log::debug(logs::timer, " timer type: %d", (config >> 3) & 1);
|
log::debug(logs::timer, " timer type: %d", (config >> 3) & 1);
|
||||||
log::debug(logs::timer, " periodic cap: %d", (config >> 4) & 1);
|
log::debug(logs::timer, " periodic cap: %d", (config >> 4) & 1);
|
||||||
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
|
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
|
||||||
log::debug(logs::timer, " 32 mode: %d", (config >> 8) & 1);
|
log::debug(logs::timer, " 32 mode: %d", (config >> 8) & 1);
|
||||||
log::debug(logs::timer, " int route: %d", (config >> 9) & 0x1f);
|
log::debug(logs::timer, " int route: %d", (config >> 9) & 0x1f);
|
||||||
log::debug(logs::timer, " FSB enable: %d", (config >> 14) & 1);
|
log::debug(logs::timer, " FSB enable: %d", (config >> 14) & 1);
|
||||||
log::debug(logs::timer, " FSB capable: %d", (config >> 15) & 1);
|
log::debug(logs::timer, " FSB capable: %d", (config >> 15) & 1);
|
||||||
log::debug(logs::timer, " rotung cap: %08x", (config >> 32));
|
log::debug(logs::timer, " rotung cap: %08x", (config >> 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
hpet::setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic)
|
hpet::setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic)
|
||||||
{
|
{
|
||||||
constexpr uint64_t femto_per_us = 1000000000ull;
|
constexpr uint64_t femto_per_us = 1000000000ull;
|
||||||
*timer_comparator(m_base, timer) =
|
*timer_comparator(m_base, timer) =
|
||||||
*counter_value(m_base) +
|
*counter_value(m_base) +
|
||||||
(interval * femto_per_us) / m_period;
|
(interval * femto_per_us) / m_period;
|
||||||
|
|
||||||
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
|
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
hpet::enable_timer(unsigned timer)
|
hpet::enable_timer(unsigned timer)
|
||||||
{
|
{
|
||||||
*timer_config(m_base, timer) = *timer_config(m_base, timer) | (1 << 2);
|
*timer_config(m_base, timer) = *timer_config(m_base, timer) | (1 << 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
hpet::disable_timer(unsigned timer)
|
hpet::disable_timer(unsigned timer)
|
||||||
{
|
{
|
||||||
*timer_config(m_base, timer) = *timer_config(m_base, timer) & ~(1ull << 2);
|
*timer_config(m_base, timer) = *timer_config(m_base, timer) & ~(1ull << 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
hpet::callback()
|
hpet::callback()
|
||||||
{
|
{
|
||||||
log::debug(logs::timer, "HPET %d got irq", m_index);
|
log::debug(logs::timer, "HPET %d got irq", m_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
hpet::enable()
|
hpet::enable()
|
||||||
{
|
{
|
||||||
log::debug(logs::timer, "HPET %d enabling", m_index);
|
log::debug(logs::timer, "HPET %d enabling", m_index);
|
||||||
*configuration(m_base) = (*configuration(m_base) & 0x3) | 1;
|
*configuration(m_base) = (*configuration(m_base) & 0x3) | 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
hpet::value() const
|
hpet::value() const
|
||||||
{
|
{
|
||||||
return *counter_value(m_base);
|
return *counter_value(m_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
hpet_clock_source(void *data)
|
hpet_clock_source(void *data)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<hpet*>(data)->value();
|
return reinterpret_cast<hpet*>(data)->value();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,35 +12,35 @@ uint64_t hpet_clock_source(void *);
|
|||||||
class hpet
|
class hpet
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg index The index of the HPET out of all HPETs
|
/// \arg index The index of the HPET out of all HPETs
|
||||||
/// \arg base The base address of the HPET for MMIO
|
/// \arg base The base address of the HPET for MMIO
|
||||||
hpet(uint8_t index, uint64_t *base);
|
hpet(uint8_t index, uint64_t *base);
|
||||||
|
|
||||||
/// Configure the timer and start it running.
|
/// Configure the timer and start it running.
|
||||||
void enable();
|
void enable();
|
||||||
|
|
||||||
/// Get the timer rate in ticks per us
|
/// Get the timer rate in ticks per us
|
||||||
inline uint64_t rate() const { return 1000000000/m_period; }
|
inline uint64_t rate() const { return 1000000000/m_period; }
|
||||||
|
|
||||||
/// Get the current timer value
|
/// Get the current timer value
|
||||||
uint64_t value() const;
|
uint64_t value() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend void hpet_irq_callback(void *);
|
friend void hpet_irq_callback(void *);
|
||||||
friend uint64_t hpet_clock_source(void *);
|
friend uint64_t hpet_clock_source(void *);
|
||||||
|
|
||||||
void setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic);
|
void setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic);
|
||||||
void enable_timer(unsigned timer);
|
void enable_timer(unsigned timer);
|
||||||
void disable_timer(unsigned timer);
|
void disable_timer(unsigned timer);
|
||||||
|
|
||||||
void callback();
|
void callback();
|
||||||
|
|
||||||
uint8_t m_timers;
|
uint8_t m_timers;
|
||||||
uint8_t m_index;
|
uint8_t m_index;
|
||||||
|
|
||||||
uint64_t m_period;
|
uint64_t m_period;
|
||||||
volatile uint64_t *m_base;
|
volatile uint64_t *m_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void idt_write(const void *idt_ptr);
|
void idt_write(const void *idt_ptr);
|
||||||
|
|
||||||
#define ISR(i, s, name) extern void name ();
|
#define ISR(i, s, name) extern void name ();
|
||||||
#define NISR(i, s, name)
|
#define NISR(i, s, name)
|
||||||
@@ -33,9 +33,9 @@ IDT::set_nmi_handler(uintptr_t address)
|
|||||||
|
|
||||||
IDT::IDT()
|
IDT::IDT()
|
||||||
{
|
{
|
||||||
kutil::memset(this, 0, sizeof(IDT));
|
kutil::memset(this, 0, sizeof(IDT));
|
||||||
m_ptr.limit = sizeof(m_entries) - 1;
|
m_ptr.limit = sizeof(m_entries) - 1;
|
||||||
m_ptr.base = &m_entries[0];
|
m_ptr.base = &m_entries[0];
|
||||||
|
|
||||||
#define ISR(i, s, name) set(i, & name, 0x08, 0x8e);
|
#define ISR(i, s, name) set(i, & name, 0x08, 0x8e);
|
||||||
#define EISR(i, s, name) set(i, & name, 0x08, 0x8e);
|
#define EISR(i, s, name) set(i, & name, 0x08, 0x8e);
|
||||||
@@ -51,14 +51,14 @@ IDT::IDT()
|
|||||||
IDT &
|
IDT &
|
||||||
IDT::current()
|
IDT::current()
|
||||||
{
|
{
|
||||||
cpu_data &cpu = current_cpu();
|
cpu_data &cpu = current_cpu();
|
||||||
return *cpu.idt;
|
return *cpu.idt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
IDT::install() const
|
IDT::install() const
|
||||||
{
|
{
|
||||||
idt_write(static_cast<const void*>(&m_ptr));
|
idt_write(static_cast<const void*>(&m_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -78,7 +78,7 @@ IDT::add_ist_entries()
|
|||||||
uint8_t
|
uint8_t
|
||||||
IDT::used_ist_entries() const
|
IDT::used_ist_entries() const
|
||||||
{
|
{
|
||||||
constexpr uint8_t entries =
|
constexpr uint8_t entries =
|
||||||
|
|
||||||
#define ISR(i, s, name) ((s) ? (1 << s) : 0) |
|
#define ISR(i, s, name) ((s) ? (1 << s) : 0) |
|
||||||
#define NISR(i, s, name) ((s) ? (1 << s) : 0) |
|
#define NISR(i, s, name) ((s) ? (1 << s) : 0) |
|
||||||
@@ -92,64 +92,64 @@ IDT::used_ist_entries() const
|
|||||||
|
|
||||||
0;
|
0;
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
IDT::set(uint8_t i, void (*handler)(), uint16_t selector, uint8_t flags)
|
IDT::set(uint8_t i, void (*handler)(), uint16_t selector, uint8_t flags)
|
||||||
{
|
{
|
||||||
uintptr_t addr = reinterpret_cast<uintptr_t>(handler);
|
uintptr_t addr = reinterpret_cast<uintptr_t>(handler);
|
||||||
|
|
||||||
m_entries[i].base_low = addr & 0xffff;
|
m_entries[i].base_low = addr & 0xffff;
|
||||||
m_entries[i].base_mid = (addr >> 16) & 0xffff;
|
m_entries[i].base_mid = (addr >> 16) & 0xffff;
|
||||||
m_entries[i].base_high = (addr >> 32) & 0xffffffff;
|
m_entries[i].base_high = (addr >> 32) & 0xffffffff;
|
||||||
m_entries[i].selector = selector;
|
m_entries[i].selector = selector;
|
||||||
m_entries[i].flags = flags;
|
m_entries[i].flags = flags;
|
||||||
m_entries[i].ist = 0;
|
m_entries[i].ist = 0;
|
||||||
m_entries[i].reserved = 0;
|
m_entries[i].reserved = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
IDT::dump(unsigned index) const
|
IDT::dump(unsigned index) const
|
||||||
{
|
{
|
||||||
unsigned start = 0;
|
unsigned start = 0;
|
||||||
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
start = index;
|
start = index;
|
||||||
count = 1;
|
count = 1;
|
||||||
log::info(logs::boot, "IDT FOR INDEX %02x", index);
|
log::info(logs::boot, "IDT FOR INDEX %02x", index);
|
||||||
} else {
|
} else {
|
||||||
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", m_ptr.base, m_ptr.limit+1);
|
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", m_ptr.base, m_ptr.limit+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const descriptor *idt =
|
const descriptor *idt =
|
||||||
reinterpret_cast<const descriptor *>(m_ptr.base);
|
reinterpret_cast<const descriptor *>(m_ptr.base);
|
||||||
|
|
||||||
for (int i = start; i < start+count; ++i) {
|
for (int i = start; i < start+count; ++i) {
|
||||||
uint64_t base =
|
uint64_t base =
|
||||||
(static_cast<uint64_t>(idt[i].base_high) << 32) |
|
(static_cast<uint64_t>(idt[i].base_high) << 32) |
|
||||||
(static_cast<uint64_t>(idt[i].base_mid) << 16) |
|
(static_cast<uint64_t>(idt[i].base_mid) << 16) |
|
||||||
idt[i].base_low;
|
idt[i].base_low;
|
||||||
|
|
||||||
char const *type;
|
char const *type;
|
||||||
switch (idt[i].flags & 0xf) {
|
switch (idt[i].flags & 0xf) {
|
||||||
case 0x5: type = " 32tsk "; break;
|
case 0x5: type = " 32tsk "; break;
|
||||||
case 0x6: type = " 16int "; break;
|
case 0x6: type = " 16int "; break;
|
||||||
case 0x7: type = " 16trp "; break;
|
case 0x7: type = " 16trp "; break;
|
||||||
case 0xe: type = " 32int "; break;
|
case 0xe: type = " 32int "; break;
|
||||||
case 0xf: type = " 32trp "; break;
|
case 0xf: type = " 32trp "; break;
|
||||||
default: type = " ????? "; break;
|
default: type = " ????? "; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idt[i].flags & 0x80) {
|
if (idt[i].flags & 0x80) {
|
||||||
log::debug(logs::boot,
|
log::debug(logs::boot,
|
||||||
" Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base,
|
" Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base,
|
||||||
(idt[i].selector & 0x3),
|
(idt[i].selector & 0x3),
|
||||||
((idt[i].selector & 0x4) >> 2),
|
((idt[i].selector & 0x4) >> 2),
|
||||||
(idt[i].selector >> 3),
|
(idt[i].selector >> 3),
|
||||||
idt[i].ist,
|
idt[i].ist,
|
||||||
type,
|
type,
|
||||||
((idt[i].flags >> 5) & 0x3));
|
((idt[i].flags >> 5) & 0x3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,62 +6,62 @@
|
|||||||
class IDT
|
class IDT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IDT();
|
IDT();
|
||||||
|
|
||||||
/// Get the currently running CPU's IDT
|
/// Get the currently running CPU's IDT
|
||||||
static IDT & current();
|
static IDT & current();
|
||||||
|
|
||||||
/// Set the global NMI handler. Must happen before creating
|
/// Set the global NMI handler. Must happen before creating
|
||||||
/// any IDTs.
|
/// any IDTs.
|
||||||
static void set_nmi_handler(uintptr_t address);
|
static void set_nmi_handler(uintptr_t address);
|
||||||
|
|
||||||
/// Install this IDT to the current CPU
|
/// Install this IDT to the current CPU
|
||||||
void install() const;
|
void install() const;
|
||||||
|
|
||||||
/// Add the IST entries listed in the ISR table into the IDT.
|
/// Add the IST entries listed in the ISR table into the IDT.
|
||||||
/// This can't be done until after memory is set up so the
|
/// This can't be done until after memory is set up so the
|
||||||
/// stacks can be created.
|
/// stacks can be created.
|
||||||
void add_ist_entries();
|
void add_ist_entries();
|
||||||
|
|
||||||
/// Get the IST entry used by an entry.
|
/// Get the IST entry used by an entry.
|
||||||
/// \arg i Which IDT entry to look in
|
/// \arg i Which IDT entry to look in
|
||||||
/// \returns The IST index used by entry i, or 0 for none
|
/// \returns The IST index used by entry i, or 0 for none
|
||||||
inline uint8_t get_ist(uint8_t i) const {
|
inline uint8_t get_ist(uint8_t i) const {
|
||||||
return m_entries[i].ist;
|
return m_entries[i].ist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the IST entry used by an entry.
|
/// Set the IST entry used by an entry.
|
||||||
/// \arg i Which IDT entry to set
|
/// \arg i Which IDT entry to set
|
||||||
/// \arg ist The IST index for entry i, or 0 for none
|
/// \arg ist The IST index for entry i, or 0 for none
|
||||||
void set_ist(uint8_t i, uint8_t ist) { m_entries[i].ist = ist; }
|
void set_ist(uint8_t i, uint8_t ist) { m_entries[i].ist = ist; }
|
||||||
|
|
||||||
/// Get the IST entries that are used by this table, as a bitmap
|
/// Get the IST entries that are used by this table, as a bitmap
|
||||||
uint8_t used_ist_entries() const;
|
uint8_t used_ist_entries() const;
|
||||||
|
|
||||||
/// Dump debug information about the IDT to the console.
|
/// Dump debug information about the IDT to the console.
|
||||||
/// \arg index Which entry to print, or -1 for all entries
|
/// \arg index Which entry to print, or -1 for all entries
|
||||||
void dump(unsigned index = -1) const;
|
void dump(unsigned index = -1) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set(uint8_t i, void (*handler)(), uint16_t selector, uint8_t flags);
|
void set(uint8_t i, void (*handler)(), uint16_t selector, uint8_t flags);
|
||||||
|
|
||||||
struct descriptor
|
struct descriptor
|
||||||
{
|
{
|
||||||
uint16_t base_low;
|
uint16_t base_low;
|
||||||
uint16_t selector;
|
uint16_t selector;
|
||||||
uint8_t ist;
|
uint8_t ist;
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
uint16_t base_mid;
|
uint16_t base_mid;
|
||||||
uint32_t base_high;
|
uint32_t base_high;
|
||||||
uint32_t reserved; // must be zero
|
uint32_t reserved; // must be zero
|
||||||
} __attribute__ ((packed, aligned(16)));
|
} __attribute__ ((packed, aligned(16)));
|
||||||
|
|
||||||
struct ptr
|
struct ptr
|
||||||
{
|
{
|
||||||
uint16_t limit;
|
uint16_t limit;
|
||||||
descriptor *base;
|
descriptor *base;
|
||||||
} __attribute__ ((packed, aligned(4)));
|
} __attribute__ ((packed, aligned(4)));
|
||||||
|
|
||||||
descriptor m_entries[256];
|
descriptor m_entries[256];
|
||||||
ptr m_ptr;
|
ptr m_ptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ static const uint16_t PIC2 = 0xa0;
|
|||||||
constexpr uintptr_t apic_eoi_addr = 0xfee000b0 + ::memory::page_offset;
|
constexpr uintptr_t apic_eoi_addr = 0xfee000b0 + ::memory::page_offset;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void isr_handler(cpu_state*);
|
void isr_handler(cpu_state*);
|
||||||
void irq_handler(cpu_state*);
|
void irq_handler(cpu_state*);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t
|
uint8_t
|
||||||
get_irq(unsigned vector)
|
get_irq(unsigned vector)
|
||||||
{
|
{
|
||||||
switch (vector) {
|
switch (vector) {
|
||||||
#define ISR(i, s, name)
|
#define ISR(i, s, name)
|
||||||
#define EISR(i, s, name)
|
#define EISR(i, s, name)
|
||||||
#define NISR(i, s, name)
|
#define NISR(i, s, name)
|
||||||
@@ -36,51 +36,51 @@ get_irq(unsigned vector)
|
|||||||
#undef EISR
|
#undef EISR
|
||||||
#undef ISR
|
#undef ISR
|
||||||
|
|
||||||
default: return 0xff;
|
default: return 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
disable_legacy_pic()
|
disable_legacy_pic()
|
||||||
{
|
{
|
||||||
// Mask all interrupts
|
// Mask all interrupts
|
||||||
outb(PIC2+1, 0xfc);
|
outb(PIC2+1, 0xfc);
|
||||||
outb(PIC1+1, 0xff);
|
outb(PIC1+1, 0xff);
|
||||||
|
|
||||||
// Start initialization sequence
|
// Start initialization sequence
|
||||||
outb(PIC1, 0x11); io_wait();
|
outb(PIC1, 0x11); io_wait();
|
||||||
outb(PIC2, 0x11); io_wait();
|
outb(PIC2, 0x11); io_wait();
|
||||||
|
|
||||||
// Remap into ignore ISRs
|
// Remap into ignore ISRs
|
||||||
outb(PIC1+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
|
outb(PIC1+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
|
||||||
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore8)); io_wait();
|
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore8)); io_wait();
|
||||||
|
|
||||||
// Tell PICs about each other
|
// Tell PICs about each other
|
||||||
outb(PIC1+1, 0x04); io_wait();
|
outb(PIC1+1, 0x04); io_wait();
|
||||||
outb(PIC2+1, 0x02); io_wait();
|
outb(PIC2+1, 0x02); io_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
isr_handler(cpu_state *regs)
|
isr_handler(cpu_state *regs)
|
||||||
{
|
{
|
||||||
uint8_t vector = regs->interrupt & 0xff;
|
uint8_t vector = regs->interrupt & 0xff;
|
||||||
if ((vector & 0xf0) == 0xf0) {
|
if ((vector & 0xf0) == 0xf0) {
|
||||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear out the IST for this vector so we just keep using
|
// Clear out the IST for this vector so we just keep using
|
||||||
// this stack
|
// this stack
|
||||||
IDT &idt = IDT::current();
|
IDT &idt = IDT::current();
|
||||||
uint8_t old_ist = idt.get_ist(vector);
|
uint8_t old_ist = idt.get_ist(vector);
|
||||||
if (old_ist)
|
if (old_ist)
|
||||||
idt.set_ist(vector, 0);
|
idt.set_ist(vector, 0);
|
||||||
|
|
||||||
char message[200];
|
char message[200];
|
||||||
|
|
||||||
switch (static_cast<isr>(vector)) {
|
switch (static_cast<isr>(vector)) {
|
||||||
|
|
||||||
case isr::isrDebug:
|
case isr::isrDebug:
|
||||||
asm volatile ("mov %%dr0, %%r8" ::: "r8");
|
asm volatile ("mov %%dr0, %%r8" ::: "r8");
|
||||||
asm volatile ("mov %%dr1, %%r9" ::: "r9");
|
asm volatile ("mov %%dr1, %%r9" ::: "r9");
|
||||||
asm volatile ("mov %%dr2, %%r10" ::: "r10");
|
asm volatile ("mov %%dr2, %%r10" ::: "r10");
|
||||||
@@ -90,15 +90,15 @@ isr_handler(cpu_state *regs)
|
|||||||
asm volatile ("mov %%dr6, %%r14" ::: "r14");
|
asm volatile ("mov %%dr6, %%r14" ::: "r14");
|
||||||
asm volatile ("mov %%dr7, %%r15" ::: "r15");
|
asm volatile ("mov %%dr7, %%r15" ::: "r15");
|
||||||
kassert(false, "Debug exception");
|
kassert(false, "Debug exception");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrDoubleFault:
|
case isr::isrDoubleFault:
|
||||||
kassert(false, "Double fault");
|
kassert(false, "Double fault");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrGPFault:
|
case isr::isrGPFault:
|
||||||
if (regs->errorcode & 0xfff0) {
|
if (regs->errorcode & 0xfff0) {
|
||||||
int index = (regs->errorcode & 0xffff) >> 4;
|
int index = (regs->errorcode & 0xffff) >> 4;
|
||||||
int ti = (regs->errorcode & 0x07) >> 1;
|
int ti = (regs->errorcode & 0x07) >> 1;
|
||||||
char const *table =
|
char const *table =
|
||||||
(ti & 1) ? "IDT" :
|
(ti & 1) ? "IDT" :
|
||||||
@@ -114,20 +114,20 @@ isr_handler(cpu_state *regs)
|
|||||||
kassert(false, message);
|
kassert(false, message);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrPageFault: {
|
case isr::isrPageFault: {
|
||||||
uintptr_t cr2 = 0;
|
uintptr_t cr2 = 0;
|
||||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
|
||||||
bool user = cr2 < memory::kernel_offset;
|
bool user = cr2 < memory::kernel_offset;
|
||||||
vm_space::fault_type ft =
|
vm_space::fault_type ft =
|
||||||
static_cast<vm_space::fault_type>(regs->errorcode);
|
static_cast<vm_space::fault_type>(regs->errorcode);
|
||||||
|
|
||||||
vm_space &space = user
|
vm_space &space = user
|
||||||
? process::current().space()
|
? process::current().space()
|
||||||
: vm_space::kernel_space();
|
: vm_space::kernel_space();
|
||||||
|
|
||||||
if (cr2 && space.handle_fault(cr2, ft))
|
if (cr2 && space.handle_fault(cr2, ft))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
snprintf(message, sizeof(message),
|
snprintf(message, sizeof(message),
|
||||||
"Page fault: %016llx%s%s%s%s%s", cr2,
|
"Page fault: %016llx%s%s%s%s%s", cr2,
|
||||||
@@ -137,42 +137,42 @@ isr_handler(cpu_state *regs)
|
|||||||
(regs->errorcode & 0x08) ? " reserved" : "",
|
(regs->errorcode & 0x08) ? " reserved" : "",
|
||||||
(regs->errorcode & 0x10) ? " ip" : "");
|
(regs->errorcode & 0x10) ? " ip" : "");
|
||||||
kassert(false, message);
|
kassert(false, message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrTimer:
|
case isr::isrTimer:
|
||||||
scheduler::get().schedule();
|
scheduler::get().schedule();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrLINT0:
|
case isr::isrLINT0:
|
||||||
case isr::isrLINT1:
|
case isr::isrLINT1:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrSpurious:
|
case isr::isrSpurious:
|
||||||
// No EOI for the spurious interrupt
|
// No EOI for the spurious interrupt
|
||||||
return;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
snprintf(message, sizeof(message), "Unknown interrupt 0x%x", regs->interrupt);
|
snprintf(message, sizeof(message), "Unknown interrupt 0x%x", regs->interrupt);
|
||||||
kassert(false, message);
|
kassert(false, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the IST for this vector to what it was
|
// Return the IST for this vector to what it was
|
||||||
if (old_ist)
|
if (old_ist)
|
||||||
idt.set_ist(vector, old_ist);
|
idt.set_ist(vector, old_ist);
|
||||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
irq_handler(cpu_state *regs)
|
irq_handler(cpu_state *regs)
|
||||||
{
|
{
|
||||||
uint8_t irq = get_irq(regs->interrupt);
|
uint8_t irq = get_irq(regs->interrupt);
|
||||||
if (! device_manager::get().dispatch_irq(irq)) {
|
if (! device_manager::get().dispatch_irq(irq)) {
|
||||||
char message[100];
|
char message[100];
|
||||||
snprintf(message, sizeof(message),
|
snprintf(message, sizeof(message),
|
||||||
"Unknown IRQ: %d (vec 0x%x)", irq, regs->interrupt);
|
"Unknown IRQ: %d (vec 0x%x)", irq, regs->interrupt);
|
||||||
kassert(false, message);
|
kassert(false, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ enum class isr : uint8_t
|
|||||||
#undef NISR
|
#undef NISR
|
||||||
#undef ISR
|
#undef ISR
|
||||||
|
|
||||||
_zero = 0
|
_zero = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper operator to add an offset to an isr vector
|
/// Helper operator to add an offset to an isr vector
|
||||||
@@ -26,11 +26,11 @@ constexpr isr operator+(const isr &lhs, int rhs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
/// Set the CPU interrupt enable flag (sti)
|
/// Set the CPU interrupt enable flag (sti)
|
||||||
void interrupts_enable();
|
void interrupts_enable();
|
||||||
|
|
||||||
/// Set the CPU interrupt disable flag (cli)
|
/// Set the CPU interrupt disable flag (cli)
|
||||||
void interrupts_disable();
|
void interrupts_disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable the legacy PIC
|
/// Disable the legacy PIC
|
||||||
|
|||||||
@@ -3,20 +3,20 @@
|
|||||||
uint8_t
|
uint8_t
|
||||||
inb(uint16_t port)
|
inb(uint16_t port)
|
||||||
{
|
{
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
outb(uint16_t port, uint8_t val)
|
outb(uint16_t port, uint8_t val)
|
||||||
{
|
{
|
||||||
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
|
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
io_wait(unsigned times)
|
io_wait(unsigned times)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < times; ++i)
|
for (unsigned i = 0; i < times; ++i)
|
||||||
outb(0x80, 0);
|
outb(0x80, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,73 +19,73 @@ static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09};
|
|||||||
static void
|
static void
|
||||||
output_log(log::area_t area, log::level severity, const char *message)
|
output_log(log::area_t area, log::level severity, const char *message)
|
||||||
{
|
{
|
||||||
auto *cons = console::get();
|
auto *cons = console::get();
|
||||||
cons->set_color(level_colors[static_cast<int>(severity)]);
|
cons->set_color(level_colors[static_cast<int>(severity)]);
|
||||||
cons->printf("%7s %5s: %s\n",
|
cons->printf("%7s %5s: %s\n",
|
||||||
g_logger.area_name(area),
|
g_logger.area_name(area),
|
||||||
g_logger.level_name(severity),
|
g_logger.level_name(severity),
|
||||||
message);
|
message);
|
||||||
cons->set_color();
|
cons->set_color();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
log_flush()
|
log_flush()
|
||||||
{
|
{
|
||||||
system &sys = system::get();
|
system &sys = system::get();
|
||||||
sys.assert_signal(j6_signal_system_has_log);
|
sys.assert_signal(j6_signal_system_has_log);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
logger_task()
|
logger_task()
|
||||||
{
|
{
|
||||||
auto *cons = console::get();
|
auto *cons = console::get();
|
||||||
|
|
||||||
log::info(logs::task, "Starting kernel logger task");
|
log::info(logs::task, "Starting kernel logger task");
|
||||||
g_logger.set_immediate(nullptr);
|
g_logger.set_immediate(nullptr);
|
||||||
g_logger.set_flush(log_flush);
|
g_logger.set_flush(log_flush);
|
||||||
|
|
||||||
thread &self = thread::current();
|
thread &self = thread::current();
|
||||||
system &sys = system::get();
|
system &sys = system::get();
|
||||||
|
|
||||||
size_t buffer_size = 1;
|
size_t buffer_size = 1;
|
||||||
uint8_t *buffer = nullptr;
|
uint8_t *buffer = nullptr;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
size_t size = g_logger.get_entry(buffer, buffer_size);
|
size_t size = g_logger.get_entry(buffer, buffer_size);
|
||||||
if (size > buffer_size) {
|
if (size > buffer_size) {
|
||||||
while (size > buffer_size) buffer_size *= 2;
|
while (size > buffer_size) buffer_size *= 2;
|
||||||
kutil::kfree(buffer);
|
kutil::kfree(buffer);
|
||||||
buffer = reinterpret_cast<uint8_t*>(kutil::kalloc(buffer_size));
|
buffer = reinterpret_cast<uint8_t*>(kutil::kalloc(buffer_size));
|
||||||
kassert(buffer, "Could not allocate logger task buffer");
|
kassert(buffer, "Could not allocate logger task buffer");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(size) {
|
if(size) {
|
||||||
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
||||||
buffer[ent->bytes] = 0;
|
buffer[ent->bytes] = 0;
|
||||||
|
|
||||||
cons->set_color(level_colors[static_cast<int>(ent->severity)]);
|
cons->set_color(level_colors[static_cast<int>(ent->severity)]);
|
||||||
cons->printf("%7s %5s: %s\n",
|
cons->printf("%7s %5s: %s\n",
|
||||||
g_logger.area_name(ent->area),
|
g_logger.area_name(ent->area),
|
||||||
g_logger.level_name(ent->severity),
|
g_logger.level_name(ent->severity),
|
||||||
ent->message);
|
ent->message);
|
||||||
cons->set_color();
|
cons->set_color();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_logger.has_log()) {
|
if (!g_logger.has_log()) {
|
||||||
sys.deassert_signal(j6_signal_system_has_log);
|
sys.deassert_signal(j6_signal_system_has_log);
|
||||||
sys.add_blocked_thread(&self);
|
sys.add_blocked_thread(&self);
|
||||||
self.wait_on_signals(j6_signal_system_has_log);
|
self.wait_on_signals(j6_signal_system_has_log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger_init()
|
void logger_init()
|
||||||
{
|
{
|
||||||
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), output_log);
|
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), output_log);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger_clear_immediate()
|
void logger_clear_immediate()
|
||||||
{
|
{
|
||||||
g_logger.set_immediate(nullptr);
|
g_logger.set_immediate(nullptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void kernel_main(kernel::init::args *args);
|
void kernel_main(kernel::init::args *args);
|
||||||
void (*__ctors)(void);
|
void (*__ctors)(void);
|
||||||
void (*__ctors_end)(void);
|
void (*__ctors_end)(void);
|
||||||
void long_ap_startup(cpu_data *cpu);
|
void long_ap_startup(cpu_data *cpu);
|
||||||
void ap_startup();
|
void ap_startup();
|
||||||
void ap_idle();
|
void ap_idle();
|
||||||
void init_ap_trampoline(void*, cpu_data *, void (*)());
|
void init_ap_trampoline(void*, cpu_data *, void (*)());
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace kernel;
|
using namespace kernel;
|
||||||
@@ -57,23 +57,23 @@ unsigned start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4);
|
|||||||
void
|
void
|
||||||
init_console()
|
init_console()
|
||||||
{
|
{
|
||||||
serial_port *com1 = new (&g_com1) serial_port(COM1);
|
serial_port *com1 = new (&g_com1) serial_port(COM1);
|
||||||
console *cons = new (&g_console) console(com1);
|
console *cons = new (&g_console) console(com1);
|
||||||
|
|
||||||
cons->set_color(0x21, 0x00);
|
cons->set_color(0x21, 0x00);
|
||||||
cons->puts("jsix OS ");
|
cons->puts("jsix OS ");
|
||||||
cons->set_color(0x08, 0x00);
|
cons->set_color(0x08, 0x00);
|
||||||
cons->puts(GIT_VERSION " booting...\n");
|
cons->puts(GIT_VERSION " booting...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run_constructors()
|
run_constructors()
|
||||||
{
|
{
|
||||||
void (**p)(void) = &__ctors;
|
void (**p)(void) = &__ctors;
|
||||||
while (p < &__ctors_end) {
|
while (p < &__ctors_end) {
|
||||||
void (*ctor)(void) = *p++;
|
void (*ctor)(void) = *p++;
|
||||||
if (ctor) ctor();
|
if (ctor) ctor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -84,230 +84,230 @@ kernel_main(init::args *args)
|
|||||||
kutil::assert::symbol_table = args->symbol_table | memory::page_offset;
|
kutil::assert::symbol_table = args->symbol_table | memory::page_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_console();
|
init_console();
|
||||||
logger_init();
|
logger_init();
|
||||||
|
|
||||||
cpu_validate();
|
cpu_validate();
|
||||||
|
|
||||||
kassert(args->magic == init::args_magic,
|
kassert(args->magic == init::args_magic,
|
||||||
"Bad kernel args magic number");
|
"Bad kernel args magic number");
|
||||||
|
|
||||||
log::debug(logs::boot, "jsix init args are at: %016lx", args);
|
log::debug(logs::boot, "jsix init args are at: %016lx", args);
|
||||||
log::debug(logs::boot, " Memory map is at: %016lx", args->mem_map);
|
log::debug(logs::boot, " Memory map is at: %016lx", args->mem_map);
|
||||||
log::debug(logs::boot, "ACPI root table is at: %016lx", args->acpi_table);
|
log::debug(logs::boot, "ACPI root table is at: %016lx", args->acpi_table);
|
||||||
log::debug(logs::boot, "Runtime service is at: %016lx", args->runtime_services);
|
log::debug(logs::boot, "Runtime service is at: %016lx", args->runtime_services);
|
||||||
log::debug(logs::boot, " Kernel PML4 is at: %016lx", args->pml4);
|
log::debug(logs::boot, " Kernel PML4 is at: %016lx", args->pml4);
|
||||||
|
|
||||||
uint64_t cr0, cr4;
|
uint64_t cr0, cr4;
|
||||||
asm ("mov %%cr0, %0" : "=r"(cr0));
|
asm ("mov %%cr0, %0" : "=r"(cr0));
|
||||||
asm ("mov %%cr4, %0" : "=r"(cr4));
|
asm ("mov %%cr4, %0" : "=r"(cr4));
|
||||||
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);
|
||||||
|
|
||||||
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;
|
||||||
extern cpu_data g_bsp_cpu_data;
|
extern cpu_data g_bsp_cpu_data;
|
||||||
extern uintptr_t idle_stack_end;
|
extern uintptr_t idle_stack_end;
|
||||||
|
|
||||||
cpu_data *cpu = &g_bsp_cpu_data;
|
cpu_data *cpu = &g_bsp_cpu_data;
|
||||||
kutil::memset(cpu, 0, sizeof(cpu_data));
|
kutil::memset(cpu, 0, sizeof(cpu_data));
|
||||||
|
|
||||||
cpu->self = cpu;
|
cpu->self = cpu;
|
||||||
cpu->idt = new (&g_bsp_idt) IDT;
|
cpu->idt = new (&g_bsp_idt) IDT;
|
||||||
cpu->tss = new (&g_bsp_tss) TSS;
|
cpu->tss = new (&g_bsp_tss) TSS;
|
||||||
cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss};
|
cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss};
|
||||||
cpu->rsp0 = idle_stack_end;
|
cpu->rsp0 = idle_stack_end;
|
||||||
cpu_early_init(cpu);
|
cpu_early_init(cpu);
|
||||||
|
|
||||||
disable_legacy_pic();
|
disable_legacy_pic();
|
||||||
|
|
||||||
memory_initialize_pre_ctors(*args);
|
memory_initialize_pre_ctors(*args);
|
||||||
run_constructors();
|
run_constructors();
|
||||||
memory_initialize_post_ctors(*args);
|
memory_initialize_post_ctors(*args);
|
||||||
|
|
||||||
cpu->tss->create_ist_stacks(cpu->idt->used_ist_entries());
|
cpu->tss->create_ist_stacks(cpu->idt->used_ist_entries());
|
||||||
|
|
||||||
syscall_initialize();
|
syscall_initialize();
|
||||||
|
|
||||||
device_manager &devices = device_manager::get();
|
device_manager &devices = device_manager::get();
|
||||||
devices.parse_acpi(args->acpi_table);
|
devices.parse_acpi(args->acpi_table);
|
||||||
|
|
||||||
// Need the local APIC to get the BSP's id
|
// Need the local APIC to get the BSP's id
|
||||||
uintptr_t apic_base = devices.get_lapic_base();
|
uintptr_t apic_base = devices.get_lapic_base();
|
||||||
|
|
||||||
lapic *apic = new lapic(apic_base);
|
lapic *apic = new lapic(apic_base);
|
||||||
apic->enable();
|
apic->enable();
|
||||||
|
|
||||||
cpu->id = apic->get_id();
|
cpu->id = apic->get_id();
|
||||||
cpu->apic = apic;
|
cpu->apic = apic;
|
||||||
|
|
||||||
cpu_init(cpu, true);
|
cpu_init(cpu, true);
|
||||||
|
|
||||||
devices.init_drivers();
|
devices.init_drivers();
|
||||||
apic->calibrate_timer();
|
apic->calibrate_timer();
|
||||||
|
|
||||||
const auto &apic_ids = devices.get_apic_ids();
|
const auto &apic_ids = devices.get_apic_ids();
|
||||||
unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4);
|
unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4);
|
||||||
|
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
g_com1.handle_interrupt();
|
g_com1.handle_interrupt();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
block_device *disk = devices->get_block_device(0);
|
block_device *disk = devices->get_block_device(0);
|
||||||
if (disk) {
|
if (disk) {
|
||||||
for (int i=0; i<1; ++i) {
|
for (int i=0; i<1; ++i) {
|
||||||
uint8_t buf[512];
|
uint8_t buf[512];
|
||||||
kutil::memset(buf, 0, 512);
|
kutil::memset(buf, 0, 512);
|
||||||
|
|
||||||
kassert(disk->read(0x200, sizeof(buf), buf),
|
kassert(disk->read(0x200, sizeof(buf), buf),
|
||||||
"Disk read returned 0");
|
"Disk read returned 0");
|
||||||
|
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
uint8_t *p = &buf[0];
|
uint8_t *p = &buf[0];
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
for (int j = 0; j < 16; ++j) {
|
for (int j = 0; j < 16; ++j) {
|
||||||
cons->printf(" %02x", *p++);
|
cons->printf(" %02x", *p++);
|
||||||
}
|
}
|
||||||
cons->putc('\n');
|
cons->putc('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::warn(logs::boot, "No block devices present.");
|
log::warn(logs::boot, "No block devices present.");
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
scheduler *sched = new scheduler {num_cpus};
|
scheduler *sched = new scheduler {num_cpus};
|
||||||
scheduler_ready = true;
|
scheduler_ready = true;
|
||||||
|
|
||||||
// Load the init server
|
// Load the init server
|
||||||
load_init_server(*args->init, args->modules);
|
load_init_server(*args->init, args->modules);
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4)
|
start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4)
|
||||||
{
|
{
|
||||||
using memory::frame_size;
|
using memory::frame_size;
|
||||||
using memory::kernel_stack_pages;
|
using memory::kernel_stack_pages;
|
||||||
|
|
||||||
extern size_t ap_startup_code_size;
|
extern size_t ap_startup_code_size;
|
||||||
extern process &g_kernel_process;
|
extern process &g_kernel_process;
|
||||||
extern vm_area_guarded &g_kernel_stacks;
|
extern vm_area_guarded &g_kernel_stacks;
|
||||||
|
|
||||||
clock &clk = clock::get();
|
clock &clk = clock::get();
|
||||||
|
|
||||||
ap_startup_count = 1; // BSP processor
|
ap_startup_count = 1; // BSP processor
|
||||||
log::info(logs::boot, "Starting %d other CPUs", ids.count() - 1);
|
log::info(logs::boot, "Starting %d other CPUs", ids.count() - 1);
|
||||||
|
|
||||||
// Since we're using address space outside kernel space, make sure
|
// Since we're using address space outside kernel space, make sure
|
||||||
// the kernel's vm_space is used
|
// the kernel's vm_space is used
|
||||||
cpu_data &bsp = current_cpu();
|
cpu_data &bsp = current_cpu();
|
||||||
bsp.process = &g_kernel_process;
|
bsp.process = &g_kernel_process;
|
||||||
|
|
||||||
uint16_t index = bsp.index;
|
uint16_t index = bsp.index;
|
||||||
|
|
||||||
// Copy the startup code somwhere the real mode trampoline can run
|
// Copy the startup code somwhere the real mode trampoline can run
|
||||||
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
|
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
|
||||||
uint8_t vector = addr >> 12;
|
uint8_t vector = addr >> 12;
|
||||||
vm_area *vma = new vm_area_fixed(addr, 0x1000, vm_flags::write);
|
vm_area *vma = new vm_area_fixed(addr, 0x1000, vm_flags::write);
|
||||||
vm_space::kernel_space().add(addr, vma);
|
vm_space::kernel_space().add(addr, vma);
|
||||||
kutil::memcpy(
|
kutil::memcpy(
|
||||||
reinterpret_cast<void*>(addr),
|
reinterpret_cast<void*>(addr),
|
||||||
reinterpret_cast<void*>(&ap_startup),
|
reinterpret_cast<void*>(&ap_startup),
|
||||||
ap_startup_code_size);
|
ap_startup_code_size);
|
||||||
|
|
||||||
// AP idle stacks need less room than normal stacks, so pack multiple
|
// AP idle stacks need less room than normal stacks, so pack multiple
|
||||||
// into a normal stack area
|
// into a normal stack area
|
||||||
static constexpr size_t idle_stack_bytes = 2048; // 2KiB is generous
|
static constexpr size_t idle_stack_bytes = 2048; // 2KiB is generous
|
||||||
static constexpr size_t full_stack_bytes = kernel_stack_pages * frame_size;
|
static constexpr size_t full_stack_bytes = kernel_stack_pages * frame_size;
|
||||||
static constexpr size_t idle_stacks_per = full_stack_bytes / idle_stack_bytes;
|
static constexpr size_t idle_stacks_per = full_stack_bytes / idle_stack_bytes;
|
||||||
|
|
||||||
uint8_t ist_entries = IDT::current().used_ist_entries();
|
uint8_t ist_entries = IDT::current().used_ist_entries();
|
||||||
|
|
||||||
size_t free_stack_count = 0;
|
size_t free_stack_count = 0;
|
||||||
uintptr_t stack_area_start = 0;
|
uintptr_t stack_area_start = 0;
|
||||||
|
|
||||||
lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert;
|
lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert;
|
||||||
apic.send_ipi_broadcast(mode, false, 0);
|
apic.send_ipi_broadcast(mode, false, 0);
|
||||||
|
|
||||||
for (uint8_t id : ids) {
|
for (uint8_t id : ids) {
|
||||||
if (id == bsp.id) continue;
|
if (id == bsp.id) continue;
|
||||||
|
|
||||||
// Set up the CPU data structures
|
// Set up the CPU data structures
|
||||||
IDT *idt = new IDT;
|
IDT *idt = new IDT;
|
||||||
TSS *tss = new TSS;
|
TSS *tss = new TSS;
|
||||||
GDT *gdt = new GDT {tss};
|
GDT *gdt = new GDT {tss};
|
||||||
cpu_data *cpu = new cpu_data;
|
cpu_data *cpu = new cpu_data;
|
||||||
kutil::memset(cpu, 0, sizeof(cpu_data));
|
kutil::memset(cpu, 0, sizeof(cpu_data));
|
||||||
|
|
||||||
cpu->self = cpu;
|
cpu->self = cpu;
|
||||||
cpu->id = id;
|
cpu->id = id;
|
||||||
cpu->index = ++index;
|
cpu->index = ++index;
|
||||||
cpu->idt = idt;
|
cpu->idt = idt;
|
||||||
cpu->tss = tss;
|
cpu->tss = tss;
|
||||||
cpu->gdt = gdt;
|
cpu->gdt = gdt;
|
||||||
|
|
||||||
tss->create_ist_stacks(ist_entries);
|
tss->create_ist_stacks(ist_entries);
|
||||||
|
|
||||||
// Set up the CPU's idle task stack
|
// Set up the CPU's idle task stack
|
||||||
if (free_stack_count == 0) {
|
if (free_stack_count == 0) {
|
||||||
stack_area_start = g_kernel_stacks.get_section();
|
stack_area_start = g_kernel_stacks.get_section();
|
||||||
free_stack_count = idle_stacks_per;
|
free_stack_count = idle_stacks_per;
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t stack_end = stack_area_start + free_stack_count-- * idle_stack_bytes;
|
uintptr_t stack_end = stack_area_start + free_stack_count-- * idle_stack_bytes;
|
||||||
stack_end -= 2 * sizeof(void*); // Null frame
|
stack_end -= 2 * sizeof(void*); // Null frame
|
||||||
*reinterpret_cast<uint64_t*>(stack_end) = 0; // pre-fault the page
|
*reinterpret_cast<uint64_t*>(stack_end) = 0; // pre-fault the page
|
||||||
cpu->rsp0 = stack_end;
|
cpu->rsp0 = stack_end;
|
||||||
|
|
||||||
// Set up the trampoline with this CPU's data
|
// Set up the trampoline with this CPU's data
|
||||||
init_ap_trampoline(kpml4, cpu, ap_idle);
|
init_ap_trampoline(kpml4, cpu, ap_idle);
|
||||||
|
|
||||||
// Kick it off!
|
// Kick it off!
|
||||||
size_t current_count = ap_startup_count;
|
size_t current_count = ap_startup_count;
|
||||||
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
|
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
|
||||||
|
|
||||||
lapic::ipi startup = lapic::ipi::startup | lapic::ipi::assert;
|
lapic::ipi startup = lapic::ipi::startup | lapic::ipi::assert;
|
||||||
|
|
||||||
apic.send_ipi(startup, vector, id);
|
apic.send_ipi(startup, vector, id);
|
||||||
for (unsigned i = 0; i < 20; ++i) {
|
for (unsigned i = 0; i < 20; ++i) {
|
||||||
if (ap_startup_count > current_count) break;
|
if (ap_startup_count > current_count) break;
|
||||||
clk.spinwait(20);
|
clk.spinwait(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the CPU already incremented ap_startup_count, it's done
|
// If the CPU already incremented ap_startup_count, it's done
|
||||||
if (ap_startup_count > current_count)
|
if (ap_startup_count > current_count)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Send the second SIPI (intel recommends this)
|
// Send the second SIPI (intel recommends this)
|
||||||
apic.send_ipi(startup, vector, id);
|
apic.send_ipi(startup, vector, id);
|
||||||
for (unsigned i = 0; i < 100; ++i) {
|
for (unsigned i = 0; i < 100; ++i) {
|
||||||
if (ap_startup_count > current_count) break;
|
if (ap_startup_count > current_count) break;
|
||||||
clk.spinwait(100);
|
clk.spinwait(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::warn(logs::boot, "No response from AP %d within timeout", id);
|
log::warn(logs::boot, "No response from AP %d within timeout", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info(logs::boot, "%d CPUs running", ap_startup_count);
|
log::info(logs::boot, "%d CPUs running", ap_startup_count);
|
||||||
vm_space::kernel_space().remove(vma);
|
vm_space::kernel_space().remove(vma);
|
||||||
return ap_startup_count;
|
return ap_startup_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
long_ap_startup(cpu_data *cpu)
|
long_ap_startup(cpu_data *cpu)
|
||||||
{
|
{
|
||||||
cpu_init(cpu, false);
|
cpu_init(cpu, false);
|
||||||
++ap_startup_count;
|
++ap_startup_count;
|
||||||
while (!scheduler_ready) asm ("pause");
|
while (!scheduler_ready) asm ("pause");
|
||||||
|
|
||||||
uintptr_t apic_base =
|
uintptr_t apic_base =
|
||||||
device_manager::get().get_lapic_base();
|
device_manager::get().get_lapic_base();
|
||||||
cpu->apic = new lapic(apic_base);
|
cpu->apic = new lapic(apic_base);
|
||||||
cpu->apic->enable();
|
cpu->apic->enable();
|
||||||
|
|
||||||
scheduler::get().start();
|
scheduler::get().start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ static kutil::no_construct<vm_area_guarded> __g_kernel_stacks_storage;
|
|||||||
vm_area_guarded &g_kernel_stacks = __g_kernel_stacks_storage.value;
|
vm_area_guarded &g_kernel_stacks = __g_kernel_stacks_storage.value;
|
||||||
|
|
||||||
vm_area_guarded g_kernel_buffers {
|
vm_area_guarded g_kernel_buffers {
|
||||||
memory::buffers_start,
|
memory::buffers_start,
|
||||||
memory::kernel_buffer_pages,
|
memory::kernel_buffer_pages,
|
||||||
memory::kernel_max_buffers,
|
memory::kernel_max_buffers,
|
||||||
vm_flags::write};
|
vm_flags::write};
|
||||||
|
|
||||||
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
|
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
|
||||||
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
|
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
|
||||||
@@ -63,149 +63,149 @@ void operator delete (void *p) noexcept { return g_kernel_heap.free(p); }
|
|||||||
void operator delete [] (void *p) noexcept { return g_kernel_heap.free(p); }
|
void operator delete [] (void *p) noexcept { return g_kernel_heap.free(p); }
|
||||||
|
|
||||||
namespace kutil {
|
namespace kutil {
|
||||||
void * kalloc(size_t size) { return g_kernel_heap.allocate(size); }
|
void * kalloc(size_t size) { return g_kernel_heap.allocate(size); }
|
||||||
void kfree(void *p) { return g_kernel_heap.free(p); }
|
void kfree(void *p) { return g_kernel_heap.free(p); }
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
uintptr_t
|
uintptr_t
|
||||||
get_physical_page(T *p) {
|
get_physical_page(T *p) {
|
||||||
return memory::page_align_down(reinterpret_cast<uintptr_t>(p));
|
return memory::page_align_down(reinterpret_cast<uintptr_t>(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
memory_initialize_pre_ctors(init::args &kargs)
|
memory_initialize_pre_ctors(init::args &kargs)
|
||||||
{
|
{
|
||||||
using kernel::init::frame_block;
|
using kernel::init::frame_block;
|
||||||
|
|
||||||
page_table *kpml4 = static_cast<page_table*>(kargs.pml4);
|
page_table *kpml4 = static_cast<page_table*>(kargs.pml4);
|
||||||
|
|
||||||
new (&g_kernel_heap) kutil::heap_allocator {heap_start, kernel_max_heap};
|
new (&g_kernel_heap) kutil::heap_allocator {heap_start, kernel_max_heap};
|
||||||
|
|
||||||
frame_block *blocks = reinterpret_cast<frame_block*>(memory::bitmap_start);
|
frame_block *blocks = reinterpret_cast<frame_block*>(memory::bitmap_start);
|
||||||
new (&g_frame_allocator) frame_allocator {blocks, kargs.frame_blocks.count};
|
new (&g_frame_allocator) frame_allocator {blocks, kargs.frame_blocks.count};
|
||||||
|
|
||||||
// Mark all the things the bootloader allocated for us as used
|
// Mark all the things the bootloader allocated for us as used
|
||||||
allocation_register *reg = kargs.allocations;
|
allocation_register *reg = kargs.allocations;
|
||||||
while (reg) {
|
while (reg) {
|
||||||
for (auto &alloc : reg->entries)
|
for (auto &alloc : reg->entries)
|
||||||
if (alloc.type != init::allocation_type::none)
|
if (alloc.type != init::allocation_type::none)
|
||||||
g_frame_allocator.used(alloc.address, alloc.count);
|
g_frame_allocator.used(alloc.address, alloc.count);
|
||||||
reg = reg->next;
|
reg = reg->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
process *kp = process::create_kernel_process(kpml4);
|
process *kp = process::create_kernel_process(kpml4);
|
||||||
vm_space &vm = kp->space();
|
vm_space &vm = kp->space();
|
||||||
|
|
||||||
vm_area *heap = new (&g_kernel_heap_area)
|
vm_area *heap = new (&g_kernel_heap_area)
|
||||||
vm_area_untracked(kernel_max_heap, vm_flags::write);
|
vm_area_untracked(kernel_max_heap, vm_flags::write);
|
||||||
|
|
||||||
vm.add(heap_start, heap);
|
vm.add(heap_start, heap);
|
||||||
|
|
||||||
vm_area *stacks = new (&g_kernel_stacks) vm_area_guarded {
|
vm_area *stacks = new (&g_kernel_stacks) vm_area_guarded {
|
||||||
memory::stacks_start,
|
memory::stacks_start,
|
||||||
memory::kernel_stack_pages,
|
memory::kernel_stack_pages,
|
||||||
memory::kernel_max_stacks,
|
memory::kernel_max_stacks,
|
||||||
vm_flags::write};
|
vm_flags::write};
|
||||||
vm.add(memory::stacks_start, &g_kernel_stacks);
|
vm.add(memory::stacks_start, &g_kernel_stacks);
|
||||||
|
|
||||||
// Clean out any remaning bootloader page table entries
|
// Clean out any remaning bootloader page table entries
|
||||||
for (unsigned i = 0; i < memory::pml4e_kernel; ++i)
|
for (unsigned i = 0; i < memory::pml4e_kernel; ++i)
|
||||||
kpml4->entries[i] = 0;
|
kpml4->entries[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
memory_initialize_post_ctors(init::args &kargs)
|
memory_initialize_post_ctors(init::args &kargs)
|
||||||
{
|
{
|
||||||
vm_space &vm = vm_space::kernel_space();
|
vm_space &vm = vm_space::kernel_space();
|
||||||
vm.add(memory::buffers_start, &g_kernel_buffers);
|
vm.add(memory::buffers_start, &g_kernel_buffers);
|
||||||
|
|
||||||
g_frame_allocator.free(
|
g_frame_allocator.free(
|
||||||
get_physical_page(kargs.page_tables.pointer),
|
get_physical_page(kargs.page_tables.pointer),
|
||||||
kargs.page_tables.count);
|
kargs.page_tables.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
log_mtrrs()
|
log_mtrrs()
|
||||||
{
|
{
|
||||||
uint64_t mtrrcap = rdmsr(msr::ia32_mtrrcap);
|
uint64_t mtrrcap = rdmsr(msr::ia32_mtrrcap);
|
||||||
uint64_t mtrrdeftype = rdmsr(msr::ia32_mtrrdeftype);
|
uint64_t mtrrdeftype = rdmsr(msr::ia32_mtrrdeftype);
|
||||||
unsigned vcap = mtrrcap & 0xff;
|
unsigned vcap = mtrrcap & 0xff;
|
||||||
log::debug(logs::boot, "MTRRs: vcap=%d %s %s def=%02x %s %s",
|
log::debug(logs::boot, "MTRRs: vcap=%d %s %s def=%02x %s %s",
|
||||||
vcap,
|
vcap,
|
||||||
(mtrrcap & (1<< 8)) ? "fix" : "",
|
(mtrrcap & (1<< 8)) ? "fix" : "",
|
||||||
(mtrrcap & (1<<10)) ? "wc" : "",
|
(mtrrcap & (1<<10)) ? "wc" : "",
|
||||||
mtrrdeftype & 0xff,
|
mtrrdeftype & 0xff,
|
||||||
(mtrrdeftype & (1<<10)) ? "fe" : "",
|
(mtrrdeftype & (1<<10)) ? "fe" : "",
|
||||||
(mtrrdeftype & (1<<11)) ? "enabled" : ""
|
(mtrrdeftype & (1<<11)) ? "enabled" : ""
|
||||||
);
|
);
|
||||||
|
|
||||||
for (unsigned i = 0; i < vcap; ++i) {
|
for (unsigned i = 0; i < vcap; ++i) {
|
||||||
uint64_t base = rdmsr(find_mtrr(msr::ia32_mtrrphysbase, i));
|
uint64_t base = rdmsr(find_mtrr(msr::ia32_mtrrphysbase, i));
|
||||||
uint64_t mask = rdmsr(find_mtrr(msr::ia32_mtrrphysmask, i));
|
uint64_t mask = rdmsr(find_mtrr(msr::ia32_mtrrphysmask, i));
|
||||||
log::debug(logs::boot, " vcap[%2d] base:%016llx mask:%016llx type:%02x %s", i,
|
log::debug(logs::boot, " vcap[%2d] base:%016llx mask:%016llx type:%02x %s", i,
|
||||||
(base & ~0xfffull),
|
(base & ~0xfffull),
|
||||||
(mask & ~0xfffull),
|
(mask & ~0xfffull),
|
||||||
(base & 0xff),
|
(base & 0xff),
|
||||||
(mask & (1<<11)) ? "valid" : "");
|
(mask & (1<<11)) ? "valid" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
msr mtrr_fixed[] = {
|
msr mtrr_fixed[] = {
|
||||||
msr::ia32_mtrrfix64k_00000,
|
msr::ia32_mtrrfix64k_00000,
|
||||||
msr::ia32_mtrrfix16k_80000,
|
msr::ia32_mtrrfix16k_80000,
|
||||||
msr::ia32_mtrrfix16k_a0000,
|
msr::ia32_mtrrfix16k_a0000,
|
||||||
msr::ia32_mtrrfix4k_c0000,
|
msr::ia32_mtrrfix4k_c0000,
|
||||||
msr::ia32_mtrrfix4k_c8000,
|
msr::ia32_mtrrfix4k_c8000,
|
||||||
msr::ia32_mtrrfix4k_d0000,
|
msr::ia32_mtrrfix4k_d0000,
|
||||||
msr::ia32_mtrrfix4k_d8000,
|
msr::ia32_mtrrfix4k_d8000,
|
||||||
msr::ia32_mtrrfix4k_e0000,
|
msr::ia32_mtrrfix4k_e0000,
|
||||||
msr::ia32_mtrrfix4k_e8000,
|
msr::ia32_mtrrfix4k_e8000,
|
||||||
msr::ia32_mtrrfix4k_f0000,
|
msr::ia32_mtrrfix4k_f0000,
|
||||||
msr::ia32_mtrrfix4k_f8000,
|
msr::ia32_mtrrfix4k_f8000,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 11; ++i) {
|
for (int i = 0; i < 11; ++i) {
|
||||||
uint64_t v = rdmsr(mtrr_fixed[i]);
|
uint64_t v = rdmsr(mtrr_fixed[i]);
|
||||||
log::debug(logs::boot, " fixed[%2d] %02x %02x %02x %02x %02x %02x %02x %02x", i,
|
log::debug(logs::boot, " fixed[%2d] %02x %02x %02x %02x %02x %02x %02x %02x", i,
|
||||||
((v << 0) & 0xff), ((v << 8) & 0xff), ((v << 16) & 0xff), ((v << 24) & 0xff),
|
((v << 0) & 0xff), ((v << 8) & 0xff), ((v << 16) & 0xff), ((v << 24) & 0xff),
|
||||||
((v << 32) & 0xff), ((v << 40) & 0xff), ((v << 48) & 0xff), ((v << 56) & 0xff));
|
((v << 32) & 0xff), ((v << 40) & 0xff), ((v << 48) & 0xff), ((v << 56) & 0xff));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t pat = rdmsr(msr::ia32_pat);
|
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||||
static const char *pat_names[] = {"UC ","WC ","XX ","XX ","WT ","WP ","WB ","UC-"};
|
static const char *pat_names[] = {"UC ","WC ","XX ","XX ","WT ","WP ","WB ","UC-"};
|
||||||
log::debug(logs::boot, " PAT: 0:%s 1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s",
|
log::debug(logs::boot, " PAT: 0:%s 1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s",
|
||||||
pat_names[(pat >> (0*8)) & 7], pat_names[(pat >> (1*8)) & 7],
|
pat_names[(pat >> (0*8)) & 7], pat_names[(pat >> (1*8)) & 7],
|
||||||
pat_names[(pat >> (2*8)) & 7], pat_names[(pat >> (3*8)) & 7],
|
pat_names[(pat >> (2*8)) & 7], pat_names[(pat >> (3*8)) & 7],
|
||||||
pat_names[(pat >> (4*8)) & 7], pat_names[(pat >> (5*8)) & 7],
|
pat_names[(pat >> (4*8)) & 7], pat_names[(pat >> (5*8)) & 7],
|
||||||
pat_names[(pat >> (6*8)) & 7], pat_names[(pat >> (7*8)) & 7]);
|
pat_names[(pat >> (6*8)) & 7], pat_names[(pat >> (7*8)) & 7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
load_init_server(init::program &program, uintptr_t modules_address)
|
load_init_server(init::program &program, uintptr_t modules_address)
|
||||||
{
|
{
|
||||||
process *p = new process;
|
process *p = new process;
|
||||||
p->add_handle(&system::get());
|
p->add_handle(&system::get());
|
||||||
|
|
||||||
vm_space &space = p->space();
|
vm_space &space = p->space();
|
||||||
for (const auto § : program.sections) {
|
for (const auto § : program.sections) {
|
||||||
vm_flags flags =
|
vm_flags flags =
|
||||||
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
||||||
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
|
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||||
|
|
||||||
vm_area *vma = new vm_area_fixed(sect.phys_addr, sect.size, flags);
|
vm_area *vma = new vm_area_fixed(sect.phys_addr, sect.size, flags);
|
||||||
space.add(sect.virt_addr, vma);
|
space.add(sect.virt_addr, vma);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t iopl = (3ull << 12);
|
uint64_t iopl = (3ull << 12);
|
||||||
|
|
||||||
thread *main = p->create_thread();
|
thread *main = p->create_thread();
|
||||||
main->add_thunk_user(program.entrypoint, 0, iopl);
|
main->add_thunk_user(program.entrypoint, 0, iopl);
|
||||||
main->set_state(thread::state::ready);
|
main->set_state(thread::state::ready);
|
||||||
|
|
||||||
// Hacky: No process exists to have created a stack for init; it needs to create
|
// Hacky: No process exists to have created a stack for init; it needs to create
|
||||||
// its own stack. We take advantage of that to use rsp to pass it the init modules
|
// its own stack. We take advantage of that to use rsp to pass it the init modules
|
||||||
// address.
|
// address.
|
||||||
auto *tcb = main->tcb();
|
auto *tcb = main->tcb();
|
||||||
tcb->rsp3 = modules_address;
|
tcb->rsp3 = modules_address;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,22 @@
|
|||||||
msr
|
msr
|
||||||
find_mtrr(msr type, unsigned index)
|
find_mtrr(msr type, unsigned index)
|
||||||
{
|
{
|
||||||
return static_cast<msr>(static_cast<uint32_t>(type) + (2 * index));
|
return static_cast<msr>(static_cast<uint32_t>(type) + (2 * index));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
rdmsr(msr addr)
|
rdmsr(msr addr)
|
||||||
{
|
{
|
||||||
uint32_t low, high;
|
uint32_t low, high;
|
||||||
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
|
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
|
||||||
return (static_cast<uint64_t>(high) << 32) | low;
|
return (static_cast<uint64_t>(high) << 32) | low;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
wrmsr(msr addr, uint64_t value)
|
wrmsr(msr addr, uint64_t value)
|
||||||
{
|
{
|
||||||
uint32_t low = value & 0xffffffff;
|
uint32_t low = value & 0xffffffff;
|
||||||
uint32_t high = value >> 32;
|
uint32_t high = value >> 32;
|
||||||
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
|
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,34 +6,34 @@
|
|||||||
|
|
||||||
enum class msr : uint32_t
|
enum class msr : uint32_t
|
||||||
{
|
{
|
||||||
ia32_mtrrcap = 0x000000fe,
|
ia32_mtrrcap = 0x000000fe,
|
||||||
ia32_mtrrdeftype = 0x000002ff,
|
ia32_mtrrdeftype = 0x000002ff,
|
||||||
|
|
||||||
ia32_mtrrphysbase = 0x00000200,
|
ia32_mtrrphysbase = 0x00000200,
|
||||||
ia32_mtrrphysmask = 0x00000201,
|
ia32_mtrrphysmask = 0x00000201,
|
||||||
|
|
||||||
ia32_mtrrfix64k_00000 = 0x00000250,
|
ia32_mtrrfix64k_00000 = 0x00000250,
|
||||||
|
|
||||||
ia32_mtrrfix16k_80000 = 0x00000258,
|
ia32_mtrrfix16k_80000 = 0x00000258,
|
||||||
ia32_mtrrfix16k_a0000 = 0x00000259,
|
ia32_mtrrfix16k_a0000 = 0x00000259,
|
||||||
|
|
||||||
ia32_mtrrfix4k_c0000 = 0x00000268,
|
ia32_mtrrfix4k_c0000 = 0x00000268,
|
||||||
ia32_mtrrfix4k_c8000 = 0x00000269,
|
ia32_mtrrfix4k_c8000 = 0x00000269,
|
||||||
ia32_mtrrfix4k_d0000 = 0x0000026A,
|
ia32_mtrrfix4k_d0000 = 0x0000026A,
|
||||||
ia32_mtrrfix4k_d8000 = 0x0000026B,
|
ia32_mtrrfix4k_d8000 = 0x0000026B,
|
||||||
ia32_mtrrfix4k_e0000 = 0x0000026C,
|
ia32_mtrrfix4k_e0000 = 0x0000026C,
|
||||||
ia32_mtrrfix4k_e8000 = 0x0000026D,
|
ia32_mtrrfix4k_e8000 = 0x0000026D,
|
||||||
ia32_mtrrfix4k_f0000 = 0x0000026E,
|
ia32_mtrrfix4k_f0000 = 0x0000026E,
|
||||||
ia32_mtrrfix4k_f8000 = 0x0000026F,
|
ia32_mtrrfix4k_f8000 = 0x0000026F,
|
||||||
|
|
||||||
ia32_pat = 0x00000277,
|
ia32_pat = 0x00000277,
|
||||||
ia32_efer = 0xc0000080,
|
ia32_efer = 0xc0000080,
|
||||||
ia32_star = 0xc0000081,
|
ia32_star = 0xc0000081,
|
||||||
ia32_lstar = 0xc0000082,
|
ia32_lstar = 0xc0000082,
|
||||||
ia32_fmask = 0xc0000084,
|
ia32_fmask = 0xc0000084,
|
||||||
|
|
||||||
ia32_gs_base = 0xc0000101,
|
ia32_gs_base = 0xc0000101,
|
||||||
ia32_kernel_gs_base = 0xc0000102
|
ia32_kernel_gs_base = 0xc0000102
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Find the msr for MTRR physical base or mask
|
/// Find the msr for MTRR physical base or mask
|
||||||
|
|||||||
@@ -9,82 +9,82 @@ extern vm_area_guarded g_kernel_buffers;
|
|||||||
constexpr size_t buffer_bytes = memory::kernel_buffer_pages * memory::frame_size;
|
constexpr size_t buffer_bytes = memory::kernel_buffer_pages * memory::frame_size;
|
||||||
|
|
||||||
channel::channel() :
|
channel::channel() :
|
||||||
m_len(0),
|
m_len(0),
|
||||||
m_data(g_kernel_buffers.get_section()),
|
m_data(g_kernel_buffers.get_section()),
|
||||||
m_buffer(reinterpret_cast<uint8_t*>(m_data), buffer_bytes),
|
m_buffer(reinterpret_cast<uint8_t*>(m_data), buffer_bytes),
|
||||||
kobject(kobject::type::channel, j6_signal_channel_can_send)
|
kobject(kobject::type::channel, j6_signal_channel_can_send)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
channel::~channel()
|
channel::~channel()
|
||||||
{
|
{
|
||||||
if (!closed()) close();
|
if (!closed()) close();
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
channel::enqueue(size_t *len, const void *data)
|
channel::enqueue(size_t *len, const void *data)
|
||||||
{
|
{
|
||||||
// TODO: Make this thread safe!
|
// TODO: Make this thread safe!
|
||||||
if (closed())
|
if (closed())
|
||||||
return j6_status_closed;
|
return j6_status_closed;
|
||||||
|
|
||||||
if (!len || !*len)
|
if (!len || !*len)
|
||||||
return j6_err_invalid_arg;
|
return j6_err_invalid_arg;
|
||||||
|
|
||||||
if (m_buffer.free_space() == 0)
|
if (m_buffer.free_space() == 0)
|
||||||
return j6_err_not_ready;
|
return j6_err_not_ready;
|
||||||
|
|
||||||
void *buffer = nullptr;
|
void *buffer = nullptr;
|
||||||
size_t avail = m_buffer.reserve(*len, &buffer);
|
size_t avail = m_buffer.reserve(*len, &buffer);
|
||||||
*len = *len > avail ? avail : *len;
|
*len = *len > avail ? avail : *len;
|
||||||
|
|
||||||
kutil::memcpy(buffer, data, *len);
|
kutil::memcpy(buffer, data, *len);
|
||||||
m_buffer.commit(*len);
|
m_buffer.commit(*len);
|
||||||
|
|
||||||
assert_signal(j6_signal_channel_can_recv);
|
assert_signal(j6_signal_channel_can_recv);
|
||||||
if (m_buffer.free_space() == 0)
|
if (m_buffer.free_space() == 0)
|
||||||
deassert_signal(j6_signal_channel_can_send);
|
deassert_signal(j6_signal_channel_can_send);
|
||||||
|
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
channel::dequeue(size_t *len, void *data)
|
channel::dequeue(size_t *len, void *data)
|
||||||
{
|
{
|
||||||
// TODO: Make this thread safe!
|
// TODO: Make this thread safe!
|
||||||
if (closed())
|
if (closed())
|
||||||
return j6_status_closed;
|
return j6_status_closed;
|
||||||
|
|
||||||
if (!len || !*len)
|
if (!len || !*len)
|
||||||
return j6_err_invalid_arg;
|
return j6_err_invalid_arg;
|
||||||
|
|
||||||
if (m_buffer.size() == 0)
|
if (m_buffer.size() == 0)
|
||||||
return j6_err_not_ready;
|
return j6_err_not_ready;
|
||||||
|
|
||||||
void *buffer = nullptr;
|
void *buffer = nullptr;
|
||||||
size_t avail = m_buffer.get_block(&buffer);
|
size_t avail = m_buffer.get_block(&buffer);
|
||||||
*len = *len > avail ? avail : *len;
|
*len = *len > avail ? avail : *len;
|
||||||
|
|
||||||
kutil::memcpy(data, buffer, *len);
|
kutil::memcpy(data, buffer, *len);
|
||||||
m_buffer.consume(*len);
|
m_buffer.consume(*len);
|
||||||
|
|
||||||
assert_signal(j6_signal_channel_can_send);
|
assert_signal(j6_signal_channel_can_send);
|
||||||
if (m_buffer.size() == 0)
|
if (m_buffer.size() == 0)
|
||||||
deassert_signal(j6_signal_channel_can_recv);
|
deassert_signal(j6_signal_channel_can_recv);
|
||||||
|
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
channel::close()
|
channel::close()
|
||||||
{
|
{
|
||||||
kobject::close();
|
kobject::close();
|
||||||
g_kernel_buffers.return_section(m_data);
|
g_kernel_buffers.return_section(m_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
channel::on_no_handles()
|
channel::on_no_handles()
|
||||||
{
|
{
|
||||||
kobject::on_no_handles();
|
kobject::on_no_handles();
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,42 +8,42 @@
|
|||||||
|
|
||||||
/// Channels are bi-directional means of sending messages
|
/// Channels are bi-directional means of sending messages
|
||||||
class channel :
|
class channel :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
channel();
|
channel();
|
||||||
virtual ~channel();
|
virtual ~channel();
|
||||||
|
|
||||||
static constexpr kobject::type type = kobject::type::channel;
|
static constexpr kobject::type type = kobject::type::channel;
|
||||||
|
|
||||||
/// Check if the channel has space for a message to be sent
|
/// Check if the channel has space for a message to be sent
|
||||||
inline bool can_send() const { return check_signal(j6_signal_channel_can_send); }
|
inline bool can_send() const { return check_signal(j6_signal_channel_can_send); }
|
||||||
|
|
||||||
/// Check if the channel has a message wiating already
|
/// Check if the channel has a message wiating already
|
||||||
inline bool can_receive() const { return check_signal(j6_signal_channel_can_recv); }
|
inline bool can_receive() const { return check_signal(j6_signal_channel_can_recv); }
|
||||||
|
|
||||||
/// Put a message into the channel
|
/// Put a message into the channel
|
||||||
/// \arg len [in] Bytes in data buffer [out] number of bytes written
|
/// \arg len [in] Bytes in data buffer [out] number of bytes written
|
||||||
/// \arg data Pointer to the message data
|
/// \arg data Pointer to the message data
|
||||||
/// \returns j6_status_ok on success
|
/// \returns j6_status_ok on success
|
||||||
j6_status_t enqueue(size_t *len, const void *data);
|
j6_status_t enqueue(size_t *len, const void *data);
|
||||||
|
|
||||||
/// Get a message from the channel, copied into a provided buffer
|
/// Get a message from the channel, copied into a provided buffer
|
||||||
/// \arg len On input, the size of the provided buffer. On output,
|
/// \arg len On input, the size of the provided buffer. On output,
|
||||||
/// the size of the message copied into the buffer.
|
/// the size of the message copied into the buffer.
|
||||||
/// \arg data Pointer to the buffer
|
/// \arg data Pointer to the buffer
|
||||||
/// \returns j6_status_ok on success
|
/// \returns j6_status_ok on success
|
||||||
j6_status_t dequeue(size_t *len, void *data);
|
j6_status_t dequeue(size_t *len, void *data);
|
||||||
|
|
||||||
/// Mark this channel as closed, all future calls to enqueue or
|
/// Mark this channel as closed, all future calls to enqueue or
|
||||||
/// dequeue messages will fail with j6_status_closed.
|
/// dequeue messages will fail with j6_status_closed.
|
||||||
virtual void close() override;
|
virtual void close() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void on_no_handles() override;
|
virtual void on_no_handles() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_len;
|
size_t m_len;
|
||||||
uintptr_t m_data;
|
uintptr_t m_data;
|
||||||
kutil::bip_buffer m_buffer;
|
kutil::bip_buffer m_buffer;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,128 +5,128 @@
|
|||||||
#include "vm_space.h"
|
#include "vm_space.h"
|
||||||
|
|
||||||
endpoint::endpoint() :
|
endpoint::endpoint() :
|
||||||
kobject {kobject::type::endpoint}
|
kobject {kobject::type::endpoint}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
endpoint::~endpoint()
|
endpoint::~endpoint()
|
||||||
{
|
{
|
||||||
if (!check_signal(j6_signal_closed))
|
if (!check_signal(j6_signal_closed))
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
endpoint::close()
|
endpoint::close()
|
||||||
{
|
{
|
||||||
kobject::close();
|
kobject::close();
|
||||||
|
|
||||||
for (auto &data : m_blocked) {
|
for (auto &data : m_blocked) {
|
||||||
if (data.th)
|
if (data.th)
|
||||||
data.th->wake_on_result(this, j6_status_closed);
|
data.th->wake_on_result(this, j6_status_closed);
|
||||||
}
|
}
|
||||||
|
|
||||||
device_manager::get().unbind_irqs(this);
|
device_manager::get().unbind_irqs(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
endpoint::send(j6_tag_t tag, size_t len, void *data)
|
endpoint::send(j6_tag_t tag, size_t len, void *data)
|
||||||
{
|
{
|
||||||
thread_data sender = { &thread::current(), data };
|
thread_data sender = { &thread::current(), data };
|
||||||
sender.len = len;
|
sender.len = len;
|
||||||
sender.tag = tag;
|
sender.tag = tag;
|
||||||
|
|
||||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||||
assert_signal(j6_signal_endpoint_can_recv);
|
assert_signal(j6_signal_endpoint_can_recv);
|
||||||
m_blocked.append(sender);
|
m_blocked.append(sender);
|
||||||
sender.th->wait_on_object(this);
|
sender.th->wait_on_object(this);
|
||||||
|
|
||||||
// we woke up having already finished the send
|
// we woke up having already finished the send
|
||||||
// because it happened in the receiver
|
// because it happened in the receiver
|
||||||
return sender.th->get_wait_result();
|
return sender.th->get_wait_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_data receiver = m_blocked.pop_front();
|
thread_data receiver = m_blocked.pop_front();
|
||||||
if (m_blocked.count() == 0)
|
if (m_blocked.count() == 0)
|
||||||
deassert_signal(j6_signal_endpoint_can_send);
|
deassert_signal(j6_signal_endpoint_can_send);
|
||||||
|
|
||||||
j6_status_t status = do_message_copy(sender, receiver);
|
j6_status_t status = do_message_copy(sender, receiver);
|
||||||
|
|
||||||
receiver.th->wake_on_result(this, status);
|
receiver.th->wake_on_result(this, status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
endpoint::receive(j6_tag_t *tag, size_t *len, void *data)
|
endpoint::receive(j6_tag_t *tag, size_t *len, void *data)
|
||||||
{
|
{
|
||||||
thread_data receiver = { &thread::current(), data };
|
thread_data receiver = { &thread::current(), data };
|
||||||
receiver.tag_p = tag;
|
receiver.tag_p = tag;
|
||||||
receiver.len_p = len;
|
receiver.len_p = len;
|
||||||
|
|
||||||
if (!check_signal(j6_signal_endpoint_can_recv)) {
|
if (!check_signal(j6_signal_endpoint_can_recv)) {
|
||||||
assert_signal(j6_signal_endpoint_can_send);
|
assert_signal(j6_signal_endpoint_can_send);
|
||||||
m_blocked.append(receiver);
|
m_blocked.append(receiver);
|
||||||
receiver.th->wait_on_object(this);
|
receiver.th->wait_on_object(this);
|
||||||
|
|
||||||
// we woke up having already finished the recv
|
// we woke up having already finished the recv
|
||||||
// because it happened in the sender
|
// because it happened in the sender
|
||||||
return receiver.th->get_wait_result();
|
return receiver.th->get_wait_result();
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_data sender = m_blocked.pop_front();
|
thread_data sender = m_blocked.pop_front();
|
||||||
if (m_blocked.count() == 0)
|
if (m_blocked.count() == 0)
|
||||||
deassert_signal(j6_signal_endpoint_can_recv);
|
deassert_signal(j6_signal_endpoint_can_recv);
|
||||||
|
|
||||||
// TODO: don't pop sender on some errors
|
// TODO: don't pop sender on some errors
|
||||||
j6_status_t status = do_message_copy(sender, receiver);
|
j6_status_t status = do_message_copy(sender, receiver);
|
||||||
|
|
||||||
if (sender.th)
|
if (sender.th)
|
||||||
sender.th->wake_on_result(this, status);
|
sender.th->wake_on_result(this, status);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
endpoint::signal_irq(unsigned irq)
|
endpoint::signal_irq(unsigned irq)
|
||||||
{
|
{
|
||||||
j6_tag_t tag = j6_tag_from_irq(irq);
|
j6_tag_t tag = j6_tag_from_irq(irq);
|
||||||
|
|
||||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||||
assert_signal(j6_signal_endpoint_can_recv);
|
assert_signal(j6_signal_endpoint_can_recv);
|
||||||
|
|
||||||
for (auto &blocked : m_blocked)
|
for (auto &blocked : m_blocked)
|
||||||
if (blocked.tag == tag)
|
if (blocked.tag == tag)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
thread_data sender = { nullptr, nullptr };
|
thread_data sender = { nullptr, nullptr };
|
||||||
sender.tag = tag;
|
sender.tag = tag;
|
||||||
m_blocked.append(sender);
|
m_blocked.append(sender);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_data receiver = m_blocked.pop_front();
|
thread_data receiver = m_blocked.pop_front();
|
||||||
if (m_blocked.count() == 0)
|
if (m_blocked.count() == 0)
|
||||||
deassert_signal(j6_signal_endpoint_can_send);
|
deassert_signal(j6_signal_endpoint_can_send);
|
||||||
|
|
||||||
*receiver.len_p = 0;
|
*receiver.len_p = 0;
|
||||||
*receiver.tag_p = tag;
|
*receiver.tag_p = tag;
|
||||||
receiver.th->wake_on_result(this, j6_status_ok);
|
receiver.th->wake_on_result(this, j6_status_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver)
|
endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver)
|
||||||
{
|
{
|
||||||
if (sender.len > *receiver.len_p)
|
if (sender.len > *receiver.len_p)
|
||||||
return j6_err_insufficient;
|
return j6_err_insufficient;
|
||||||
|
|
||||||
if (sender.len) {
|
if (sender.len) {
|
||||||
vm_space &source = sender.th->parent().space();
|
vm_space &source = sender.th->parent().space();
|
||||||
vm_space &dest = receiver.th->parent().space();
|
vm_space &dest = receiver.th->parent().space();
|
||||||
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
*receiver.len_p = sender.len;
|
*receiver.len_p = sender.len;
|
||||||
*receiver.tag_p = sender.tag;
|
*receiver.tag_p = sender.tag;
|
||||||
|
|
||||||
// TODO: this will not work if non-contiguous pages are mapped!!
|
// TODO: this will not work if non-contiguous pages are mapped!!
|
||||||
|
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,59 +7,59 @@
|
|||||||
|
|
||||||
/// Endpoints are objects that enable synchronous message-passing IPC
|
/// Endpoints are objects that enable synchronous message-passing IPC
|
||||||
class endpoint :
|
class endpoint :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
endpoint();
|
endpoint();
|
||||||
virtual ~endpoint();
|
virtual ~endpoint();
|
||||||
|
|
||||||
static constexpr kobject::type type = kobject::type::endpoint;
|
static constexpr kobject::type type = kobject::type::endpoint;
|
||||||
|
|
||||||
/// Close the endpoint, waking all waiting processes with an error
|
/// Close the endpoint, waking all waiting processes with an error
|
||||||
virtual void close() override;
|
virtual void close() override;
|
||||||
|
|
||||||
/// Check if the endpoint has space for a message to be sent
|
/// Check if the endpoint has space for a message to be sent
|
||||||
inline bool can_send() const { return check_signal(j6_signal_endpoint_can_send); }
|
inline bool can_send() const { return check_signal(j6_signal_endpoint_can_send); }
|
||||||
|
|
||||||
/// Check if the endpoint has a message wiating already
|
/// Check if the endpoint has a message wiating already
|
||||||
inline bool can_receive() const { return check_signal(j6_signal_endpoint_can_recv); }
|
inline bool can_receive() const { return check_signal(j6_signal_endpoint_can_recv); }
|
||||||
|
|
||||||
/// Send a message to a thread waiting to receive on this endpoint. If no threads
|
/// Send a message to a thread waiting to receive on this endpoint. If no threads
|
||||||
/// are currently trying to receive, block the current thread.
|
/// are currently trying to receive, block the current thread.
|
||||||
/// \arg tag The application-specified message tag
|
/// \arg tag The application-specified message tag
|
||||||
/// \arg len The size in bytes of the message
|
/// \arg len The size in bytes of the message
|
||||||
/// \arg data The message data
|
/// \arg data The message data
|
||||||
/// \returns j6_status_ok on success
|
/// \returns j6_status_ok on success
|
||||||
j6_status_t send(j6_tag_t tag, size_t len, void *data);
|
j6_status_t send(j6_tag_t tag, size_t len, void *data);
|
||||||
|
|
||||||
/// Receive a message from a thread waiting to send on this endpoint. If no threads
|
/// Receive a message from a thread waiting to send on this endpoint. If no threads
|
||||||
/// are currently trying to send, block the current thread.
|
/// are currently trying to send, block the current thread.
|
||||||
/// \arg tag [in] The sender-specified message tag
|
/// \arg tag [in] The sender-specified message tag
|
||||||
/// \arg len [in] The size in bytes of the buffer [out] Number of bytes in the message
|
/// \arg len [in] The size in bytes of the buffer [out] Number of bytes in the message
|
||||||
/// \arg data Buffer for copying message data into
|
/// \arg data Buffer for copying message data into
|
||||||
/// \returns j6_status_ok on success
|
/// \returns j6_status_ok on success
|
||||||
j6_status_t receive(j6_tag_t *tag, size_t *len, void *data);
|
j6_status_t receive(j6_tag_t *tag, size_t *len, void *data);
|
||||||
|
|
||||||
/// Give the listener on the endpoint a message that a bound IRQ has been signalled
|
/// Give the listener on the endpoint a message that a bound IRQ has been signalled
|
||||||
/// \arg irq The IRQ that caused this signal
|
/// \arg irq The IRQ that caused this signal
|
||||||
void signal_irq(unsigned irq);
|
void signal_irq(unsigned irq);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct thread_data
|
struct thread_data
|
||||||
{
|
{
|
||||||
thread *th;
|
thread *th;
|
||||||
void *data;
|
void *data;
|
||||||
union {
|
union {
|
||||||
j6_tag_t *tag_p;
|
j6_tag_t *tag_p;
|
||||||
j6_tag_t tag;
|
j6_tag_t tag;
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
size_t *len_p;
|
size_t *len_p;
|
||||||
size_t len;
|
size_t len;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
j6_status_t do_message_copy(const thread_data &sender, thread_data &receiver);
|
j6_status_t do_message_copy(const thread_data &sender, thread_data &receiver);
|
||||||
|
|
||||||
kutil::vector<thread_data> m_blocked;
|
kutil::vector<thread_data> m_blocked;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
#include "objects/kobject.h"
|
#include "objects/kobject.h"
|
||||||
|
|
||||||
class event :
|
class event :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
event() :
|
event() :
|
||||||
kobject(type::event) {}
|
kobject(type::event) {}
|
||||||
|
|
||||||
static constexpr kobject::type type = kobject::type::event;
|
static constexpr kobject::type type = kobject::type::event;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,67 +8,67 @@
|
|||||||
static j6_koid_t next_koids [static_cast<size_t>(kobject::type::max)] = { 0 };
|
static j6_koid_t next_koids [static_cast<size_t>(kobject::type::max)] = { 0 };
|
||||||
|
|
||||||
kobject::kobject(type t, j6_signal_t signals) :
|
kobject::kobject(type t, j6_signal_t signals) :
|
||||||
m_koid(koid_generate(t)),
|
m_koid(koid_generate(t)),
|
||||||
m_signals(signals),
|
m_signals(signals),
|
||||||
m_handle_count(0)
|
m_handle_count(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
kobject::~kobject()
|
kobject::~kobject()
|
||||||
{
|
{
|
||||||
for (auto *t : m_blocked_threads)
|
for (auto *t : m_blocked_threads)
|
||||||
t->wake_on_result(this, j6_status_destroyed);
|
t->wake_on_result(this, j6_status_destroyed);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_koid_t
|
j6_koid_t
|
||||||
kobject::koid_generate(type t)
|
kobject::koid_generate(type t)
|
||||||
{
|
{
|
||||||
kassert(t < type::max, "Object type out of bounds");
|
kassert(t < type::max, "Object type out of bounds");
|
||||||
uint64_t type_int = static_cast<uint64_t>(t);
|
uint64_t type_int = static_cast<uint64_t>(t);
|
||||||
return (type_int << 48) | next_koids[type_int]++;
|
return (type_int << 48) | next_koids[type_int]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
kobject::type
|
kobject::type
|
||||||
kobject::koid_type(j6_koid_t koid)
|
kobject::koid_type(j6_koid_t koid)
|
||||||
{
|
{
|
||||||
return static_cast<type>((koid >> 48) & 0xffffull);
|
return static_cast<type>((koid >> 48) & 0xffffull);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::assert_signal(j6_signal_t s)
|
kobject::assert_signal(j6_signal_t s)
|
||||||
{
|
{
|
||||||
m_signals |= s;
|
m_signals |= s;
|
||||||
notify_signal_observers();
|
notify_signal_observers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::deassert_signal(j6_signal_t s)
|
kobject::deassert_signal(j6_signal_t s)
|
||||||
{
|
{
|
||||||
m_signals &= ~s;
|
m_signals &= ~s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::notify_signal_observers()
|
kobject::notify_signal_observers()
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
while (i < m_blocked_threads.count()) {
|
while (i < m_blocked_threads.count()) {
|
||||||
thread *t = m_blocked_threads[i];
|
thread *t = m_blocked_threads[i];
|
||||||
|
|
||||||
if (t->wake_on_signals(this, m_signals)) {
|
if (t->wake_on_signals(this, m_signals)) {
|
||||||
m_blocked_threads.remove_swap_at(i);
|
m_blocked_threads.remove_swap_at(i);
|
||||||
} else {
|
} else {
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::close()
|
kobject::close()
|
||||||
{
|
{
|
||||||
assert_signal(j6_signal_closed);
|
assert_signal(j6_signal_closed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::on_no_handles()
|
kobject::on_no_handles()
|
||||||
{
|
{
|
||||||
assert_signal(j6_signal_no_handles);
|
assert_signal(j6_signal_no_handles);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,88 +13,88 @@ class thread;
|
|||||||
class kobject
|
class kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Types of kernel objects.
|
/// Types of kernel objects.
|
||||||
enum class type : uint16_t
|
enum class type : uint16_t
|
||||||
{
|
{
|
||||||
#define OBJECT_TYPE( name, val ) name = val,
|
#define OBJECT_TYPE( name, val ) name = val,
|
||||||
#include "j6/tables/object_types.inc"
|
#include "j6/tables/object_types.inc"
|
||||||
#undef OBJECT_TYPE
|
#undef OBJECT_TYPE
|
||||||
|
|
||||||
max
|
max
|
||||||
};
|
};
|
||||||
|
|
||||||
kobject(type t, j6_signal_t signals = 0ull);
|
kobject(type t, j6_signal_t signals = 0ull);
|
||||||
virtual ~kobject();
|
virtual ~kobject();
|
||||||
|
|
||||||
/// Generate a new koid for a given type
|
/// Generate a new koid for a given type
|
||||||
/// \arg t The object type
|
/// \arg t The object type
|
||||||
/// \returns A new unique koid
|
/// \returns A new unique koid
|
||||||
static j6_koid_t koid_generate(type t);
|
static j6_koid_t koid_generate(type t);
|
||||||
|
|
||||||
/// Get the kobject type from a given koid
|
/// Get the kobject type from a given koid
|
||||||
/// \arg koid An existing koid
|
/// \arg koid An existing koid
|
||||||
/// \returns The object type for the koid
|
/// \returns The object type for the koid
|
||||||
static type koid_type(j6_koid_t koid);
|
static type koid_type(j6_koid_t koid);
|
||||||
|
|
||||||
/// Get this object's type
|
/// Get this object's type
|
||||||
inline type get_type() const { return koid_type(m_koid); }
|
inline type get_type() const { return koid_type(m_koid); }
|
||||||
|
|
||||||
/// Get this object's koid
|
/// Get this object's koid
|
||||||
inline j6_koid_t koid() const { return m_koid; }
|
inline j6_koid_t koid() const { return m_koid; }
|
||||||
|
|
||||||
/// Set the given signals active on this object
|
/// Set the given signals active on this object
|
||||||
/// \arg s The set of signals to assert
|
/// \arg s The set of signals to assert
|
||||||
void assert_signal(j6_signal_t s);
|
void assert_signal(j6_signal_t s);
|
||||||
|
|
||||||
/// Clear the given signals on this object
|
/// Clear the given signals on this object
|
||||||
/// \arg s The set of signals to deassert
|
/// \arg s The set of signals to deassert
|
||||||
void deassert_signal(j6_signal_t s);
|
void deassert_signal(j6_signal_t s);
|
||||||
|
|
||||||
/// Check if the given signals are set on this object
|
/// Check if the given signals are set on this object
|
||||||
/// \arg s The set of signals to check
|
/// \arg s The set of signals to check
|
||||||
inline bool check_signal(j6_signal_t s) const { return (m_signals & s) == s; }
|
inline bool check_signal(j6_signal_t s) const { return (m_signals & s) == s; }
|
||||||
|
|
||||||
/// Get the current object signal state
|
/// Get the current object signal state
|
||||||
inline j6_signal_t signals() const { return m_signals; }
|
inline j6_signal_t signals() const { return m_signals; }
|
||||||
|
|
||||||
/// Increment the handle refcount
|
/// Increment the handle refcount
|
||||||
inline void handle_retain() { ++m_handle_count; }
|
inline void handle_retain() { ++m_handle_count; }
|
||||||
|
|
||||||
/// Decrement the handle refcount
|
/// Decrement the handle refcount
|
||||||
inline void handle_release() {
|
inline void handle_release() {
|
||||||
if (--m_handle_count == 0) on_no_handles();
|
if (--m_handle_count == 0) on_no_handles();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the given thread to the list of threads waiting on this object.
|
/// Add the given thread to the list of threads waiting on this object.
|
||||||
inline void add_blocked_thread(thread *t) { m_blocked_threads.append(t); }
|
inline void add_blocked_thread(thread *t) { m_blocked_threads.append(t); }
|
||||||
|
|
||||||
/// Remove the given thread from the list of threads waiting on this object.
|
/// Remove the given thread from the list of threads waiting on this object.
|
||||||
inline void remove_blocked_thread(thread *t) { m_blocked_threads.remove_swap(t); }
|
inline void remove_blocked_thread(thread *t) { m_blocked_threads.remove_swap(t); }
|
||||||
|
|
||||||
/// Perform any cleanup actions necessary to mark this object closed
|
/// Perform any cleanup actions necessary to mark this object closed
|
||||||
virtual void close();
|
virtual void close();
|
||||||
|
|
||||||
/// Check if this object has been closed
|
/// Check if this object has been closed
|
||||||
inline bool closed() const { return check_signal(j6_signal_closed); }
|
inline bool closed() const { return check_signal(j6_signal_closed); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Interface for subclasses to handle when all handles are closed. Subclasses
|
/// Interface for subclasses to handle when all handles are closed. Subclasses
|
||||||
/// should either call the base version, or assert j6_signal_no_handles.
|
/// should either call the base version, or assert j6_signal_no_handles.
|
||||||
virtual void on_no_handles();
|
virtual void on_no_handles();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kobject() = delete;
|
kobject() = delete;
|
||||||
kobject(const kobject &other) = delete;
|
kobject(const kobject &other) = delete;
|
||||||
kobject(const kobject &&other) = delete;
|
kobject(const kobject &&other) = delete;
|
||||||
|
|
||||||
/// Notifiy observers of this object
|
/// Notifiy observers of this object
|
||||||
/// \arg result The result to pass to the observers
|
/// \arg result The result to pass to the observers
|
||||||
void notify_signal_observers();
|
void notify_signal_observers();
|
||||||
|
|
||||||
j6_koid_t m_koid;
|
j6_koid_t m_koid;
|
||||||
j6_signal_t m_signals;
|
j6_signal_t m_signals;
|
||||||
uint16_t m_handle_count;
|
uint16_t m_handle_count;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
kutil::vector<thread*> m_blocked_threads;
|
kutil::vector<thread*> m_blocked_threads;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,27 +14,27 @@ process &g_kernel_process = __g_kernel_process_storage.value;
|
|||||||
|
|
||||||
|
|
||||||
process::process() :
|
process::process() :
|
||||||
kobject {kobject::type::process},
|
kobject {kobject::type::process},
|
||||||
m_next_handle {1},
|
m_next_handle {1},
|
||||||
m_state {state::running}
|
m_state {state::running}
|
||||||
{
|
{
|
||||||
j6_handle_t self = add_handle(this);
|
j6_handle_t self = add_handle(this);
|
||||||
kassert(self == self_handle(), "Process self-handle is not 1");
|
kassert(self == self_handle(), "Process self-handle is not 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The "kernel process"-only constructor
|
// The "kernel process"-only constructor
|
||||||
process::process(page_table *kpml4) :
|
process::process(page_table *kpml4) :
|
||||||
kobject {kobject::type::process},
|
kobject {kobject::type::process},
|
||||||
m_space {kpml4},
|
m_space {kpml4},
|
||||||
m_next_handle {self_handle()+1},
|
m_next_handle {self_handle()+1},
|
||||||
m_state {state::running}
|
m_state {state::running}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
process::~process()
|
process::~process()
|
||||||
{
|
{
|
||||||
for (auto &it : m_handles)
|
for (auto &it : m_handles)
|
||||||
if (it.val) it.val->handle_release();
|
if (it.val) it.val->handle_release();
|
||||||
}
|
}
|
||||||
|
|
||||||
process & process::current() { return *current_cpu().process; }
|
process & process::current() { return *current_cpu().process; }
|
||||||
@@ -43,115 +43,115 @@ process & process::kernel_process() { return g_kernel_process; }
|
|||||||
process *
|
process *
|
||||||
process::create_kernel_process(page_table *pml4)
|
process::create_kernel_process(page_table *pml4)
|
||||||
{
|
{
|
||||||
return new (&g_kernel_process) process {pml4};
|
return new (&g_kernel_process) process {pml4};
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process::exit(int32_t code)
|
process::exit(int32_t code)
|
||||||
{
|
{
|
||||||
// TODO: make this thread-safe
|
// TODO: make this thread-safe
|
||||||
m_state = state::exited;
|
m_state = state::exited;
|
||||||
m_return_code = code;
|
m_return_code = code;
|
||||||
close();
|
close();
|
||||||
|
|
||||||
for (auto *thread : m_threads) {
|
for (auto *thread : m_threads) {
|
||||||
thread->exit(code);
|
thread->exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this == current_cpu().process)
|
if (this == current_cpu().process)
|
||||||
scheduler::get().schedule();
|
scheduler::get().schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process::update()
|
process::update()
|
||||||
{
|
{
|
||||||
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
uint32_t status = 0;
|
uint32_t status = 0;
|
||||||
while (i < m_threads.count()) {
|
while (i < m_threads.count()) {
|
||||||
thread *th = m_threads[i];
|
thread *th = m_threads[i];
|
||||||
if (th->has_state(thread::state::exited)) {
|
if (th->has_state(thread::state::exited)) {
|
||||||
status = th->m_return_code;
|
status = th->m_return_code;
|
||||||
m_threads.remove_swap_at(i);
|
m_threads.remove_swap_at(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_threads.count() == 0) {
|
if (m_threads.count() == 0) {
|
||||||
// TODO: What really is the return code in this case?
|
// TODO: What really is the return code in this case?
|
||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread *
|
thread *
|
||||||
process::create_thread(uint8_t priority, bool user)
|
process::create_thread(uint8_t priority, bool user)
|
||||||
{
|
{
|
||||||
if (priority == default_priority)
|
if (priority == default_priority)
|
||||||
priority = scheduler::default_priority;
|
priority = scheduler::default_priority;
|
||||||
|
|
||||||
thread *th = new thread(*this, priority);
|
thread *th = new thread(*this, priority);
|
||||||
kassert(th, "Failed to create thread!");
|
kassert(th, "Failed to create thread!");
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
uintptr_t stack_top = stacks_top - (m_threads.count() * stack_size);
|
uintptr_t stack_top = stacks_top - (m_threads.count() * stack_size);
|
||||||
|
|
||||||
vm_flags flags = vm_flags::zero|vm_flags::write;
|
vm_flags flags = vm_flags::zero|vm_flags::write;
|
||||||
vm_area *vma = new vm_area_open(stack_size, flags);
|
vm_area *vma = new vm_area_open(stack_size, flags);
|
||||||
m_space.add(stack_top - stack_size, vma);
|
m_space.add(stack_top - stack_size, vma);
|
||||||
|
|
||||||
// Space for null frame - because the page gets zeroed on
|
// Space for null frame - because the page gets zeroed on
|
||||||
// allocation, just pointing rsp here does the trick
|
// allocation, just pointing rsp here does the trick
|
||||||
th->tcb()->rsp3 = stack_top - 2 * sizeof(uint64_t);
|
th->tcb()->rsp3 = stack_top - 2 * sizeof(uint64_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_threads.append(th);
|
m_threads.append(th);
|
||||||
scheduler::get().add_thread(th->tcb());
|
scheduler::get().add_thread(th->tcb());
|
||||||
return th;
|
return th;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
process::thread_exited(thread *th)
|
process::thread_exited(thread *th)
|
||||||
{
|
{
|
||||||
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
||||||
uint32_t status = th->m_return_code;
|
uint32_t status = th->m_return_code;
|
||||||
m_threads.remove_swap(th);
|
m_threads.remove_swap(th);
|
||||||
remove_handle(th->self_handle());
|
remove_handle(th->self_handle());
|
||||||
delete th;
|
delete th;
|
||||||
|
|
||||||
// TODO: delete the thread's stack VMA
|
// TODO: delete the thread's stack VMA
|
||||||
|
|
||||||
if (m_threads.count() == 0) {
|
if (m_threads.count() == 0) {
|
||||||
exit(status);
|
exit(status);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_handle_t
|
j6_handle_t
|
||||||
process::add_handle(kobject *obj)
|
process::add_handle(kobject *obj)
|
||||||
{
|
{
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return j6_handle_invalid;
|
return j6_handle_invalid;
|
||||||
|
|
||||||
obj->handle_retain();
|
obj->handle_retain();
|
||||||
j6_handle_t handle = m_next_handle++;
|
j6_handle_t handle = m_next_handle++;
|
||||||
m_handles.insert(handle, obj);
|
m_handles.insert(handle, obj);
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
process::remove_handle(j6_handle_t handle)
|
process::remove_handle(j6_handle_t handle)
|
||||||
{
|
{
|
||||||
kobject *obj = m_handles.find(handle);
|
kobject *obj = m_handles.find(handle);
|
||||||
if (obj) obj->handle_release();
|
if (obj) obj->handle_release();
|
||||||
return m_handles.erase(handle);
|
return m_handles.erase(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
kobject *
|
kobject *
|
||||||
process::lookup_handle(j6_handle_t handle)
|
process::lookup_handle(j6_handle_t handle)
|
||||||
{
|
{
|
||||||
return m_handles.find(handle);
|
return m_handles.find(handle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,89 +9,89 @@
|
|||||||
#include "vm_space.h"
|
#include "vm_space.h"
|
||||||
|
|
||||||
class process :
|
class process :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Top of memory area where thread stacks are allocated
|
/// Top of memory area where thread stacks are allocated
|
||||||
constexpr static uintptr_t stacks_top = 0x0000800000000000;
|
constexpr static uintptr_t stacks_top = 0x0000800000000000;
|
||||||
|
|
||||||
/// Size of userspace thread stacks
|
/// Size of userspace thread stacks
|
||||||
constexpr static size_t stack_size = 0x4000000; // 64MiB
|
constexpr static size_t stack_size = 0x4000000; // 64MiB
|
||||||
|
|
||||||
/// Value that represents default priority
|
/// Value that represents default priority
|
||||||
constexpr static uint8_t default_priority = 0xff;
|
constexpr static uint8_t default_priority = 0xff;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
process();
|
process();
|
||||||
|
|
||||||
/// Destructor.
|
/// Destructor.
|
||||||
virtual ~process();
|
virtual ~process();
|
||||||
|
|
||||||
static constexpr kobject::type type = kobject::type::process;
|
static constexpr kobject::type type = kobject::type::process;
|
||||||
|
|
||||||
/// Get the currently executing process.
|
/// Get the currently executing process.
|
||||||
static process & current();
|
static process & current();
|
||||||
|
|
||||||
/// Terminate this process.
|
/// Terminate this process.
|
||||||
/// \arg code The return code to exit with.
|
/// \arg code The return code to exit with.
|
||||||
void exit(int32_t code);
|
void exit(int32_t code);
|
||||||
|
|
||||||
/// Update internal bookkeeping about threads.
|
/// Update internal bookkeeping about threads.
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
/// Get the process' virtual memory space
|
/// Get the process' virtual memory space
|
||||||
vm_space & space() { return m_space; }
|
vm_space & space() { return m_space; }
|
||||||
|
|
||||||
/// Create a new thread in this process
|
/// Create a new thread in this process
|
||||||
/// \args priority The new thread's scheduling priority
|
/// \args priority The new thread's scheduling priority
|
||||||
/// \args user If true, create a userspace stack for this thread
|
/// \args user If true, create a userspace stack for this thread
|
||||||
/// \returns The newly created thread object
|
/// \returns The newly created thread object
|
||||||
thread * create_thread(uint8_t priorty = default_priority, bool user = true);
|
thread * create_thread(uint8_t priorty = default_priority, bool user = true);
|
||||||
|
|
||||||
/// Start tracking an object with a handle.
|
/// Start tracking an object with a handle.
|
||||||
/// \args obj The object this handle refers to
|
/// \args obj The object this handle refers to
|
||||||
/// \returns The new handle for this object
|
/// \returns The new handle for this object
|
||||||
j6_handle_t add_handle(kobject *obj);
|
j6_handle_t add_handle(kobject *obj);
|
||||||
|
|
||||||
/// Stop tracking an object with a handle.
|
/// Stop tracking an object with a handle.
|
||||||
/// \args handle The handle that refers to the object
|
/// \args handle The handle that refers to the object
|
||||||
/// \returns True if the handle was removed
|
/// \returns True if the handle was removed
|
||||||
bool remove_handle(j6_handle_t handle);
|
bool remove_handle(j6_handle_t handle);
|
||||||
|
|
||||||
/// Lookup an object for a handle
|
/// Lookup an object for a handle
|
||||||
/// \args handle The handle to the object
|
/// \args handle The handle to the object
|
||||||
/// \returns Pointer to the object, or null if not found
|
/// \returns Pointer to the object, or null if not found
|
||||||
kobject * lookup_handle(j6_handle_t handle);
|
kobject * lookup_handle(j6_handle_t handle);
|
||||||
|
|
||||||
/// Inform the process of an exited thread
|
/// Inform the process of an exited thread
|
||||||
/// \args th The thread which has exited
|
/// \args th The thread which has exited
|
||||||
/// \returns True if this thread ending has ended the process
|
/// \returns True if this thread ending has ended the process
|
||||||
bool thread_exited(thread *th);
|
bool thread_exited(thread *th);
|
||||||
|
|
||||||
/// Get the handle for this process to refer to itself
|
/// Get the handle for this process to refer to itself
|
||||||
inline j6_handle_t self_handle() const { return 1; }
|
inline j6_handle_t self_handle() const { return 1; }
|
||||||
|
|
||||||
/// Get the process object that owns kernel threads and the
|
/// Get the process object that owns kernel threads and the
|
||||||
/// kernel address space
|
/// kernel address space
|
||||||
static process & kernel_process();
|
static process & kernel_process();
|
||||||
|
|
||||||
/// Create the special kernel process that owns kernel tasks
|
/// Create the special kernel process that owns kernel tasks
|
||||||
/// \arg pml4 The kernel-only pml4
|
/// \arg pml4 The kernel-only pml4
|
||||||
/// \returns The kernel process object
|
/// \returns The kernel process object
|
||||||
static process * create_kernel_process(page_table *pml4);
|
static process * create_kernel_process(page_table *pml4);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This constructor is called by create_kernel_process
|
// This constructor is called by create_kernel_process
|
||||||
process(page_table *kpml4);
|
process(page_table *kpml4);
|
||||||
|
|
||||||
int32_t m_return_code;
|
int32_t m_return_code;
|
||||||
|
|
||||||
vm_space m_space;
|
vm_space m_space;
|
||||||
|
|
||||||
kutil::vector<thread*> m_threads;
|
kutil::vector<thread*> m_threads;
|
||||||
kutil::map<j6_handle_t, kobject*> m_handles;
|
kutil::map<j6_handle_t, kobject*> m_handles;
|
||||||
j6_handle_t m_next_handle;
|
j6_handle_t m_next_handle;
|
||||||
|
|
||||||
enum class state : uint8_t { running, exited };
|
enum class state : uint8_t { running, exited };
|
||||||
state m_state;
|
state m_state;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
#include "objects/kobject.h"
|
#include "objects/kobject.h"
|
||||||
|
|
||||||
class system :
|
class system :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr kobject::type type = kobject::type::event;
|
static constexpr kobject::type type = kobject::type::event;
|
||||||
|
|
||||||
inline static system & get() { return s_instance; }
|
inline static system & get() { return s_instance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static system s_instance;
|
static system s_instance;
|
||||||
system() : kobject(type::system) {}
|
system() : kobject(type::system) {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,35 +12,35 @@ static constexpr j6_signal_t thread_default_signals = 0;
|
|||||||
extern vm_area_guarded &g_kernel_stacks;
|
extern vm_area_guarded &g_kernel_stacks;
|
||||||
|
|
||||||
thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
||||||
kobject(kobject::type::thread, thread_default_signals),
|
kobject(kobject::type::thread, thread_default_signals),
|
||||||
m_parent(parent),
|
m_parent(parent),
|
||||||
m_state(state::loading),
|
m_state(state::loading),
|
||||||
m_wait_type(wait_type::none),
|
m_wait_type(wait_type::none),
|
||||||
m_wait_data(0),
|
m_wait_data(0),
|
||||||
m_wait_obj(0)
|
m_wait_obj(0)
|
||||||
{
|
{
|
||||||
parent.space().initialize_tcb(m_tcb);
|
parent.space().initialize_tcb(m_tcb);
|
||||||
m_tcb.priority = pri;
|
m_tcb.priority = pri;
|
||||||
|
|
||||||
if (!rsp0)
|
if (!rsp0)
|
||||||
setup_kernel_stack();
|
setup_kernel_stack();
|
||||||
else
|
else
|
||||||
m_tcb.rsp0 = rsp0;
|
m_tcb.rsp0 = rsp0;
|
||||||
|
|
||||||
m_self_handle = parent.add_handle(this);
|
m_self_handle = parent.add_handle(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread::~thread()
|
thread::~thread()
|
||||||
{
|
{
|
||||||
g_kernel_stacks.return_section(m_tcb.kernel_stack);
|
g_kernel_stacks.return_section(m_tcb.kernel_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread *
|
thread *
|
||||||
thread::from_tcb(TCB *tcb)
|
thread::from_tcb(TCB *tcb)
|
||||||
{
|
{
|
||||||
static ptrdiff_t offset =
|
static ptrdiff_t offset =
|
||||||
-1 * static_cast<ptrdiff_t>(offsetof(thread, m_tcb));
|
-1 * static_cast<ptrdiff_t>(offsetof(thread, m_tcb));
|
||||||
return reinterpret_cast<thread*>(kutil::offset_pointer(tcb, offset));
|
return reinterpret_cast<thread*>(kutil::offset_pointer(tcb, offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
thread & thread::current() { return *current_cpu().thread; }
|
thread & thread::current() { return *current_cpu().thread; }
|
||||||
@@ -50,176 +50,176 @@ inline void schedule_if_current(thread *t) { if (t == current_cpu().thread) sche
|
|||||||
void
|
void
|
||||||
thread::wait_on_signals(j6_signal_t signals)
|
thread::wait_on_signals(j6_signal_t signals)
|
||||||
{
|
{
|
||||||
m_wait_type = wait_type::signal;
|
m_wait_type = wait_type::signal;
|
||||||
m_wait_data = signals;
|
m_wait_data = signals;
|
||||||
clear_state(state::ready);
|
clear_state(state::ready);
|
||||||
|
|
||||||
schedule_if_current(this);
|
schedule_if_current(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::wait_on_time(uint64_t t)
|
thread::wait_on_time(uint64_t t)
|
||||||
{
|
{
|
||||||
m_wait_type = wait_type::time;
|
m_wait_type = wait_type::time;
|
||||||
m_wait_data = t;
|
m_wait_data = t;
|
||||||
clear_state(state::ready);
|
clear_state(state::ready);
|
||||||
|
|
||||||
schedule_if_current(this);
|
schedule_if_current(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::wait_on_object(kobject *o)
|
thread::wait_on_object(kobject *o)
|
||||||
{
|
{
|
||||||
m_wait_type = wait_type::object;
|
m_wait_type = wait_type::object;
|
||||||
m_wait_data = reinterpret_cast<uint64_t>(o);
|
m_wait_data = reinterpret_cast<uint64_t>(o);
|
||||||
clear_state(state::ready);
|
clear_state(state::ready);
|
||||||
|
|
||||||
schedule_if_current(this);
|
schedule_if_current(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
thread::wake_on_signals(kobject *obj, j6_signal_t signals)
|
thread::wake_on_signals(kobject *obj, j6_signal_t signals)
|
||||||
{
|
{
|
||||||
if (m_wait_type != wait_type::signal ||
|
if (m_wait_type != wait_type::signal ||
|
||||||
(signals & m_wait_data) == 0)
|
(signals & m_wait_data) == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
m_wait_type = wait_type::none;
|
||||||
m_wait_result = j6_status_ok;
|
m_wait_result = j6_status_ok;
|
||||||
m_wait_data = signals;
|
m_wait_data = signals;
|
||||||
m_wait_obj = obj->koid();
|
m_wait_obj = obj->koid();
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
thread::wake_on_time(uint64_t now)
|
thread::wake_on_time(uint64_t now)
|
||||||
{
|
{
|
||||||
if (m_wait_type != wait_type::time ||
|
if (m_wait_type != wait_type::time ||
|
||||||
now < m_wait_data)
|
now < m_wait_data)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
m_wait_type = wait_type::none;
|
||||||
m_wait_result = j6_status_ok;
|
m_wait_result = j6_status_ok;
|
||||||
m_wait_data = now;
|
m_wait_data = now;
|
||||||
m_wait_obj = 0;
|
m_wait_obj = 0;
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
thread::wake_on_object(kobject *o)
|
thread::wake_on_object(kobject *o)
|
||||||
{
|
{
|
||||||
if (m_wait_type != wait_type::object ||
|
if (m_wait_type != wait_type::object ||
|
||||||
reinterpret_cast<uint64_t>(o) != m_wait_data)
|
reinterpret_cast<uint64_t>(o) != m_wait_data)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
m_wait_type = wait_type::none;
|
||||||
m_wait_result = j6_status_ok;
|
m_wait_result = j6_status_ok;
|
||||||
m_wait_obj = o->koid();
|
m_wait_obj = o->koid();
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::wake_on_result(kobject *obj, j6_status_t result)
|
thread::wake_on_result(kobject *obj, j6_status_t result)
|
||||||
{
|
{
|
||||||
m_wait_type = wait_type::none;
|
m_wait_type = wait_type::none;
|
||||||
m_wait_result = result;
|
m_wait_result = result;
|
||||||
m_wait_data = 0;
|
m_wait_data = 0;
|
||||||
m_wait_obj = obj->koid();
|
m_wait_obj = obj->koid();
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::exit(int32_t code)
|
thread::exit(int32_t code)
|
||||||
{
|
{
|
||||||
m_return_code = code;
|
m_return_code = code;
|
||||||
set_state(state::exited);
|
set_state(state::exited);
|
||||||
clear_state(state::ready);
|
clear_state(state::ready);
|
||||||
close();
|
close();
|
||||||
|
|
||||||
schedule_if_current(this);
|
schedule_if_current(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::add_thunk_kernel(uintptr_t rip)
|
thread::add_thunk_kernel(uintptr_t rip)
|
||||||
{
|
{
|
||||||
// This adds just enough values to the top of the
|
// This adds just enough values to the top of the
|
||||||
// kernel stack to come out of task_switch correctly
|
// kernel stack to come out of task_switch correctly
|
||||||
// and start executing at rip (still in kernel mode)
|
// and start executing at rip (still in kernel mode)
|
||||||
|
|
||||||
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
||||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||||
|
|
||||||
stack[6] = rip; // return rip
|
stack[6] = rip; // return rip
|
||||||
stack[5] = m_tcb.rsp0; // rbp
|
stack[5] = m_tcb.rsp0; // rbp
|
||||||
stack[4] = 0xbbbbbbbb; // rbx
|
stack[4] = 0xbbbbbbbb; // rbx
|
||||||
stack[3] = 0x12121212; // r12
|
stack[3] = 0x12121212; // r12
|
||||||
stack[2] = 0x13131313; // r13
|
stack[2] = 0x13131313; // r13
|
||||||
stack[1] = 0x14141414; // r14
|
stack[1] = 0x14141414; // r14
|
||||||
stack[0] = 0x15151515; // r15
|
stack[0] = 0x15151515; // r15
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
|
thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
|
||||||
{
|
{
|
||||||
// This sets up the stack to:
|
// This sets up the stack to:
|
||||||
// a) come out of task_switch and return to rip0 (default is the
|
// a) come out of task_switch and return to rip0 (default is the
|
||||||
// kernel/user trampoline) (via add_thunk_kernel) - if this is
|
// kernel/user trampoline) (via add_thunk_kernel) - if this is
|
||||||
// changed, it needs to end up at the trampoline with the stack
|
// changed, it needs to end up at the trampoline with the stack
|
||||||
// as it was
|
// as it was
|
||||||
// b) come out of the kernel/user trampoline and start executing
|
// b) come out of the kernel/user trampoline and start executing
|
||||||
// in user mode at rip
|
// in user mode at rip
|
||||||
|
|
||||||
m_tcb.rsp -= sizeof(uintptr_t) * 8;
|
m_tcb.rsp -= sizeof(uintptr_t) * 8;
|
||||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||||
flags |= 0x200;
|
flags |= 0x200;
|
||||||
|
|
||||||
stack[7] = rip3; // return rip in rcx
|
stack[7] = rip3; // return rip in rcx
|
||||||
stack[6] = m_tcb.rsp3; // rbp
|
stack[6] = m_tcb.rsp3; // rbp
|
||||||
stack[5] = 0xbbbbbbbb; // rbx
|
stack[5] = 0xbbbbbbbb; // rbx
|
||||||
stack[4] = flags; // r11 sets RFLAGS
|
stack[4] = flags; // r11 sets RFLAGS
|
||||||
stack[3] = 0x12121212; // r12
|
stack[3] = 0x12121212; // r12
|
||||||
stack[2] = 0x13131313; // r13
|
stack[2] = 0x13131313; // r13
|
||||||
stack[1] = 0x14141414; // r14
|
stack[1] = 0x14141414; // r14
|
||||||
stack[0] = 0x15151515; // r15
|
stack[0] = 0x15151515; // r15
|
||||||
|
|
||||||
static const uintptr_t trampoline =
|
static const uintptr_t trampoline =
|
||||||
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
||||||
add_thunk_kernel(rip0 ? rip0 : trampoline);
|
add_thunk_kernel(rip0 ? rip0 : trampoline);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
thread::setup_kernel_stack()
|
thread::setup_kernel_stack()
|
||||||
{
|
{
|
||||||
using memory::frame_size;
|
using memory::frame_size;
|
||||||
using memory::kernel_stack_pages;
|
using memory::kernel_stack_pages;
|
||||||
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||||
|
|
||||||
constexpr unsigned null_frame_entries = 2;
|
constexpr unsigned null_frame_entries = 2;
|
||||||
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||||
|
|
||||||
uintptr_t stack_addr = g_kernel_stacks.get_section();
|
uintptr_t stack_addr = g_kernel_stacks.get_section();
|
||||||
uintptr_t stack_end = stack_addr + stack_bytes;
|
uintptr_t stack_end = stack_addr + stack_bytes;
|
||||||
|
|
||||||
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_end - null_frame_size);
|
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_end - null_frame_size);
|
||||||
for (unsigned i = 0; i < null_frame_entries; ++i)
|
for (unsigned i = 0; i < null_frame_entries; ++i)
|
||||||
null_frame[i] = 0;
|
null_frame[i] = 0;
|
||||||
|
|
||||||
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
||||||
stack_addr, stack_bytes);
|
stack_addr, stack_bytes);
|
||||||
|
|
||||||
m_tcb.kernel_stack = stack_addr;
|
m_tcb.kernel_stack = stack_addr;
|
||||||
m_tcb.rsp0 = reinterpret_cast<uintptr_t>(null_frame);
|
m_tcb.rsp0 = reinterpret_cast<uintptr_t>(null_frame);
|
||||||
m_tcb.rsp = m_tcb.rsp0;
|
m_tcb.rsp = m_tcb.rsp0;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread *
|
thread *
|
||||||
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0)
|
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0)
|
||||||
{
|
{
|
||||||
thread *idle = new thread(kernel, pri, rsp0);
|
thread *idle = new thread(kernel, pri, rsp0);
|
||||||
idle->set_state(state::constant);
|
idle->set_state(state::constant);
|
||||||
idle->set_state(state::ready);
|
idle->set_state(state::ready);
|
||||||
return idle;
|
return idle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,179 +10,179 @@ class process;
|
|||||||
|
|
||||||
struct TCB
|
struct TCB
|
||||||
{
|
{
|
||||||
// Data used by assembly task control routines. If you change any of these,
|
// Data used by assembly task control routines. If you change any of these,
|
||||||
// be sure to change the assembly definitions in 'tasking.inc'
|
// be sure to change the assembly definitions in 'tasking.inc'
|
||||||
uintptr_t rsp;
|
uintptr_t rsp;
|
||||||
uintptr_t rsp0;
|
uintptr_t rsp0;
|
||||||
uintptr_t rsp3;
|
uintptr_t rsp3;
|
||||||
uintptr_t pml4;
|
uintptr_t pml4;
|
||||||
|
|
||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
// note: 3 bytes padding
|
// note: 3 bytes padding
|
||||||
|
|
||||||
// TODO: move state into TCB?
|
// TODO: move state into TCB?
|
||||||
|
|
||||||
uintptr_t kernel_stack;
|
uintptr_t kernel_stack;
|
||||||
|
|
||||||
uint32_t time_left;
|
uint32_t time_left;
|
||||||
uint64_t last_ran;
|
uint64_t last_ran;
|
||||||
};
|
};
|
||||||
|
|
||||||
using tcb_list = kutil::linked_list<TCB>;
|
using tcb_list = kutil::linked_list<TCB>;
|
||||||
using tcb_node = tcb_list::item_type;
|
using tcb_node = tcb_list::item_type;
|
||||||
|
|
||||||
class thread :
|
class thread :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class wait_type : uint8_t { none, signal, time, object };
|
enum class wait_type : uint8_t { none, signal, time, object };
|
||||||
enum class state : uint8_t {
|
enum class state : uint8_t {
|
||||||
ready = 0x01,
|
ready = 0x01,
|
||||||
loading = 0x02,
|
loading = 0x02,
|
||||||
exited = 0x04,
|
exited = 0x04,
|
||||||
constant = 0x80,
|
constant = 0x80,
|
||||||
none = 0x00
|
none = 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get the pointer to the thread object containing this TCB
|
/// Get the pointer to the thread object containing this TCB
|
||||||
static thread * from_tcb(TCB *tcb);
|
static thread * from_tcb(TCB *tcb);
|
||||||
|
|
||||||
/// Destructor
|
/// Destructor
|
||||||
virtual ~thread();
|
virtual ~thread();
|
||||||
|
|
||||||
static constexpr kobject::type type = kobject::type::thread;
|
static constexpr kobject::type type = kobject::type::thread;
|
||||||
|
|
||||||
/// Get the currently executing thread.
|
/// Get the currently executing thread.
|
||||||
static thread & current();
|
static thread & current();
|
||||||
|
|
||||||
/// Get the `ready` state of the thread.
|
/// Get the `ready` state of the thread.
|
||||||
/// \returns True if the thread is ready to execute.
|
/// \returns True if the thread is ready to execute.
|
||||||
inline bool ready() const { return has_state(state::ready); }
|
inline bool ready() const { return has_state(state::ready); }
|
||||||
|
|
||||||
/// Get the `loading` state of the thread.
|
/// Get the `loading` state of the thread.
|
||||||
/// \returns True if the thread has not finished loading.
|
/// \returns True if the thread has not finished loading.
|
||||||
inline bool loading() const { return has_state(state::loading); }
|
inline bool loading() const { return has_state(state::loading); }
|
||||||
|
|
||||||
/// Get the `constant` state of the thread.
|
/// Get the `constant` state of the thread.
|
||||||
/// \returns True if the thread has constant priority.
|
/// \returns True if the thread has constant priority.
|
||||||
inline bool constant() const { return has_state(state::constant); }
|
inline bool constant() const { return has_state(state::constant); }
|
||||||
|
|
||||||
/// Get the thread priority.
|
/// Get the thread priority.
|
||||||
inline uint8_t priority() const { return m_tcb.priority; }
|
inline uint8_t priority() const { return m_tcb.priority; }
|
||||||
|
|
||||||
/// Set the thread priority.
|
/// Set the thread priority.
|
||||||
/// \arg p The new thread priority
|
/// \arg p The new thread priority
|
||||||
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
||||||
|
|
||||||
/// Block the thread, waiting an object's signals.
|
/// Block the thread, waiting an object's signals.
|
||||||
/// \arg signals Mask of signals to wait for
|
/// \arg signals Mask of signals to wait for
|
||||||
void wait_on_signals(j6_signal_t signals);
|
void wait_on_signals(j6_signal_t signals);
|
||||||
|
|
||||||
/// Block the thread, waiting for a given clock value
|
/// Block the thread, waiting for a given clock value
|
||||||
/// \arg t Clock value to wait for
|
/// \arg t Clock value to wait for
|
||||||
void wait_on_time(uint64_t t);
|
void wait_on_time(uint64_t t);
|
||||||
|
|
||||||
/// Block the thread, waiting on the given object
|
/// Block the thread, waiting on the given object
|
||||||
/// \arg o The ojbect that should wake this thread
|
/// \arg o The ojbect that should wake this thread
|
||||||
void wait_on_object(kobject *o);
|
void wait_on_object(kobject *o);
|
||||||
|
|
||||||
/// Wake the thread if it is waiting on signals.
|
/// Wake the thread if it is waiting on signals.
|
||||||
/// \arg obj Object that changed signals
|
/// \arg obj Object that changed signals
|
||||||
/// \arg signals Signal state of the object
|
/// \arg signals Signal state of the object
|
||||||
/// \returns True if this action unblocked the thread
|
/// \returns True if this action unblocked the thread
|
||||||
bool wake_on_signals(kobject *obj, j6_signal_t signals);
|
bool wake_on_signals(kobject *obj, j6_signal_t signals);
|
||||||
|
|
||||||
/// Wake the thread if it is waiting on the clock.
|
/// Wake the thread if it is waiting on the clock.
|
||||||
/// \arg now Current clock value
|
/// \arg now Current clock value
|
||||||
/// \returns True if this action unblocked the thread
|
/// \returns True if this action unblocked the thread
|
||||||
bool wake_on_time(uint64_t now);
|
bool wake_on_time(uint64_t now);
|
||||||
|
|
||||||
/// Wake the thread if it is waiting on the given object.
|
/// Wake the thread if it is waiting on the given object.
|
||||||
/// \arg o Object trying to wake the thread
|
/// \arg o Object trying to wake the thread
|
||||||
/// \returns True if this action unblocked the thread
|
/// \returns True if this action unblocked the thread
|
||||||
bool wake_on_object(kobject *o);
|
bool wake_on_object(kobject *o);
|
||||||
|
|
||||||
/// Wake the thread with a given result code.
|
/// Wake the thread with a given result code.
|
||||||
/// \arg obj Object that changed signals
|
/// \arg obj Object that changed signals
|
||||||
/// \arg result Result code to return to the thread
|
/// \arg result Result code to return to the thread
|
||||||
void wake_on_result(kobject *obj, j6_status_t result);
|
void wake_on_result(kobject *obj, j6_status_t result);
|
||||||
|
|
||||||
/// Get the result status code from the last blocking operation
|
/// Get the result status code from the last blocking operation
|
||||||
j6_status_t get_wait_result() const { return m_wait_result; }
|
j6_status_t get_wait_result() const { return m_wait_result; }
|
||||||
|
|
||||||
/// Get the current blocking opreation's wait data
|
/// Get the current blocking opreation's wait data
|
||||||
uint64_t get_wait_data() const { return m_wait_data; }
|
uint64_t get_wait_data() const { return m_wait_data; }
|
||||||
|
|
||||||
/// Get the current blocking operation's wait ojbect (as a handle)
|
/// Get the current blocking operation's wait ojbect (as a handle)
|
||||||
j6_koid_t get_wait_object() const { return m_wait_obj; }
|
j6_koid_t get_wait_object() const { return m_wait_obj; }
|
||||||
|
|
||||||
inline bool has_state(state s) const {
|
inline bool has_state(state s) const {
|
||||||
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set_state(state s) {
|
inline void set_state(state s) {
|
||||||
m_state = static_cast<state>(static_cast<uint8_t>(m_state) | static_cast<uint8_t>(s));
|
m_state = static_cast<state>(static_cast<uint8_t>(m_state) | static_cast<uint8_t>(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void clear_state(state s) {
|
inline void clear_state(state s) {
|
||||||
m_state = static_cast<state>(static_cast<uint8_t>(m_state) & ~static_cast<uint8_t>(s));
|
m_state = static_cast<state>(static_cast<uint8_t>(m_state) & ~static_cast<uint8_t>(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline tcb_node * tcb() { return &m_tcb; }
|
inline tcb_node * tcb() { return &m_tcb; }
|
||||||
inline process & parent() { return m_parent; }
|
inline process & parent() { return m_parent; }
|
||||||
|
|
||||||
/// Terminate this thread.
|
/// Terminate this thread.
|
||||||
/// \arg code The return code to exit with.
|
/// \arg code The return code to exit with.
|
||||||
void exit(int32_t code);
|
void exit(int32_t code);
|
||||||
|
|
||||||
/// Add a stack header that returns to the given address in kernel space.
|
/// Add a stack header that returns to the given address in kernel space.
|
||||||
/// \arg rip The address to return to, must be kernel space
|
/// \arg rip The address to return to, must be kernel space
|
||||||
void add_thunk_kernel(uintptr_t rip);
|
void add_thunk_kernel(uintptr_t rip);
|
||||||
|
|
||||||
/// Add a stack header that returns to the given address in user space
|
/// Add a stack header that returns to the given address in user space
|
||||||
/// via a function in kernel space.
|
/// via a function in kernel space.
|
||||||
/// \arg rip3 The user space address to return to
|
/// \arg rip3 The user space address to return to
|
||||||
/// \arg rip0 The kernel function to pass through, optional
|
/// \arg rip0 The kernel function to pass through, optional
|
||||||
/// \arg flags Extra RFLAGS values to set, optional
|
/// \arg flags Extra RFLAGS values to set, optional
|
||||||
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0);
|
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0);
|
||||||
|
|
||||||
/// Get the handle representing this thread to its process
|
/// Get the handle representing this thread to its process
|
||||||
j6_handle_t self_handle() const { return m_self_handle; }
|
j6_handle_t self_handle() const { return m_self_handle; }
|
||||||
|
|
||||||
/// Create the kernel idle thread
|
/// Create the kernel idle thread
|
||||||
/// \arg kernel The process object that owns kernel tasks
|
/// \arg kernel The process object that owns kernel tasks
|
||||||
/// \arg pri The idle thread priority value
|
/// \arg pri The idle thread priority value
|
||||||
/// \arg rsp The existing stack for the idle thread
|
/// \arg rsp The existing stack for the idle thread
|
||||||
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
thread() = delete;
|
thread() = delete;
|
||||||
thread(const thread &other) = delete;
|
thread(const thread &other) = delete;
|
||||||
thread(const thread &&other) = delete;
|
thread(const thread &&other) = delete;
|
||||||
friend class process;
|
friend class process;
|
||||||
|
|
||||||
/// Constructor. Used when a kernel stack already exists.
|
/// Constructor. Used when a kernel stack already exists.
|
||||||
/// \arg parent The process which owns this thread
|
/// \arg parent The process which owns this thread
|
||||||
/// \arg pri Initial priority level of this thread
|
/// \arg pri Initial priority level of this thread
|
||||||
/// \arg rsp0 The existing kernel stack rsp, 0 for none
|
/// \arg rsp0 The existing kernel stack rsp, 0 for none
|
||||||
thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0);
|
thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0);
|
||||||
|
|
||||||
/// Set up a new empty kernel stack for this thread.
|
/// Set up a new empty kernel stack for this thread.
|
||||||
void setup_kernel_stack();
|
void setup_kernel_stack();
|
||||||
|
|
||||||
tcb_node m_tcb;
|
tcb_node m_tcb;
|
||||||
|
|
||||||
process &m_parent;
|
process &m_parent;
|
||||||
|
|
||||||
state m_state;
|
state m_state;
|
||||||
wait_type m_wait_type;
|
wait_type m_wait_type;
|
||||||
// There should be 1 byte of padding here
|
// There should be 1 byte of padding here
|
||||||
|
|
||||||
int32_t m_return_code;
|
int32_t m_return_code;
|
||||||
|
|
||||||
uint64_t m_wait_data;
|
uint64_t m_wait_data;
|
||||||
j6_status_t m_wait_result;
|
j6_status_t m_wait_result;
|
||||||
j6_koid_t m_wait_obj;
|
j6_koid_t m_wait_obj;
|
||||||
|
|
||||||
j6_handle_t m_self_handle;
|
j6_handle_t m_self_handle;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
using memory::frame_size;
|
using memory::frame_size;
|
||||||
|
|
||||||
vm_area::vm_area(size_t size, vm_flags flags) :
|
vm_area::vm_area(size_t size, vm_flags flags) :
|
||||||
m_size {size},
|
m_size {size},
|
||||||
m_flags {flags},
|
m_flags {flags},
|
||||||
m_spaces {m_vector_static, 0, static_size},
|
m_spaces {m_vector_static, 0, static_size},
|
||||||
kobject {kobject::type::vma}
|
kobject {kobject::type::vma}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,50 +19,50 @@ vm_area::~vm_area() {}
|
|||||||
bool
|
bool
|
||||||
vm_area::add_to(vm_space *space)
|
vm_area::add_to(vm_space *space)
|
||||||
{
|
{
|
||||||
for (auto *s : m_spaces) {
|
for (auto *s : m_spaces) {
|
||||||
if (s == space)
|
if (s == space)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
m_spaces.append(space);
|
m_spaces.append(space);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vm_area::remove_from(vm_space *space)
|
vm_area::remove_from(vm_space *space)
|
||||||
{
|
{
|
||||||
m_spaces.remove_swap(space);
|
m_spaces.remove_swap(space);
|
||||||
return
|
return
|
||||||
!m_spaces.count() &&
|
!m_spaces.count() &&
|
||||||
!(m_flags && vm_flags::mmio);
|
!(m_flags && vm_flags::mmio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
vm_area::on_no_handles()
|
vm_area::on_no_handles()
|
||||||
{
|
{
|
||||||
kobject::on_no_handles();
|
kobject::on_no_handles();
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
vm_area::resize(size_t size)
|
vm_area::resize(size_t size)
|
||||||
{
|
{
|
||||||
if (can_resize(size))
|
if (can_resize(size))
|
||||||
m_size = size;
|
m_size = size;
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vm_area::can_resize(size_t size)
|
vm_area::can_resize(size_t size)
|
||||||
{
|
{
|
||||||
for (auto *space : m_spaces)
|
for (auto *space : m_spaces)
|
||||||
if (!space->can_resize(*this, size))
|
if (!space->can_resize(*this, size))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) :
|
vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) :
|
||||||
m_start {start},
|
m_start {start},
|
||||||
vm_area {size, flags}
|
vm_area {size, flags}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,22 +70,22 @@ vm_area_fixed::~vm_area_fixed() {}
|
|||||||
|
|
||||||
size_t vm_area_fixed::resize(size_t size)
|
size_t vm_area_fixed::resize(size_t size)
|
||||||
{
|
{
|
||||||
// Not resizable
|
// Not resizable
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
bool vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
||||||
{
|
{
|
||||||
if (offset > m_size)
|
if (offset > m_size)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
phys = m_start + offset;
|
phys = m_start + offset;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vm_area_untracked::vm_area_untracked(size_t size, vm_flags flags) :
|
vm_area_untracked::vm_area_untracked(size_t size, vm_flags flags) :
|
||||||
vm_area {size, flags}
|
vm_area {size, flags}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,24 +94,24 @@ vm_area_untracked::~vm_area_untracked() {}
|
|||||||
bool
|
bool
|
||||||
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys)
|
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys)
|
||||||
{
|
{
|
||||||
if (offset > m_size)
|
if (offset > m_size)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return frame_allocator::get().allocate(1, &phys);
|
return frame_allocator::get().allocate(1, &phys);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vm_area_untracked::add_to(vm_space *space)
|
vm_area_untracked::add_to(vm_space *space)
|
||||||
{
|
{
|
||||||
if (!m_spaces.count())
|
if (!m_spaces.count())
|
||||||
return vm_area::add_to(space);
|
return vm_area::add_to(space);
|
||||||
return m_spaces[0] == space;
|
return m_spaces[0] == space;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vm_area_open::vm_area_open(size_t size, vm_flags flags) :
|
vm_area_open::vm_area_open(size_t size, vm_flags flags) :
|
||||||
m_mapped {nullptr},
|
m_mapped {nullptr},
|
||||||
vm_area {size, flags}
|
vm_area {size, flags}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,15 +120,15 @@ vm_area_open::~vm_area_open() {}
|
|||||||
bool
|
bool
|
||||||
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys)
|
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys)
|
||||||
{
|
{
|
||||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size, vm_flags flags) :
|
vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size, vm_flags flags) :
|
||||||
m_start {start},
|
m_start {start},
|
||||||
m_pages {buf_pages},
|
m_pages {buf_pages},
|
||||||
m_next {memory::frame_size},
|
m_next {memory::frame_size},
|
||||||
vm_area_untracked {size, flags}
|
vm_area_untracked {size, flags}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,33 +137,33 @@ vm_area_guarded::~vm_area_guarded() {}
|
|||||||
uintptr_t
|
uintptr_t
|
||||||
vm_area_guarded::get_section()
|
vm_area_guarded::get_section()
|
||||||
{
|
{
|
||||||
if (m_cache.count() > 0) {
|
if (m_cache.count() > 0) {
|
||||||
return m_cache.pop();
|
return m_cache.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t addr = m_next;
|
uintptr_t addr = m_next;
|
||||||
m_next += (m_pages + 1) * memory::frame_size;
|
m_next += (m_pages + 1) * memory::frame_size;
|
||||||
return m_start + addr;
|
return m_start + addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
vm_area_guarded::return_section(uintptr_t addr)
|
vm_area_guarded::return_section(uintptr_t addr)
|
||||||
{
|
{
|
||||||
m_cache.append(addr);
|
m_cache.append(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
||||||
{
|
{
|
||||||
if (offset > m_next)
|
if (offset > m_next)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// make sure this isn't in a guard page. (sections are
|
// make sure this isn't in a guard page. (sections are
|
||||||
// m_pages big plus 1 leading guard page, so page 0 is
|
// m_pages big plus 1 leading guard page, so page 0 is
|
||||||
// invalid)
|
// invalid)
|
||||||
if ((offset >> 12) % (m_pages+1) == 0)
|
if ((offset >> 12) % (m_pages+1) == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return vm_area_untracked::get_page(offset, phys);
|
return vm_area_untracked::get_page(offset, phys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,158 +20,158 @@ enum class vm_flags : uint32_t
|
|||||||
#define VM_FLAG(name, v) name = v,
|
#define VM_FLAG(name, v) name = v,
|
||||||
#include "j6/tables/vm_flags.inc"
|
#include "j6/tables/vm_flags.inc"
|
||||||
#undef VM_FLAG
|
#undef VM_FLAG
|
||||||
driver_mask = 0x000fffff, ///< flags allowed via syscall for drivers
|
driver_mask = 0x000fffff, ///< flags allowed via syscall for drivers
|
||||||
user_mask = 0x0000ffff, ///< flags allowed via syscall for non-drivers
|
user_mask = 0x0000ffff, ///< flags allowed via syscall for non-drivers
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Virtual memory areas allow control over memory allocation
|
/// Virtual memory areas allow control over memory allocation
|
||||||
class vm_area :
|
class vm_area :
|
||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr kobject::type type = kobject::type::vma;
|
static constexpr kobject::type type = kobject::type::vma;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg size Initial virtual size of the memory area
|
/// \arg size Initial virtual size of the memory area
|
||||||
/// \arg flags Flags for this memory area
|
/// \arg flags Flags for this memory area
|
||||||
vm_area(size_t size, vm_flags flags = vm_flags::none);
|
vm_area(size_t size, vm_flags flags = vm_flags::none);
|
||||||
|
|
||||||
virtual ~vm_area();
|
virtual ~vm_area();
|
||||||
|
|
||||||
/// Get the current virtual size of the memory area
|
/// Get the current virtual size of the memory area
|
||||||
inline size_t size() const { return m_size; }
|
inline size_t size() const { return m_size; }
|
||||||
|
|
||||||
/// Get the flags set for this area
|
/// Get the flags set for this area
|
||||||
inline vm_flags flags() const { return m_flags; }
|
inline vm_flags flags() const { return m_flags; }
|
||||||
|
|
||||||
/// Track that this area was added to a vm_space
|
/// Track that this area was added to a vm_space
|
||||||
/// \arg space The space to add this area to
|
/// \arg space The space to add this area to
|
||||||
/// \returns False if this area cannot be added
|
/// \returns False if this area cannot be added
|
||||||
virtual bool add_to(vm_space *space);
|
virtual bool add_to(vm_space *space);
|
||||||
|
|
||||||
/// Track that this area was removed frm a vm_space
|
/// Track that this area was removed frm a vm_space
|
||||||
/// \arg space The space that is removing this area
|
/// \arg space The space that is removing this area
|
||||||
/// \returns True if the removing space should free the pages
|
/// \returns True if the removing space should free the pages
|
||||||
/// mapped for this area
|
/// mapped for this area
|
||||||
virtual bool remove_from(vm_space *space);
|
virtual bool remove_from(vm_space *space);
|
||||||
|
|
||||||
/// Change the virtual size of the memory area. This may cause
|
/// Change the virtual size of the memory area. This may cause
|
||||||
/// deallocation if the new size is smaller than the current size.
|
/// deallocation if the new size is smaller than the current size.
|
||||||
/// Note that if resizing is unsuccessful, the previous size will
|
/// Note that if resizing is unsuccessful, the previous size will
|
||||||
/// be returned.
|
/// be returned.
|
||||||
/// \arg size The desired new virtual size
|
/// \arg size The desired new virtual size
|
||||||
/// \returns The new virtual size
|
/// \returns The new virtual size
|
||||||
virtual size_t resize(size_t size);
|
virtual size_t resize(size_t size);
|
||||||
|
|
||||||
/// Get the physical page for the given offset
|
/// Get the physical page for the given offset
|
||||||
/// \arg offset The offset into the VMA
|
/// \arg offset The offset into the VMA
|
||||||
/// \arg phys [out] Receives the physical page address, if any
|
/// \arg phys [out] Receives the physical page address, if any
|
||||||
/// \returns True if there should be a page at the given offset
|
/// \returns True if there should be a page at the given offset
|
||||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) = 0;
|
virtual bool get_page(uintptr_t offset, uintptr_t &phys) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void on_no_handles() override;
|
virtual void on_no_handles() override;
|
||||||
bool can_resize(size_t size);
|
bool can_resize(size_t size);
|
||||||
|
|
||||||
size_t m_size;
|
size_t m_size;
|
||||||
vm_flags m_flags;
|
vm_flags m_flags;
|
||||||
kutil::vector<vm_space*> m_spaces;
|
kutil::vector<vm_space*> m_spaces;
|
||||||
|
|
||||||
// Initial static space for m_spaces - most areas will never grow
|
// Initial static space for m_spaces - most areas will never grow
|
||||||
// beyond this size, so avoid allocations
|
// beyond this size, so avoid allocations
|
||||||
static constexpr size_t static_size = 2;
|
static constexpr size_t static_size = 2;
|
||||||
vm_space *m_vector_static[static_size];
|
vm_space *m_vector_static[static_size];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// A shareable but non-allocatable memory area of contiguous physical
|
/// A shareable but non-allocatable memory area of contiguous physical
|
||||||
/// addresses (like mmio)
|
/// addresses (like mmio)
|
||||||
class vm_area_fixed :
|
class vm_area_fixed :
|
||||||
public vm_area
|
public vm_area
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg start Starting physical address of this area
|
/// \arg start Starting physical address of this area
|
||||||
/// \arg size Size of the physical memory area
|
/// \arg size Size of the physical memory area
|
||||||
/// \arg flags Flags for this memory area
|
/// \arg flags Flags for this memory area
|
||||||
vm_area_fixed(uintptr_t start, size_t size, vm_flags flags = vm_flags::none);
|
vm_area_fixed(uintptr_t start, size_t size, vm_flags flags = vm_flags::none);
|
||||||
virtual ~vm_area_fixed();
|
virtual ~vm_area_fixed();
|
||||||
|
|
||||||
virtual size_t resize(size_t size) override;
|
virtual size_t resize(size_t size) override;
|
||||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uintptr_t m_start;
|
uintptr_t m_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Area that allows open allocation
|
/// Area that allows open allocation
|
||||||
class vm_area_open :
|
class vm_area_open :
|
||||||
public vm_area
|
public vm_area
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg size Initial virtual size of the memory area
|
/// \arg size Initial virtual size of the memory area
|
||||||
/// \arg flags Flags for this memory area
|
/// \arg flags Flags for this memory area
|
||||||
vm_area_open(size_t size, vm_flags flags);
|
vm_area_open(size_t size, vm_flags flags);
|
||||||
virtual ~vm_area_open();
|
virtual ~vm_area_open();
|
||||||
|
|
||||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
page_tree *m_mapped;
|
page_tree *m_mapped;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Area that does not track its allocations and thus cannot be shared
|
/// Area that does not track its allocations and thus cannot be shared
|
||||||
class vm_area_untracked :
|
class vm_area_untracked :
|
||||||
public vm_area
|
public vm_area
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg size Initial virtual size of the memory area
|
/// \arg size Initial virtual size of the memory area
|
||||||
/// \arg flags Flags for this memory area
|
/// \arg flags Flags for this memory area
|
||||||
vm_area_untracked(size_t size, vm_flags flags);
|
vm_area_untracked(size_t size, vm_flags flags);
|
||||||
virtual ~vm_area_untracked();
|
virtual ~vm_area_untracked();
|
||||||
|
|
||||||
virtual bool add_to(vm_space *space) override;
|
virtual bool add_to(vm_space *space) override;
|
||||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Area split into standard-sized segments, separated by guard pages.
|
/// Area split into standard-sized segments, separated by guard pages.
|
||||||
/// Based on vm_area_untracked, can not be shared.
|
/// Based on vm_area_untracked, can not be shared.
|
||||||
class vm_area_guarded :
|
class vm_area_guarded :
|
||||||
public vm_area_untracked
|
public vm_area_untracked
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg start Initial address where this area is mapped
|
/// \arg start Initial address where this area is mapped
|
||||||
/// \arg sec_pages Pages in an individual section
|
/// \arg sec_pages Pages in an individual section
|
||||||
/// \arg size Initial virtual size of the memory area
|
/// \arg size Initial virtual size of the memory area
|
||||||
/// \arg flags Flags for this memory area
|
/// \arg flags Flags for this memory area
|
||||||
vm_area_guarded(
|
vm_area_guarded(
|
||||||
uintptr_t start,
|
uintptr_t start,
|
||||||
size_t sec_pages,
|
size_t sec_pages,
|
||||||
size_t size,
|
size_t size,
|
||||||
vm_flags flags);
|
vm_flags flags);
|
||||||
|
|
||||||
virtual ~vm_area_guarded();
|
virtual ~vm_area_guarded();
|
||||||
|
|
||||||
/// Get an available section in this area
|
/// Get an available section in this area
|
||||||
uintptr_t get_section();
|
uintptr_t get_section();
|
||||||
|
|
||||||
/// Return a section address to the available pool
|
/// Return a section address to the available pool
|
||||||
void return_section(uintptr_t addr);
|
void return_section(uintptr_t addr);
|
||||||
|
|
||||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kutil::vector<uintptr_t> m_cache;
|
kutil::vector<uintptr_t> m_cache;
|
||||||
uintptr_t m_start;
|
uintptr_t m_start;
|
||||||
size_t m_pages;
|
size_t m_pages;
|
||||||
uintptr_t m_next;
|
uintptr_t m_next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,21 +14,21 @@ constexpr size_t page_table::entry_sizes[4];
|
|||||||
|
|
||||||
|
|
||||||
constexpr page_table::flag table_flags =
|
constexpr page_table::flag table_flags =
|
||||||
page_table::flag::present |
|
page_table::flag::present |
|
||||||
page_table::flag::write;
|
page_table::flag::write;
|
||||||
|
|
||||||
|
|
||||||
page_table::iterator::iterator(uintptr_t virt, page_table *pml4) :
|
page_table::iterator::iterator(uintptr_t virt, page_table *pml4) :
|
||||||
m_table {pml4, 0, 0, 0}
|
m_table {pml4, 0, 0, 0}
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < D; ++i)
|
for (unsigned i = 0; i < D; ++i)
|
||||||
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
|
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
page_table::iterator::iterator(const page_table::iterator &o)
|
page_table::iterator::iterator(const page_table::iterator &o)
|
||||||
{
|
{
|
||||||
kutil::memcpy(&m_table, &o.m_table, sizeof(m_table));
|
kutil::memcpy(&m_table, &o.m_table, sizeof(m_table));
|
||||||
kutil::memcpy(&m_index, &o.m_index, sizeof(m_index));
|
kutil::memcpy(&m_index, &o.m_index, sizeof(m_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static level to_lv(unsigned i) { return static_cast<level>(i); }
|
inline static level to_lv(unsigned i) { return static_cast<level>(i); }
|
||||||
@@ -37,136 +37,136 @@ inline static unsigned to_un(level i) { return static_cast<unsigned>(i); }
|
|||||||
uintptr_t
|
uintptr_t
|
||||||
page_table::iterator::start(level l) const
|
page_table::iterator::start(level l) const
|
||||||
{
|
{
|
||||||
uintptr_t address = 0;
|
uintptr_t address = 0;
|
||||||
|
|
||||||
for (level i = level::pml4; i <= l; ++i)
|
for (level i = level::pml4; i <= l; ++i)
|
||||||
address |= static_cast<uintptr_t>(index(i)) << (12 + 9*(3-unsigned(i)));
|
address |= static_cast<uintptr_t>(index(i)) << (12 + 9*(3-unsigned(i)));
|
||||||
|
|
||||||
// canonicalize the address
|
// canonicalize the address
|
||||||
if (address & (1ull<<47))
|
if (address & (1ull<<47))
|
||||||
address |= (0xffffull<<48);
|
address |= (0xffffull<<48);
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t
|
uintptr_t
|
||||||
page_table::iterator::end(level l) const
|
page_table::iterator::end(level l) const
|
||||||
{
|
{
|
||||||
kassert(l != level::pml4, "Called end() with level::pml4");
|
kassert(l != level::pml4, "Called end() with level::pml4");
|
||||||
|
|
||||||
uintptr_t address = 0;
|
uintptr_t address = 0;
|
||||||
|
|
||||||
for (level i = level::pml4; i < l; ++i) {
|
for (level i = level::pml4; i < l; ++i) {
|
||||||
uintptr_t idx = index(i) +
|
uintptr_t idx = index(i) +
|
||||||
((i == l) ? 1 : 0);
|
((i == l) ? 1 : 0);
|
||||||
address |= idx << (12 + 9*(3-unsigned(i)));
|
address |= idx << (12 + 9*(3-unsigned(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// canonicalize the address
|
// canonicalize the address
|
||||||
if (address & (1ull<<47))
|
if (address & (1ull<<47))
|
||||||
address |= (0xffffull<<48);
|
address |= (0xffffull<<48);
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
level
|
level
|
||||||
page_table::iterator::align() const
|
page_table::iterator::align() const
|
||||||
{
|
{
|
||||||
for (int i = 4; i > 0; --i)
|
for (int i = 4; i > 0; --i)
|
||||||
if (m_index[i-1]) return level(i);
|
if (m_index[i-1]) return level(i);
|
||||||
return level::pml4;
|
return level::pml4;
|
||||||
}
|
}
|
||||||
|
|
||||||
page_table::level
|
page_table::level
|
||||||
page_table::iterator::depth() const
|
page_table::iterator::depth() const
|
||||||
{
|
{
|
||||||
for (level i = level::pml4; i < level::page; ++i)
|
for (level i = level::pml4; i < level::page; ++i)
|
||||||
if (!(entry(i) & 1)) return i;
|
if (!(entry(i) & 1)) return i;
|
||||||
return level::pt;
|
return level::pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
page_table::iterator::next(level l)
|
page_table::iterator::next(level l)
|
||||||
{
|
{
|
||||||
kassert(l != level::pml4, "Called next() with level::pml4");
|
kassert(l != level::pml4, "Called next() with level::pml4");
|
||||||
kassert(l <= level::page, "Called next() with too deep level");
|
kassert(l <= level::page, "Called next() with too deep level");
|
||||||
|
|
||||||
for (level i = l; i < level::page; ++i)
|
for (level i = l; i < level::page; ++i)
|
||||||
index(i) = 0;
|
index(i) = 0;
|
||||||
|
|
||||||
while (++index(--l) == 512) {
|
while (++index(--l) == 512) {
|
||||||
kassert(to_un(l), "iterator ran off the end of memory");
|
kassert(to_un(l), "iterator ran off the end of memory");
|
||||||
index(l) = 0;
|
index(l) = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
page_table::iterator::operator!=(const iterator &o) const
|
page_table::iterator::operator!=(const iterator &o) const
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < D; ++i)
|
for (unsigned i = 0; i < D; ++i)
|
||||||
if (o.m_index[i] != m_index[i])
|
if (o.m_index[i] != m_index[i])
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return o.m_table[0] != m_table[0];
|
return o.m_table[0] != m_table[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
page_table::iterator::check_table(level l) const
|
page_table::iterator::check_table(level l) const
|
||||||
{
|
{
|
||||||
// We're only dealing with D levels of paging, and
|
// We're only dealing with D levels of paging, and
|
||||||
// there must always be a PML4.
|
// there must always be a PML4.
|
||||||
if (l == level::pml4 || l > level::pt)
|
if (l == level::pml4 || l > level::pt)
|
||||||
return l == level::pml4;
|
return l == level::pml4;
|
||||||
|
|
||||||
uint64_t parent = entry(l - 1);
|
uint64_t parent = entry(l - 1);
|
||||||
if (parent & 1) {
|
if (parent & 1) {
|
||||||
table(l) = reinterpret_cast<page_table*>(page_offset | (parent & ~0xfffull));
|
table(l) = reinterpret_cast<page_table*>(page_offset | (parent & ~0xfffull));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
page_table::iterator::ensure_table(level l)
|
page_table::iterator::ensure_table(level l)
|
||||||
{
|
{
|
||||||
// We're only dealing with D levels of paging, and
|
// We're only dealing with D levels of paging, and
|
||||||
// there must always be a PML4.
|
// there must always be a PML4.
|
||||||
if (l == level::pml4 || l > level::pt) return;
|
if (l == level::pml4 || l > level::pt) return;
|
||||||
if (check_table(l)) return;
|
if (check_table(l)) return;
|
||||||
|
|
||||||
page_table *table = page_table::get_table_page();
|
page_table *table = page_table::get_table_page();
|
||||||
uintptr_t phys = reinterpret_cast<uintptr_t>(table) & ~page_offset;
|
uintptr_t phys = reinterpret_cast<uintptr_t>(table) & ~page_offset;
|
||||||
|
|
||||||
uint64_t &parent = entry(l - 1);
|
uint64_t &parent = entry(l - 1);
|
||||||
flag flags = table_flags;
|
flag flags = table_flags;
|
||||||
if (m_index[0] < memory::pml4e_kernel)
|
if (m_index[0] < memory::pml4e_kernel)
|
||||||
flags |= flag::user;
|
flags |= flag::user;
|
||||||
|
|
||||||
m_table[unsigned(l)] = table;
|
m_table[unsigned(l)] = table;
|
||||||
parent = (phys & ~0xfffull) | flags;
|
parent = (phys & ~0xfffull) | flags;
|
||||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||||
}
|
}
|
||||||
|
|
||||||
page_table *
|
page_table *
|
||||||
page_table::get(int i, uint16_t *flags) const
|
page_table::get(int i, uint16_t *flags) const
|
||||||
{
|
{
|
||||||
uint64_t entry = entries[i];
|
uint64_t entry = entries[i];
|
||||||
|
|
||||||
if ((entry & 0x1) == 0)
|
if ((entry & 0x1) == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (flags)
|
if (flags)
|
||||||
*flags = entry & 0xfffull;
|
*flags = entry & 0xfffull;
|
||||||
|
|
||||||
return reinterpret_cast<page_table *>((entry & ~0xfffull) + page_offset);
|
return reinterpret_cast<page_table *>((entry & ~0xfffull) + page_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
page_table::set(int i, page_table *p, uint16_t flags)
|
page_table::set(int i, page_table *p, uint16_t flags)
|
||||||
{
|
{
|
||||||
entries[i] =
|
entries[i] =
|
||||||
(reinterpret_cast<uint64_t>(p) - page_offset) |
|
(reinterpret_cast<uint64_t>(p) - page_offset) |
|
||||||
(flags & 0xfff);
|
(flags & 0xfff);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct free_page_header { free_page_header *next; };
|
struct free_page_header { free_page_header *next; };
|
||||||
@@ -174,105 +174,105 @@ struct free_page_header { free_page_header *next; };
|
|||||||
page_table *
|
page_table *
|
||||||
page_table::get_table_page()
|
page_table::get_table_page()
|
||||||
{
|
{
|
||||||
if (!s_cache_count)
|
if (!s_cache_count)
|
||||||
fill_table_page_cache();
|
fill_table_page_cache();
|
||||||
|
|
||||||
free_page_header *page = s_page_cache;
|
free_page_header *page = s_page_cache;
|
||||||
s_page_cache = s_page_cache->next;
|
s_page_cache = s_page_cache->next;
|
||||||
--s_cache_count;
|
--s_cache_count;
|
||||||
|
|
||||||
kutil::memset(page, 0, memory::frame_size);
|
kutil::memset(page, 0, memory::frame_size);
|
||||||
return reinterpret_cast<page_table*>(page);
|
return reinterpret_cast<page_table*>(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
page_table::free_table_page(page_table *pt)
|
page_table::free_table_page(page_table *pt)
|
||||||
{
|
{
|
||||||
free_page_header *page =
|
free_page_header *page =
|
||||||
reinterpret_cast<free_page_header*>(pt);
|
reinterpret_cast<free_page_header*>(pt);
|
||||||
page->next = s_page_cache;
|
page->next = s_page_cache;
|
||||||
s_page_cache = page->next;
|
s_page_cache = page->next;
|
||||||
++s_cache_count;
|
++s_cache_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
page_table::fill_table_page_cache()
|
page_table::fill_table_page_cache()
|
||||||
{
|
{
|
||||||
constexpr size_t min_pages = 16;
|
constexpr size_t min_pages = 16;
|
||||||
|
|
||||||
frame_allocator &fa = frame_allocator::get();
|
frame_allocator &fa = frame_allocator::get();
|
||||||
while (s_cache_count < min_pages) {
|
while (s_cache_count < min_pages) {
|
||||||
uintptr_t phys = 0;
|
uintptr_t phys = 0;
|
||||||
size_t n = fa.allocate(min_pages - s_cache_count, &phys);
|
size_t n = fa.allocate(min_pages - s_cache_count, &phys);
|
||||||
|
|
||||||
free_page_header *start =
|
free_page_header *start =
|
||||||
memory::to_virtual<free_page_header>(phys);
|
memory::to_virtual<free_page_header>(phys);
|
||||||
|
|
||||||
for (int i = 0; i < n - 1; ++i)
|
for (int i = 0; i < n - 1; ++i)
|
||||||
kutil::offset_pointer(start, i * memory::frame_size)
|
kutil::offset_pointer(start, i * memory::frame_size)
|
||||||
->next = kutil::offset_pointer(start, (i+1) * memory::frame_size);
|
->next = kutil::offset_pointer(start, (i+1) * memory::frame_size);
|
||||||
|
|
||||||
free_page_header *end =
|
free_page_header *end =
|
||||||
kutil::offset_pointer(start, (n-1) * memory::frame_size);
|
kutil::offset_pointer(start, (n-1) * memory::frame_size);
|
||||||
|
|
||||||
end->next = s_page_cache;
|
end->next = s_page_cache;
|
||||||
s_page_cache = start;
|
s_page_cache = start;
|
||||||
s_cache_count += n;
|
s_cache_count += n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
page_table::free(page_table::level l)
|
page_table::free(page_table::level l)
|
||||||
{
|
{
|
||||||
unsigned last = l == level::pml4
|
unsigned last = l == level::pml4
|
||||||
? memory::pml4e_kernel
|
? memory::pml4e_kernel
|
||||||
: memory::table_entries;
|
: memory::table_entries;
|
||||||
|
|
||||||
frame_allocator &fa = frame_allocator::get();
|
frame_allocator &fa = frame_allocator::get();
|
||||||
for (unsigned i = 0; i < last; ++i) {
|
for (unsigned i = 0; i < last; ++i) {
|
||||||
if (!is_present(i)) continue;
|
if (!is_present(i)) continue;
|
||||||
if (is_page(l, i)) {
|
if (is_page(l, i)) {
|
||||||
size_t count = memory::page_count(entry_sizes[unsigned(l)]);
|
size_t count = memory::page_count(entry_sizes[unsigned(l)]);
|
||||||
fa.free(entries[i] & ~0xfffull, count);
|
fa.free(entries[i] & ~0xfffull, count);
|
||||||
} else {
|
} else {
|
||||||
get(i)->free(l + 1);
|
get(i)->free(l + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free_table_page(this);
|
free_table_page(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
page_table::dump(page_table::level lvl, bool recurse)
|
page_table::dump(page_table::level lvl, bool recurse)
|
||||||
{
|
{
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
|
|
||||||
cons->printf("\nLevel %d page table @ %lx:\n", lvl, this);
|
cons->printf("\nLevel %d page table @ %lx:\n", lvl, this);
|
||||||
for (int i=0; i<memory::table_entries; ++i) {
|
for (int i=0; i<memory::table_entries; ++i) {
|
||||||
uint64_t ent = entries[i];
|
uint64_t ent = entries[i];
|
||||||
|
|
||||||
if ((ent & 0x1) == 0)
|
if ((ent & 0x1) == 0)
|
||||||
cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent);
|
cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent);
|
||||||
|
|
||||||
else if ((lvl == level::pdp || lvl == level::pd) && (ent & 0x80) == 0x80)
|
else if ((lvl == level::pdp || lvl == level::pd) && (ent & 0x80) == 0x80)
|
||||||
cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull);
|
cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||||
|
|
||||||
else if (lvl == level::pt)
|
else if (lvl == level::pt)
|
||||||
cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull);
|
cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||||
|
|
||||||
else
|
else
|
||||||
cons->printf(" %3d: %016lx -> Level %d table at %016lx\n",
|
cons->printf(" %3d: %016lx -> Level %d table at %016lx\n",
|
||||||
i, ent, deeper(lvl), (ent & ~0xfffull) + page_offset);
|
i, ent, deeper(lvl), (ent & ~0xfffull) + page_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lvl != level::pt && recurse) {
|
if (lvl != level::pt && recurse) {
|
||||||
for (int i=0; i<=memory::table_entries; ++i) {
|
for (int i=0; i<=memory::table_entries; ++i) {
|
||||||
if (is_large_page(lvl, i))
|
if (is_large_page(lvl, i))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
page_table *next = get(i);
|
page_table *next = get(i);
|
||||||
if (next)
|
if (next)
|
||||||
next->dump(deeper(lvl), true);
|
next->dump(deeper(lvl), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,184 +11,184 @@ struct free_page_header;
|
|||||||
/// Struct to allow easy accessing of a memory page being used as a page table.
|
/// Struct to allow easy accessing of a memory page being used as a page table.
|
||||||
struct page_table
|
struct page_table
|
||||||
{
|
{
|
||||||
/// Enum representing the table levels in 4-level paging
|
/// Enum representing the table levels in 4-level paging
|
||||||
enum class level : unsigned { pml4, pdp, pd, pt, page };
|
enum class level : unsigned { pml4, pdp, pd, pt, page };
|
||||||
|
|
||||||
/// Page entry flags
|
/// Page entry flags
|
||||||
enum class flag : uint64_t
|
enum class flag : uint64_t
|
||||||
{
|
{
|
||||||
none = 0x0000,
|
none = 0x0000,
|
||||||
present = 0x0001, /// Entry is present in the table
|
present = 0x0001, /// Entry is present in the table
|
||||||
write = 0x0002, /// Section may be written
|
write = 0x0002, /// Section may be written
|
||||||
user = 0x0004, /// User-accessible
|
user = 0x0004, /// User-accessible
|
||||||
pat0 = 0x0008, /// PAT selector bit 0
|
pat0 = 0x0008, /// PAT selector bit 0
|
||||||
pat1 = 0x0010, /// PAT selector bit 1
|
pat1 = 0x0010, /// PAT selector bit 1
|
||||||
accessed = 0x0020, /// Entry has been accessed
|
accessed = 0x0020, /// Entry has been accessed
|
||||||
dirty = 0x0040, /// Page has been written to
|
dirty = 0x0040, /// Page has been written to
|
||||||
page = 0x0080, /// Entry is a large page
|
page = 0x0080, /// Entry is a large page
|
||||||
pat2 = 0x0080, /// PAT selector bit 2 on PT entries
|
pat2 = 0x0080, /// PAT selector bit 2 on PT entries
|
||||||
global = 0x0100, /// Entry is not PCID-specific
|
global = 0x0100, /// Entry is not PCID-specific
|
||||||
pat2_lg = 0x1000, /// PAT selector bit 2 on large/huge pages
|
pat2_lg = 0x1000, /// PAT selector bit 2 on large/huge pages
|
||||||
|
|
||||||
wb = none,
|
wb = none,
|
||||||
wt = pat0,
|
wt = pat0,
|
||||||
uc_ = pat1,
|
uc_ = pat1,
|
||||||
uc = pat0 | pat1,
|
uc = pat0 | pat1,
|
||||||
wc = pat0 | pat1 | pat2,
|
wc = pat0 | pat1 | pat2,
|
||||||
wc_lg = pat0 | pat1 | pat2_lg,
|
wc_lg = pat0 | pat1 | pat2_lg,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper for getting the next level value
|
/// Helper for getting the next level value
|
||||||
inline static level deeper(level l) {
|
inline static level deeper(level l) {
|
||||||
return static_cast<level>(static_cast<unsigned>(l) + 1);
|
return static_cast<level>(static_cast<unsigned>(l) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr size_t entry_sizes[] = {
|
static constexpr size_t entry_sizes[] = {
|
||||||
0x8000000000, // PML4 entry: 512 GiB
|
0x8000000000, // PML4 entry: 512 GiB
|
||||||
0x40000000, // PDP entry: 1 GiB
|
0x40000000, // PDP entry: 1 GiB
|
||||||
0x200000, // PD entry: 2 MiB
|
0x200000, // PD entry: 2 MiB
|
||||||
0x1000}; // PT entry: 4 KiB
|
0x1000}; // PT entry: 4 KiB
|
||||||
|
|
||||||
/// Iterator over page table entries.
|
/// Iterator over page table entries.
|
||||||
class iterator
|
class iterator
|
||||||
{
|
{
|
||||||
/// The number of levels
|
/// The number of levels
|
||||||
static constexpr unsigned D = 4;
|
static constexpr unsigned D = 4;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg virt Virtual address this iterator is starting at
|
/// \arg virt Virtual address this iterator is starting at
|
||||||
/// \arg pml4 Root of the page tables to iterate
|
/// \arg pml4 Root of the page tables to iterate
|
||||||
iterator(uintptr_t virt, page_table *pml4);
|
iterator(uintptr_t virt, page_table *pml4);
|
||||||
|
|
||||||
/// Copy constructor.
|
/// Copy constructor.
|
||||||
iterator(const iterator &o);
|
iterator(const iterator &o);
|
||||||
|
|
||||||
/// Get the starting address of pages mapped by the current table
|
/// Get the starting address of pages mapped by the current table
|
||||||
/// of level l.
|
/// of level l.
|
||||||
uintptr_t start(level l) const;
|
uintptr_t start(level l) const;
|
||||||
|
|
||||||
/// Get the ending address of pages mapped by the current table
|
/// Get the ending address of pages mapped by the current table
|
||||||
/// of level l.
|
/// of level l.
|
||||||
uintptr_t end(level l) const;
|
uintptr_t end(level l) const;
|
||||||
|
|
||||||
/// Get the widest table type the current address is aligned to
|
/// Get the widest table type the current address is aligned to
|
||||||
level align() const;
|
level align() const;
|
||||||
|
|
||||||
/// Get the current virtual address
|
/// Get the current virtual address
|
||||||
inline uintptr_t vaddress() const { return start(level::pt); }
|
inline uintptr_t vaddress() const { return start(level::pt); }
|
||||||
|
|
||||||
/// Get the nth page table of the current address
|
/// Get the nth page table of the current address
|
||||||
inline page_table *& table(level l) const { return m_table[unsigned(l)]; }
|
inline page_table *& table(level l) const { return m_table[unsigned(l)]; }
|
||||||
|
|
||||||
/// Get the index in the nth page table of the current address
|
/// Get the index in the nth page table of the current address
|
||||||
inline uint16_t & index(level l) { return m_index[unsigned(l)]; }
|
inline uint16_t & index(level l) { return m_index[unsigned(l)]; }
|
||||||
|
|
||||||
/// Get the index in the nth page table of the current address
|
/// Get the index in the nth page table of the current address
|
||||||
inline uint16_t index(level l) const { return m_index[unsigned(l)]; }
|
inline uint16_t index(level l) const { return m_index[unsigned(l)]; }
|
||||||
|
|
||||||
/// Get the current table entry of the table at the given level.
|
/// Get the current table entry of the table at the given level.
|
||||||
inline uint64_t entry(level l) const {
|
inline uint64_t entry(level l) const {
|
||||||
for (unsigned i = 1; i <= unsigned(l); ++i)
|
for (unsigned i = 1; i <= unsigned(l); ++i)
|
||||||
if (!check_table(level(i))) return 0;
|
if (!check_table(level(i))) return 0;
|
||||||
return table(l)->entries[index(l)];
|
return table(l)->entries[index(l)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a *non-const* reference to the current table entry of
|
/// Get a *non-const* reference to the current table entry of
|
||||||
/// the table at the given level.
|
/// the table at the given level.
|
||||||
inline uint64_t & entry(level l) {
|
inline uint64_t & entry(level l) {
|
||||||
for (unsigned i = 1; i <= unsigned(l); ++i) ensure_table(level(i));
|
for (unsigned i = 1; i <= unsigned(l); ++i) ensure_table(level(i));
|
||||||
return table(l)->entries[index(l)];
|
return table(l)->entries[index(l)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the depth of tables that actually exist for the current address
|
/// Get the depth of tables that actually exist for the current address
|
||||||
level depth() const;
|
level depth() const;
|
||||||
|
|
||||||
/// Increment iteration to the next entry aligned to the given level
|
/// Increment iteration to the next entry aligned to the given level
|
||||||
void next(level l);
|
void next(level l);
|
||||||
|
|
||||||
/// Increment iteration to the next entry at the deepest level
|
/// Increment iteration to the next entry at the deepest level
|
||||||
inline void increment() { next(level::page); }
|
inline void increment() { next(level::page); }
|
||||||
|
|
||||||
inline uint64_t & operator*() { return entry(level::pt); }
|
inline uint64_t & operator*() { return entry(level::pt); }
|
||||||
inline iterator & operator++() { increment(); return *this; }
|
inline iterator & operator++() { increment(); return *this; }
|
||||||
inline iterator operator++(int) { iterator old {*this}; increment(); return old; }
|
inline iterator operator++(int) { iterator old {*this}; increment(); return old; }
|
||||||
|
|
||||||
bool operator!=(const iterator &o) const;
|
bool operator!=(const iterator &o) const;
|
||||||
|
|
||||||
bool check_table(level l) const;
|
bool check_table(level l) const;
|
||||||
void ensure_table(level l);
|
void ensure_table(level l);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The table array is mutable because we might update it with existing
|
// The table array is mutable because we might update it with existing
|
||||||
// tables; a 'view switch'. therefore, be careful not to modify table
|
// tables; a 'view switch'. therefore, be careful not to modify table
|
||||||
// contents in const functions.
|
// contents in const functions.
|
||||||
mutable page_table *m_table[D];
|
mutable page_table *m_table[D];
|
||||||
uint16_t m_index[D];
|
uint16_t m_index[D];
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Allocate a page for a page table, or pull one from the cache
|
/// Allocate a page for a page table, or pull one from the cache
|
||||||
/// \returns An empty page, mapped in the linear offset area
|
/// \returns An empty page, mapped in the linear offset area
|
||||||
static page_table * get_table_page();
|
static page_table * get_table_page();
|
||||||
|
|
||||||
/// Return a page table's page to the page cache.
|
/// Return a page table's page to the page cache.
|
||||||
/// \arg pt The page to be returned
|
/// \arg pt The page to be returned
|
||||||
static void free_table_page(page_table *pt);
|
static void free_table_page(page_table *pt);
|
||||||
|
|
||||||
// Ensure the page table page cache has a minimum number of pages
|
// Ensure the page table page cache has a minimum number of pages
|
||||||
// in it.
|
// in it.
|
||||||
static void fill_table_page_cache();
|
static void fill_table_page_cache();
|
||||||
|
|
||||||
static free_page_header *s_page_cache; ///< Cache of free pages to use for tables
|
static free_page_header *s_page_cache; ///< Cache of free pages to use for tables
|
||||||
static size_t s_cache_count; ///< Number of pages in s_page_cache
|
static size_t s_cache_count; ///< Number of pages in s_page_cache
|
||||||
|
|
||||||
/// Get an entry in the page table as a page_table pointer
|
/// Get an entry in the page table as a page_table pointer
|
||||||
/// \arg i Index of the entry in this page table
|
/// \arg i Index of the entry in this page table
|
||||||
/// \arg flags [out] If set, this will receive the entry's flags
|
/// \arg flags [out] If set, this will receive the entry's flags
|
||||||
/// \returns The corresponding entry, offset into page-offset memory
|
/// \returns The corresponding entry, offset into page-offset memory
|
||||||
page_table * get(int i, uint16_t *flags = nullptr) const;
|
page_table * get(int i, uint16_t *flags = nullptr) const;
|
||||||
|
|
||||||
/// Set an entry in the page table as a page_table pointer
|
/// Set an entry in the page table as a page_table pointer
|
||||||
/// \arg i Index of the entry in this page table
|
/// \arg i Index of the entry in this page table
|
||||||
/// \arg flags The flags for the entry
|
/// \arg flags The flags for the entry
|
||||||
void set(int i, page_table *p, uint16_t flags);
|
void set(int i, page_table *p, uint16_t flags);
|
||||||
|
|
||||||
/// Check if the given entry represents a large or huge page
|
/// Check if the given entry represents a large or huge page
|
||||||
inline bool is_large_page(level l, int i) const {
|
inline bool is_large_page(level l, int i) const {
|
||||||
return (l == level::pdp || l == level::pd) && (entries[i] & 0x80) == 0x80;
|
return (l == level::pdp || l == level::pd) && (entries[i] & 0x80) == 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the given entry is marked as present in the table
|
/// Check if the given entry is marked as present in the table
|
||||||
inline bool is_present(int i) const { return (entries[i] & 0x1) == 0x1; }
|
inline bool is_present(int i) const { return (entries[i] & 0x1) == 0x1; }
|
||||||
|
|
||||||
/// Check if the given entry represents a page (of any size)
|
/// Check if the given entry represents a page (of any size)
|
||||||
inline bool is_page(level l, int i) const { return (l == level::pt) || is_large_page(l, i); }
|
inline bool is_page(level l, int i) const { return (l == level::pt) || is_large_page(l, i); }
|
||||||
|
|
||||||
/// Free this page table and all resources it references
|
/// Free this page table and all resources it references
|
||||||
/// \arg l The level of this page table
|
/// \arg l The level of this page table
|
||||||
void free(level l);
|
void free(level l);
|
||||||
|
|
||||||
/// Print this table to the debug console.
|
/// Print this table to the debug console.
|
||||||
void dump(level lvl = level::pml4, bool recurse = true);
|
void dump(level lvl = level::pml4, bool recurse = true);
|
||||||
|
|
||||||
uint64_t entries[memory::table_entries];
|
uint64_t entries[memory::table_entries];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
inline page_table::level operator+(page_table::level a, unsigned b) {
|
inline page_table::level operator+(page_table::level a, unsigned b) {
|
||||||
return static_cast<page_table::level>(static_cast<unsigned>(a) + b);
|
return static_cast<page_table::level>(static_cast<unsigned>(a) + b);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline page_table::level operator-(page_table::level a, unsigned b) {
|
inline page_table::level operator-(page_table::level a, unsigned b) {
|
||||||
return static_cast<page_table::level>(static_cast<unsigned>(a) - b);
|
return static_cast<page_table::level>(static_cast<unsigned>(a) - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator>(page_table::level a, page_table::level b) {
|
inline bool operator>(page_table::level a, page_table::level b) {
|
||||||
return static_cast<unsigned>(a) > static_cast<unsigned>(b);
|
return static_cast<unsigned>(a) > static_cast<unsigned>(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator<(page_table::level a, page_table::level b) {
|
inline bool operator<(page_table::level a, page_table::level b) {
|
||||||
return static_cast<unsigned>(a) < static_cast<unsigned>(b);
|
return static_cast<unsigned>(a) < static_cast<unsigned>(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline page_table::level& operator++(page_table::level& l) { l = l + 1; return l; }
|
inline page_table::level& operator++(page_table::level& l) { l = l + 1; return l; }
|
||||||
|
|||||||
@@ -19,134 +19,134 @@ static constexpr unsigned max_level = 5;
|
|||||||
static constexpr unsigned bits_per_level = 6;
|
static constexpr unsigned bits_per_level = 6;
|
||||||
|
|
||||||
inline uint64_t to_word(uint64_t base, uint64_t level, uint64_t flags = 0) {
|
inline uint64_t to_word(uint64_t base, uint64_t level, uint64_t flags = 0) {
|
||||||
// Clear out the non-appropriate bits for this level
|
// Clear out the non-appropriate bits for this level
|
||||||
base &= (~0x3full << (level*bits_per_level));
|
base &= (~0x3full << (level*bits_per_level));
|
||||||
|
|
||||||
return
|
return
|
||||||
(base & 0x3ffffffffff) |
|
(base & 0x3ffffffffff) |
|
||||||
((level & 0x7) << 42) |
|
((level & 0x7) << 42) |
|
||||||
((flags & 0x7ffff) << 45);
|
((flags & 0x7ffff) << 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t to_base(uint64_t word) {
|
inline uint64_t to_base(uint64_t word) {
|
||||||
return word & 0x3ffffffffff;
|
return word & 0x3ffffffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t to_level(uint64_t word) {
|
inline uint64_t to_level(uint64_t word) {
|
||||||
return (word >> 42) & 0x3f;
|
return (word >> 42) & 0x3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t to_flags(uint64_t word) {
|
inline uint64_t to_flags(uint64_t word) {
|
||||||
return (word >> 45);
|
return (word >> 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool contains(uint64_t page_off, uint64_t word, uint8_t &index) {
|
inline bool contains(uint64_t page_off, uint64_t word, uint8_t &index) {
|
||||||
uint64_t base = to_base(word);
|
uint64_t base = to_base(word);
|
||||||
uint64_t bits = to_level(word) * bits_per_level;
|
uint64_t bits = to_level(word) * bits_per_level;
|
||||||
index = (page_off >> bits) & 0x3f;
|
index = (page_off >> bits) & 0x3f;
|
||||||
return (page_off & (~0x3full << bits)) == base;
|
return (page_off & (~0x3full << bits)) == base;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t index_for(uint64_t page_off, uint8_t level) {
|
inline uint64_t index_for(uint64_t page_off, uint8_t level) {
|
||||||
return (page_off >> (level*bits_per_level)) & 0x3f;
|
return (page_off >> (level*bits_per_level)) & 0x3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
page_tree::page_tree(uint64_t base, uint8_t level) :
|
page_tree::page_tree(uint64_t base, uint8_t level) :
|
||||||
m_base {to_word(base, level)}
|
m_base {to_word(base, level)}
|
||||||
{
|
{
|
||||||
kutil::memset(m_entries, 0, sizeof(m_entries));
|
kutil::memset(m_entries, 0, sizeof(m_entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page)
|
page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page)
|
||||||
{
|
{
|
||||||
uint64_t page_off = offset >> 12; // change to pagewise offset
|
uint64_t page_off = offset >> 12; // change to pagewise offset
|
||||||
page_tree const *node = root;
|
page_tree const *node = root;
|
||||||
while (node) {
|
while (node) {
|
||||||
uint8_t level = to_level(node->m_base);
|
uint8_t level = to_level(node->m_base);
|
||||||
uint8_t index = 0;
|
uint8_t index = 0;
|
||||||
if (!contains(page_off, node->m_base, index))
|
if (!contains(page_off, node->m_base, index))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!level) {
|
if (!level) {
|
||||||
uintptr_t entry = node->m_entries[index].entry;
|
uintptr_t entry = node->m_entries[index].entry;
|
||||||
page = entry & ~1ull; // bit 0 marks 'present'
|
page = entry & ~1ull; // bit 0 marks 'present'
|
||||||
return (entry & 1);
|
return (entry & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node->m_entries[index].child;
|
node = node->m_entries[index].child;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page)
|
page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page)
|
||||||
{
|
{
|
||||||
uint64_t page_off = offset >> 12; // change to pagewise offset
|
uint64_t page_off = offset >> 12; // change to pagewise offset
|
||||||
page_tree *level0 = nullptr;
|
page_tree *level0 = nullptr;
|
||||||
|
|
||||||
if (!root) {
|
if (!root) {
|
||||||
// There's no root yet, just make a level0 and make it
|
// There's no root yet, just make a level0 and make it
|
||||||
// the root.
|
// the root.
|
||||||
level0 = new page_tree(page_off, 0);
|
level0 = new page_tree(page_off, 0);
|
||||||
root = level0;
|
root = level0;
|
||||||
} else {
|
} else {
|
||||||
// Find or insert an existing level0
|
// Find or insert an existing level0
|
||||||
page_tree **parent = &root;
|
page_tree **parent = &root;
|
||||||
page_tree *node = root;
|
page_tree *node = root;
|
||||||
uint8_t parent_level = max_level + 1;
|
uint8_t parent_level = max_level + 1;
|
||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
uint8_t level = to_level(node->m_base);
|
uint8_t level = to_level(node->m_base);
|
||||||
uint8_t index = 0;
|
uint8_t index = 0;
|
||||||
if (!contains(page_off, node->m_base, index)) {
|
if (!contains(page_off, node->m_base, index)) {
|
||||||
// We found a valid parent but the slot where this node should
|
// We found a valid parent but the slot where this node should
|
||||||
// go contains another node. Insert an intermediate parent of
|
// go contains another node. Insert an intermediate parent of
|
||||||
// this node and a new level0 into the parent.
|
// this node and a new level0 into the parent.
|
||||||
uint64_t other = to_base(node->m_base);
|
uint64_t other = to_base(node->m_base);
|
||||||
uint8_t lcl = parent_level;
|
uint8_t lcl = parent_level;
|
||||||
while (index_for(page_off, lcl) == index_for(other, lcl))
|
while (index_for(page_off, lcl) == index_for(other, lcl))
|
||||||
--lcl;
|
--lcl;
|
||||||
|
|
||||||
page_tree *inter = new page_tree(page_off, lcl);
|
page_tree *inter = new page_tree(page_off, lcl);
|
||||||
inter->m_entries[index_for(other, lcl)].child = node;
|
inter->m_entries[index_for(other, lcl)].child = node;
|
||||||
*parent = inter;
|
*parent = inter;
|
||||||
|
|
||||||
level0 = new page_tree(page_off, 0);
|
level0 = new page_tree(page_off, 0);
|
||||||
inter->m_entries[index_for(page_off, lcl)].child = level0;
|
inter->m_entries[index_for(page_off, lcl)].child = level0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!level) {
|
if (!level) {
|
||||||
level0 = node;
|
level0 = node;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent = &node->m_entries[index].child;
|
parent = &node->m_entries[index].child;
|
||||||
node = *parent;
|
node = *parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
kassert( node || parent, "Both node and parent were null in find_or_add");
|
kassert( node || parent, "Both node and parent were null in find_or_add");
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
// We found a parent with an empty spot where this node should
|
// We found a parent with an empty spot where this node should
|
||||||
// be. Insert a new level0 there.
|
// be. Insert a new level0 there.
|
||||||
level0 = new page_tree(page_off, 0);
|
level0 = new page_tree(page_off, 0);
|
||||||
*parent = level0;
|
*parent = level0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kassert(level0, "Got through find_or_add without a level0");
|
kassert(level0, "Got through find_or_add without a level0");
|
||||||
uint8_t index = index_for(page_off, 0);
|
uint8_t index = index_for(page_off, 0);
|
||||||
uint64_t &ent = level0->m_entries[index].entry;
|
uint64_t &ent = level0->m_entries[index].entry;
|
||||||
if (!(ent & 1)) {
|
if (!(ent & 1)) {
|
||||||
// No entry for this page exists, so make one
|
// No entry for this page exists, so make one
|
||||||
if (!frame_allocator::get().allocate(1, &ent))
|
if (!frame_allocator::get().allocate(1, &ent))
|
||||||
return false;
|
return false;
|
||||||
ent |= 1;
|
ent |= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
page = ent & ~0xfffull;
|
page = ent & ~0xfffull;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,32 +8,32 @@
|
|||||||
class page_tree
|
class page_tree
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Get the physical address of the page at the given offset.
|
/// Get the physical address of the page at the given offset.
|
||||||
/// \arg root The root node of the tree
|
/// \arg root The root node of the tree
|
||||||
/// \arg offset Offset into the VMA, in bytes
|
/// \arg offset Offset into the VMA, in bytes
|
||||||
/// \arg page [out] Receives the page physical address, if found
|
/// \arg page [out] Receives the page physical address, if found
|
||||||
/// \returns True if a page was found
|
/// \returns True if a page was found
|
||||||
static bool find(const page_tree *root, uint64_t offset, uintptr_t &page);
|
static bool find(const page_tree *root, uint64_t offset, uintptr_t &page);
|
||||||
|
|
||||||
/// Get the physical address of the page at the given offset. If one does
|
/// Get the physical address of the page at the given offset. If one does
|
||||||
/// not exist yet, allocate a page, insert it, and return that.
|
/// not exist yet, allocate a page, insert it, and return that.
|
||||||
/// \arg root [inout] The root node of the tree. This pointer may be updated.
|
/// \arg root [inout] The root node of the tree. This pointer may be updated.
|
||||||
/// \arg offset Offset into the VMA, in bytes
|
/// \arg offset Offset into the VMA, in bytes
|
||||||
/// \arg page [out] Receives the page physical address, if found
|
/// \arg page [out] Receives the page physical address, if found
|
||||||
/// \returns True if a page was found
|
/// \returns True if a page was found
|
||||||
static bool find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page);
|
static bool find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
page_tree(uint64_t base, uint8_t level);
|
page_tree(uint64_t base, uint8_t level);
|
||||||
|
|
||||||
/// Stores the page offset of the start of this node's pages in bits 0:41
|
/// Stores the page offset of the start of this node's pages in bits 0:41
|
||||||
/// and the depth of tree this node represents in bits 42:44 (0-7)
|
/// and the depth of tree this node represents in bits 42:44 (0-7)
|
||||||
uint64_t m_base;
|
uint64_t m_base;
|
||||||
|
|
||||||
/// For a level 0 node, the entries area all physical page addresses.
|
/// For a level 0 node, the entries area all physical page addresses.
|
||||||
/// Other nodes contain pointers to child tree nodes.
|
/// Other nodes contain pointers to child tree nodes.
|
||||||
union {
|
union {
|
||||||
uintptr_t entry;
|
uintptr_t entry;
|
||||||
page_tree *child;
|
page_tree *child;
|
||||||
} m_entries[64];
|
} m_entries[64];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ class symbol_table;
|
|||||||
|
|
||||||
struct frame
|
struct frame
|
||||||
{
|
{
|
||||||
frame *prev;
|
frame *prev;
|
||||||
uintptr_t return_addr;
|
uintptr_t return_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void print_header(serial_port &out, const char *message);
|
void print_header(serial_port &out, const char *message);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ void panic_handler(
|
|||||||
panic::symbol_table syms(symbol_data);
|
panic::symbol_table syms(symbol_data);
|
||||||
|
|
||||||
panic::frame const *fp = nullptr;
|
panic::frame const *fp = nullptr;
|
||||||
asm ( "mov %%rbp, %0" : "=r" (fp) );
|
asm ( "mov %%rbp, %0" : "=r" (fp) );
|
||||||
|
|
||||||
print_header(com1, message);
|
print_header(com1, message);
|
||||||
print_callstack(com1, syms, fp);
|
print_callstack(com1, syms, fp);
|
||||||
|
|||||||
@@ -21,22 +21,22 @@ inline void outb(uint16_t port, uint8_t val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t inb(uint16_t port) {
|
inline uint8_t inb(uint16_t port) {
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
asm ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
asm ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
serial_port::serial_port(uint16_t port) :
|
serial_port::serial_port(uint16_t port) :
|
||||||
m_port(port)
|
m_port(port)
|
||||||
{
|
{
|
||||||
outb(port + IER, 0x00); // Disable all interrupts
|
outb(port + IER, 0x00); // Disable all interrupts
|
||||||
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||||
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
||||||
outb(port + DLH, 0x00); // Divisor high byte
|
outb(port + DLH, 0x00); // Divisor high byte
|
||||||
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
||||||
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||||
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; }
|
inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; }
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ namespace panic {
|
|||||||
class serial_port
|
class serial_port
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg port The IO address of the serial port
|
/// \arg port The IO address of the serial port
|
||||||
serial_port(uint16_t port);
|
serial_port(uint16_t port);
|
||||||
|
|
||||||
void write(const char *s);
|
void write(const char *s);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t m_port;
|
uint16_t m_port;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint16_t COM1 = 0x03f8;
|
constexpr uint16_t COM1 = 0x03f8;
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ symbol_table::symbol_table(const void *data) :
|
|||||||
const char *
|
const char *
|
||||||
symbol_table::find_symbol(uintptr_t addr) const
|
symbol_table::find_symbol(uintptr_t addr) const
|
||||||
{
|
{
|
||||||
// TODO: binary search
|
// TODO: binary search
|
||||||
for (auto &e : m_entries) {
|
for (auto &e : m_entries) {
|
||||||
if (addr >= e.address && addr < e.address + e.size)
|
if (addr >= e.address && addr < e.address + e.size)
|
||||||
return reinterpret_cast<const char*>(m_data) + e.name;
|
return reinterpret_cast<const char*>(m_data) + e.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace panic
|
} // namespace panic
|
||||||
|
|||||||
@@ -8,22 +8,22 @@ namespace panic {
|
|||||||
class symbol_table
|
class symbol_table
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg data Pointer to the start of the symbol_table data.
|
/// \arg data Pointer to the start of the symbol_table data.
|
||||||
symbol_table(const void *data);
|
symbol_table(const void *data);
|
||||||
|
|
||||||
/// Find the name of the symbol at the given address
|
/// Find the name of the symbol at the given address
|
||||||
/// \args addr Address to search for
|
/// \args addr Address to search for
|
||||||
/// \returns Name of the symbol if found, or null
|
/// \returns Name of the symbol if found, or null
|
||||||
const char * find_symbol(uintptr_t addr) const;
|
const char * find_symbol(uintptr_t addr) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct entry
|
struct entry
|
||||||
{
|
{
|
||||||
uintptr_t address;
|
uintptr_t address;
|
||||||
size_t size;
|
size_t size;
|
||||||
uintptr_t name;
|
uintptr_t name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const void *m_data;
|
const void *m_data;
|
||||||
counted<entry const> m_entries;
|
counted<entry const> m_entries;
|
||||||
|
|||||||
@@ -6,174 +6,174 @@
|
|||||||
|
|
||||||
struct pci_cap_msi
|
struct pci_cap_msi
|
||||||
{
|
{
|
||||||
pci_cap::type id;
|
pci_cap::type id;
|
||||||
uint8_t next;
|
uint8_t next;
|
||||||
uint16_t control;
|
uint16_t control;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct pci_cap_msi32
|
struct pci_cap_msi32
|
||||||
{
|
{
|
||||||
pci_cap::type id;
|
pci_cap::type id;
|
||||||
uint8_t next;
|
uint8_t next;
|
||||||
uint16_t control;
|
uint16_t control;
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
uint16_t data;
|
uint16_t data;
|
||||||
uint16_t reserved;
|
uint16_t reserved;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
uint32_t pending;
|
uint32_t pending;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
struct pci_cap_msi64
|
struct pci_cap_msi64
|
||||||
{
|
{
|
||||||
pci_cap::type id;
|
pci_cap::type id;
|
||||||
uint8_t next;
|
uint8_t next;
|
||||||
uint16_t control;
|
uint16_t control;
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
uint16_t data;
|
uint16_t data;
|
||||||
uint16_t reserved;
|
uint16_t reserved;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
uint32_t pending;
|
uint32_t pending;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
void dump_msi(pci_cap_msi *cap)
|
void dump_msi(pci_cap_msi *cap)
|
||||||
{
|
{
|
||||||
auto cons = console::get();
|
auto cons = console::get();
|
||||||
cons->printf("MSI Cap:\n");
|
cons->printf("MSI Cap:\n");
|
||||||
cons->printf(" id: %02x\n", cap->id);
|
cons->printf(" id: %02x\n", cap->id);
|
||||||
cons->printf(" next: %02x\n", cap->next);
|
cons->printf(" next: %02x\n", cap->next);
|
||||||
cons->printf("control: %04x\n", cap->control);
|
cons->printf("control: %04x\n", cap->control);
|
||||||
if (cap->control & 0x0080) {
|
if (cap->control & 0x0080) {
|
||||||
pci_cap_msi64 *cap64 = reinterpret_cast<pci_cap_msi64 *>(cap);
|
pci_cap_msi64 *cap64 = reinterpret_cast<pci_cap_msi64 *>(cap);
|
||||||
cons->printf("address: %016x\n", cap64->address);
|
cons->printf("address: %016x\n", cap64->address);
|
||||||
cons->printf(" data: %04x\n", cap64->data);
|
cons->printf(" data: %04x\n", cap64->data);
|
||||||
if (cap->control & 0x100) {
|
if (cap->control & 0x100) {
|
||||||
cons->printf(" mask: %08x\n", cap64->mask);
|
cons->printf(" mask: %08x\n", cap64->mask);
|
||||||
cons->printf("pending: %08x\n", cap64->pending);
|
cons->printf("pending: %08x\n", cap64->pending);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pci_cap_msi32 *cap32 = reinterpret_cast<pci_cap_msi32 *>(cap);
|
pci_cap_msi32 *cap32 = reinterpret_cast<pci_cap_msi32 *>(cap);
|
||||||
cons->printf("address: %08x\n", cap32->address);
|
cons->printf("address: %08x\n", cap32->address);
|
||||||
cons->printf(" data: %04x\n", cap32->data);
|
cons->printf(" data: %04x\n", cap32->data);
|
||||||
if (cap->control & 0x100) {
|
if (cap->control & 0x100) {
|
||||||
cons->printf(" mask: %08x\n", cap32->mask);
|
cons->printf(" mask: %08x\n", cap32->mask);
|
||||||
cons->printf("pending: %08x\n", cap32->pending);
|
cons->printf("pending: %08x\n", cap32->pending);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cons->putc('\n');
|
cons->putc('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
pci_device::pci_device() :
|
pci_device::pci_device() :
|
||||||
m_base(nullptr),
|
m_base(nullptr),
|
||||||
m_bus_addr(0),
|
m_bus_addr(0),
|
||||||
m_vendor(0),
|
m_vendor(0),
|
||||||
m_device(0),
|
m_device(0),
|
||||||
m_class(0),
|
m_class(0),
|
||||||
m_subclass(0),
|
m_subclass(0),
|
||||||
m_progif(0),
|
m_progif(0),
|
||||||
m_revision(0),
|
m_revision(0),
|
||||||
m_irq(isr::isrIgnoreF),
|
m_irq(isr::isrIgnoreF),
|
||||||
m_header_type(0)
|
m_header_type(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
|
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
|
||||||
m_base(group.base_for(bus, device, func)),
|
m_base(group.base_for(bus, device, func)),
|
||||||
m_msi(nullptr),
|
m_msi(nullptr),
|
||||||
m_bus_addr(bus_addr(bus, device, func)),
|
m_bus_addr(bus_addr(bus, device, func)),
|
||||||
m_irq(isr::isrIgnoreF)
|
m_irq(isr::isrIgnoreF)
|
||||||
{
|
{
|
||||||
m_vendor = m_base[0] & 0xffff;
|
m_vendor = m_base[0] & 0xffff;
|
||||||
m_device = (m_base[0] >> 16) & 0xffff;
|
m_device = (m_base[0] >> 16) & 0xffff;
|
||||||
|
|
||||||
m_revision = m_base[2] & 0xff;
|
m_revision = m_base[2] & 0xff;
|
||||||
m_progif = (m_base[2] >> 8) & 0xff;
|
m_progif = (m_base[2] >> 8) & 0xff;
|
||||||
m_subclass = (m_base[2] >> 16) & 0xff;
|
m_subclass = (m_base[2] >> 16) & 0xff;
|
||||||
m_class = (m_base[2] >> 24) & 0xff;
|
m_class = (m_base[2] >> 24) & 0xff;
|
||||||
|
|
||||||
m_header_type = (m_base[3] >> 16) & 0x7f;
|
m_header_type = (m_base[3] >> 16) & 0x7f;
|
||||||
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
|
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
|
||||||
|
|
||||||
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
|
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
|
||||||
*command |= 0x400; // Mask old INTx style interrupts
|
*command |= 0x400; // Mask old INTx style interrupts
|
||||||
|
|
||||||
uint16_t *status = command + 1;
|
uint16_t *status = command + 1;
|
||||||
|
|
||||||
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
|
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
|
||||||
bus, device, func, m_class, m_subclass, m_progif, m_vendor, m_device);
|
bus, device, func, m_class, m_subclass, m_progif, m_vendor, m_device);
|
||||||
|
|
||||||
if (*status & 0x0010) {
|
if (*status & 0x0010) {
|
||||||
// Walk the extended capabilities list
|
// Walk the extended capabilities list
|
||||||
uint8_t next = m_base[13] & 0xff;
|
uint8_t next = m_base[13] & 0xff;
|
||||||
while (next) {
|
while (next) {
|
||||||
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
|
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
|
||||||
next = cap->next;
|
next = cap->next;
|
||||||
log::debug(logs::device, " - found PCI cap type %02x", cap->id);
|
log::debug(logs::device, " - found PCI cap type %02x", cap->id);
|
||||||
|
|
||||||
if (cap->id == pci_cap::type::msi) {
|
if (cap->id == pci_cap::type::msi) {
|
||||||
m_msi = cap;
|
m_msi = cap;
|
||||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
|
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
|
||||||
mcap->control &= ~0x70; // at most 1 vector allocated
|
mcap->control &= ~0x70; // at most 1 vector allocated
|
||||||
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
|
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
pci_device::get_bar(unsigned i)
|
pci_device::get_bar(unsigned i)
|
||||||
{
|
{
|
||||||
if (m_header_type == 0) {
|
if (m_header_type == 0) {
|
||||||
kassert(i < 6, "Requested BAR >5 for PCI device");
|
kassert(i < 6, "Requested BAR >5 for PCI device");
|
||||||
} else if (m_header_type == 1) {
|
} else if (m_header_type == 1) {
|
||||||
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
||||||
} else {
|
} else {
|
||||||
kassert(0, "Requested BAR for other PCI device type");
|
kassert(0, "Requested BAR for other PCI device type");
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_base[4+i];
|
return m_base[4+i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
pci_device::set_bar(unsigned i, uint32_t val)
|
pci_device::set_bar(unsigned i, uint32_t val)
|
||||||
{
|
{
|
||||||
if (m_header_type == 0) {
|
if (m_header_type == 0) {
|
||||||
kassert(i < 6, "Requested BAR >5 for PCI device");
|
kassert(i < 6, "Requested BAR >5 for PCI device");
|
||||||
} else if (m_header_type == 1) {
|
} else if (m_header_type == 1) {
|
||||||
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
||||||
} else {
|
} else {
|
||||||
kassert(0, "Requested BAR for other PCI device type");
|
kassert(0, "Requested BAR for other PCI device type");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_base[4+i] = val;
|
m_base[4+i] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
pci_device::write_msi_regs(uintptr_t address, uint16_t data)
|
pci_device::write_msi_regs(uintptr_t address, uint16_t data)
|
||||||
{
|
{
|
||||||
kassert(m_msi, "Tried to write MSI for a device without that cap");
|
kassert(m_msi, "Tried to write MSI for a device without that cap");
|
||||||
if (m_msi->id == pci_cap::type::msi) {
|
if (m_msi->id == pci_cap::type::msi) {
|
||||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
|
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
|
||||||
if (mcap->control & 0x0080) {
|
if (mcap->control & 0x0080) {
|
||||||
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
|
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
|
||||||
mcap64->address = address;
|
mcap64->address = address;
|
||||||
mcap64->data = data;
|
mcap64->data = data;
|
||||||
} else {
|
} else {
|
||||||
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
|
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
|
||||||
mcap32->address = address;
|
mcap32->address = address;
|
||||||
mcap32->data = data;
|
mcap32->data = data;
|
||||||
}
|
}
|
||||||
uint16_t control = mcap->control;
|
uint16_t control = mcap->control;
|
||||||
control &= 0xff8f; // We're allocating one vector, clear 6::4
|
control &= 0xff8f; // We're allocating one vector, clear 6::4
|
||||||
control |= 0x0001; // Enable MSI
|
control |= 0x0001; // Enable MSI
|
||||||
mcap->control = control;
|
mcap->control = control;
|
||||||
} else {
|
} else {
|
||||||
kassert(0, "MIS-X is NYI");
|
kassert(0, "MIS-X is NYI");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
|
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
|
||||||
{
|
{
|
||||||
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
|
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
|
||||||
}
|
}
|
||||||
|
|||||||
182
src/kernel/pci.h
182
src/kernel/pci.h
@@ -10,14 +10,14 @@ enum class isr : uint8_t;
|
|||||||
|
|
||||||
struct pci_cap
|
struct pci_cap
|
||||||
{
|
{
|
||||||
enum class type : uint8_t
|
enum class type : uint8_t
|
||||||
{
|
{
|
||||||
msi = 0x05,
|
msi = 0x05,
|
||||||
msix = 0x11
|
msix = 0x11
|
||||||
};
|
};
|
||||||
|
|
||||||
type id;
|
type id;
|
||||||
uint8_t next;
|
uint8_t next;
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|
||||||
@@ -25,117 +25,117 @@ struct pci_cap
|
|||||||
class pci_device
|
class pci_device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Default constructor creates an empty object.
|
/// Default constructor creates an empty object.
|
||||||
pci_device();
|
pci_device();
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg group The group of this device's bus
|
/// \arg group The group of this device's bus
|
||||||
/// \arg bus The bus number this device is on
|
/// \arg bus The bus number this device is on
|
||||||
/// \arg device The device number of this device
|
/// \arg device The device number of this device
|
||||||
/// \arg func The function number of this device
|
/// \arg func The function number of this device
|
||||||
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
|
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
|
||||||
|
|
||||||
/// Check if this device is multi-function.
|
/// Check if this device is multi-function.
|
||||||
/// \returns True if this device is multi-function
|
/// \returns True if this device is multi-function
|
||||||
inline bool multi() const { return m_multi; }
|
inline bool multi() const { return m_multi; }
|
||||||
|
|
||||||
/// Get the bus number this device is on.
|
/// Get the bus number this device is on.
|
||||||
/// \returns The bus number
|
/// \returns The bus number
|
||||||
inline uint8_t bus() const { return (m_bus_addr >> 8); }
|
inline uint8_t bus() const { return (m_bus_addr >> 8); }
|
||||||
|
|
||||||
/// Get the device number of this device on its bus
|
/// Get the device number of this device on its bus
|
||||||
/// \returns The device number
|
/// \returns The device number
|
||||||
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
|
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
|
||||||
|
|
||||||
/// Get the function number of this device on its device
|
/// Get the function number of this device on its device
|
||||||
/// \returns The function number
|
/// \returns The function number
|
||||||
inline uint8_t function() const { return m_bus_addr & 0x7; }
|
inline uint8_t function() const { return m_bus_addr & 0x7; }
|
||||||
|
|
||||||
/// Get the device class
|
/// Get the device class
|
||||||
/// \returns The PCI device class
|
/// \returns The PCI device class
|
||||||
inline uint8_t devclass() const { return m_class; }
|
inline uint8_t devclass() const { return m_class; }
|
||||||
|
|
||||||
/// Get the device subclass
|
/// Get the device subclass
|
||||||
/// \returns The PCI device subclass
|
/// \returns The PCI device subclass
|
||||||
inline uint8_t subclass() const { return m_subclass; }
|
inline uint8_t subclass() const { return m_subclass; }
|
||||||
|
|
||||||
/// Get the device program interface
|
/// Get the device program interface
|
||||||
/// \returns The PCI device program interface
|
/// \returns The PCI device program interface
|
||||||
inline uint8_t progif() const { return m_progif; }
|
inline uint8_t progif() const { return m_progif; }
|
||||||
|
|
||||||
/// Read one of the device's Base Address Registers
|
/// Read one of the device's Base Address Registers
|
||||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||||
/// \returns The contents of the BAR
|
/// \returns The contents of the BAR
|
||||||
uint32_t get_bar(unsigned i);
|
uint32_t get_bar(unsigned i);
|
||||||
|
|
||||||
/// Write one of the device's Base Address Registers
|
/// Write one of the device's Base Address Registers
|
||||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||||
/// \arg val The value to write
|
/// \arg val The value to write
|
||||||
void set_bar(unsigned i, uint32_t val);
|
void set_bar(unsigned i, uint32_t val);
|
||||||
|
|
||||||
/// Write to the MSI registers
|
/// Write to the MSI registers
|
||||||
/// \arg addr The address to write to the MSI address registers
|
/// \arg addr The address to write to the MSI address registers
|
||||||
/// \arg data The value to write to the MSI data register
|
/// \arg data The value to write to the MSI data register
|
||||||
void write_msi_regs(uintptr_t addr, uint16_t data);
|
void write_msi_regs(uintptr_t addr, uint16_t data);
|
||||||
|
|
||||||
/// Get a bus address, given the bus/device/function numbers.
|
/// Get a bus address, given the bus/device/function numbers.
|
||||||
/// \arg bus Number of the bus
|
/// \arg bus Number of the bus
|
||||||
/// \arg device Index of the device on the bus
|
/// \arg device Index of the device on the bus
|
||||||
/// \arg func The function number within the device
|
/// \arg func The function number within the device
|
||||||
/// \returns The computed bus_addr
|
/// \returns The computed bus_addr
|
||||||
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
|
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
|
||||||
{
|
{
|
||||||
return bus << 8 | device << 3 | func;
|
return bus << 8 | device << 3 | func;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t *m_base;
|
uint32_t *m_base;
|
||||||
pci_cap *m_msi;
|
pci_cap *m_msi;
|
||||||
|
|
||||||
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
||||||
uint16_t m_bus_addr;
|
uint16_t m_bus_addr;
|
||||||
|
|
||||||
uint16_t m_vendor;
|
uint16_t m_vendor;
|
||||||
uint16_t m_device;
|
uint16_t m_device;
|
||||||
|
|
||||||
uint8_t m_class;
|
uint8_t m_class;
|
||||||
uint8_t m_subclass;
|
uint8_t m_subclass;
|
||||||
uint8_t m_progif;
|
uint8_t m_progif;
|
||||||
uint8_t m_revision;
|
uint8_t m_revision;
|
||||||
bool m_multi;
|
bool m_multi;
|
||||||
|
|
||||||
// Might as well cache these to fill out the struct align
|
// Might as well cache these to fill out the struct align
|
||||||
isr m_irq;
|
isr m_irq;
|
||||||
uint8_t m_header_type;
|
uint8_t m_header_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Represents data about a PCI bus group from the ACPI MCFG
|
/// Represents data about a PCI bus group from the ACPI MCFG
|
||||||
struct pci_group
|
struct pci_group
|
||||||
{
|
{
|
||||||
uint16_t group;
|
uint16_t group;
|
||||||
uint16_t bus_start;
|
uint16_t bus_start;
|
||||||
uint16_t bus_end;
|
uint16_t bus_end;
|
||||||
|
|
||||||
uint32_t *base;
|
uint32_t *base;
|
||||||
|
|
||||||
/// Get the base address of the MMIO configuration registers for a device
|
/// Get the base address of the MMIO configuration registers for a device
|
||||||
/// \arg bus The bus number of the device (relative to this group)
|
/// \arg bus The bus number of the device (relative to this group)
|
||||||
/// \arg device The device number on the given bus
|
/// \arg device The device number on the given bus
|
||||||
/// \arg func The function number on the device
|
/// \arg func The function number on the device
|
||||||
/// \returns A pointer to the memory-mapped configuration registers
|
/// \returns A pointer to the memory-mapped configuration registers
|
||||||
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
|
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
|
||||||
{
|
{
|
||||||
return kutil::offset_pointer(base,
|
return kutil::offset_pointer(base,
|
||||||
pci_device::bus_addr(bus, device, func) << 12);
|
pci_device::bus_addr(bus, device, func) << 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the given device function is present.
|
/// Check if the given device function is present.
|
||||||
/// \arg bus The bus number of the device (relative to this group)
|
/// \arg bus The bus number of the device (relative to this group)
|
||||||
/// \arg device The device number on the given bus
|
/// \arg device The device number on the given bus
|
||||||
/// \arg func The function number on the device
|
/// \arg func The function number on the device
|
||||||
/// \returns True if the device function is present
|
/// \returns True if the device function is present
|
||||||
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
|
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,304 +31,304 @@ scheduler *scheduler::s_instance = nullptr;
|
|||||||
|
|
||||||
struct run_queue
|
struct run_queue
|
||||||
{
|
{
|
||||||
tcb_node *current = nullptr;
|
tcb_node *current = nullptr;
|
||||||
tcb_list ready[scheduler::num_priorities];
|
tcb_list ready[scheduler::num_priorities];
|
||||||
tcb_list blocked;
|
tcb_list blocked;
|
||||||
|
|
||||||
uint64_t last_promotion = 0;
|
uint64_t last_promotion = 0;
|
||||||
uint64_t last_steal = 0;
|
uint64_t last_steal = 0;
|
||||||
kutil::spinlock lock;
|
kutil::spinlock lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
scheduler::scheduler(unsigned cpus) :
|
scheduler::scheduler(unsigned cpus) :
|
||||||
m_next_pid {1},
|
m_next_pid {1},
|
||||||
m_clock {0}
|
m_clock {0}
|
||||||
{
|
{
|
||||||
kassert(!s_instance, "Created multiple schedulers!");
|
kassert(!s_instance, "Created multiple schedulers!");
|
||||||
if (!s_instance)
|
if (!s_instance)
|
||||||
s_instance = this;
|
s_instance = this;
|
||||||
|
|
||||||
m_run_queues.set_size(cpus);
|
m_run_queues.set_size(cpus);
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler::~scheduler()
|
scheduler::~scheduler()
|
||||||
{
|
{
|
||||||
// Not truly necessary - if the scheduler is going away, the whole
|
// Not truly necessary - if the scheduler is going away, the whole
|
||||||
// system is probably going down. But let's be clean.
|
// system is probably going down. But let's be clean.
|
||||||
if (s_instance == this)
|
if (s_instance == this)
|
||||||
s_instance = nullptr;
|
s_instance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) {
|
inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) {
|
||||||
rsp -= size;
|
rsp -= size;
|
||||||
T *p = reinterpret_cast<T*>(rsp);
|
T *p = reinterpret_cast<T*>(rsp);
|
||||||
rsp &= ~(sizeof(uint64_t)-1); // Align the stack
|
rsp &= ~(sizeof(uint64_t)-1); // Align the stack
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant)
|
scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant)
|
||||||
{
|
{
|
||||||
thread *th = process::kernel_process().create_thread(priority, false);
|
thread *th = process::kernel_process().create_thread(priority, false);
|
||||||
auto *tcb = th->tcb();
|
auto *tcb = th->tcb();
|
||||||
|
|
||||||
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(task));
|
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(task));
|
||||||
|
|
||||||
tcb->time_left = quantum(priority);
|
tcb->time_left = quantum(priority);
|
||||||
if (constant)
|
if (constant)
|
||||||
th->set_state(thread::state::constant);
|
th->set_state(thread::state::constant);
|
||||||
|
|
||||||
th->set_state(thread::state::ready);
|
th->set_state(thread::state::ready);
|
||||||
|
|
||||||
log::debug(logs::task, "Creating kernel task: thread %llx pri %d", th->koid(), tcb->priority);
|
log::debug(logs::task, "Creating kernel task: thread %llx pri %d", th->koid(), tcb->priority);
|
||||||
log::debug(logs::task, " RSP0 %016lx", tcb->rsp0);
|
log::debug(logs::task, " RSP0 %016lx", tcb->rsp0);
|
||||||
log::debug(logs::task, " RSP %016lx", tcb->rsp);
|
log::debug(logs::task, " RSP %016lx", tcb->rsp);
|
||||||
log::debug(logs::task, " PML4 %016lx", tcb->pml4);
|
log::debug(logs::task, " PML4 %016lx", tcb->pml4);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
scheduler::quantum(int priority)
|
scheduler::quantum(int priority)
|
||||||
{
|
{
|
||||||
return quantum_micros << priority;
|
return quantum_micros << priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scheduler::start()
|
scheduler::start()
|
||||||
{
|
{
|
||||||
cpu_data &cpu = current_cpu();
|
cpu_data &cpu = current_cpu();
|
||||||
run_queue &queue = m_run_queues[cpu.index];
|
run_queue &queue = m_run_queues[cpu.index];
|
||||||
|
|
||||||
{
|
{
|
||||||
kutil::scoped_lock lock {queue.lock};
|
kutil::scoped_lock lock {queue.lock};
|
||||||
|
|
||||||
process *kp = &process::kernel_process();
|
process *kp = &process::kernel_process();
|
||||||
thread *idle = thread::create_idle_thread(*kp, max_priority, cpu.rsp0);
|
thread *idle = thread::create_idle_thread(*kp, max_priority, cpu.rsp0);
|
||||||
|
|
||||||
auto *tcb = idle->tcb();
|
auto *tcb = idle->tcb();
|
||||||
cpu.process = kp;
|
cpu.process = kp;
|
||||||
cpu.thread = idle;
|
cpu.thread = idle;
|
||||||
cpu.tcb = tcb;
|
cpu.tcb = tcb;
|
||||||
|
|
||||||
queue.current = tcb;
|
queue.current = tcb;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.apic->enable_timer(isr::isrTimer, false);
|
cpu.apic->enable_timer(isr::isrTimer, false);
|
||||||
cpu.apic->reset_timer(10);
|
cpu.apic->reset_timer(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scheduler::add_thread(TCB *t)
|
scheduler::add_thread(TCB *t)
|
||||||
{
|
{
|
||||||
cpu_data &cpu = current_cpu();
|
cpu_data &cpu = current_cpu();
|
||||||
run_queue &queue = m_run_queues[cpu.index];
|
run_queue &queue = m_run_queues[cpu.index];
|
||||||
kutil::scoped_lock lock {queue.lock};
|
kutil::scoped_lock lock {queue.lock};
|
||||||
|
|
||||||
queue.blocked.push_back(static_cast<tcb_node*>(t));
|
queue.blocked.push_back(static_cast<tcb_node*>(t));
|
||||||
t->time_left = quantum(t->priority);
|
t->time_left = quantum(t->priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::prune(run_queue &queue, uint64_t now)
|
void scheduler::prune(run_queue &queue, uint64_t now)
|
||||||
{
|
{
|
||||||
// Find processes that are ready or have exited and
|
// Find processes that are ready or have exited and
|
||||||
// move them to the appropriate lists.
|
// move them to the appropriate lists.
|
||||||
auto *tcb = queue.blocked.front();
|
auto *tcb = queue.blocked.front();
|
||||||
while (tcb) {
|
while (tcb) {
|
||||||
thread *th = thread::from_tcb(tcb);
|
thread *th = thread::from_tcb(tcb);
|
||||||
uint8_t priority = tcb->priority;
|
uint8_t priority = tcb->priority;
|
||||||
|
|
||||||
bool ready = th->has_state(thread::state::ready);
|
bool ready = th->has_state(thread::state::ready);
|
||||||
bool exited = th->has_state(thread::state::exited);
|
bool exited = th->has_state(thread::state::exited);
|
||||||
bool constant = th->has_state(thread::state::constant);
|
bool constant = th->has_state(thread::state::constant);
|
||||||
bool current = tcb == queue.current;
|
bool current = tcb == queue.current;
|
||||||
|
|
||||||
ready |= th->wake_on_time(now);
|
ready |= th->wake_on_time(now);
|
||||||
|
|
||||||
auto *remove = tcb;
|
auto *remove = tcb;
|
||||||
tcb = tcb->next();
|
tcb = tcb->next();
|
||||||
if (!exited && !ready)
|
if (!exited && !ready)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (exited) {
|
if (exited) {
|
||||||
// If the current thread has exited, wait until the next call
|
// If the current thread has exited, wait until the next call
|
||||||
// to prune() to delete it, because we may be deleting our current
|
// to prune() to delete it, because we may be deleting our current
|
||||||
// page tables
|
// page tables
|
||||||
if (current) continue;
|
if (current) continue;
|
||||||
|
|
||||||
queue.blocked.remove(remove);
|
queue.blocked.remove(remove);
|
||||||
process &p = th->parent();
|
process &p = th->parent();
|
||||||
|
|
||||||
// thread_exited deletes the thread, and returns true if the process
|
// thread_exited deletes the thread, and returns true if the process
|
||||||
// should also now be deleted
|
// should also now be deleted
|
||||||
if(!current && p.thread_exited(th))
|
if(!current && p.thread_exited(th))
|
||||||
delete &p;
|
delete &p;
|
||||||
} else {
|
} else {
|
||||||
queue.blocked.remove(remove);
|
queue.blocked.remove(remove);
|
||||||
log::debug(logs::sched, "Prune: readying unblocked thread %llx", th->koid());
|
log::debug(logs::sched, "Prune: readying unblocked thread %llx", th->koid());
|
||||||
queue.ready[remove->priority].push_back(remove);
|
queue.ready[remove->priority].push_back(remove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scheduler::check_promotions(run_queue &queue, uint64_t now)
|
scheduler::check_promotions(run_queue &queue, uint64_t now)
|
||||||
{
|
{
|
||||||
for (auto &pri_list : queue.ready) {
|
for (auto &pri_list : queue.ready) {
|
||||||
for (auto *tcb : pri_list) {
|
for (auto *tcb : pri_list) {
|
||||||
const thread *th = thread::from_tcb(queue.current);
|
const thread *th = thread::from_tcb(queue.current);
|
||||||
const bool constant = th->has_state(thread::state::constant);
|
const bool constant = th->has_state(thread::state::constant);
|
||||||
if (constant)
|
if (constant)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const uint64_t age = now - tcb->last_ran;
|
const uint64_t age = now - tcb->last_ran;
|
||||||
const uint8_t priority = tcb->priority;
|
const uint8_t priority = tcb->priority;
|
||||||
|
|
||||||
bool stale =
|
bool stale =
|
||||||
age > quantum(priority) * 2 &&
|
age > quantum(priority) * 2 &&
|
||||||
tcb->priority > promote_limit &&
|
tcb->priority > promote_limit &&
|
||||||
!constant;
|
!constant;
|
||||||
|
|
||||||
if (stale) {
|
if (stale) {
|
||||||
// If the thread is stale, promote it
|
// If the thread is stale, promote it
|
||||||
queue.ready[priority].remove(tcb);
|
queue.ready[priority].remove(tcb);
|
||||||
tcb->priority -= 1;
|
tcb->priority -= 1;
|
||||||
tcb->time_left = quantum(tcb->priority);
|
tcb->time_left = quantum(tcb->priority);
|
||||||
queue.ready[tcb->priority].push_back(tcb);
|
queue.ready[tcb->priority].push_back(tcb);
|
||||||
log::info(logs::sched, "Scheduler promoting thread %llx, priority %d",
|
log::info(logs::sched, "Scheduler promoting thread %llx, priority %d",
|
||||||
th->koid(), tcb->priority);
|
th->koid(), tcb->priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.last_promotion = now;
|
queue.last_promotion = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
balance_lists(tcb_list &to, tcb_list &from)
|
balance_lists(tcb_list &to, tcb_list &from)
|
||||||
{
|
{
|
||||||
size_t to_len = to.length();
|
size_t to_len = to.length();
|
||||||
size_t from_len = from.length();
|
size_t from_len = from.length();
|
||||||
|
|
||||||
// Only steal from the rich, don't be Dennis Moore
|
// Only steal from the rich, don't be Dennis Moore
|
||||||
if (from_len <= to_len)
|
if (from_len <= to_len)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
size_t steal = (from_len - to_len) / 2;
|
size_t steal = (from_len - to_len) / 2;
|
||||||
for (size_t i = 0; i < steal; ++i)
|
for (size_t i = 0; i < steal; ++i)
|
||||||
to.push_front(from.pop_front());
|
to.push_front(from.pop_front());
|
||||||
return steal;
|
return steal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scheduler::steal_work(cpu_data &cpu)
|
scheduler::steal_work(cpu_data &cpu)
|
||||||
{
|
{
|
||||||
// Lock this cpu's queue for the whole time while we modify it
|
// Lock this cpu's queue for the whole time while we modify it
|
||||||
run_queue &my_queue = m_run_queues[cpu.index];
|
run_queue &my_queue = m_run_queues[cpu.index];
|
||||||
kutil::scoped_lock my_queue_lock {my_queue.lock};
|
kutil::scoped_lock my_queue_lock {my_queue.lock};
|
||||||
|
|
||||||
const unsigned count = m_run_queues.count();
|
const unsigned count = m_run_queues.count();
|
||||||
for (unsigned i = 0; i < count; ++i) {
|
for (unsigned i = 0; i < count; ++i) {
|
||||||
if (i == cpu.index) continue;
|
if (i == cpu.index) continue;
|
||||||
|
|
||||||
run_queue &other_queue = m_run_queues[i];
|
run_queue &other_queue = m_run_queues[i];
|
||||||
kutil::scoped_lock other_queue_lock {other_queue.lock};
|
kutil::scoped_lock other_queue_lock {other_queue.lock};
|
||||||
|
|
||||||
size_t stolen = 0;
|
size_t stolen = 0;
|
||||||
|
|
||||||
// Don't steal from max_priority, that's the idle thread
|
// Don't steal from max_priority, that's the idle thread
|
||||||
for (unsigned pri = 0; pri < max_priority; ++pri)
|
for (unsigned pri = 0; pri < max_priority; ++pri)
|
||||||
stolen += balance_lists(my_queue.ready[pri], other_queue.ready[pri]);
|
stolen += balance_lists(my_queue.ready[pri], other_queue.ready[pri]);
|
||||||
|
|
||||||
stolen += balance_lists(my_queue.blocked, other_queue.blocked);
|
stolen += balance_lists(my_queue.blocked, other_queue.blocked);
|
||||||
|
|
||||||
if (stolen)
|
if (stolen)
|
||||||
log::debug(logs::sched, "CPU%02x stole %2d tasks from CPU%02x",
|
log::debug(logs::sched, "CPU%02x stole %2d tasks from CPU%02x",
|
||||||
cpu.index, stolen, i);
|
cpu.index, stolen, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scheduler::schedule()
|
scheduler::schedule()
|
||||||
{
|
{
|
||||||
cpu_data &cpu = current_cpu();
|
cpu_data &cpu = current_cpu();
|
||||||
run_queue &queue = m_run_queues[cpu.index];
|
run_queue &queue = m_run_queues[cpu.index];
|
||||||
lapic &apic = *cpu.apic;
|
lapic &apic = *cpu.apic;
|
||||||
uint32_t remaining = apic.stop_timer();
|
uint32_t remaining = apic.stop_timer();
|
||||||
|
|
||||||
// Only one CPU can be stealing at a time
|
// Only one CPU can be stealing at a time
|
||||||
if (m_steal_turn == cpu.index &&
|
if (m_steal_turn == cpu.index &&
|
||||||
m_clock - queue.last_steal > steal_frequency) {
|
m_clock - queue.last_steal > steal_frequency) {
|
||||||
steal_work(cpu);
|
steal_work(cpu);
|
||||||
queue.last_steal = m_clock;
|
queue.last_steal = m_clock;
|
||||||
m_steal_turn = (m_steal_turn + 1) % m_run_queues.count();
|
m_steal_turn = (m_steal_turn + 1) % m_run_queues.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to explicitly lock/unlock here instead of
|
// We need to explicitly lock/unlock here instead of
|
||||||
// using a scoped lock, because the scope doesn't "end"
|
// using a scoped lock, because the scope doesn't "end"
|
||||||
// for the current thread until it gets scheduled again
|
// for the current thread until it gets scheduled again
|
||||||
kutil::spinlock::waiter waiter;
|
kutil::spinlock::waiter waiter;
|
||||||
queue.lock.acquire(&waiter);
|
queue.lock.acquire(&waiter);
|
||||||
|
|
||||||
queue.current->time_left = remaining;
|
queue.current->time_left = remaining;
|
||||||
thread *th = thread::from_tcb(queue.current);
|
thread *th = thread::from_tcb(queue.current);
|
||||||
uint8_t priority = queue.current->priority;
|
uint8_t priority = queue.current->priority;
|
||||||
const bool constant = th->has_state(thread::state::constant);
|
const bool constant = th->has_state(thread::state::constant);
|
||||||
|
|
||||||
if (remaining == 0) {
|
if (remaining == 0) {
|
||||||
if (priority < max_priority && !constant) {
|
if (priority < max_priority && !constant) {
|
||||||
// Process used its whole timeslice, demote it
|
// Process used its whole timeslice, demote it
|
||||||
++queue.current->priority;
|
++queue.current->priority;
|
||||||
log::debug(logs::sched, "Scheduler demoting thread %llx, priority %d",
|
log::debug(logs::sched, "Scheduler demoting thread %llx, priority %d",
|
||||||
th->koid(), queue.current->priority);
|
th->koid(), queue.current->priority);
|
||||||
}
|
}
|
||||||
queue.current->time_left = quantum(queue.current->priority);
|
queue.current->time_left = quantum(queue.current->priority);
|
||||||
} else if (remaining > 0) {
|
} else if (remaining > 0) {
|
||||||
// Process gave up CPU, give it a small bonus to its
|
// Process gave up CPU, give it a small bonus to its
|
||||||
// remaining timeslice.
|
// remaining timeslice.
|
||||||
uint32_t bonus = quantum(priority) >> 4;
|
uint32_t bonus = quantum(priority) >> 4;
|
||||||
queue.current->time_left += bonus;
|
queue.current->time_left += bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (th->has_state(thread::state::ready)) {
|
if (th->has_state(thread::state::ready)) {
|
||||||
queue.ready[queue.current->priority].push_back(queue.current);
|
queue.ready[queue.current->priority].push_back(queue.current);
|
||||||
} else {
|
} else {
|
||||||
queue.blocked.push_back(queue.current);
|
queue.blocked.push_back(queue.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
clock::get().update();
|
clock::get().update();
|
||||||
prune(queue, ++m_clock);
|
prune(queue, ++m_clock);
|
||||||
if (m_clock - queue.last_promotion > promote_frequency)
|
if (m_clock - queue.last_promotion > promote_frequency)
|
||||||
check_promotions(queue, m_clock);
|
check_promotions(queue, m_clock);
|
||||||
|
|
||||||
priority = 0;
|
priority = 0;
|
||||||
while (queue.ready[priority].empty()) {
|
while (queue.ready[priority].empty()) {
|
||||||
++priority;
|
++priority;
|
||||||
kassert(priority < num_priorities, "All runlists are empty");
|
kassert(priority < num_priorities, "All runlists are empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.current->last_ran = m_clock;
|
queue.current->last_ran = m_clock;
|
||||||
|
|
||||||
auto *next = queue.ready[priority].pop_front();
|
auto *next = queue.ready[priority].pop_front();
|
||||||
next->last_ran = m_clock;
|
next->last_ran = m_clock;
|
||||||
apic.reset_timer(next->time_left);
|
apic.reset_timer(next->time_left);
|
||||||
|
|
||||||
if (next == queue.current) {
|
if (next == queue.current) {
|
||||||
queue.lock.release(&waiter);
|
queue.lock.release(&waiter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread *next_thread = thread::from_tcb(next);
|
thread *next_thread = thread::from_tcb(next);
|
||||||
|
|
||||||
cpu.thread = next_thread;
|
cpu.thread = next_thread;
|
||||||
cpu.process = &next_thread->parent();
|
cpu.process = &next_thread->parent();
|
||||||
queue.current = next;
|
queue.current = next;
|
||||||
|
|
||||||
log::debug(logs::sched, "CPU%02x switching threads %llx->%llx",
|
log::debug(logs::sched, "CPU%02x switching threads %llx->%llx",
|
||||||
cpu.index, th->koid(), next_thread->koid());
|
cpu.index, th->koid(), next_thread->koid());
|
||||||
log::debug(logs::sched, " priority %d time left %d @ %lld.",
|
log::debug(logs::sched, " priority %d time left %d @ %lld.",
|
||||||
next->priority, next->time_left, m_clock);
|
next->priority, next->time_left, m_clock);
|
||||||
log::debug(logs::sched, " PML4 %llx", next->pml4);
|
log::debug(logs::sched, " PML4 %llx", next->pml4);
|
||||||
|
|
||||||
queue.lock.release(&waiter);
|
queue.lock.release(&waiter);
|
||||||
task_switch(queue.current);
|
task_switch(queue.current);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
namespace args {
|
namespace args {
|
||||||
struct program;
|
struct program;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
struct cpu_data;
|
struct cpu_data;
|
||||||
@@ -22,82 +22,82 @@ struct run_queue;
|
|||||||
class scheduler
|
class scheduler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Total number of priority levels
|
/// Total number of priority levels
|
||||||
static const uint8_t num_priorities = 8;
|
static const uint8_t num_priorities = 8;
|
||||||
|
|
||||||
/// Maximum (least urgent/interactive) priority
|
/// Maximum (least urgent/interactive) priority
|
||||||
static const uint8_t max_priority = num_priorities - 1;
|
static const uint8_t max_priority = num_priorities - 1;
|
||||||
|
|
||||||
/// Default priority on process creation
|
/// Default priority on process creation
|
||||||
static const uint8_t default_priority = 1;
|
static const uint8_t default_priority = 1;
|
||||||
|
|
||||||
/// Lowest (most urgent) priority achieved via promotion
|
/// Lowest (most urgent) priority achieved via promotion
|
||||||
static const uint8_t promote_limit = 1;
|
static const uint8_t promote_limit = 1;
|
||||||
|
|
||||||
/// How long the base timer quantum is, in us
|
/// How long the base timer quantum is, in us
|
||||||
static const uint64_t quantum_micros = 500;
|
static const uint64_t quantum_micros = 500;
|
||||||
|
|
||||||
/// How many quanta a process gets before being rescheduled
|
/// How many quanta a process gets before being rescheduled
|
||||||
static const uint16_t process_quanta = 10;
|
static const uint16_t process_quanta = 10;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg cpus The number of CPUs to schedule for
|
/// \arg cpus The number of CPUs to schedule for
|
||||||
scheduler(unsigned cpus);
|
scheduler(unsigned cpus);
|
||||||
~scheduler();
|
~scheduler();
|
||||||
|
|
||||||
/// Create a new process from a program image in memory.
|
/// Create a new process from a program image in memory.
|
||||||
/// \arg program The descriptor of the pogram in memory
|
/// \arg program The descriptor of the pogram in memory
|
||||||
/// \returns The main thread of the loaded process
|
/// \returns The main thread of the loaded process
|
||||||
thread * load_process(kernel::args::program &program);
|
thread * load_process(kernel::args::program &program);
|
||||||
|
|
||||||
/// Create a new kernel task
|
/// Create a new kernel task
|
||||||
/// \arg proc Function to run as a kernel task
|
/// \arg proc Function to run as a kernel task
|
||||||
/// \arg priority Priority to start the process with
|
/// \arg priority Priority to start the process with
|
||||||
/// \arg constant True if this task cannot be promoted/demoted
|
/// \arg constant True if this task cannot be promoted/demoted
|
||||||
void create_kernel_task(
|
void create_kernel_task(
|
||||||
void (*task)(),
|
void (*task)(),
|
||||||
uint8_t priority,
|
uint8_t priority,
|
||||||
bool constant = false);
|
bool constant = false);
|
||||||
|
|
||||||
/// Get the quantum for a given priority.
|
/// Get the quantum for a given priority.
|
||||||
static uint32_t quantum(int priority);
|
static uint32_t quantum(int priority);
|
||||||
|
|
||||||
/// Start the scheduler working. This may involve starting
|
/// Start the scheduler working. This may involve starting
|
||||||
/// timer interrupts or other preemption methods.
|
/// timer interrupts or other preemption methods.
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
/// Run the scheduler, possibly switching to a new task
|
/// Run the scheduler, possibly switching to a new task
|
||||||
void schedule();
|
void schedule();
|
||||||
|
|
||||||
/// Start scheduling a new thread.
|
/// Start scheduling a new thread.
|
||||||
/// \arg t The new thread's TCB
|
/// \arg t The new thread's TCB
|
||||||
void add_thread(TCB *t);
|
void add_thread(TCB *t);
|
||||||
|
|
||||||
/// Get a reference to the scheduler
|
/// Get a reference to the scheduler
|
||||||
/// \returns A reference to the global system scheduler
|
/// \returns A reference to the global system scheduler
|
||||||
static scheduler & get() { return *s_instance; }
|
static scheduler & get() { return *s_instance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class process;
|
friend class process;
|
||||||
|
|
||||||
static constexpr uint64_t promote_frequency = 10;
|
static constexpr uint64_t promote_frequency = 10;
|
||||||
static constexpr uint64_t steal_frequency = 10;
|
static constexpr uint64_t steal_frequency = 10;
|
||||||
|
|
||||||
void prune(run_queue &queue, uint64_t now);
|
void prune(run_queue &queue, uint64_t now);
|
||||||
void check_promotions(run_queue &queue, uint64_t now);
|
void check_promotions(run_queue &queue, uint64_t now);
|
||||||
void steal_work(cpu_data &cpu);
|
void steal_work(cpu_data &cpu);
|
||||||
|
|
||||||
uint32_t m_next_pid;
|
uint32_t m_next_pid;
|
||||||
uint32_t m_tick_count;
|
uint32_t m_tick_count;
|
||||||
|
|
||||||
process *m_kernel_process;
|
process *m_kernel_process;
|
||||||
|
|
||||||
kutil::vector<run_queue> m_run_queues;
|
kutil::vector<run_queue> m_run_queues;
|
||||||
|
|
||||||
// TODO: lol a real clock
|
// TODO: lol a real clock
|
||||||
uint64_t m_clock = 0;
|
uint64_t m_clock = 0;
|
||||||
|
|
||||||
unsigned m_steal_turn = 0;
|
unsigned m_steal_turn = 0;
|
||||||
static scheduler *s_instance;
|
static scheduler *s_instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,28 +31,28 @@ uint8_t com1_out_buffer[4096*4];
|
|||||||
uint8_t com1_in_buffer[512];
|
uint8_t com1_in_buffer[512];
|
||||||
|
|
||||||
serial_port::serial_port() :
|
serial_port::serial_port() :
|
||||||
m_writing(false),
|
m_writing(false),
|
||||||
m_port(0)
|
m_port(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
serial_port::serial_port(uint16_t port) :
|
serial_port::serial_port(uint16_t port) :
|
||||||
m_writing(false),
|
m_writing(false),
|
||||||
m_port(port),
|
m_port(port),
|
||||||
m_out_buffer(com1_out_buffer, sizeof(com1_out_buffer)),
|
m_out_buffer(com1_out_buffer, sizeof(com1_out_buffer)),
|
||||||
m_in_buffer(com1_in_buffer, sizeof(com1_in_buffer))
|
m_in_buffer(com1_in_buffer, sizeof(com1_in_buffer))
|
||||||
{
|
{
|
||||||
outb(port + IER, 0x00); // Disable all interrupts
|
outb(port + IER, 0x00); // Disable all interrupts
|
||||||
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||||
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
||||||
outb(port + DLH, 0x00); // Divisor high byte
|
outb(port + DLH, 0x00); // Divisor high byte
|
||||||
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
||||||
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||||
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||||
outb(port + IER, 0x03); // Enable interrupts
|
outb(port + IER, 0x03); // Enable interrupts
|
||||||
|
|
||||||
// Clear out pending interrupts
|
// Clear out pending interrupts
|
||||||
handle_interrupt();
|
handle_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; }
|
inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; }
|
||||||
@@ -61,102 +61,102 @@ inline bool write_ready(uint16_t port) { return (inb(port + LSR) & 0x20) != 0; }
|
|||||||
void
|
void
|
||||||
serial_port::handle_interrupt()
|
serial_port::handle_interrupt()
|
||||||
{
|
{
|
||||||
interrupts_disable();
|
interrupts_disable();
|
||||||
uint8_t iir = inb(m_port + IIR);
|
uint8_t iir = inb(m_port + IIR);
|
||||||
|
|
||||||
while ((iir & 1) == 0) {
|
while ((iir & 1) == 0) {
|
||||||
uint8_t reg = 0;
|
uint8_t reg = 0;
|
||||||
switch ((iir>>1) & 0x3) {
|
switch ((iir>>1) & 0x3) {
|
||||||
case 0: // Modem status change
|
case 0: // Modem status change
|
||||||
reg = inb(m_port + MSR);
|
reg = inb(m_port + MSR);
|
||||||
handle_error(MSR, reg);
|
handle_error(MSR, reg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: // Transmit buffer empty
|
case 1: // Transmit buffer empty
|
||||||
do_write();
|
do_write();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // Received data available
|
case 2: // Received data available
|
||||||
do_read();
|
do_read();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: // Line status change
|
case 3: // Line status change
|
||||||
reg = inb(m_port + LSR);
|
reg = inb(m_port + LSR);
|
||||||
handle_error(LSR, reg);
|
handle_error(LSR, reg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
iir = inb(m_port + IIR);
|
iir = inb(m_port + IIR);
|
||||||
}
|
}
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
serial_port::do_read()
|
serial_port::do_read()
|
||||||
{
|
{
|
||||||
size_t used = 0;
|
size_t used = 0;
|
||||||
uint8_t *data = nullptr;
|
uint8_t *data = nullptr;
|
||||||
size_t avail = m_in_buffer.reserve(fifo_size, reinterpret_cast<void**>(&data));
|
size_t avail = m_in_buffer.reserve(fifo_size, reinterpret_cast<void**>(&data));
|
||||||
|
|
||||||
while (used < avail && read_ready(m_port)) {
|
while (used < avail && read_ready(m_port)) {
|
||||||
*data++ = inb(m_port);
|
*data++ = inb(m_port);
|
||||||
used++;
|
used++;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_in_buffer.commit(used);
|
m_in_buffer.commit(used);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
serial_port::do_write()
|
serial_port::do_write()
|
||||||
{
|
{
|
||||||
uint8_t *data = nullptr;
|
uint8_t *data = nullptr;
|
||||||
uint8_t tmp[fifo_size];
|
uint8_t tmp[fifo_size];
|
||||||
|
|
||||||
size_t n = m_out_buffer.get_block(reinterpret_cast<void**>(&data));
|
size_t n = m_out_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||||
|
|
||||||
m_writing = (n > 0);
|
m_writing = (n > 0);
|
||||||
if (!m_writing)
|
if (!m_writing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (n > fifo_size)
|
if (n > fifo_size)
|
||||||
n = fifo_size;
|
n = fifo_size;
|
||||||
|
|
||||||
kutil::memcpy(tmp, data, n);
|
kutil::memcpy(tmp, data, n);
|
||||||
m_out_buffer.consume(n);
|
m_out_buffer.consume(n);
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i)
|
for (size_t i = 0; i < n; ++i)
|
||||||
outb(m_port, data[i]);
|
outb(m_port, data[i]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
serial_port::handle_error(uint16_t reg, uint8_t value)
|
serial_port::handle_error(uint16_t reg, uint8_t value)
|
||||||
{
|
{
|
||||||
kassert(false, "serial line error");
|
kassert(false, "serial line error");
|
||||||
}
|
}
|
||||||
|
|
||||||
char
|
char
|
||||||
serial_port::read()
|
serial_port::read()
|
||||||
{
|
{
|
||||||
uint8_t *data = nullptr;
|
uint8_t *data = nullptr;
|
||||||
size_t n = m_in_buffer.get_block(reinterpret_cast<void**>(&data));
|
size_t n = m_in_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||||
if (!n) return 0;
|
if (!n) return 0;
|
||||||
char c = *data;
|
char c = *data;
|
||||||
m_in_buffer.consume(1);
|
m_in_buffer.consume(1);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
serial_port::write(char c)
|
serial_port::write(char c)
|
||||||
{
|
{
|
||||||
uint8_t *data = nullptr;
|
uint8_t *data = nullptr;
|
||||||
size_t avail = m_out_buffer.reserve(1, reinterpret_cast<void**>(&data));
|
size_t avail = m_out_buffer.reserve(1, reinterpret_cast<void**>(&data));
|
||||||
if (!avail)
|
if (!avail)
|
||||||
return;
|
return;
|
||||||
*data = c;
|
*data = c;
|
||||||
m_out_buffer.commit(1);
|
m_out_buffer.commit(1);
|
||||||
|
|
||||||
if (!m_writing)
|
if (!m_writing)
|
||||||
do_write();
|
do_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,26 +8,26 @@
|
|||||||
class serial_port
|
class serial_port
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg port The IO address of the serial port
|
/// \arg port The IO address of the serial port
|
||||||
serial_port(uint16_t port);
|
serial_port(uint16_t port);
|
||||||
|
|
||||||
serial_port();
|
serial_port();
|
||||||
|
|
||||||
void write(char c);
|
void write(char c);
|
||||||
char read();
|
char read();
|
||||||
|
|
||||||
void handle_interrupt();
|
void handle_interrupt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_writing;
|
bool m_writing;
|
||||||
uint16_t m_port;
|
uint16_t m_port;
|
||||||
kutil::bip_buffer m_out_buffer;
|
kutil::bip_buffer m_out_buffer;
|
||||||
kutil::bip_buffer m_in_buffer;
|
kutil::bip_buffer m_in_buffer;
|
||||||
|
|
||||||
void do_read();
|
void do_read();
|
||||||
void do_write();
|
void do_write();
|
||||||
void handle_error(uint16_t reg, uint8_t value);
|
void handle_error(uint16_t reg, uint8_t value);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern serial_port &g_com1;
|
extern serial_port &g_com1;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void syscall_invalid(uint64_t call);
|
void syscall_invalid(uint64_t call);
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t syscall_registry[256] __attribute__((section(".syscall_registry")));
|
uintptr_t syscall_registry[256] __attribute__((section(".syscall_registry")));
|
||||||
@@ -18,34 +18,34 @@ static constexpr size_t num_syscalls = sizeof(syscall_registry) / sizeof(syscall
|
|||||||
void
|
void
|
||||||
syscall_invalid(uint64_t call)
|
syscall_invalid(uint64_t call)
|
||||||
{
|
{
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
cons->set_color(9);
|
cons->set_color(9);
|
||||||
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
||||||
|
|
||||||
cons->printf(" Known syscalls:\n");
|
cons->printf(" Known syscalls:\n");
|
||||||
cons->printf(" invalid %016lx\n", syscall_invalid);
|
cons->printf(" invalid %016lx\n", syscall_invalid);
|
||||||
|
|
||||||
for (unsigned i = 0; i < num_syscalls; ++i) {
|
for (unsigned i = 0; i < num_syscalls; ++i) {
|
||||||
const char *name = syscall_names[i];
|
const char *name = syscall_names[i];
|
||||||
uintptr_t handler = syscall_registry[i];
|
uintptr_t handler = syscall_registry[i];
|
||||||
if (name)
|
if (name)
|
||||||
cons->printf(" %02x %10s %016lx\n", i, name, handler);
|
cons->printf(" %02x %10s %016lx\n", i, name, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
cons->set_color();
|
cons->set_color();
|
||||||
_halt();
|
_halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
syscall_initialize()
|
syscall_initialize()
|
||||||
{
|
{
|
||||||
kutil::memset(&syscall_registry, 0, sizeof(syscall_registry));
|
kutil::memset(&syscall_registry, 0, sizeof(syscall_registry));
|
||||||
kutil::memset(&syscall_names, 0, sizeof(syscall_names));
|
kutil::memset(&syscall_names, 0, sizeof(syscall_names));
|
||||||
|
|
||||||
#define SYSCALL(id, name, result, ...) \
|
#define SYSCALL(id, name, result, ...) \
|
||||||
syscall_registry[id] = reinterpret_cast<uintptr_t>(syscalls::name); \
|
syscall_registry[id] = reinterpret_cast<uintptr_t>(syscalls::name); \
|
||||||
syscall_names[id] = #name; \
|
syscall_names[id] = #name; \
|
||||||
log::debug(logs::syscall, "Enabling syscall 0x%02x as " #name , id);
|
log::debug(logs::syscall, "Enabling syscall 0x%02x as " #name , id);
|
||||||
#include "j6/tables/syscalls.inc"
|
#include "j6/tables/syscalls.inc"
|
||||||
#undef SYSCALL
|
#undef SYSCALL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,24 +9,24 @@ namespace syscalls {
|
|||||||
j6_status_t
|
j6_status_t
|
||||||
channel_create(j6_handle_t *handle)
|
channel_create(j6_handle_t *handle)
|
||||||
{
|
{
|
||||||
construct_handle<channel>(handle);
|
construct_handle<channel>(handle);
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
channel_send(j6_handle_t handle, size_t *len, void *data)
|
channel_send(j6_handle_t handle, size_t *len, void *data)
|
||||||
{
|
{
|
||||||
channel *c = get_handle<channel>(handle);
|
channel *c = get_handle<channel>(handle);
|
||||||
if (!c) return j6_err_invalid_arg;
|
if (!c) return j6_err_invalid_arg;
|
||||||
return c->enqueue(len, data);
|
return c->enqueue(len, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
channel_receive(j6_handle_t handle, size_t *len, void *data)
|
channel_receive(j6_handle_t handle, size_t *len, void *data)
|
||||||
{
|
{
|
||||||
channel *c = get_handle<channel>(handle);
|
channel *c = get_handle<channel>(handle);
|
||||||
if (!c) return j6_err_invalid_arg;
|
if (!c) return j6_err_invalid_arg;
|
||||||
return c->dequeue(len, data);
|
return c->dequeue(len, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace syscalls
|
} // namespace syscalls
|
||||||
|
|||||||
@@ -10,58 +10,58 @@ namespace syscalls {
|
|||||||
j6_status_t
|
j6_status_t
|
||||||
endpoint_create(j6_handle_t *handle)
|
endpoint_create(j6_handle_t *handle)
|
||||||
{
|
{
|
||||||
construct_handle<endpoint>(handle);
|
construct_handle<endpoint>(handle);
|
||||||
return j6_status_ok;
|
return j6_status_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
endpoint_send(j6_handle_t handle, j6_tag_t tag, size_t len, void *data)
|
endpoint_send(j6_handle_t handle, j6_tag_t tag, size_t len, void *data)
|
||||||
{
|
{
|
||||||
if (tag & j6_tag_system_flag)
|
if (tag & j6_tag_system_flag)
|
||||||
return j6_err_invalid_arg;
|
return j6_err_invalid_arg;
|
||||||
|
|
||||||
endpoint *e = get_handle<endpoint>(handle);
|
endpoint *e = get_handle<endpoint>(handle);
|
||||||
if (!e) return j6_err_invalid_arg;
|
if (!e) return j6_err_invalid_arg;
|
||||||
|
|
||||||
return e->send(tag, len, data);
|
return e->send(tag, len, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
endpoint_receive(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
endpoint_receive(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||||
{
|
{
|
||||||
if (!tag || !len || (*len && !data))
|
if (!tag || !len || (*len && !data))
|
||||||
return j6_err_invalid_arg;
|
return j6_err_invalid_arg;
|
||||||
|
|
||||||
endpoint *e = get_handle<endpoint>(handle);
|
endpoint *e = get_handle<endpoint>(handle);
|
||||||
if (!e) return j6_err_invalid_arg;
|
if (!e) return j6_err_invalid_arg;
|
||||||
|
|
||||||
j6_tag_t out_tag = j6_tag_invalid;
|
j6_tag_t out_tag = j6_tag_invalid;
|
||||||
size_t out_len = *len;
|
size_t out_len = *len;
|
||||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||||
*tag = out_tag;
|
*tag = out_tag;
|
||||||
*len = out_len;
|
*len = out_len;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
endpoint_sendrecv(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
endpoint_sendrecv(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||||
{
|
{
|
||||||
if (!tag || (*tag & j6_tag_system_flag))
|
if (!tag || (*tag & j6_tag_system_flag))
|
||||||
return j6_err_invalid_arg;
|
return j6_err_invalid_arg;
|
||||||
|
|
||||||
endpoint *e = get_handle<endpoint>(handle);
|
endpoint *e = get_handle<endpoint>(handle);
|
||||||
if (!e) return j6_err_invalid_arg;
|
if (!e) return j6_err_invalid_arg;
|
||||||
|
|
||||||
j6_status_t status = e->send(*tag, *len, data);
|
j6_status_t status = e->send(*tag, *len, data);
|
||||||
if (status != j6_status_ok)
|
if (status != j6_status_ok)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
j6_tag_t out_tag = j6_tag_invalid;
|
j6_tag_t out_tag = j6_tag_invalid;
|
||||||
size_t out_len = *len;
|
size_t out_len = *len;
|
||||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||||
*tag = out_tag;
|
*tag = out_tag;
|
||||||
*len = out_len;
|
*len = out_len;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace syscalls
|
} // namespace syscalls
|
||||||
|
|||||||
@@ -11,38 +11,38 @@ namespace syscalls {
|
|||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
T * construct_handle(j6_handle_t *handle, Args... args)
|
T * construct_handle(j6_handle_t *handle, Args... args)
|
||||||
{
|
{
|
||||||
process &p = process::current();
|
process &p = process::current();
|
||||||
T *o = new T {args...};
|
T *o = new T {args...};
|
||||||
*handle = p.add_handle(o);
|
*handle = p.add_handle(o);
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T * get_handle(j6_handle_t handle)
|
T * get_handle(j6_handle_t handle)
|
||||||
{
|
{
|
||||||
process &p = process::current();
|
process &p = process::current();
|
||||||
kobject *o = p.lookup_handle(handle);
|
kobject *o = p.lookup_handle(handle);
|
||||||
if (!o || o->get_type() != T::type)
|
if (!o || o->get_type() != T::type)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return static_cast<T*>(o);
|
return static_cast<T*>(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline kobject * get_handle<kobject>(j6_handle_t handle)
|
inline kobject * get_handle<kobject>(j6_handle_t handle)
|
||||||
{
|
{
|
||||||
process &p = process::current();
|
process &p = process::current();
|
||||||
return p.lookup_handle(handle);
|
return p.lookup_handle(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T * remove_handle(j6_handle_t handle)
|
T * remove_handle(j6_handle_t handle)
|
||||||
{
|
{
|
||||||
T *o = get_handle<T>(handle);
|
T *o = get_handle<T>(handle);
|
||||||
if (o) {
|
if (o) {
|
||||||
process &p = process::current();
|
process &p = process::current();
|
||||||
p.remove_handle(handle);
|
p.remove_handle(handle);
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user