Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5b8902d8f | ||
|
|
799fbbdd10 | ||
|
|
d33f1bc6f2 | ||
|
|
28a90e550e | ||
|
|
647801f096 | ||
|
|
1664566bd2 | ||
|
|
cd09c17d71 | ||
|
|
f74f3f03d1 | ||
|
|
23006b2b43 | ||
|
|
7f69a6c9b1 | ||
|
|
1726d10554 | ||
|
|
757bc21550 | ||
|
|
e187679f93 | ||
|
|
2597e2002b | ||
|
|
e6f819ed90 | ||
|
|
0c8bcb2400 | ||
|
|
c5761cc51e | ||
|
|
24ccf65aba | ||
|
|
814d6f1de6 | ||
|
|
bfaab294e6 | ||
|
|
0ddcf668cb | ||
|
|
4005e9e791 | ||
|
|
abaa007c54 | ||
|
|
87d80f84c2 | ||
|
|
3fdf246a22 | ||
|
|
79b95d0045 | ||
|
|
1e66e5cd82 | ||
|
|
193d9939f0 | ||
|
|
81fc559802 | ||
|
|
0d75cc999c | ||
|
|
a5da56d02f | ||
|
|
a7e20fd390 | ||
|
|
9f38e7e5f5 | ||
|
|
93e60cc136 | ||
|
|
5f7ec50055 | ||
|
|
ff0019841f | ||
|
|
7eeeced2ca | ||
|
|
0fc369789e | ||
|
|
09f72f5ac6 | ||
|
|
716109bab5 | ||
|
|
0684fcf7e9 |
4
NOTES.md
4
NOTES.md
@@ -11,6 +11,9 @@
|
||||
- Serial out based on circular/bip biffer and interrupts, not spinning on
|
||||
`write_ready()`
|
||||
- Split out more code into kutil for testing
|
||||
- AHCI / MSI interrupts on Vbox break?
|
||||
- FXSAVE to save XMM registers.
|
||||
- optimization using #NM (0x7) to detect SSE usage
|
||||
|
||||
|
||||
- Device Tree
|
||||
@@ -21,4 +24,3 @@
|
||||
- Multiprocessing
|
||||
- Processes in Ring 3
|
||||
- Stack tracer
|
||||
- build system upgrade (CMake / Waf / etc)
|
||||
|
||||
12
scripts/vmem_translate.py
Executable file
12
scripts/vmem_translate.py
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
def translate(i4 = 0, i3 = 0, i2 = 0, i1 = 0, offset = 0):
|
||||
addr = (i4 << 39) + (i3 << 30) + (i2 << 21) + (i1 << 12) + offset
|
||||
if addr & (1 << 47):
|
||||
addr |= 0xffff000000000000
|
||||
return addr
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
print("{:016x}".format(translate(*map(int, sys.argv[1:]))))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
OFFSET = 0xFFFF800000000000;
|
||||
OFFSET = 0xFFFFFF0000000000;
|
||||
. = OFFSET + 0x100000;
|
||||
|
||||
.header : {
|
||||
|
||||
@@ -215,7 +215,7 @@ loader_load_kernel(
|
||||
if (status == EFI_NOT_FOUND)
|
||||
continue;
|
||||
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", kernel_name);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_elf: %s", kernel_name);
|
||||
|
||||
data->font = (void *)((uint64_t)data->kernel + data->kernel_length);
|
||||
status = loader_load_font(bootsvc, root, data);
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_VIRT_ADDRESS
|
||||
#define KERNEL_VIRT_ADDRESS 0xFFFF800000000000
|
||||
#endif
|
||||
|
||||
#ifndef VIRTUAL_OFFSET
|
||||
#define VIRTUAL_OFFSET 0xf00000000
|
||||
#define KERNEL_VIRT_ADDRESS 0xFFFFFF0000000000
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_MEMTYPE
|
||||
|
||||
@@ -82,9 +82,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
status = loader_load_kernel(bootsvc, &load);
|
||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||
|
||||
con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
|
||||
con_printf(L" %u font bytes at 0x%x\r\n", load.font_length, load.font);
|
||||
con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data);
|
||||
con_printf(L" image bytes at 0x%x : %x\r\n", load.kernel, load.kernel_length);
|
||||
con_printf(L" font bytes at 0x%x : %x\r\n", load.font, load.font_length);
|
||||
con_printf(L" data bytes at 0x%x : %x\r\n", load.data, load.data_length);
|
||||
|
||||
struct kernel_header *version = (struct kernel_header *)load.kernel;
|
||||
if (version->magic != KERNEL_HEADER_MAGIC) {
|
||||
|
||||
@@ -191,6 +191,7 @@ memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
|
||||
case KERNEL_FONT_MEMTYPE:
|
||||
case KERNEL_DATA_MEMTYPE:
|
||||
d->Attribute |= EFI_MEMORY_RUNTIME;
|
||||
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
|
||||
|
||||
default:
|
||||
if (d->Attribute & EFI_MEMORY_RUNTIME) {
|
||||
|
||||
@@ -16,13 +16,3 @@ ahci_driver::register_device(pci_device *device)
|
||||
|
||||
ahci::hba &hba = m_devices.emplace(device);
|
||||
}
|
||||
|
||||
ahci::port *
|
||||
ahci_driver::find_disk()
|
||||
{
|
||||
for (auto &hba : m_devices) {
|
||||
ahci::port *d = hba.find_disk();
|
||||
if (d) return d;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,6 @@ public:
|
||||
/// \arg device The PCI device to remove
|
||||
void unregister_device(pci_device *device);
|
||||
|
||||
/// Debug: find the first disk
|
||||
ahci::port * find_disk();
|
||||
|
||||
private:
|
||||
kutil::vector<ahci::hba> m_devices;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <stdint.h>
|
||||
#include "ahci/ata.h"
|
||||
#include "ahci/hba.h"
|
||||
#include "console.h"
|
||||
#include "device_manager.h"
|
||||
#include "fs/gpt.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
#include "pci.h"
|
||||
@@ -57,10 +59,14 @@ void irq_cb(void *data)
|
||||
hba::hba(pci_device *device)
|
||||
{
|
||||
page_manager *pm = page_manager::get();
|
||||
device_manager &dm = device_manager::get();
|
||||
|
||||
uint32_t bar5 = device->get_bar(5);
|
||||
m_data = reinterpret_cast<hba_data *>(bar5 & ~0xfffull);
|
||||
pm->map_offset_pointer(reinterpret_cast<void **>(&m_data), 0x2000);
|
||||
log::debug(logs::driver, "HBA raw BAR5 is %08lx", bar5);
|
||||
|
||||
void *data = reinterpret_cast<void *>(bar5 & ~0xfffull);
|
||||
pm->map_offset_pointer(&data, 0x2000);
|
||||
m_data = reinterpret_cast<hba_data volatile *>(data);
|
||||
|
||||
if (! bitfield_has(m_data->cap, hba_cap::ahci_only))
|
||||
m_data->host_control |= 0x80000000; // Enable AHCI mode
|
||||
@@ -70,17 +76,17 @@ hba::hba(pci_device *device)
|
||||
unsigned ports = (icap & 0xf) + 1;
|
||||
unsigned slots = ((icap >> 8) & 0x1f) + 1;
|
||||
|
||||
log::debug(logs::driver, " %d ports", ports);
|
||||
log::debug(logs::driver, " %d ports: %08x", ports, m_data->port_impl);
|
||||
log::debug(logs::driver, " %d command slots", slots);
|
||||
|
||||
port_data *pd = reinterpret_cast<port_data *>(
|
||||
auto *pd = reinterpret_cast<port_data volatile *>(
|
||||
kutil::offset_pointer(m_data, 0x100));
|
||||
|
||||
bool needs_interrupt = false;
|
||||
m_ports.ensure_capacity(ports);
|
||||
for (unsigned i = 0; i < ports; ++i) {
|
||||
bool impl = ((m_data->port_impl & (1 << i)) != 0);
|
||||
port &p = m_ports.emplace(i, kutil::offset_pointer(pd, 0x80 * i), impl);
|
||||
port &p = m_ports.emplace(this, i, kutil::offset_pointer(pd, 0x80 * i), impl);
|
||||
if (p.get_state() == port::state::active)
|
||||
needs_interrupt = true;
|
||||
}
|
||||
@@ -89,29 +95,48 @@ hba::hba(pci_device *device)
|
||||
device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this);
|
||||
m_data->host_control |= 0x02; // enable interrupts
|
||||
}
|
||||
}
|
||||
|
||||
port *
|
||||
hba::find_disk()
|
||||
{
|
||||
for (auto &port : m_ports) {
|
||||
if (port.get_state() == port::state::active &&
|
||||
port.get_type() == sata_signature::sata_drive)
|
||||
return &port;
|
||||
for (auto &p : m_ports) {
|
||||
if (!p.active()) continue;
|
||||
|
||||
if (p.get_type() == sata_signature::sata_drive) {
|
||||
p.sata_reconnect();
|
||||
/*
|
||||
if (fs::partition::load(&p) == 0)
|
||||
dm.register_block_device(&p);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
hba::handle_interrupt()
|
||||
{
|
||||
uint32_t status = m_data->int_status;
|
||||
for (auto &port : m_ports) {
|
||||
if (m_data->int_status & (1 << port.index())) {
|
||||
if (status & (1 << port.index())) {
|
||||
port.handle_interrupt();
|
||||
}
|
||||
}
|
||||
m_data->int_status = 0;
|
||||
// Write 1 to the handled interrupts
|
||||
m_data->int_status = status;
|
||||
}
|
||||
|
||||
void
|
||||
hba::dump()
|
||||
{
|
||||
console *cons = console::get();
|
||||
static const char *regs[] = {
|
||||
" CAP", " GHC", " IS", " PI", " VS", " C3C",
|
||||
" C3P", " EML", " EMC", "CAP2", "BOHC"
|
||||
};
|
||||
|
||||
cons->printf("HBA Registers:\n");
|
||||
auto *data = reinterpret_cast<uint32_t volatile *>(m_data);
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
cons->printf(" %s: %08x\n", regs[i], data[i]);
|
||||
}
|
||||
cons->putc('\n');
|
||||
}
|
||||
|
||||
} // namespace ahci
|
||||
|
||||
@@ -25,12 +25,12 @@ public:
|
||||
/// Interrupt handler.
|
||||
void handle_interrupt();
|
||||
|
||||
/// Debug: find the first disk
|
||||
port * find_disk();
|
||||
/// Dump the HBA registers to the console
|
||||
void dump();
|
||||
|
||||
private:
|
||||
pci_device *m_device;
|
||||
hba_data *m_data;
|
||||
hba_data volatile *m_data;
|
||||
kutil::vector<port> m_ports;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,13 +2,21 @@
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "ahci/ata.h"
|
||||
#include "ahci/hba.h"
|
||||
#include "ahci/fis.h"
|
||||
#include "ahci/port.h"
|
||||
#include "console.h"
|
||||
#include "io.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
|
||||
namespace ahci {
|
||||
enum class cmd_list_flags : uint16_t;
|
||||
}
|
||||
|
||||
IS_BITFIELD(ahci::port_cmd);
|
||||
IS_BITFIELD(volatile ahci::port_cmd);
|
||||
IS_BITFIELD(ahci::cmd_list_flags);
|
||||
|
||||
namespace ahci {
|
||||
|
||||
@@ -110,10 +118,11 @@ struct port_data
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
port::port(uint8_t index, port_data *data, bool impl) :
|
||||
port::port(hba *device, uint8_t index, port_data volatile *data, bool impl) :
|
||||
m_index(index),
|
||||
m_type(sata_signature::none),
|
||||
m_state(state::unimpl),
|
||||
m_hba(device),
|
||||
m_data(data),
|
||||
m_fis(nullptr),
|
||||
m_cmd_list(nullptr),
|
||||
@@ -154,7 +163,13 @@ port::update()
|
||||
|
||||
rebase();
|
||||
m_pending.set_size(32);
|
||||
m_data->interrupt_enable = 1;
|
||||
for (auto &pend : m_pending) {
|
||||
pend.type = command_type::none;
|
||||
}
|
||||
|
||||
// Clear any old pending interrupts and enable interrupts
|
||||
m_data->interrupt_status = m_data->interrupt_status;
|
||||
m_data->interrupt_enable = 0xffffffff;
|
||||
} else {
|
||||
m_state = state::inactive;
|
||||
}
|
||||
@@ -190,19 +205,27 @@ port::stop_commands()
|
||||
}
|
||||
|
||||
int
|
||||
port::make_command(size_t length)
|
||||
port::make_command(size_t length, fis_register_h2d **fis)
|
||||
{
|
||||
int slot = -1;
|
||||
uint32_t used_slots = (m_data->serial_active | m_data->cmd_issue);
|
||||
uint32_t used_slots =
|
||||
m_data->serial_active |
|
||||
m_data->cmd_issue |
|
||||
m_data->interrupt_status;
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
if ((used_slots & (1 << i)) == 0) {
|
||||
if (used_slots & (1 << i)) continue;
|
||||
|
||||
if (m_pending[i].type == command_type::none) {
|
||||
slot = i;
|
||||
break;
|
||||
} else {
|
||||
log::debug(logs::driver, "Type is %d", m_pending[i].type);
|
||||
}
|
||||
}
|
||||
|
||||
if (slot < 0) {
|
||||
log::info(logs::driver, "AHCI could not get a free command slot.");
|
||||
log::error(logs::driver, "AHCI could not get a free command slot.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -214,6 +237,14 @@ port::make_command(size_t length)
|
||||
kutil::memset(&cmdt, 0, sizeof(cmd_table) +
|
||||
max_prd_count * sizeof(prdt_entry));
|
||||
|
||||
ent.flags = cmd_list_fis_size(sizeof(fis_register_h2d));
|
||||
|
||||
fis_register_h2d *cfis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
|
||||
kutil::memset(cfis, 0, sizeof(fis_register_h2d));
|
||||
cfis->type = fis_type::register_h2d;
|
||||
cfis->pm_port = 0x80; // set command register flag
|
||||
*fis = cfis;
|
||||
|
||||
size_t remaining = length;
|
||||
for (int i = 0; i < max_prd_count; ++i) {
|
||||
size_t prd_len = std::min(remaining, 0x200000ul);
|
||||
@@ -240,21 +271,20 @@ port::make_command(size_t length)
|
||||
return slot;
|
||||
}
|
||||
|
||||
bool
|
||||
port::read(uint64_t sector, size_t length, void *dest)
|
||||
int
|
||||
port::read_async(uint64_t offset, size_t length, void *dest)
|
||||
{
|
||||
int slot = make_command(length);
|
||||
fis_register_h2d *fis;
|
||||
int slot = make_command(length, &fis);
|
||||
if (slot < 0)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
|
||||
fis_register_h2d *fis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
|
||||
fis->type = fis_type::register_h2d;
|
||||
fis->pm_port = 0x80; // set command register flag
|
||||
fis->command = ata_cmd::read_dma_ext;
|
||||
fis->device = 0x40; // ATA8-ACS p.175
|
||||
|
||||
uint64_t sector = offset >> 9;
|
||||
fis->lba0 = (sector ) & 0xff;
|
||||
fis->lba1 = (sector >> 8) & 0xff;
|
||||
fis->lba2 = (sector >> 16) & 0xff;
|
||||
@@ -270,8 +300,64 @@ port::read(uint64_t sector, size_t length, void *dest)
|
||||
count, sector, sector*512);
|
||||
|
||||
m_pending[slot].type = command_type::read;
|
||||
m_pending[slot].offset = offset % 512;
|
||||
m_pending[slot].count = 0;
|
||||
m_pending[slot].data = dest;
|
||||
return issue_command(slot);
|
||||
if(issue_command(slot))
|
||||
return slot;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t
|
||||
port::read(uint64_t offset, size_t length, void *dest)
|
||||
{
|
||||
int slot = read_async(offset, length, dest);
|
||||
|
||||
int timeout = 0;
|
||||
while (m_pending[slot].type == command_type::read) {
|
||||
if (timeout++ > 5) {
|
||||
return 0;
|
||||
}
|
||||
asm("hlt");
|
||||
}
|
||||
kassert(m_pending[slot].type == command_type::finished,
|
||||
"Read got unexpected command type");
|
||||
|
||||
size_t count = m_pending[slot].count;
|
||||
m_pending[slot].type = command_type::none;
|
||||
m_pending[slot].count = 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
port::identify_async()
|
||||
{
|
||||
fis_register_h2d *fis;
|
||||
int slot = make_command(512, &fis);
|
||||
if (slot < 0)
|
||||
return 0;
|
||||
|
||||
fis->command = ata_cmd::identify;
|
||||
|
||||
m_pending[slot].type = command_type::identify;
|
||||
m_pending[slot].offset = 0;
|
||||
m_pending[slot].count = 0;
|
||||
m_pending[slot].data = 0;
|
||||
|
||||
if(issue_command(slot))
|
||||
return slot;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
port::sata_reconnect()
|
||||
{
|
||||
m_data->serial_control |= 1;
|
||||
io_wait(1000); // About 1ms
|
||||
m_data->serial_control &= ~1;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -297,14 +383,23 @@ port::issue_command(int slot)
|
||||
void
|
||||
port::handle_interrupt()
|
||||
{
|
||||
log::debug(logs::driver, "AHCI port %d got an interrupt");
|
||||
log::debug(logs::driver, "AHCI port %d got an interrupt", m_index);
|
||||
|
||||
// TODO: handle other states in interrupt_status
|
||||
|
||||
if (m_data->interrupt_status & 0x40000000) {
|
||||
uint32_t is = m_data->interrupt_status;
|
||||
|
||||
if (is & 0x00000040) {
|
||||
// Port connect status change: For now clear the "exchange"
|
||||
// bit in SERR, this should probably kick off diagnostics.
|
||||
m_data->serial_error = 0x04000000;
|
||||
identify_async();
|
||||
}
|
||||
|
||||
if (is & 0x40000000) {
|
||||
log::error(logs::driver, "AHCI task file error");
|
||||
// TODO: clean up!
|
||||
return;
|
||||
dump();
|
||||
kassert(0, "Task file error");
|
||||
}
|
||||
|
||||
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
|
||||
@@ -312,19 +407,25 @@ port::handle_interrupt()
|
||||
|
||||
uint32_t ci = m_data->cmd_issue;
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
// Skip commands still listed as "issued"
|
||||
if (ci & (1 << i)) continue;
|
||||
|
||||
// Any commands not still listed as "issued" that are still pending for
|
||||
// the driver are now finished, so handle them.
|
||||
pending &p = m_pending[i];
|
||||
switch (p.type) {
|
||||
case command_type::read:
|
||||
finish_read(i);
|
||||
break;
|
||||
case command_type::identify:
|
||||
finish_identify(i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p.type = command_type::none;
|
||||
p.data = nullptr;
|
||||
}
|
||||
|
||||
// Clear the whole status register to mark it as handled
|
||||
m_data->interrupt_status = m_data->interrupt_status;
|
||||
}
|
||||
|
||||
@@ -335,7 +436,9 @@ port::finish_read(int slot)
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
cmd_list_entry &ent = m_cmd_list[slot];
|
||||
|
||||
size_t count = 0;
|
||||
void *p = m_pending[slot].data;
|
||||
uint8_t offset = m_pending[slot].offset;
|
||||
for (int i = 0; i < ent.prd_table_length; ++i) {
|
||||
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
|
||||
|
||||
@@ -343,13 +446,82 @@ port::finish_read(int slot)
|
||||
static_cast<addr_t>(cmdt.entries[i].data_base_low) |
|
||||
static_cast<addr_t>(cmdt.entries[i].data_base_high) << 32;
|
||||
|
||||
void *mem = pm->offset_virt(phys);
|
||||
void *mem = kutil::offset_pointer(pm->offset_virt(phys), offset);
|
||||
|
||||
log::debug(logs::driver, "Reading PRD %2d: %016lx->%016lx [%lxb]", i, mem, p, prd_len);
|
||||
|
||||
kutil::memcpy(p, mem, prd_len);
|
||||
p = kutil::offset_pointer(p, prd_len);
|
||||
p = kutil::offset_pointer(p, prd_len - offset);
|
||||
count += (prd_len - offset);
|
||||
offset = 0;
|
||||
|
||||
pm->unmap_pages(mem, page_count(prd_len));
|
||||
}
|
||||
|
||||
m_pending[slot].count = count;
|
||||
m_pending[slot].type = command_type::finished;
|
||||
m_pending[slot].data = nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
ident_strcpy(uint16_t *from, int words, char *dest)
|
||||
{
|
||||
for (int i = 0; i < words; ++i) {
|
||||
*dest++ = *from >> 8;
|
||||
*dest++ = *from & 0xff;
|
||||
from++;
|
||||
}
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
void
|
||||
port::finish_identify(int slot)
|
||||
{
|
||||
page_manager *pm = page_manager::get();
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
cmd_list_entry &ent = m_cmd_list[slot];
|
||||
|
||||
kassert(ent.prd_table_length == 1, "AHCI identify used multiple PRDs");
|
||||
|
||||
size_t prd_len = (cmdt.entries[0].byte_count & 0x7fffffff) + 1;
|
||||
|
||||
addr_t phys =
|
||||
static_cast<addr_t>(cmdt.entries[0].data_base_low) |
|
||||
static_cast<addr_t>(cmdt.entries[0].data_base_high) << 32;
|
||||
|
||||
log::debug(logs::driver, "Reading ident PRD:");
|
||||
|
||||
uint16_t *mem = reinterpret_cast<uint16_t *>(pm->offset_virt(phys));
|
||||
char string[41];
|
||||
|
||||
ident_strcpy(&mem[10], 10, &string[0]);
|
||||
log::debug(logs::driver, " Device serial: %s", string);
|
||||
|
||||
ident_strcpy(&mem[23], 4, &string[0]);
|
||||
log::debug(logs::driver, " Device version: %s", string);
|
||||
|
||||
ident_strcpy(&mem[27], 20, &string[0]);
|
||||
log::debug(logs::driver, " Device model: %s", string);
|
||||
|
||||
uint32_t sectors = mem[60] | (mem[61] << 16);
|
||||
log::debug(logs::driver, " Max sectors: %xh", sectors);
|
||||
|
||||
uint16_t lb_size = mem[106];
|
||||
log::debug(logs::driver, " lsects per psect: %d %s %s", 1 << (lb_size & 0xf),
|
||||
lb_size & 0x20 ? "multiple logical per physical" : "",
|
||||
lb_size & 0x10 ? "physical > 512b" : "");
|
||||
|
||||
uint32_t b_per_ls = 2 * (mem[117] | (mem[118] << 16));
|
||||
log::debug(logs::driver, " b per lsect: %d", b_per_ls);
|
||||
|
||||
/*
|
||||
for (int i=0; i<256; i += 4)
|
||||
log::debug(logs::driver, " %3d: %04x %3d: %04x %3d: %04x %3d: %04x",
|
||||
i, mem[i], i+1, mem[i+1], i+2, mem[i+2], i+3, mem[i+3]);
|
||||
*/
|
||||
|
||||
pm->unmap_pages(mem, page_count(prd_len));
|
||||
m_pending[slot].type = command_type::none;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -430,4 +602,22 @@ port::rebase()
|
||||
start_commands();
|
||||
}
|
||||
|
||||
void
|
||||
port::dump()
|
||||
{
|
||||
console *cons = console::get();
|
||||
static const char *regs[] = {
|
||||
" CLB", "+CLB", " FB", " +FB", " IS", " IE",
|
||||
" CMD", nullptr, " TFD", " SIG", "SSTS", "SCTL", "SERR",
|
||||
"SACT", " CI", "SNTF", " FBS", "DEVS"
|
||||
};
|
||||
|
||||
cons->printf("Port Registers:\n");
|
||||
auto *data = reinterpret_cast<volatile uint32_t *>(m_data);
|
||||
for (int i = 0; i < 18; ++i) {
|
||||
if (regs[i]) cons->printf(" %s: %08x\n", regs[i], data[i]);
|
||||
}
|
||||
cons->putc('\n');
|
||||
}
|
||||
|
||||
} // namespace ahci
|
||||
|
||||
@@ -4,25 +4,30 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "kutil/vector.h"
|
||||
#include "block_device.h"
|
||||
|
||||
namespace ahci {
|
||||
|
||||
struct cmd_list_entry;
|
||||
struct cmd_table;
|
||||
struct fis_register_h2d;
|
||||
class hba;
|
||||
enum class sata_signature : uint32_t;
|
||||
enum class port_cmd : uint32_t;
|
||||
struct port_data;
|
||||
|
||||
|
||||
/// A port on an AHCI HBA
|
||||
class port
|
||||
class port :
|
||||
public block_device
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg index Index of the port on its HBA
|
||||
/// \arg data Pointer to the device's registers for this port
|
||||
/// \arg impl Whether this port is marked as implemented in the HBA
|
||||
port(uint8_t index, port_data *data, bool impl);
|
||||
/// \arg device The HBA device this port belongs to
|
||||
/// \arg index Index of the port on its HBA
|
||||
/// \arg data Pointer to the device's registers for this port
|
||||
/// \arg impl Whether this port is marked as implemented in the HBA
|
||||
port(hba *device, uint8_t index, port_data volatile *data, bool impl);
|
||||
|
||||
/// Destructor
|
||||
~port();
|
||||
@@ -37,6 +42,10 @@ public:
|
||||
/// \returns An enum representing the state
|
||||
inline state get_state() const { return m_state; }
|
||||
|
||||
/// Check if this device is active
|
||||
/// \returns True if the device state is active
|
||||
inline bool active() const { return m_state == state::active; }
|
||||
|
||||
/// Get the type signature of this device
|
||||
/// \returns An enum representing the type of device
|
||||
inline sata_signature get_type() const { return m_type; }
|
||||
@@ -53,16 +62,34 @@ public:
|
||||
/// Stop command processing from this port
|
||||
void stop_commands();
|
||||
|
||||
/// Read data from the drive.
|
||||
/// \arg sector Starting sector to read
|
||||
/// Start a read operation from the drive.
|
||||
/// \arg offset Offset to start from
|
||||
/// \arg length Number of bytes to read
|
||||
/// \arg dest A buffer where the data will be placed
|
||||
/// \returns True if the command succeeded
|
||||
bool read(uint64_t sector, size_t length, void *dest);
|
||||
/// \returns A handle to the read operation, or -1 on error
|
||||
int read_async(uint64_t offset, size_t length, void *dest);
|
||||
|
||||
/// Read from the drive, blocking until finished.
|
||||
/// \arg offset Offset to start from
|
||||
/// \arg length Number of bytes to read
|
||||
/// \arg dest A buffer where the data will be placed
|
||||
/// \returns The number of bytes read
|
||||
virtual size_t read(uint64_t offset, size_t length, void *dest);
|
||||
|
||||
/// Start an identify operation for the drive.
|
||||
/// \returns A handle to the read operation, or -1 on error
|
||||
int identify_async();
|
||||
|
||||
/// Tell the HBA to reconnect to the SATA device. A successful
|
||||
/// reconnect will kick off an identify command.
|
||||
void sata_reconnect();
|
||||
|
||||
/// Handle an incoming interrupt
|
||||
void handle_interrupt();
|
||||
|
||||
/// Dump the port registers to the console
|
||||
void dump();
|
||||
|
||||
private:
|
||||
/// Rebase the port command structures to a new location in system
|
||||
/// memory, to be allocated from the page manager.
|
||||
@@ -70,8 +97,9 @@ private:
|
||||
|
||||
/// Initialize a command structure
|
||||
/// \arg length The number of bytes of data needed in the PRDs
|
||||
/// \arg fis [out] The FIS for this command
|
||||
/// \returns The index of the command slot, or -1 if none available
|
||||
int make_command(size_t length);
|
||||
int make_command(size_t length, fis_register_h2d **fis);
|
||||
|
||||
/// Send a constructed command to the hardware
|
||||
/// \arg slot The index of the command slot used
|
||||
@@ -88,20 +116,29 @@ private:
|
||||
/// \arg slot The command slot that the read command used
|
||||
void finish_read(int slot);
|
||||
|
||||
/// Finish an identify command started by `identify_async()`.
|
||||
/// This will free the structures allocated, so `free_command()` is
|
||||
/// not necessary.
|
||||
/// \arg slot The command slot that the read command used
|
||||
void finish_identify(int slot);
|
||||
|
||||
sata_signature m_type;
|
||||
uint8_t m_index;
|
||||
state m_state;
|
||||
|
||||
port_data *m_data;
|
||||
hba *m_hba;
|
||||
port_data volatile *m_data;
|
||||
void *m_fis;
|
||||
cmd_list_entry *m_cmd_list;
|
||||
cmd_table *m_cmd_table;
|
||||
|
||||
enum class command_type : uint8_t { none, read, write };
|
||||
enum class command_type : uint8_t { none, read, write, identify, finished };
|
||||
|
||||
struct pending
|
||||
{
|
||||
command_type type;
|
||||
uint8_t offset;
|
||||
size_t count;
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
#include "kutil/memory_manager.h"
|
||||
#include "log.h"
|
||||
|
||||
kutil::memory_manager g_kernel_memory_manager;
|
||||
|
||||
// kutil malloc/free implementation
|
||||
namespace kutil {
|
||||
void * malloc(size_t n) { return g_kernel_memory_manager.allocate(n); }
|
||||
void free(void *p) { g_kernel_memory_manager.free(p); }
|
||||
|
||||
void *
|
||||
malloc(size_t n) {
|
||||
void *p = g_kernel_memory_manager.allocate(n);
|
||||
log::debug(logs::memory, "Heap allocated %ld bytes: %016lx", n, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void free(void *p) { g_kernel_memory_manager.free(p); }
|
||||
|
||||
}
|
||||
|
||||
@@ -52,28 +52,38 @@ lapic::lapic(uint32_t *base, isr spurious) :
|
||||
void
|
||||
lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat)
|
||||
{
|
||||
uint32_t divbits = 0;
|
||||
|
||||
switch (divisor) {
|
||||
case 1: divisor = 11; break;
|
||||
case 2: divisor = 0; break;
|
||||
case 4: divisor = 1; break;
|
||||
case 8: divisor = 2; break;
|
||||
case 16: divisor = 3; break;
|
||||
case 32: divisor = 8; break;
|
||||
case 64: divisor = 9; break;
|
||||
case 128: divisor = 10; break;
|
||||
case 1: divbits = 0xb; break;
|
||||
case 2: divbits = 0x0; break;
|
||||
case 4: divbits = 0x1; break;
|
||||
case 8: divbits = 0x2; break;
|
||||
case 16: divbits = 0x3; break;
|
||||
case 32: divbits = 0x8; break;
|
||||
case 64: divbits = 0x9; break;
|
||||
case 128: divbits = 0xa; break;
|
||||
default:
|
||||
kassert(0, "Invalid divisor passed to lapic::enable_timer");
|
||||
}
|
||||
|
||||
apic_write(m_base, 0x3e0, divisor);
|
||||
apic_write(m_base, 0x380, count);
|
||||
|
||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||
if (repeat)
|
||||
lvte |= 0x20000;
|
||||
|
||||
log::debug(logs::apic, "Enabling APIC timer with isr %d.", vector);
|
||||
apic_write(m_base, 0x320, lvte);
|
||||
apic_write(m_base, 0x3e0, divbits);
|
||||
|
||||
reset_timer(count);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
lapic::reset_timer(uint32_t count)
|
||||
{
|
||||
uint32_t remaining = apic_read(m_base, 0x380);
|
||||
apic_write(m_base, 0x380, count);
|
||||
return remaining;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -37,6 +37,15 @@ public:
|
||||
/// \arg repeat If false, this timer is one-off, otherwise repeating
|
||||
void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true);
|
||||
|
||||
/// Reset the timer countdown.
|
||||
/// \arg count The count of ticks before an interrupt, or 0 to stop the timer
|
||||
/// \returns The count of ticks that were remaining before reset
|
||||
uint32_t reset_timer(uint32_t count);
|
||||
|
||||
/// Stop the timer.
|
||||
/// \returns The count of ticks remaining before an interrupt was to happen
|
||||
inline uint32_t stop_timer() { return reset_timer(0); }
|
||||
|
||||
/// Enable interrupts for the LAPIC LINT0 pin.
|
||||
/// \arg num Local interrupt number (0 or 1)
|
||||
/// \arg vector Interrupt vector LINT0 should use
|
||||
|
||||
@@ -7,7 +7,7 @@ __kernel_assert(const char *file, unsigned line, const char *message)
|
||||
console *cons = console::get();
|
||||
if (cons) {
|
||||
cons->set_color(9 , 0);
|
||||
cons->puts("\n\n ERROR: ");
|
||||
cons->puts("\n\n ERROR: ");
|
||||
cons->puts(file);
|
||||
cons->puts(":");
|
||||
cons->put_dec(line);
|
||||
@@ -15,15 +15,6 @@ __kernel_assert(const char *file, unsigned line, const char *message)
|
||||
cons->puts(message);
|
||||
}
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movq %0, %%r8;"
|
||||
"movq %1, %%r9;"
|
||||
"movq %2, %%r10;"
|
||||
"movq $0, %%rdx;"
|
||||
"divq %%rdx;"
|
||||
: // no outputs
|
||||
: "r"((uint64_t)line), "r"(file), "r"(message)
|
||||
: "rax", "rdx", "r8", "r9", "r10");
|
||||
|
||||
while (1);
|
||||
__asm__ ( "int $0e7h" );
|
||||
while (1) __asm__ ("hlt");
|
||||
}
|
||||
|
||||
11
src/kernel/block_device.h
Normal file
11
src/kernel/block_device.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
/// \file block_device.h
|
||||
/// Interface definition for block devices
|
||||
#include <stddef.h>
|
||||
|
||||
/// Interface for block devices
|
||||
class block_device
|
||||
{
|
||||
public:
|
||||
virtual size_t read(size_t offset, size_t length, void *buffer) = 0;
|
||||
};
|
||||
@@ -28,8 +28,8 @@ _start:
|
||||
extern kernel_main
|
||||
call kernel_main
|
||||
|
||||
cli
|
||||
|
||||
; Kernel init is over, wait for the scheduler to
|
||||
; take over
|
||||
.hang:
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "kutil/coord.h"
|
||||
#include "kutil/guid.h"
|
||||
#include "kutil/memory.h"
|
||||
#include "console.h"
|
||||
#include "font.h"
|
||||
@@ -334,6 +335,23 @@ void console::vprintf(const char *fmt, va_list args)
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case 'G': {
|
||||
// Special: GUID type
|
||||
kutil::guid g = va_arg(args, kutil::guid);
|
||||
put_hex<uint32_t>(g.a, 8, '0');
|
||||
putc('-');
|
||||
put_hex<uint16_t>((g.a >> 32) & 0xffff, 4, '0');
|
||||
putc('-');
|
||||
put_hex<uint16_t>((g.a >> 48) & 0xffff, 4, '0');
|
||||
putc('-');
|
||||
put_hex<uint16_t>((kutil::byteswap(g.b) >> 16) & 0xffff, 4, '0');
|
||||
putc('-');
|
||||
put_hex<uint16_t>(kutil::byteswap(g.b) & 0xffff, 4, '0');
|
||||
put_hex<uint32_t>(kutil::byteswap(g.b >> 32), 8, '0');
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
switch (*r++) {
|
||||
case 'x': put_hex<uint64_t>(va_arg(args, uint64_t), right ? width : -width, pad); done = true; break;
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
struct cpu_state
|
||||
{
|
||||
uint64_t ds;
|
||||
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
|
||||
uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax;
|
||||
uint64_t interrupt, errorcode;
|
||||
uint64_t rip, cs, rflags, user_rsp, ss;
|
||||
};
|
||||
|
||||
class cpu_id
|
||||
{
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
global get_rsp
|
||||
get_rsp:
|
||||
mov rax, rsp
|
||||
ret
|
||||
|
||||
section .text
|
||||
global do_the_set_registers
|
||||
do_the_set_registers:
|
||||
mov rax, 0xdeadbeef0badc0de
|
||||
mov r8, rcx
|
||||
mov r9, rdi
|
||||
global get_rip
|
||||
get_rip:
|
||||
pop rax ; do the same thing as 'ret', except with 'jmp'
|
||||
jmp rax ; with the return address still in rax
|
||||
|
||||
global _halt
|
||||
_halt:
|
||||
|
||||
@@ -320,3 +320,9 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
|
||||
static_cast<uint16_t>(vector));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::register_block_device(block_device *blockdev)
|
||||
{
|
||||
m_blockdevs.append(blockdev);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
struct acpi_xsdt;
|
||||
struct acpi_apic;
|
||||
struct acpi_mcfg;
|
||||
class block_device;
|
||||
class lapic;
|
||||
class ioapic;
|
||||
|
||||
@@ -65,6 +66,23 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Register the existance of a block device.
|
||||
/// \arg blockdev Pointer to the block device
|
||||
void register_block_device(block_device *blockdev);
|
||||
|
||||
/// Get the number of block devices in the system
|
||||
/// \returns A count of devices
|
||||
inline unsigned get_num_block_devices() const { return m_blockdevs.count(); }
|
||||
|
||||
/// Get a block device
|
||||
/// \arg i Index of the device to get
|
||||
/// \returns A pointer to the requested device, or nullptr
|
||||
inline block_device * get_block_device(unsigned i)
|
||||
{
|
||||
return i < m_blockdevs.count() ?
|
||||
m_blockdevs[i] : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Parse the ACPI XSDT and load relevant sub-tables.
|
||||
/// \arg xsdt Pointer to the XSDT from the firmware
|
||||
@@ -100,6 +118,8 @@ private:
|
||||
};
|
||||
kutil::vector<irq_allocation> m_irqs;
|
||||
|
||||
kutil::vector<block_device *> m_blockdevs;
|
||||
|
||||
static device_manager s_instance;
|
||||
|
||||
device_manager() = delete;
|
||||
|
||||
114
src/kernel/fs/gpt.cpp
Normal file
114
src/kernel/fs/gpt.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/guid.h"
|
||||
#include "kutil/memory.h"
|
||||
#include "device_manager.h"
|
||||
#include "fs/gpt.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace fs {
|
||||
|
||||
const kutil::guid efi_system_part = kutil::make_guid(0xC12A7328, 0xF81F, 0x11D2, 0xBA4B, 0x00A0C93EC93B);
|
||||
const kutil::guid efi_unused_part = kutil::make_guid(0, 0, 0, 0, 0);
|
||||
|
||||
const uint64_t gpt_signature = 0x5452415020494645; // "EFI PART"
|
||||
const size_t block_size = 512;
|
||||
|
||||
struct gpt_header
|
||||
{
|
||||
uint64_t signature;
|
||||
uint32_t revision;
|
||||
uint32_t headersize;
|
||||
uint32_t crc32;
|
||||
uint32_t reserved;
|
||||
uint64_t my_lba;
|
||||
uint64_t alt_lba;
|
||||
uint64_t first_usable_lba;
|
||||
uint64_t last_usable_lba;
|
||||
kutil::guid disk_guid;
|
||||
uint64_t table_lba;
|
||||
uint32_t entry_count;
|
||||
uint32_t entry_length;
|
||||
uint32_t table_crc32;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gpt_entry
|
||||
{
|
||||
kutil::guid type;
|
||||
kutil::guid part_guid;
|
||||
uint64_t start_lba;
|
||||
uint64_t end_lba;
|
||||
uint64_t attributes;
|
||||
uint16_t name_wide[36];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
partition::partition(block_device *parent, size_t start, size_t length) :
|
||||
m_parent(parent),
|
||||
m_start(start),
|
||||
m_length(length)
|
||||
{
|
||||
}
|
||||
|
||||
size_t
|
||||
partition::read(size_t offset, size_t length, void *buffer)
|
||||
{
|
||||
if (offset + length > m_length)
|
||||
offset = m_length - offset;
|
||||
return m_parent->read(m_start + offset, length, buffer);
|
||||
}
|
||||
|
||||
|
||||
unsigned
|
||||
partition::load(block_device *device)
|
||||
{
|
||||
// Read LBA 1
|
||||
uint8_t block[block_size];
|
||||
size_t count = device->read(block_size, block_size, &block);
|
||||
kassert(count == block_size, "Short read for GPT header.");
|
||||
|
||||
gpt_header *header = reinterpret_cast<gpt_header *>(&block);
|
||||
if (header->signature != gpt_signature)
|
||||
return 0;
|
||||
|
||||
size_t arraysize = header->entry_length * header->entry_count;
|
||||
log::debug(logs::fs, "Found GPT header: %d paritions, size 0x%lx",
|
||||
header->entry_count, arraysize);
|
||||
|
||||
uint8_t *array = new uint8_t[arraysize];
|
||||
count = device->read(block_size * header->table_lba, arraysize, array);
|
||||
kassert(count == arraysize, "Short read for GPT entry array.");
|
||||
|
||||
auto &dm = device_manager::get();
|
||||
|
||||
unsigned found = 0;
|
||||
gpt_entry *entry0 = reinterpret_cast<gpt_entry *>(array);
|
||||
for (uint32_t i = 0; i < header->entry_count; ++i) {
|
||||
gpt_entry *entry = kutil::offset_pointer(entry0, i * header->entry_length);
|
||||
if (entry->type == efi_unused_part) continue;
|
||||
|
||||
// TODO: real UTF16->UTF8
|
||||
char name[sizeof(gpt_entry::name_wide) / 2];
|
||||
for (int i = 0; i < sizeof(name); ++i)
|
||||
name[i] = entry->name_wide[i];
|
||||
|
||||
log::debug(logs::fs, "Found partition %02x at %lx-%lx", i, entry->start_lba, entry->end_lba);
|
||||
if (entry->type == efi_system_part)
|
||||
log::debug(logs::fs, " type EFI SYSTEM PARTITION");
|
||||
else
|
||||
log::debug(logs::fs, " type %G", entry->type);
|
||||
log::debug(logs::fs, " name %s", name);
|
||||
log::debug(logs::fs, " attr %016lx", entry->attributes);
|
||||
|
||||
found += 1;
|
||||
partition *part = new partition(
|
||||
device,
|
||||
entry->start_lba * block_size,
|
||||
(entry->end_lba - entry->start_lba) * block_size);
|
||||
dm.register_block_device(part);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
} // namespace fs
|
||||
37
src/kernel/fs/gpt.h
Normal file
37
src/kernel/fs/gpt.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
/// \file gpt.h
|
||||
/// Definitions for dealing with GUID Partition Tables
|
||||
#include "block_device.h"
|
||||
|
||||
namespace fs {
|
||||
|
||||
|
||||
class partition :
|
||||
public block_device
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg parent The block device this partition is a part of
|
||||
/// \arg start The starting offset in bytes from the start of the parent
|
||||
/// \arg lenght The length in bytes of this partition
|
||||
partition(block_device *parent, size_t start, size_t length);
|
||||
|
||||
/// Read bytes from the partition.
|
||||
/// \arg offset The offset in bytes at which to start reading
|
||||
/// \arg length The number of bytes to read
|
||||
/// \arg buffer [out] Data is read into this buffer
|
||||
/// \returns The number of bytes read
|
||||
virtual size_t read(size_t offset, size_t length, void *buffer);
|
||||
|
||||
/// Find partitions on a block device and add them to the device manager
|
||||
/// \arg device The device to search for partitions
|
||||
/// \returns The number of partitions found
|
||||
static unsigned load(block_device *device);
|
||||
|
||||
private:
|
||||
block_device *m_parent;
|
||||
size_t m_start;
|
||||
size_t m_length;
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
273
src/kernel/gdt.cpp
Normal file
273
src/kernel/gdt.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "kutil/memory.h"
|
||||
#include "console.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
enum class gdt_type : uint8_t
|
||||
{
|
||||
accessed = 0x01,
|
||||
read_write = 0x02,
|
||||
conforming = 0x04,
|
||||
execute = 0x08,
|
||||
system = 0x10,
|
||||
ring1 = 0x20,
|
||||
ring2 = 0x40,
|
||||
ring3 = 0x60,
|
||||
present = 0x80
|
||||
};
|
||||
IS_BITFIELD(gdt_type);
|
||||
|
||||
struct gdt_descriptor
|
||||
{
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
gdt_type type;
|
||||
uint8_t size;
|
||||
uint8_t base_high;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct tss_descriptor
|
||||
{
|
||||
uint16_t limit_low;
|
||||
uint16_t base_00;
|
||||
uint8_t base_16;
|
||||
gdt_type type;
|
||||
uint8_t size;
|
||||
uint8_t base_24;
|
||||
uint32_t base_32;
|
||||
uint32_t reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct tss_entry
|
||||
{
|
||||
uint32_t reserved0;
|
||||
|
||||
uint64_t rsp[3]; // stack pointers for CPL 0-2
|
||||
uint64_t ist[8]; // ist[0] is reserved
|
||||
|
||||
uint64_t reserved1;
|
||||
uint16_t reserved2;
|
||||
uint16_t iomap_offset;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct idt_descriptor
|
||||
{
|
||||
uint16_t base_low;
|
||||
uint16_t selector;
|
||||
uint8_t ist;
|
||||
uint8_t flags;
|
||||
uint16_t base_mid;
|
||||
uint32_t base_high;
|
||||
uint32_t reserved; // must be zero
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct table_ptr
|
||||
{
|
||||
uint16_t limit;
|
||||
uint64_t base;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
gdt_descriptor g_gdt_table[10];
|
||||
idt_descriptor g_idt_table[256];
|
||||
table_ptr g_gdtr;
|
||||
table_ptr g_idtr;
|
||||
tss_entry g_tss;
|
||||
|
||||
|
||||
extern "C" {
|
||||
void idt_write();
|
||||
void idt_load();
|
||||
|
||||
void gdt_write(uint16_t cs, uint16_t ds, uint16_t tr);
|
||||
void gdt_load();
|
||||
}
|
||||
|
||||
void
|
||||
gdt_set_entry(uint8_t i, uint32_t base, uint64_t limit, bool is64, gdt_type type)
|
||||
{
|
||||
g_gdt_table[i].limit_low = limit & 0xffff;
|
||||
g_gdt_table[i].size = (limit >> 16) & 0xf;
|
||||
g_gdt_table[i].size |= (is64 ? 0xa0 : 0xc0);
|
||||
|
||||
g_gdt_table[i].base_low = base & 0xffff;
|
||||
g_gdt_table[i].base_mid = (base >> 16) & 0xff;
|
||||
g_gdt_table[i].base_high = (base >> 24) & 0xff;
|
||||
|
||||
g_gdt_table[i].type = type | gdt_type::system | gdt_type::present;
|
||||
}
|
||||
|
||||
void
|
||||
tss_set_entry(uint8_t i, uint64_t base, uint64_t limit)
|
||||
{
|
||||
tss_descriptor tssd;
|
||||
tssd.limit_low = limit & 0xffff;
|
||||
tssd.size = (limit >> 16) & 0xf;
|
||||
|
||||
tssd.base_00 = base & 0xffff;
|
||||
tssd.base_16 = (base >> 16) & 0xff;
|
||||
tssd.base_24 = (base >> 24) & 0xff;
|
||||
tssd.base_32 = (base >> 32) & 0xffffffff;
|
||||
tssd.reserved = 0;
|
||||
|
||||
tssd.type =
|
||||
gdt_type::accessed |
|
||||
gdt_type::execute |
|
||||
gdt_type::ring3 |
|
||||
gdt_type::present;
|
||||
kutil::memcpy(&g_gdt_table[i], &tssd, sizeof(tss_descriptor));
|
||||
}
|
||||
|
||||
void
|
||||
idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags)
|
||||
{
|
||||
g_idt_table[i].base_low = addr & 0xffff;
|
||||
g_idt_table[i].base_mid = (addr >> 16) & 0xffff;
|
||||
g_idt_table[i].base_high = (addr >> 32) & 0xffffffff;
|
||||
g_idt_table[i].selector = selector;
|
||||
g_idt_table[i].flags = flags;
|
||||
g_idt_table[i].ist = 0;
|
||||
g_idt_table[i].reserved = 0;
|
||||
}
|
||||
|
||||
void
|
||||
tss_set_stack(int ring, addr_t rsp)
|
||||
{
|
||||
kassert(ring < 3, "Bad ring passed to tss_set_stack.");
|
||||
g_tss.rsp[ring] = rsp;
|
||||
}
|
||||
|
||||
addr_t
|
||||
tss_get_stack(int ring)
|
||||
{
|
||||
kassert(ring < 3, "Bad ring passed to tss_get_stack.");
|
||||
return g_tss.rsp[ring];
|
||||
}
|
||||
|
||||
void
|
||||
gdt_init()
|
||||
{
|
||||
kutil::memset(&g_gdt_table, 0, sizeof(g_gdt_table));
|
||||
kutil::memset(&g_idt_table, 0, sizeof(g_idt_table));
|
||||
|
||||
g_gdtr.limit = sizeof(g_gdt_table) - 1;
|
||||
g_gdtr.base = reinterpret_cast<uint64_t>(&g_gdt_table);
|
||||
|
||||
// Kernel CS/SS - always 64bit
|
||||
gdt_set_entry(1, 0, 0xfffff, true, gdt_type::read_write | gdt_type::execute);
|
||||
gdt_set_entry(2, 0, 0xfffff, true, gdt_type::read_write);
|
||||
|
||||
// User CS32/SS/CS64 - layout expected by SYSRET
|
||||
gdt_set_entry(3, 0, 0xfffff, false, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute);
|
||||
gdt_set_entry(4, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write);
|
||||
gdt_set_entry(5, 0, 0xfffff, true, gdt_type::ring3 | gdt_type::read_write | gdt_type::execute);
|
||||
|
||||
kutil::memset(&g_tss, 0, sizeof(tss_entry));
|
||||
g_tss.iomap_offset = sizeof(tss_entry);
|
||||
|
||||
addr_t tss_base = reinterpret_cast<addr_t>(&g_tss);
|
||||
|
||||
// Note that this takes TWO GDT entries
|
||||
tss_set_entry(6, tss_base, sizeof(tss_entry));
|
||||
|
||||
gdt_write(1 << 3, 2 << 3, 6 << 3);
|
||||
|
||||
g_idtr.limit = sizeof(g_idt_table) - 1;
|
||||
g_idtr.base = reinterpret_cast<uint64_t>(&g_idt_table);
|
||||
|
||||
idt_write();
|
||||
}
|
||||
|
||||
void
|
||||
gdt_dump()
|
||||
{
|
||||
const table_ptr &table = g_gdtr;
|
||||
|
||||
console *cons = console::get();
|
||||
cons->printf(" GDT: loc:%lx size:%d\n", table.base, table.limit+1);
|
||||
|
||||
int count = (table.limit + 1) / sizeof(gdt_descriptor);
|
||||
const gdt_descriptor *gdt =
|
||||
reinterpret_cast<const gdt_descriptor *>(table.base);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint32_t base =
|
||||
(gdt[i].base_high << 24) |
|
||||
(gdt[i].base_mid << 16) |
|
||||
gdt[i].base_low;
|
||||
|
||||
uint32_t limit =
|
||||
static_cast<uint32_t>(gdt[i].size & 0x0f) << 16 |
|
||||
gdt[i].limit_low;
|
||||
|
||||
cons->printf(" %02d:", i);
|
||||
if (! bitfield_has(gdt[i].type, gdt_type::present)) {
|
||||
cons->puts(" Not Present\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
cons->printf(" Base %08x limit %05x ", base, limit);
|
||||
|
||||
switch (gdt[i].type & gdt_type::ring3) {
|
||||
case gdt_type::ring3: cons->puts("ring3"); break;
|
||||
case gdt_type::ring2: cons->puts("ring2"); break;
|
||||
case gdt_type::ring1: cons->puts("ring1"); break;
|
||||
default: cons->puts("ring0"); break;
|
||||
}
|
||||
|
||||
cons->printf(" %s %s %s %s %s %s %s\n",
|
||||
bitfield_has(gdt[i].type, gdt_type::accessed) ? "A" : " ",
|
||||
bitfield_has(gdt[i].type, gdt_type::read_write) ? "RW" : " ",
|
||||
bitfield_has(gdt[i].type, gdt_type::conforming) ? "C" : " ",
|
||||
bitfield_has(gdt[i].type, gdt_type::execute) ? "EX" : " ",
|
||||
bitfield_has(gdt[i].type, gdt_type::system) ? "S" : " ",
|
||||
(gdt[i].size & 0x80) ? "KB" : " B",
|
||||
(gdt[i].size & 0x60) == 0x20 ? "64" :
|
||||
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
idt_dump()
|
||||
{
|
||||
const table_ptr &table = g_idtr;
|
||||
|
||||
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1);
|
||||
|
||||
int count = (table.limit + 1) / sizeof(idt_descriptor);
|
||||
const idt_descriptor *idt =
|
||||
reinterpret_cast<const idt_descriptor *>(table.base);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint64_t base =
|
||||
(static_cast<uint64_t>(idt[i].base_high) << 32) |
|
||||
(static_cast<uint64_t>(idt[i].base_mid) << 16) |
|
||||
idt[i].base_low;
|
||||
|
||||
char const *type;
|
||||
switch (idt[i].flags & 0xf) {
|
||||
case 0x5: type = " 32tsk "; break;
|
||||
case 0x6: type = " 16int "; break;
|
||||
case 0x7: type = " 16trp "; break;
|
||||
case 0xe: type = " 32int "; break;
|
||||
case 0xf: type = " 32trp "; break;
|
||||
default: type = " ????? "; break;
|
||||
}
|
||||
|
||||
if (idt[i].flags & 0x80) {
|
||||
log::debug(logs::boot,
|
||||
" Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base,
|
||||
(idt[i].selector & 0x3),
|
||||
((idt[i].selector & 0x4) >> 2),
|
||||
(idt[i].selector >> 3),
|
||||
idt[i].ist,
|
||||
type,
|
||||
((idt[i].flags >> 5) & 0x3));
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/kernel/gdt.h
Normal file
32
src/kernel/gdt.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
/// \file gdt.h
|
||||
/// Definitions relating to system descriptor tables: GDT, IDT, TSS
|
||||
#include <stdint.h>
|
||||
#include "kutil/memory.h"
|
||||
|
||||
/// Set up the GDT and TSS, and switch segment registers to point
|
||||
/// to them.
|
||||
void gdt_init();
|
||||
|
||||
/// Set an entry in the IDT
|
||||
/// \arg i Index in the IDT (vector of the interrupt this handles)
|
||||
/// \arg addr Address of the handler
|
||||
/// \arg selector GDT selector to set when invoking this handler
|
||||
/// \arg flags Descriptor flags to set
|
||||
void idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags);
|
||||
|
||||
/// Set the stack pointer for a given ring in the TSS
|
||||
/// \arg ring Ring to set for (0-2)
|
||||
/// \arg rsp Stack pointer to set
|
||||
void tss_set_stack(int ring, addr_t rsp);
|
||||
|
||||
/// Get the stack pointer for a given ring in the TSS
|
||||
/// \arg ring Ring to get (0-2)
|
||||
/// \returns Stack pointers for that ring
|
||||
addr_t tss_get_stack(int ring);
|
||||
|
||||
/// Dump information about the current GDT to the screen
|
||||
void gdt_dump();
|
||||
|
||||
/// Dump information about the current IDT to the screen
|
||||
void idt_dump();
|
||||
@@ -134,6 +134,8 @@ IRQ (0x7d, 0x5d, irq5D)
|
||||
IRQ (0x7e, 0x5e, irq5E)
|
||||
IRQ (0x7f, 0x5f, irq5F)
|
||||
|
||||
ISR (0xe7, isrAssert)
|
||||
|
||||
ISR (0xec, isrTimer)
|
||||
ISR (0xed, isrLINT0)
|
||||
ISR (0xee, isrLINT1)
|
||||
|
||||
@@ -1,72 +1,18 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "kutil/memory.h"
|
||||
#include "console.h"
|
||||
#include "cpu.h"
|
||||
#include "device_manager.h"
|
||||
#include "gdt.h"
|
||||
#include "interrupts.h"
|
||||
#include "io.h"
|
||||
#include "log.h"
|
||||
|
||||
enum class gdt_flags : uint8_t
|
||||
{
|
||||
ac = 0x01,
|
||||
rw = 0x02,
|
||||
dc = 0x04,
|
||||
ex = 0x08,
|
||||
r1 = 0x20,
|
||||
r2 = 0x40,
|
||||
r3 = 0x60,
|
||||
pr = 0x80,
|
||||
|
||||
res_1 = 0x10
|
||||
};
|
||||
IS_BITFIELD(gdt_flags);
|
||||
|
||||
struct gdt_descriptor
|
||||
{
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
uint8_t flags;
|
||||
uint8_t granularity;
|
||||
uint8_t base_high;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct idt_descriptor
|
||||
{
|
||||
uint16_t base_low;
|
||||
uint16_t selector;
|
||||
uint8_t ist;
|
||||
uint8_t flags;
|
||||
uint16_t base_mid;
|
||||
uint32_t base_high;
|
||||
uint32_t reserved; // must be zero
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct table_ptr
|
||||
{
|
||||
uint16_t limit;
|
||||
uint64_t base;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
gdt_descriptor g_gdt_table[10];
|
||||
idt_descriptor g_idt_table[256];
|
||||
table_ptr g_gdtr;
|
||||
table_ptr g_idtr;
|
||||
|
||||
struct registers;
|
||||
#include "scheduler.h"
|
||||
|
||||
extern "C" {
|
||||
void idt_write();
|
||||
void idt_load();
|
||||
|
||||
void gdt_write();
|
||||
void gdt_load();
|
||||
|
||||
void isr_handler(registers);
|
||||
void irq_handler(registers);
|
||||
addr_t isr_handler(addr_t, cpu_state);
|
||||
addr_t irq_handler(addr_t, cpu_state);
|
||||
addr_t syscall_handler(addr_t, cpu_state);
|
||||
|
||||
#define ISR(i, name) extern void name ();
|
||||
#define EISR(i, name) extern void name ();
|
||||
@@ -77,9 +23,6 @@ extern "C" {
|
||||
#undef ISR
|
||||
}
|
||||
|
||||
void idt_dump(const table_ptr &table);
|
||||
void gdt_dump(const table_ptr &table);
|
||||
|
||||
isr
|
||||
operator+(const isr &lhs, int rhs)
|
||||
{
|
||||
@@ -103,33 +46,6 @@ get_irq(unsigned vector)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_gdt_entry(uint8_t i, uint32_t base, uint32_t limit, bool is64, gdt_flags flags)
|
||||
{
|
||||
g_gdt_table[i].limit_low = limit & 0xffff;
|
||||
|
||||
g_gdt_table[i].base_low = base & 0xffff;
|
||||
g_gdt_table[i].base_mid = (base >> 16) & 0xff;
|
||||
g_gdt_table[i].base_high = (base >> 24) & 0xff;
|
||||
|
||||
g_gdt_table[i].granularity =
|
||||
((limit >> 16) & 0xf) | (is64 ? 0xa0 : 0xc0);
|
||||
|
||||
g_gdt_table[i].flags = static_cast<uint8_t>(flags | gdt_flags::res_1 | gdt_flags::pr);
|
||||
}
|
||||
|
||||
void
|
||||
set_idt_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags)
|
||||
{
|
||||
g_idt_table[i].base_low = addr & 0xffff;
|
||||
g_idt_table[i].base_mid = (addr >> 16) & 0xffff;
|
||||
g_idt_table[i].base_high = (addr >> 32) & 0xffffffff;
|
||||
g_idt_table[i].selector = selector;
|
||||
g_idt_table[i].flags = flags;
|
||||
g_idt_table[i].ist = 0;
|
||||
g_idt_table[i].reserved = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
disable_legacy_pic()
|
||||
{
|
||||
@@ -164,48 +80,20 @@ enable_serial_interrupts()
|
||||
void
|
||||
interrupts_init()
|
||||
{
|
||||
kutil::memset(&g_gdt_table, 0, sizeof(g_gdt_table));
|
||||
kutil::memset(&g_idt_table, 0, sizeof(g_idt_table));
|
||||
|
||||
g_gdtr.limit = sizeof(g_gdt_table) - 1;
|
||||
g_gdtr.base = reinterpret_cast<uint64_t>(&g_gdt_table);
|
||||
|
||||
set_gdt_entry(1, 0, 0xfffff, false, gdt_flags::rw);
|
||||
set_gdt_entry(2, 0, 0xfffff, false, gdt_flags::rw | gdt_flags::ex | gdt_flags::dc);
|
||||
set_gdt_entry(3, 0, 0xfffff, false, gdt_flags::rw);
|
||||
set_gdt_entry(4, 0, 0xfffff, false, gdt_flags::rw | gdt_flags::ex);
|
||||
|
||||
set_gdt_entry(6, 0, 0xfffff, false, gdt_flags::rw);
|
||||
set_gdt_entry(7, 0, 0xfffff, true, gdt_flags::rw | gdt_flags::ex);
|
||||
|
||||
gdt_write();
|
||||
|
||||
g_idtr.limit = sizeof(g_idt_table) - 1;
|
||||
g_idtr.base = reinterpret_cast<uint64_t>(&g_idt_table);
|
||||
|
||||
#define ISR(i, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
|
||||
#define EISR(i, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
|
||||
#define IRQ(i, q, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
|
||||
#define ISR(i, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
|
||||
#define EISR(i, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
|
||||
#define IRQ(i, q, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
|
||||
#include "interrupt_isrs.inc"
|
||||
#undef IRQ
|
||||
#undef EISR
|
||||
#undef ISR
|
||||
|
||||
idt_write();
|
||||
disable_legacy_pic();
|
||||
enable_serial_interrupts();
|
||||
|
||||
log::info(logs::boot, "Interrupts enabled.");
|
||||
}
|
||||
|
||||
struct registers
|
||||
{
|
||||
uint64_t ds;
|
||||
uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
|
||||
uint64_t interrupt, errorcode;
|
||||
uint64_t rip, cs, eflags, user_esp, ss;
|
||||
};
|
||||
|
||||
#define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value));
|
||||
|
||||
extern "C" uint64_t get_frame(int frame);
|
||||
@@ -222,14 +110,16 @@ print_stacktrace(int skip = 0)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isr_handler(registers regs)
|
||||
addr_t
|
||||
isr_handler(addr_t return_rsp, cpu_state regs)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
switch (static_cast<isr>(regs.interrupt & 0xff)) {
|
||||
case isr::isrTimer:
|
||||
cons->puts("\nTICK\n");
|
||||
case isr::isrTimer: {
|
||||
scheduler &s = scheduler::get();
|
||||
return_rsp = s.tick(return_rsp);
|
||||
}
|
||||
break;
|
||||
|
||||
case isr::isrLINT0:
|
||||
@@ -248,11 +138,67 @@ isr_handler(registers regs)
|
||||
case isr::isrIgnore5:
|
||||
case isr::isrIgnore6:
|
||||
case isr::isrIgnore7:
|
||||
break;
|
||||
|
||||
/*
|
||||
cons->printf("\nIGNORED PIC INTERRUPT %d\n",
|
||||
(regs.interrupt % 0xff) - 0xf0);
|
||||
*/
|
||||
case isr::isrGPFault: {
|
||||
cons->set_color(9);
|
||||
cons->puts("\nGeneral Protection Fault:\n");
|
||||
cons->set_color();
|
||||
|
||||
cons->puts(" flags:");
|
||||
if (regs.errorcode & 0x01) cons->puts(" external");
|
||||
|
||||
int index = (regs.errorcode & 0xf8) >> 3;
|
||||
if (index) {
|
||||
switch (regs.errorcode & 0x06) {
|
||||
case 0:
|
||||
cons->printf(" GDT[%d]\n", index);
|
||||
gdt_dump();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
cons->printf(" IDT[%d]\n", index);
|
||||
idt_dump();
|
||||
break;
|
||||
|
||||
default:
|
||||
cons->printf(" LDT[%d]??\n", index);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cons->putc('\n');
|
||||
}
|
||||
|
||||
cons->puts("\n");
|
||||
print_reg(" ds", regs.ds);
|
||||
print_reg(" cs", regs.cs);
|
||||
print_reg(" ss", regs.ss);
|
||||
|
||||
|
||||
cons->puts("\n");
|
||||
print_reg("rax", regs.rax);
|
||||
print_reg("rbx", regs.rbx);
|
||||
print_reg("rcx", regs.rcx);
|
||||
print_reg("rdx", regs.rdx);
|
||||
print_reg("rdi", regs.rdi);
|
||||
print_reg("rsi", regs.rsi);
|
||||
|
||||
cons->puts("\n");
|
||||
print_reg("rbp", regs.rbp);
|
||||
|
||||
cons->puts("\n");
|
||||
print_reg("rip", regs.rip);
|
||||
print_stacktrace(2);
|
||||
|
||||
cons->puts("\nStack:\n");
|
||||
uint64_t sp = regs.user_rsp;
|
||||
while (sp <= regs.rbp) {
|
||||
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
|
||||
sp += sizeof(uint64_t);
|
||||
}
|
||||
}
|
||||
while(1) asm("hlt");
|
||||
break;
|
||||
|
||||
case isr::isrPageFault: {
|
||||
@@ -272,6 +218,7 @@ isr_handler(registers regs)
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
print_reg("cr2", cr2);
|
||||
|
||||
print_reg("rsp", regs.user_rsp);
|
||||
print_reg("rip", regs.rip);
|
||||
|
||||
cons->puts("\n");
|
||||
@@ -280,6 +227,28 @@ isr_handler(registers regs)
|
||||
while(1) asm("hlt");
|
||||
break;
|
||||
|
||||
case isr::isrAssert: {
|
||||
cons->set_color();
|
||||
|
||||
cons->puts("\n");
|
||||
print_reg("rax", regs.rax);
|
||||
print_reg("rbx", regs.rbx);
|
||||
print_reg("rcx", regs.rcx);
|
||||
print_reg("rdx", regs.rdx);
|
||||
print_reg("rdi", regs.rdi);
|
||||
print_reg("rsi", regs.rsi);
|
||||
|
||||
cons->puts("\n");
|
||||
print_reg("rbp", regs.rbp);
|
||||
print_reg("rsp", regs.user_rsp);
|
||||
|
||||
cons->puts("\n");
|
||||
print_reg("rip", regs.rip);
|
||||
print_stacktrace(2);
|
||||
}
|
||||
while(1) asm("hlt");
|
||||
break;
|
||||
|
||||
default:
|
||||
cons->set_color(9);
|
||||
cons->puts("\nReceived ISR interrupt:\n");
|
||||
@@ -293,7 +262,6 @@ isr_handler(registers regs)
|
||||
print_reg("rdi", regs.rdi);
|
||||
print_reg("rsi", regs.rsi);
|
||||
print_reg("rbp", regs.rbp);
|
||||
print_reg("rsp", regs.rsp);
|
||||
print_reg("rbx", regs.rbx);
|
||||
print_reg("rdx", regs.rdx);
|
||||
print_reg("rcx", regs.rcx);
|
||||
@@ -302,20 +270,21 @@ isr_handler(registers regs)
|
||||
|
||||
print_reg("rip", regs.rip);
|
||||
print_reg(" cs", regs.cs);
|
||||
print_reg(" ef", regs.eflags);
|
||||
print_reg("esp", regs.user_esp);
|
||||
print_reg(" ef", regs.rflags);
|
||||
print_reg("rsp", regs.user_rsp);
|
||||
print_reg(" ss", regs.ss);
|
||||
|
||||
cons->puts("\n");
|
||||
print_stacktrace(2);
|
||||
while(1) asm("hlt");
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
|
||||
|
||||
return return_rsp;
|
||||
}
|
||||
|
||||
void
|
||||
irq_handler(registers regs)
|
||||
addr_t
|
||||
irq_handler(addr_t return_rsp, cpu_state regs)
|
||||
{
|
||||
console *cons = console::get();
|
||||
uint8_t irq = get_irq(regs.interrupt);
|
||||
@@ -329,7 +298,6 @@ irq_handler(registers regs)
|
||||
print_reg("rdi", regs.rdi);
|
||||
print_reg("rsi", regs.rsi);
|
||||
print_reg("rbp", regs.rbp);
|
||||
print_reg("rsp", regs.rsp);
|
||||
print_reg("rbx", regs.rbx);
|
||||
print_reg("rdx", regs.rdx);
|
||||
print_reg("rcx", regs.rcx);
|
||||
@@ -338,87 +306,46 @@ irq_handler(registers regs)
|
||||
|
||||
print_reg("rip", regs.rip);
|
||||
print_reg(" cs", regs.cs);
|
||||
print_reg(" ef", regs.eflags);
|
||||
print_reg("esp", regs.user_esp);
|
||||
print_reg(" ef", regs.rflags);
|
||||
print_reg("rsp", regs.user_rsp);
|
||||
print_reg(" ss", regs.ss);
|
||||
while(1) asm("hlt");
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
|
||||
return return_rsp;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gdt_dump(const table_ptr &table)
|
||||
addr_t
|
||||
syscall_handler(addr_t return_rsp, cpu_state regs)
|
||||
{
|
||||
log::info(logs::boot, "Loaded GDT at: %lx size: %d bytes", table.base, table.limit+1);
|
||||
console *cons = console::get();
|
||||
cons->printf("SYSCALL\n");
|
||||
|
||||
int count = (table.limit + 1) / sizeof(gdt_descriptor);
|
||||
const gdt_descriptor *gdt =
|
||||
reinterpret_cast<const gdt_descriptor *>(table.base);
|
||||
cons->puts("\n");
|
||||
print_reg("rax", regs.rax);
|
||||
print_reg("rbx", regs.rbx);
|
||||
print_reg("rcx", regs.rcx);
|
||||
print_reg("rdx", regs.rdx);
|
||||
print_reg("rdi", regs.rdi);
|
||||
print_reg("rsi", regs.rsi);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint32_t base =
|
||||
(gdt[i].base_high << 24) |
|
||||
(gdt[i].base_mid << 16) |
|
||||
gdt[i].base_low;
|
||||
cons->puts("\n");
|
||||
print_reg(" r8", regs.r8);
|
||||
print_reg(" r9", regs.r9);
|
||||
print_reg("r10", regs.r10);
|
||||
print_reg("r11", regs.r11);
|
||||
print_reg("r12", regs.r12);
|
||||
print_reg("r13", regs.r13);
|
||||
print_reg("r14", regs.r14);
|
||||
print_reg("r15", regs.r15);
|
||||
|
||||
uint32_t limit =
|
||||
static_cast<uint32_t>(gdt[i].granularity & 0x0f) << 16 |
|
||||
gdt[i].limit_low;
|
||||
cons->puts("\n");
|
||||
print_reg("rbp", regs.rbp);
|
||||
print_reg("rsp", regs.user_rsp);
|
||||
print_reg("sp0", tss_get_stack(0));
|
||||
|
||||
if (gdt[i].flags & 0x80) {
|
||||
log::debug(logs::boot,
|
||||
" Entry %3d: Base %x limit %x privs %d flags %s%s%s%s%s%s",
|
||||
i, base, limit, ((gdt[i].flags >> 5) & 0x03),
|
||||
|
||||
(gdt[i].flags & 0x80) ? "P " : " ",
|
||||
(gdt[i].flags & 0x08) ? "ex " : " ",
|
||||
(gdt[i].flags & 0x04) ? "dc " : " ",
|
||||
(gdt[i].flags & 0x02) ? "rw " : " ",
|
||||
|
||||
(gdt[i].granularity & 0x80) ? "kb " : "b ",
|
||||
(gdt[i].granularity & 0x60) == 0x60 ? "64" :
|
||||
(gdt[i].granularity & 0x60) == 0x40 ? "32" : "16"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
idt_dump(const table_ptr &table)
|
||||
{
|
||||
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1);
|
||||
|
||||
int count = (table.limit + 1) / sizeof(idt_descriptor);
|
||||
const idt_descriptor *idt =
|
||||
reinterpret_cast<const idt_descriptor *>(table.base);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint64_t base =
|
||||
(static_cast<uint64_t>(idt[i].base_high) << 32) |
|
||||
(static_cast<uint64_t>(idt[i].base_mid) << 16) |
|
||||
idt[i].base_low;
|
||||
|
||||
char const *type;
|
||||
switch (idt[i].flags & 0xf) {
|
||||
case 0x5: type = " 32tsk "; break;
|
||||
case 0x6: type = " 16int "; break;
|
||||
case 0x7: type = " 16trp "; break;
|
||||
case 0xe: type = " 32int "; break;
|
||||
case 0xf: type = " 32trp "; break;
|
||||
default: type = " ????? "; break;
|
||||
}
|
||||
|
||||
if (idt[i].flags & 0x80) {
|
||||
log::debug(logs::boot,
|
||||
" Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base,
|
||||
(idt[i].selector & 0x3),
|
||||
((idt[i].selector & 0x4) >> 2),
|
||||
(idt[i].selector >> 3),
|
||||
idt[i].ist,
|
||||
type,
|
||||
((idt[i].flags >> 5) & 0x3));
|
||||
}
|
||||
}
|
||||
cons->puts("\n");
|
||||
print_reg("rip", regs.rip);
|
||||
return return_rsp;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
/// \file interrupts.h
|
||||
/// Free functions and definitions related to interrupt service vectors
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/// Enum of all defined ISR/IRQ vectors
|
||||
@@ -17,11 +18,17 @@ enum class isr : uint8_t
|
||||
_zero = 0
|
||||
};
|
||||
|
||||
/// Helper operator to add an offset to an isr vector
|
||||
isr operator+(const isr &lhs, int rhs);
|
||||
|
||||
extern "C" {
|
||||
/// Set the CPU interrupt enable flag (sti)
|
||||
void interrupts_enable();
|
||||
|
||||
/// Set the CPU interrupt disable flag (cli)
|
||||
void interrupts_disable();
|
||||
}
|
||||
|
||||
/// Fill the IDT with our ISRs, and disable the legacy
|
||||
/// PIC interrupts.
|
||||
void interrupts_init();
|
||||
|
||||
@@ -14,6 +14,18 @@ idt_load:
|
||||
global gdt_write
|
||||
gdt_write:
|
||||
lgdt [rel g_gdtr]
|
||||
mov ax, si ; second arg is data segment
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
push qword rdi ; first arg is code segment
|
||||
lea rax, [rel .next]
|
||||
push rax
|
||||
o64 retf
|
||||
.next:
|
||||
ltr dx
|
||||
ret
|
||||
|
||||
global gdt_load
|
||||
@@ -26,11 +38,19 @@ gdt_load:
|
||||
push rcx
|
||||
push rdx
|
||||
push rbx
|
||||
push rsp
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
mov ax, ds
|
||||
push rax
|
||||
%endmacro
|
||||
@@ -42,31 +62,32 @@ gdt_load:
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rsp
|
||||
pop rbx
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
%endmacro
|
||||
|
||||
%macro load_kernel_segments 0
|
||||
mov ax, 0x10 ; load the kernel data segment
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
%endmacro
|
||||
|
||||
extern isr_handler
|
||||
global isr_handler_prelude
|
||||
isr_handler_prelude:
|
||||
push_all_and_segments
|
||||
load_kernel_segments
|
||||
|
||||
mov rdi, rsp
|
||||
call isr_handler
|
||||
mov rsp, rax
|
||||
|
||||
pop_all_and_segments
|
||||
|
||||
@@ -78,9 +99,10 @@ extern irq_handler
|
||||
global irq_handler_prelude
|
||||
irq_handler_prelude:
|
||||
push_all_and_segments
|
||||
load_kernel_segments
|
||||
|
||||
mov rdi, rsp
|
||||
call irq_handler
|
||||
mov rsp, rax
|
||||
|
||||
pop_all_and_segments
|
||||
|
||||
@@ -120,3 +142,75 @@ irq_handler_prelude:
|
||||
|
||||
section .isrs
|
||||
%include "interrupt_isrs.inc"
|
||||
|
||||
extern syscall_handler
|
||||
syscall_handler_prelude:
|
||||
push 0 ; ss, doesn't matter here
|
||||
push rsp
|
||||
pushf
|
||||
push 0 ; cs, doesn't matter here
|
||||
push rcx ; user rip
|
||||
push 0 ; bogus interrupt
|
||||
push 0 ; bogus errorcode
|
||||
push_all_and_segments
|
||||
|
||||
mov rdi, rsp
|
||||
call syscall_handler
|
||||
mov rsp, rax
|
||||
|
||||
pop_all_and_segments
|
||||
add rsp, 16 ; ignore bogus interrupt / error
|
||||
pop rcx ; user rip
|
||||
add rsp, 32 ; ignore cs, flags, rsp, ss
|
||||
|
||||
o64 sysret
|
||||
|
||||
global syscall_enable
|
||||
syscall_enable:
|
||||
; IA32_EFER - set bit 0, syscall enable
|
||||
mov rcx, 0xc0000080
|
||||
rdmsr
|
||||
or rax, 0x1
|
||||
wrmsr
|
||||
|
||||
; IA32_STAR - cs for syscall
|
||||
mov rcx, 0xc0000081
|
||||
mov rax, 0 ; not used
|
||||
mov rdx, 0x00180008 ; GDT:3 (user code), GDT:1 (kernel code)
|
||||
wrmsr
|
||||
|
||||
; IA32_LSTAR - RIP for syscall
|
||||
mov rcx, 0xc0000082
|
||||
lea rax, [rel syscall_handler_prelude]
|
||||
mov rdx, rax
|
||||
shr rdx, 32
|
||||
wrmsr
|
||||
|
||||
; IA32_FMASK - FLAGS mask inside syscall
|
||||
mov rcx, 0xc0000084
|
||||
mov rax, 0x200
|
||||
mov rdx, 0
|
||||
wrmsr
|
||||
|
||||
|
||||
ret
|
||||
|
||||
global taskA
|
||||
taskA:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
mov rax, 0xaaaaaaaaaaaaaaaa
|
||||
|
||||
.loop:
|
||||
syscall
|
||||
jmp .loop
|
||||
|
||||
global taskB
|
||||
taskB:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
mov rax, 0xbbbbbbbbbbbbbbbb
|
||||
|
||||
.loop:
|
||||
syscall
|
||||
jmp .loop
|
||||
|
||||
@@ -31,7 +31,8 @@ wrmsr(uint64_t addr, uint64_t value)
|
||||
}
|
||||
|
||||
void
|
||||
io_wait()
|
||||
io_wait(unsigned times)
|
||||
{
|
||||
outb(0x80, 0);
|
||||
for (unsigned i = 0; i < times; ++i)
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ uint64_t rdmsr(uint64_t addr);
|
||||
void wrmsr(uint64_t addr, uint64_t value);
|
||||
|
||||
/// Pause briefly by doing IO to port 0x80
|
||||
void io_wait();
|
||||
/// \arg times Number of times to delay by writing
|
||||
void io_wait(unsigned times = 1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@ static const uint8_t level_colors[] = {0x07, 0x0f, 0x0b, 0x09};
|
||||
|
||||
static const char *levels[] = {"debug", " info", " warn", "error", "fatal"};
|
||||
static const char *areas[] = {
|
||||
"boot",
|
||||
"mem ",
|
||||
"apic",
|
||||
"dev ",
|
||||
"driv",
|
||||
"boot ",
|
||||
"memory",
|
||||
"apic ",
|
||||
"device",
|
||||
"driver",
|
||||
"file ",
|
||||
"task ",
|
||||
|
||||
nullptr
|
||||
};
|
||||
|
||||
@@ -12,6 +12,8 @@ enum class logs
|
||||
apic,
|
||||
device,
|
||||
driver,
|
||||
fs,
|
||||
task,
|
||||
|
||||
max
|
||||
};
|
||||
|
||||
@@ -3,32 +3,29 @@
|
||||
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/memory.h"
|
||||
|
||||
#include "ahci/driver.h"
|
||||
#include "ahci/port.h"
|
||||
|
||||
#include "block_device.h"
|
||||
#include "console.h"
|
||||
#include "cpu.h"
|
||||
#include "device_manager.h"
|
||||
#include "font.h"
|
||||
#include "gdt.h"
|
||||
#include "interrupts.h"
|
||||
#include "io.h"
|
||||
#include "kernel_data.h"
|
||||
#include "log.h"
|
||||
#include "memory.h"
|
||||
#include "page_manager.h"
|
||||
#include "scheduler.h"
|
||||
#include "screen.h"
|
||||
#include "serial.h"
|
||||
|
||||
extern "C" {
|
||||
void do_the_set_registers(popcorn_data *header);
|
||||
void kernel_main(popcorn_data *header);
|
||||
void syscall_enable();
|
||||
|
||||
void *__bss_start, *__bss_end;
|
||||
}
|
||||
|
||||
extern ahci_driver ahcid;
|
||||
|
||||
extern void __kernel_assert(const char *, unsigned, const char *);
|
||||
|
||||
void
|
||||
@@ -58,7 +55,9 @@ init_console(const popcorn_data *header)
|
||||
log::enable(logs::apic, log::level::info);
|
||||
log::enable(logs::device, log::level::debug);
|
||||
log::enable(logs::driver, log::level::debug);
|
||||
log::enable(logs::memory, log::level::debug);
|
||||
log::enable(logs::memory, log::level::info);
|
||||
log::enable(logs::fs, log::level::debug);
|
||||
log::enable(logs::task, log::level::debug);
|
||||
}
|
||||
|
||||
void do_error_3() { volatile int x = 1; volatile int y = 0; volatile int z = x / y; }
|
||||
@@ -70,6 +69,9 @@ kernel_main(popcorn_data *header)
|
||||
{
|
||||
kutil::assert_set_callback(__kernel_assert);
|
||||
|
||||
gdt_init();
|
||||
interrupts_init();
|
||||
|
||||
page_manager *pager = new (&g_page_manager) page_manager;
|
||||
|
||||
memory_initialize(
|
||||
@@ -82,43 +84,73 @@ kernel_main(popcorn_data *header)
|
||||
header->frame_buffer_length);
|
||||
|
||||
init_console(header);
|
||||
// pager->dump_blocks();
|
||||
|
||||
interrupts_init();
|
||||
log::debug(logs::boot, " Popcorn header is at: %016lx", header);
|
||||
log::debug(logs::boot, " Framebuffer is at: %016lx", header->frame_buffer);
|
||||
log::debug(logs::boot, " Font data is at: %016lx", header->font);
|
||||
log::debug(logs::boot, " Kernel data is at: %016lx", header->data);
|
||||
log::debug(logs::boot, " Memory map is at: %016lx", header->memory_map);
|
||||
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
|
||||
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime);
|
||||
|
||||
/*
|
||||
pager->dump_pml4(nullptr, 0);
|
||||
pager->dump_blocks(true);
|
||||
*/
|
||||
|
||||
device_manager *devices =
|
||||
new (&device_manager::get()) device_manager(header->acpi_table);
|
||||
|
||||
interrupts_enable();
|
||||
|
||||
/*
|
||||
cpu_id cpu;
|
||||
log::info(logs::boot, "CPU Vendor: %s", cpu.vendor_id());
|
||||
log::info(logs::boot, "CPU Family %x Model %x Stepping %x",
|
||||
cpu.family(), cpu.model(), cpu.stepping());
|
||||
|
||||
auto r = cpu.get(0x15);
|
||||
log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);
|
||||
|
||||
addr_t cr4 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr4, %0" : "=r" (cr4) );
|
||||
log::info(logs::boot, "cr4: %016x", cr4);
|
||||
*/
|
||||
|
||||
devices->init_drivers();
|
||||
|
||||
ahci::port *disk = ahcid.find_disk();
|
||||
/*
|
||||
block_device *disk = devices->get_block_device(0);
|
||||
if (disk) {
|
||||
uint8_t buf[512];
|
||||
kutil::memset(buf, 0, 512);
|
||||
for (int i=0; i<1; ++i) {
|
||||
uint8_t buf[512];
|
||||
kutil::memset(buf, 0, 512);
|
||||
|
||||
disk->read(1, sizeof(buf), buf);
|
||||
while (buf[0] == 0) io_wait();
|
||||
kassert(disk->read(0x200, sizeof(buf), buf),
|
||||
"Disk read returned 0");
|
||||
|
||||
console *cons = console::get();
|
||||
uint8_t *p = &buf[0];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
cons->printf(" %02x", *p++);
|
||||
console *cons = console::get();
|
||||
uint8_t *p = &buf[0];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
cons->printf(" %02x", *p++);
|
||||
}
|
||||
cons->putc('\n');
|
||||
}
|
||||
cons->putc('\n');
|
||||
}
|
||||
} else {
|
||||
log::warn(logs::boot, "No block devices present.");
|
||||
}
|
||||
*/
|
||||
|
||||
// do_error_1();
|
||||
// __asm__ __volatile__("int $15");
|
||||
|
||||
g_console.puts("boogity!");
|
||||
do_the_set_registers(header);
|
||||
// pager->dump_pml4();
|
||||
|
||||
syscall_enable();
|
||||
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic());
|
||||
sched->start();
|
||||
|
||||
g_console.puts("boogity!\n");
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "page_manager.h"
|
||||
|
||||
const unsigned efi_page_size = 0x1000;
|
||||
const unsigned ident_page_flags = 0xb;
|
||||
|
||||
enum class efi_memory_type : uint32_t
|
||||
{
|
||||
@@ -306,7 +307,7 @@ copy_new_table(page_table *base, unsigned index, page_table *new_table)
|
||||
for (int i = 0; i < 512; ++i) new_table->entries[i] = 0;
|
||||
}
|
||||
|
||||
base->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb;
|
||||
base->entries[index] = reinterpret_cast<uint64_t>(new_table) | ident_page_flags;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
@@ -363,7 +364,7 @@ check_needs_page_ident(page_table *table, unsigned index, page_table **free_page
|
||||
|
||||
page_table *new_table = (*free_pages)++;
|
||||
for (int i=0; i<512; ++i) new_table->entries[i] = 0;
|
||||
table->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb;
|
||||
table->entries[index] = reinterpret_cast<uint64_t>(new_table) | ident_page_flags;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -390,12 +391,24 @@ page_in_ident(
|
||||
tables[1]->entries[idx[1]] & ~0xfffull);
|
||||
|
||||
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) {
|
||||
if (idx[3] == 0 &&
|
||||
count >= 512 &&
|
||||
tables[2]->get(idx[2]) == nullptr) {
|
||||
// Do a 2MiB page instead
|
||||
tables[2]->entries[idx[2]] = phys_addr | 0x80 | ident_page_flags;
|
||||
|
||||
phys_addr += page_manager::page_size * 512;
|
||||
count -= 512;
|
||||
if (count == 0) return pages_consumed;
|
||||
continue;
|
||||
}
|
||||
|
||||
pages_consumed += check_needs_page_ident(tables[2], idx[2], &free_pages);
|
||||
tables[3] = reinterpret_cast<page_table *>(
|
||||
tables[2]->entries[idx[2]] & ~0xfffull);
|
||||
|
||||
for (; idx[3] < 512; idx[3] += 1) {
|
||||
tables[3]->entries[idx[3]] = phys_addr | 0xb;
|
||||
tables[3]->entries[idx[3]] = phys_addr | ident_page_flags;
|
||||
phys_addr += page_manager::page_size;
|
||||
if (--count == 0) return pages_consumed;
|
||||
}
|
||||
@@ -423,7 +436,7 @@ memory_initialize(const void *memory_map, size_t map_length, size_t desc_length)
|
||||
|
||||
// Offset-map this region into the higher half.
|
||||
uint64_t free_region_start_virt =
|
||||
free_region_start_phys + page_manager::high_offset;
|
||||
free_region_start_phys + page_manager::page_offset;
|
||||
|
||||
uint64_t free_next = free_region_start_virt;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/memory_manager.h"
|
||||
#include "console.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
|
||||
@@ -140,7 +141,7 @@ page_block::dump(page_block *list, const char *name, bool show_unmapped)
|
||||
|
||||
if (cur->virtual_address) {
|
||||
page_table_indices start{cur->virtual_address};
|
||||
log::info(logs::memory, " %lx %x [%6d] %lx (%d,%d,%d,%d)",
|
||||
log::info(logs::memory, " %016lx %08x [%6d] %016lx (%d,%d,%d,%d)",
|
||||
cur->physical_address,
|
||||
cur->flags,
|
||||
cur->count,
|
||||
@@ -148,7 +149,7 @@ page_block::dump(page_block *list, const char *name, bool show_unmapped)
|
||||
start[0], start[1], start[2], start[3]);
|
||||
} else {
|
||||
page_table_indices start{cur->virtual_address};
|
||||
log::info(logs::memory, " %lx %x [%6d]",
|
||||
log::info(logs::memory, " %016lx %08x [%6d]",
|
||||
cur->physical_address,
|
||||
cur->flags,
|
||||
cur->count);
|
||||
@@ -218,6 +219,34 @@ page_manager::init(
|
||||
new (&g_kernel_memory_manager) kutil::memory_manager(
|
||||
reinterpret_cast<void *>(end),
|
||||
mm_grow_callback);
|
||||
|
||||
m_kernel_pml4 = get_pml4();
|
||||
}
|
||||
|
||||
page_table *
|
||||
page_manager::create_process_map()
|
||||
{
|
||||
page_table *table = get_table_page();
|
||||
|
||||
kutil::memset(table, 0, page_size);
|
||||
table->entries[510] = m_kernel_pml4->entries[510];
|
||||
table->entries[511] = m_kernel_pml4->entries[511];
|
||||
|
||||
// Create the initial user stack
|
||||
map_pages(
|
||||
initial_stack - (initial_stack_pages * page_size),
|
||||
initial_stack_pages,
|
||||
true, // This is the ring3 stack, user = true
|
||||
table);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void
|
||||
page_manager::delete_process_map(page_table *table)
|
||||
{
|
||||
// TODO: recurse table entries and mark them free
|
||||
unmap_pages(table, 1);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -247,10 +276,19 @@ page_manager::map_offset_pointer(void **pointer, size_t length)
|
||||
}
|
||||
|
||||
void
|
||||
page_manager::dump_blocks()
|
||||
page_manager::dump_blocks(bool used_only)
|
||||
{
|
||||
page_block::dump(m_used, "used", true);
|
||||
page_block::dump(m_free, "free", true);
|
||||
if (!used_only)
|
||||
page_block::dump(m_free, "free", true);
|
||||
}
|
||||
|
||||
void
|
||||
page_manager::dump_pml4(page_table *pml4, int max_index)
|
||||
{
|
||||
if (pml4 == nullptr)
|
||||
pml4 = get_pml4();
|
||||
pml4->dump(4, max_index);
|
||||
}
|
||||
|
||||
page_block *
|
||||
@@ -338,10 +376,11 @@ page_manager::consolidate_blocks()
|
||||
}
|
||||
|
||||
void *
|
||||
page_manager::map_pages(addr_t address, size_t count)
|
||||
page_manager::map_pages(addr_t address, size_t count, bool user, page_table *pml4)
|
||||
{
|
||||
void *ret = reinterpret_cast<void *>(address);
|
||||
page_table *pml4 = get_pml4();
|
||||
if (pml4 == nullptr)
|
||||
pml4 = get_pml4();
|
||||
|
||||
while (count) {
|
||||
kassert(m_free, "page_manager::map_pages ran out of free pages!");
|
||||
@@ -358,7 +397,10 @@ page_manager::map_pages(addr_t address, size_t count)
|
||||
page_block_flags::mapped;
|
||||
page_block::insert(m_used, block);
|
||||
|
||||
page_in(pml4, phys, address, n);
|
||||
log::debug(logs::memory, "Paging in %d pages at p:%016lx to v:%016lx into %016lx table",
|
||||
n, phys, address, pml4);
|
||||
|
||||
page_in(pml4, phys, address, n, user);
|
||||
|
||||
address += n * page_size;
|
||||
count -= n;
|
||||
@@ -374,7 +416,6 @@ page_manager::map_offset_pages(size_t count)
|
||||
page_block *free = m_free;
|
||||
page_block *prev = nullptr;
|
||||
|
||||
log::debug(logs::memory, "Got request to offset map %d pages", count);
|
||||
|
||||
while (free) {
|
||||
if (free->count < count) {
|
||||
@@ -405,6 +446,7 @@ page_manager::map_offset_pages(size_t count)
|
||||
m_block_cache = free;
|
||||
}
|
||||
|
||||
log::debug(logs::memory, "Got request for offset map %016lx [%d]", used->virtual_address, count);
|
||||
page_in(pml4, used->physical_address, used->virtual_address, count);
|
||||
return reinterpret_cast<void *>(used->virtual_address);
|
||||
}
|
||||
@@ -480,36 +522,47 @@ page_manager::unmap_pages(void* address, size_t count)
|
||||
}
|
||||
|
||||
void
|
||||
page_manager::check_needs_page(page_table *table, unsigned index)
|
||||
page_manager::check_needs_page(page_table *table, unsigned index, bool user)
|
||||
{
|
||||
if ((table->entries[index] & 0x1) == 1) return;
|
||||
|
||||
page_table *new_table = get_table_page();
|
||||
for (int i=0; i<512; ++i) new_table->entries[i] = 0;
|
||||
table->entries[index] = pt_to_phys(new_table) | 0xb;
|
||||
table->entries[index] = pt_to_phys(new_table) | (user ? 0xf : 0xb);
|
||||
}
|
||||
|
||||
void
|
||||
page_manager::page_in(page_table *pml4, addr_t phys_addr, addr_t virt_addr, size_t count)
|
||||
page_manager::page_in(page_table *pml4, addr_t phys_addr, addr_t virt_addr, size_t count, bool user)
|
||||
{
|
||||
page_table_indices idx{virt_addr};
|
||||
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
|
||||
|
||||
for (; idx[0] < 512; idx[0] += 1) {
|
||||
check_needs_page(tables[0], idx[0]);
|
||||
check_needs_page(tables[0], idx[0], user);
|
||||
tables[1] = tables[0]->get(idx[0]);
|
||||
|
||||
for (; idx[1] < 512; idx[1] += 1, idx[2] = 0, idx[3] = 0) {
|
||||
check_needs_page(tables[1], idx[1]);
|
||||
check_needs_page(tables[1], idx[1], user);
|
||||
tables[2] = tables[1]->get(idx[1]);
|
||||
|
||||
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) {
|
||||
check_needs_page(tables[2], idx[2]);
|
||||
if (idx[3] == 0 &&
|
||||
count >= 512 &&
|
||||
tables[2]->get(idx[2]) == nullptr) {
|
||||
// Do a 2MiB page instead
|
||||
tables[2]->entries[idx[2]] = phys_addr | (user ? 0x8f : 0x8b);
|
||||
phys_addr += page_size * 512;
|
||||
count -= 512;
|
||||
if (count == 0) return;
|
||||
continue;
|
||||
}
|
||||
|
||||
check_needs_page(tables[2], idx[2], user);
|
||||
tables[3] = tables[2]->get(idx[2]);
|
||||
|
||||
for (; idx[3] < 512; idx[3] += 1) {
|
||||
tables[3]->entries[idx[3]] = phys_addr | 0xb;
|
||||
phys_addr += page_manager::page_size;
|
||||
tables[3]->entries[idx[3]] = phys_addr | (user ? 0xf : 0xb);
|
||||
phys_addr += page_size;
|
||||
if (--count == 0) return;
|
||||
}
|
||||
}
|
||||
@@ -571,40 +624,40 @@ page_manager::pop_pages(size_t count, addr_t *address)
|
||||
|
||||
|
||||
void
|
||||
page_table::dump(int level, uint64_t offset)
|
||||
page_table::dump(int level, int max_index, uint64_t offset)
|
||||
{
|
||||
log::info(logs::memory, "Level %d page table @ %lx (off %lx):", level, this, offset);
|
||||
console *cons = console::get();
|
||||
|
||||
cons->printf("\nLevel %d page table @ %lx (off %lx):\n", level, this, offset);
|
||||
for (int i=0; i<512; ++i) {
|
||||
uint64_t ent = entries[i];
|
||||
if (ent == 0) continue;
|
||||
|
||||
if ((ent & 0x1) == 0) {
|
||||
log::info(logs::memory, " %3d: %lx NOT PRESENT", i, ent);
|
||||
cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((level == 2 || level == 3) && (ent & 0x80) == 0x80) {
|
||||
log::info(logs::memory, " %3d: %lx -> Large page at %lx",
|
||||
i, ent, ent & ~0xfffull);
|
||||
cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||
continue;
|
||||
} else if (level == 1) {
|
||||
log::info(logs::memory, " %3d: %lx -> Page at %lx",
|
||||
i, ent, ent & ~0xfffull);
|
||||
cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||
} else {
|
||||
log::info(logs::memory, " %3d: %lx -> Level %d table at %lx",
|
||||
cons->printf(" %3d: %016lx -> Level %d table at %016lx\n",
|
||||
i, ent, level - 1, (ent & ~0xfffull) + offset);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (--level > 0) {
|
||||
for (int i=0; i<512; ++i) {
|
||||
for (int i=0; i<=max_index; ++i) {
|
||||
uint64_t ent = entries[i];
|
||||
if ((ent & 0x1) == 0) continue;
|
||||
if ((ent & 0x80)) continue;
|
||||
|
||||
page_table *next = reinterpret_cast<page_table *>((ent & ~0xffful) + offset);
|
||||
next->dump(level, offset);
|
||||
next->dump(level, 511, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,18 +21,51 @@ public:
|
||||
static const size_t page_size = 0x1000;
|
||||
|
||||
/// Start of the higher half.
|
||||
static const addr_t high_offset = 0xffff800000000000;
|
||||
static const addr_t high_offset = 0xffffff0000000000;
|
||||
|
||||
/// Offset from physical where page tables are mapped.
|
||||
static const addr_t page_offset = 0xffffff8000000000;
|
||||
|
||||
/// Initial process thread's stack address
|
||||
static const addr_t initial_stack = 0x0000800000000000;
|
||||
|
||||
/// Initial process thread's stack size, in pages
|
||||
static const unsigned initial_stack_pages = 1;
|
||||
|
||||
page_manager();
|
||||
|
||||
/// Helper to read the PML4 table from CR3.
|
||||
/// \returns A pointer to the current PML4 table.
|
||||
static inline page_table * get_pml4()
|
||||
{
|
||||
addr_t pml4 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||
return reinterpret_cast<page_table *>((pml4 & ~0xfffull) + page_offset);
|
||||
}
|
||||
|
||||
/// Helper to set the PML4 table pointer in CR3.
|
||||
/// \arg pml4 A pointer to the PML4 table to install.
|
||||
static inline void set_pml4(page_table *pml4)
|
||||
{
|
||||
addr_t p = reinterpret_cast<addr_t>(pml4) - page_offset;
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
|
||||
}
|
||||
|
||||
/// Allocate but don't switch to a new PML4 table. This table
|
||||
/// should only have global kernel pages mapped.
|
||||
/// \returns A pointer to the PML4 table
|
||||
page_table * create_process_map();
|
||||
|
||||
/// Deallocate a process' PML4 table.
|
||||
void delete_process_map(page_table *table);
|
||||
|
||||
/// Allocate and map pages into virtual memory.
|
||||
/// \arg address The virtual address at which to map the pages
|
||||
/// \arg count The number of pages to map
|
||||
/// \arg user True is this memory is user-accessible
|
||||
/// \arg pml4 The pml4 to map into - null for the current one
|
||||
/// \returns A pointer to the start of the mapped region
|
||||
void * map_pages(addr_t address, size_t count);
|
||||
void * map_pages(addr_t address, size_t count, bool user = false, page_table *pml4 = nullptr);
|
||||
|
||||
/// Allocate and map contiguous pages into virtual memory, with
|
||||
/// a constant offset from their physical address.
|
||||
@@ -68,7 +101,13 @@ public:
|
||||
}
|
||||
|
||||
/// Log the current free/used block lists.
|
||||
void dump_blocks();
|
||||
/// \arg used_only If true, skip printing free list. Default false.
|
||||
void dump_blocks(bool used_only = false);
|
||||
|
||||
/// Dump the given or current PML4 to the console
|
||||
/// \arg pml4 The page table to use, null for the current one
|
||||
/// \arg max_index The max index of pml4 to print
|
||||
void dump_pml4(page_table *pml4 = nullptr, int max_index = 511);
|
||||
|
||||
/// Get the system page manager.
|
||||
/// \returns A pointer to the system page manager
|
||||
@@ -105,40 +144,26 @@ private:
|
||||
/// to the cache.
|
||||
void consolidate_blocks();
|
||||
|
||||
/// Helper to read the PML4 table from CR3.
|
||||
/// \returns A pointer to the current PML4 table.
|
||||
static inline page_table * get_pml4()
|
||||
{
|
||||
addr_t pml4 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||
return reinterpret_cast<page_table *>((pml4 & ~0xfffull) + page_offset);
|
||||
}
|
||||
|
||||
/// Helper to set the PML4 table pointer in CR3.
|
||||
/// \arg pml4 A pointer to the PML4 table to install.
|
||||
static inline void set_pml4(page_table *pml4)
|
||||
{
|
||||
addr_t p = reinterpret_cast<addr_t>(pml4) - page_offset;
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
|
||||
}
|
||||
|
||||
/// Helper function to allocate a new page table. If table entry `i` in
|
||||
/// table `base` is empty, allocate a new page table and point `base[i]` at
|
||||
/// it.
|
||||
/// \arg base Existing page table being indexed into
|
||||
/// \arg i Index into the existing table to check
|
||||
void check_needs_page(page_table *base, unsigned i);
|
||||
/// \art user True if this is a userspace mapping
|
||||
void check_needs_page(page_table *base, unsigned i, bool user);
|
||||
|
||||
/// Low-level routine for mapping a number of pages into the given page table.
|
||||
/// \arg pml4 The root page table to map into
|
||||
/// \arg phys_addr The starting physical address of the pages to be mapped
|
||||
/// \arg virt_addr The starting virtual address ot the memory to be mapped
|
||||
/// \arg count The number of pages to map
|
||||
/// \art user True if this is a userspace mapping
|
||||
void page_in(
|
||||
page_table *pml4,
|
||||
addr_t phys_addr,
|
||||
addr_t virt_addr,
|
||||
size_t count);
|
||||
size_t count,
|
||||
bool user = true);
|
||||
|
||||
/// Low-level routine for unmapping a number of pages from the given page table.
|
||||
/// \arg pml4 The root page table for this mapping
|
||||
@@ -157,6 +182,8 @@ private:
|
||||
/// \returns The number of pages retrieved
|
||||
size_t pop_pages(size_t count, addr_t *address);
|
||||
|
||||
page_table *m_kernel_pml4; ///< The PML4 of just kernel pages
|
||||
|
||||
page_block *m_free; ///< Free pages list
|
||||
page_block *m_used; ///< In-use pages list
|
||||
|
||||
@@ -278,7 +305,7 @@ struct page_table
|
||||
entries[i] = (reinterpret_cast<uint64_t>(p) - pm::page_offset) | (flags & 0xfff);
|
||||
}
|
||||
|
||||
void dump(int level = 4, uint64_t offset = page_manager::page_offset);
|
||||
void dump(int level = 4, int max_index = 511, uint64_t offset = page_manager::page_offset);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
#include "kutil/assert.h"
|
||||
#include "console.h"
|
||||
#include "log.h"
|
||||
#include "interrupts.h"
|
||||
#include "pci.h"
|
||||
|
||||
struct pci_cap_msi :
|
||||
public pci_cap
|
||||
struct pci_cap_msi
|
||||
{
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pci_cap_msi32
|
||||
{
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
uint32_t address;
|
||||
uint16_t data;
|
||||
uint16_t reserved;
|
||||
uint32_t mask;
|
||||
uint32_t pending;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pci_cap_msi64
|
||||
{
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
uint64_t address;
|
||||
uint16_t data;
|
||||
@@ -14,17 +35,33 @@ struct pci_cap_msi :
|
||||
uint32_t pending;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pci_cap_msix :
|
||||
public pci_cap
|
||||
{
|
||||
uint16_t control;
|
||||
uint64_t address;
|
||||
uint16_t data;
|
||||
uint16_t reserved;
|
||||
uint32_t mask;
|
||||
uint32_t pending;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
void dump_msi(pci_cap_msi *cap)
|
||||
{
|
||||
auto cons = console::get();
|
||||
cons->printf("MSI Cap:\n");
|
||||
cons->printf(" id: %02x\n", cap->id);
|
||||
cons->printf(" next: %02x\n", cap->next);
|
||||
cons->printf("control: %04x\n", cap->control);
|
||||
if (cap->control & 0x0080) {
|
||||
pci_cap_msi64 *cap64 = reinterpret_cast<pci_cap_msi64 *>(cap);
|
||||
cons->printf("address: %016x\n", cap64->address);
|
||||
cons->printf(" data: %04x\n", cap64->data);
|
||||
if (cap->control & 0x100) {
|
||||
cons->printf(" mask: %08x\n", cap64->mask);
|
||||
cons->printf("pending: %08x\n", cap64->pending);
|
||||
}
|
||||
} else {
|
||||
pci_cap_msi32 *cap32 = reinterpret_cast<pci_cap_msi32 *>(cap);
|
||||
cons->printf("address: %08x\n", cap32->address);
|
||||
cons->printf(" data: %04x\n", cap32->data);
|
||||
if (cap->control & 0x100) {
|
||||
cons->printf(" mask: %08x\n", cap32->mask);
|
||||
cons->printf("pending: %08x\n", cap32->pending);
|
||||
}
|
||||
}
|
||||
cons->putc('\n');
|
||||
};
|
||||
|
||||
pci_device::pci_device() :
|
||||
m_base(nullptr),
|
||||
@@ -60,19 +97,25 @@ pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t fu
|
||||
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
|
||||
*command |= 0x400; // Mask old INTx style interrupts
|
||||
|
||||
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %d.%d id %04x:%04x",
|
||||
bus, device, func, m_class, m_subclass, m_vendor, m_device);
|
||||
uint16_t *status = command + 1;
|
||||
|
||||
// Walk the extended capabilities list
|
||||
uint8_t next = m_base[13] & 0xff;
|
||||
while (next) {
|
||||
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
|
||||
next = cap->next;
|
||||
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);
|
||||
|
||||
if (cap->id == pci_cap::type::msi) {
|
||||
m_msi = cap;
|
||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
|
||||
mcap->control |= ~0x1; // Mask interrupts
|
||||
if (*status & 0x0010) {
|
||||
// Walk the extended capabilities list
|
||||
uint8_t next = m_base[13] & 0xff;
|
||||
while (next) {
|
||||
pci_cap *cap = reinterpret_cast<pci_cap *>(kutil::offset_pointer(m_base, next));
|
||||
next = cap->next;
|
||||
log::debug(logs::device, " - found PCI cap type %02x", cap->id);
|
||||
|
||||
if (cap->id == pci_cap::type::msi) {
|
||||
m_msi = cap;
|
||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(cap);
|
||||
mcap->control &= ~0x70; // at most 1 vector allocated
|
||||
mcap->control |= 0x01; // Enable interrupts, at most 1 vector allocated
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,9 +154,19 @@ pci_device::write_msi_regs(addr_t address, uint16_t data)
|
||||
kassert(m_msi, "Tried to write MSI for a device without that cap");
|
||||
if (m_msi->id == pci_cap::type::msi) {
|
||||
pci_cap_msi *mcap = reinterpret_cast<pci_cap_msi *>(m_msi);
|
||||
mcap->address = address;
|
||||
mcap->data = data;
|
||||
mcap->control |= 1;
|
||||
if (mcap->control & 0x0080) {
|
||||
pci_cap_msi64 *mcap64 = reinterpret_cast<pci_cap_msi64 *>(m_msi);
|
||||
mcap64->address = address;
|
||||
mcap64->data = data;
|
||||
} else {
|
||||
pci_cap_msi32 *mcap32 = reinterpret_cast<pci_cap_msi32 *>(m_msi);
|
||||
mcap32->address = address;
|
||||
mcap32->data = data;
|
||||
}
|
||||
uint16_t control = mcap->control;
|
||||
control &= 0xff8f; // We're allocating one vector, clear 6::4
|
||||
control |= 0x0001; // Enable MSI
|
||||
mcap->control = control;
|
||||
} else {
|
||||
kassert(0, "MIS-X is NYI");
|
||||
}
|
||||
|
||||
90
src/kernel/scheduler.cpp
Normal file
90
src/kernel/scheduler.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "apic.h"
|
||||
#include "console.h"
|
||||
#include "cpu.h"
|
||||
#include "gdt.h"
|
||||
#include "interrupts.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
scheduler scheduler::s_instance(nullptr);
|
||||
//static const uint32_t quantum = 2000000;
|
||||
static const uint32_t quantum = 20000000;
|
||||
|
||||
const int stack_size = 0x1000;
|
||||
|
||||
extern "C" void taskA();
|
||||
extern "C" void taskB();
|
||||
|
||||
|
||||
scheduler::scheduler(lapic *apic) :
|
||||
m_apic(apic),
|
||||
m_current(0)
|
||||
{
|
||||
m_processes.ensure_capacity(50);
|
||||
}
|
||||
|
||||
static process
|
||||
create_process(uint16_t pid, void (*rip)())
|
||||
{
|
||||
uint64_t flags;
|
||||
__asm__ __volatile__ ( "pushf; pop %0" : "=r" (flags) );
|
||||
|
||||
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
|
||||
uint16_t cs = (5 << 3) | 3; // User CS is GDT entry 5, ring 3
|
||||
|
||||
uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0
|
||||
uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3
|
||||
|
||||
void *stack0 = kutil::malloc(stack_size);
|
||||
void *sp0 = kutil::offset_pointer(stack0, stack_size);
|
||||
cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1;
|
||||
kutil::memset(state, 0, sizeof(cpu_state));
|
||||
|
||||
state->ds = state->ss = ss;
|
||||
state->cs = cs;
|
||||
state->rflags = 0x202;
|
||||
state->rip = reinterpret_cast<uint64_t>(rip);
|
||||
|
||||
page_table *pml4 = page_manager::get()->create_process_map();
|
||||
state->user_rsp = page_manager::initial_stack;
|
||||
|
||||
log::debug(logs::task, "Creating PID %d:", pid);
|
||||
log::debug(logs::task, " RSP0 %016lx", state);
|
||||
log::debug(logs::task, " RSP3 %016lx", state->user_rsp);
|
||||
log::debug(logs::task, " PML4 %016lx", pml4);
|
||||
|
||||
return {pid, reinterpret_cast<addr_t>(state), pml4};
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::start()
|
||||
{
|
||||
log::info(logs::task, "Starting scheduler.");
|
||||
|
||||
m_apic->enable_timer(isr::isrTimer, 128, quantum, false);
|
||||
|
||||
m_processes.append({0, 0, page_manager::get_pml4()}); // The kernel idle task, also the thread we're in now
|
||||
m_processes.append(create_process(1, &taskA));
|
||||
m_processes.append(create_process(2, &taskB));
|
||||
}
|
||||
|
||||
addr_t
|
||||
scheduler::tick(addr_t rsp0)
|
||||
{
|
||||
log::debug(logs::task, "Scheduler tick.");
|
||||
|
||||
m_processes[m_current].rsp = rsp0;
|
||||
m_current = (m_current + 1) % m_processes.count();
|
||||
rsp0 = m_processes[m_current].rsp;
|
||||
|
||||
// Set rsp0 to after the end of the about-to-be-popped cpu state
|
||||
tss_set_stack(0, rsp0 + sizeof(cpu_state));
|
||||
|
||||
// Swap page tables
|
||||
page_table *pml4 = m_processes[m_current].pml4;
|
||||
page_manager::set_pml4(pml4);
|
||||
|
||||
m_apic->reset_timer(quantum);
|
||||
return rsp0;
|
||||
}
|
||||
46
src/kernel/scheduler.h
Normal file
46
src/kernel/scheduler.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
/// \file scheduler.h
|
||||
/// The task scheduler and related definitions
|
||||
#include "kutil/memory.h"
|
||||
#include "kutil/vector.h"
|
||||
|
||||
class lapic;
|
||||
struct page_table;
|
||||
|
||||
|
||||
struct process
|
||||
{
|
||||
uint16_t pid;
|
||||
addr_t rsp;
|
||||
page_table *pml4;
|
||||
};
|
||||
|
||||
|
||||
/// The task scheduler
|
||||
class scheduler
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg apic Pointer to the local APIC object
|
||||
scheduler(lapic *apic);
|
||||
|
||||
/// Start the scheduler working. This may involve starting
|
||||
/// timer interrupts or other preemption methods.
|
||||
void start();
|
||||
|
||||
/// Handle a timer tick
|
||||
/// \arg rsp0 The stack pointer of the current interrupt handler
|
||||
/// \returns The stack pointer to handler to switch to
|
||||
addr_t tick(addr_t rsp0);
|
||||
|
||||
/// Get a reference to the system scheduler
|
||||
/// \returns A reference to the global system scheduler
|
||||
static scheduler & get() { return s_instance; }
|
||||
|
||||
private:
|
||||
lapic *m_apic;
|
||||
kutil::vector<process> m_processes;
|
||||
uint16_t m_current;
|
||||
|
||||
static scheduler s_instance;
|
||||
};
|
||||
@@ -29,7 +29,11 @@ def build(bld):
|
||||
return node.path_from(node.ctx.launch_node())
|
||||
def run(self):
|
||||
from subprocess import check_output
|
||||
args = self.env.objdump + ["--source", "-D", self.inputs[0].abspath()]
|
||||
args = self.env.objdump + [
|
||||
"--source",
|
||||
"-D",
|
||||
"-M", "intel",
|
||||
self.inputs[0].abspath()]
|
||||
with file(self.outputs[0].abspath(), 'w') as output:
|
||||
output.write(check_output(args))
|
||||
|
||||
|
||||
@@ -8,27 +8,27 @@ struct is_enum_bitfield { static constexpr bool value = false; };
|
||||
#define IS_BITFIELD(name) \
|
||||
template<> struct ::is_enum_bitfield<name> {static constexpr bool value=true;}
|
||||
|
||||
template <typename E>
|
||||
template <typename E, typename F>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||
operator & (E lhs, E rhs)
|
||||
operator & (E lhs, F rhs)
|
||||
{
|
||||
return static_cast<E> (
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) &
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
template <typename E, typename F>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||
operator | (E lhs, E rhs)
|
||||
operator | (E lhs, F rhs)
|
||||
{
|
||||
return static_cast<E> (
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) |
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
template <typename E, typename F>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||
operator ^ (E lhs, E rhs)
|
||||
operator ^ (E lhs, F rhs)
|
||||
{
|
||||
return static_cast<E> (
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) ^
|
||||
@@ -42,9 +42,9 @@ operator ~ (E rhs)
|
||||
return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
template <typename E, typename F>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||
operator |= (E &lhs, E rhs)
|
||||
operator |= (E &lhs, F rhs)
|
||||
{
|
||||
lhs = static_cast<E>(
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) |
|
||||
@@ -53,9 +53,9 @@ operator |= (E &lhs, E rhs)
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
template <typename E, typename F>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||
operator &= (E &lhs, E rhs)
|
||||
operator &= (E &lhs, F rhs)
|
||||
{
|
||||
lhs = static_cast<E>(
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) &
|
||||
@@ -64,9 +64,9 @@ operator &= (E &lhs, E rhs)
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
template <typename E, typename F>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||
operator ^= (E &lhs, E rhs)
|
||||
operator ^= (E &lhs, F rhs)
|
||||
{
|
||||
lhs = static_cast<E>(
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) ^
|
||||
|
||||
38
src/modules/kutil/guid.h
Normal file
38
src/modules/kutil/guid.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
/// \file guid.h
|
||||
/// Definition of the guid type and related functions
|
||||
#include <stdint.h>
|
||||
#include "kutil/misc.h"
|
||||
|
||||
namespace kutil {
|
||||
|
||||
|
||||
/// A GUID
|
||||
struct guid
|
||||
{
|
||||
uint64_t a, b;
|
||||
};
|
||||
|
||||
/// Make a GUID by writing it naturally-ordered in code:
|
||||
/// AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE becomes:
|
||||
/// make_guid(0xAAAAAAAA, 0xBBBB, 0xCCCC, 0xDDDD, 0xEEEEEEEEEEEE);
|
||||
/// \returns The guid object
|
||||
inline constexpr guid make_guid(uint32_t a, uint16_t b, uint16_t c, uint16_t d, uint64_t e)
|
||||
{
|
||||
const uint64_t h =
|
||||
static_cast<uint64_t>(c) << 48 |
|
||||
static_cast<uint64_t>(b) << 32 |
|
||||
a;
|
||||
|
||||
const uint64_t l =
|
||||
static_cast<uint64_t>(byteswap(e & 0xffffffff)) << 32 |
|
||||
(byteswap(e >> 32) & 0xffff0000) |
|
||||
((d << 8) & 0xff00) | ((d >> 8) & 0xff);
|
||||
|
||||
return {h, l};
|
||||
}
|
||||
|
||||
} // namespace kutil
|
||||
|
||||
|
||||
inline bool operator==(const kutil::guid &a, const kutil::guid &b) { return a.a == b.a && a.b == b.b; }
|
||||
55
wscript
55
wscript
@@ -80,6 +80,7 @@ def configure(ctx):
|
||||
'-ffreestanding',
|
||||
'-nodefaultlibs',
|
||||
'-fno-builtin',
|
||||
'-mno-sse',
|
||||
'-fno-omit-frame-pointer',
|
||||
'-mno-red-zone',
|
||||
]
|
||||
@@ -141,6 +142,20 @@ def configure(ctx):
|
||||
'-nostartfiles',
|
||||
])
|
||||
|
||||
ctx.env.append_value('QEMUOPTS', [
|
||||
'-smp', '1',
|
||||
'-m', '512',
|
||||
'-d', 'mmu,int,guest_errors',
|
||||
'-D', 'popcorn.log',
|
||||
'-cpu', 'Broadwell',
|
||||
'-M', 'q35',
|
||||
'-no-reboot',
|
||||
'-nographic',
|
||||
])
|
||||
|
||||
if os.path.exists('/dev/kvm'):
|
||||
ctx.env.append_value('QEMUOPTS', ['--enable-kvm'])
|
||||
|
||||
env = ctx.env
|
||||
ctx.setenv('boot', env=env)
|
||||
ctx.recurse(join("src", "boot"))
|
||||
@@ -268,6 +283,14 @@ def build(bld):
|
||||
bld.recurse(join("src", "tests"))
|
||||
|
||||
|
||||
class QemuContext(BuildContext):
|
||||
cmd = 'qemu'
|
||||
fun = 'qemu'
|
||||
|
||||
class DebugQemuContext(QemuContext):
|
||||
cmd = 'debug'
|
||||
fun = 'qemu'
|
||||
|
||||
def qemu(ctx):
|
||||
import subprocess
|
||||
subprocess.call("rm popcorn.log", shell=True)
|
||||
@@ -275,24 +298,34 @@ def qemu(ctx):
|
||||
'qemu-system-x86_64',
|
||||
'-drive', 'if=pflash,format=raw,file={}/flash.img'.format(out),
|
||||
'-drive', 'format=raw,file={}/popcorn.img'.format(out),
|
||||
'-smp', '1',
|
||||
'-m', '512',
|
||||
'-d', 'mmu,int,guest_errors',
|
||||
'-D', 'popcorn.log',
|
||||
'-cpu', 'Broadwell',
|
||||
'-M', 'q35',
|
||||
'-no-reboot',
|
||||
'-nographic',
|
||||
])
|
||||
] + ctx.env.QEMUOPTS)
|
||||
|
||||
def debug(ctx):
|
||||
import subprocess
|
||||
subprocess.call("rm popcorn.log", shell=True)
|
||||
subprocess.call([
|
||||
'qemu-system-x86_64',
|
||||
'-drive', 'if=pflash,format=raw,file={}/flash.img'.format(out),
|
||||
'-drive', 'format=raw,file={}/popcorn.img'.format(out),
|
||||
'-s',
|
||||
] + ctx.env.QEMUOPTS)
|
||||
|
||||
|
||||
def vbox(ctx):
|
||||
import os
|
||||
from os.path import join
|
||||
from shutil import copy
|
||||
from subprocess import call
|
||||
|
||||
dest = os.getenv("VBOX_DEST")
|
||||
copy("{}/popcorn.img".format(out), "{}/popcorn.img".format(dest))
|
||||
vbox_dest = os.getenv("VBOX_DEST")
|
||||
|
||||
ext = 'qed'
|
||||
dest = join(vbox_dest, "popcorn.{}".format(ext))
|
||||
call("qemu-img convert -f raw -O {} build/popcorn.img {}".format(ext, dest), shell=True)
|
||||
copy(join("build", "src", "kernel", "popcorn.elf"), vbox_dest);
|
||||
|
||||
def listen(ctx):
|
||||
from subprocess import call
|
||||
call("nc -l -p 5555", shell=True)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user