[project] Lose the battle between tabs & spaces
I'm a tabs guy. I like tabs, it's an elegant way to represent indentation instead of brute-forcing it. But I have to admit that the world seems to be going towards spaces, and tooling tends not to play nice with tabs. So here we go, changing the whole repo to spaces since I'm getting tired of all the inconsistent formatting.
This commit is contained in:
committed by
Justin C. Miller
parent
d36b2d8057
commit
8f529046a9
@@ -9,22 +9,22 @@
|
||||
|
||||
struct acpi_table_header
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t length;
|
||||
uint8_t revision;
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
char oem_table[8];
|
||||
uint32_t oem_revision;
|
||||
uint32_t creator_id;
|
||||
uint32_t creator_revision;
|
||||
uint32_t type;
|
||||
uint32_t length;
|
||||
uint8_t revision;
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
char oem_table[8];
|
||||
uint32_t oem_revision;
|
||||
uint32_t creator_id;
|
||||
uint32_t creator_revision;
|
||||
|
||||
bool validate(uint32_t expected_type = 0) const;
|
||||
bool validate(uint32_t expected_type = 0) const;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define TABLE_HEADER(signature) \
|
||||
static constexpr uint32_t type_id = kutil::byteswap(signature); \
|
||||
acpi_table_header header;
|
||||
static constexpr uint32_t type_id = kutil::byteswap(signature); \
|
||||
acpi_table_header header;
|
||||
|
||||
|
||||
template <typename T>
|
||||
@@ -33,62 +33,62 @@ bool acpi_validate(const T *t) { return t->header.validate(T::type_id); }
|
||||
template <typename T>
|
||||
size_t acpi_table_entries(const T *t, size_t size)
|
||||
{
|
||||
return (t->header.length - sizeof(T)) / size;
|
||||
return (t->header.length - sizeof(T)) / size;
|
||||
}
|
||||
|
||||
enum class acpi_gas_type : uint8_t
|
||||
{
|
||||
system_memory,
|
||||
system_io,
|
||||
pci_config,
|
||||
embedded,
|
||||
smbus,
|
||||
platform_channel = 0x0a,
|
||||
functional_fixed = 0x7f
|
||||
system_memory,
|
||||
system_io,
|
||||
pci_config,
|
||||
embedded,
|
||||
smbus,
|
||||
platform_channel = 0x0a,
|
||||
functional_fixed = 0x7f
|
||||
};
|
||||
|
||||
struct acpi_gas
|
||||
{
|
||||
acpi_gas_type type;
|
||||
acpi_gas_type type;
|
||||
|
||||
uint8_t reg_bits;
|
||||
uint8_t reg_offset;
|
||||
uint8_t access_size;
|
||||
uint8_t reg_bits;
|
||||
uint8_t reg_offset;
|
||||
uint8_t access_size;
|
||||
|
||||
uint64_t address;
|
||||
uint64_t address;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
enum class acpi_fadt_flags : uint32_t
|
||||
{
|
||||
wbinvd = 0x00000001,
|
||||
wbinvd_flush = 0x00000002,
|
||||
proc_c1 = 0x00000004,
|
||||
p_lvl2_up = 0x00000008,
|
||||
pwr_button = 0x00000010,
|
||||
slp_button = 0x00000020,
|
||||
fix_rtc = 0x00000040,
|
||||
rtc_s4 = 0x00000080,
|
||||
tmr_val_ext = 0x00000100,
|
||||
dck_cap = 0x00000200,
|
||||
reset_reg_sup = 0x00000400,
|
||||
sealed_case = 0x00000800,
|
||||
headless = 0x00001000,
|
||||
cpu_sw_slp = 0x00002000,
|
||||
pci_exp_wak = 0x00004000,
|
||||
use_plat_clock = 0x00008000,
|
||||
s4_rtc_sts_val = 0x00010000,
|
||||
remote_pwr_cap = 0x00020000,
|
||||
apic_cluster = 0x00040000,
|
||||
apic_physical = 0x00080000,
|
||||
hw_reduced_acpi = 0x00100000,
|
||||
low_pwr_s0_idle = 0x00200000
|
||||
wbinvd = 0x00000001,
|
||||
wbinvd_flush = 0x00000002,
|
||||
proc_c1 = 0x00000004,
|
||||
p_lvl2_up = 0x00000008,
|
||||
pwr_button = 0x00000010,
|
||||
slp_button = 0x00000020,
|
||||
fix_rtc = 0x00000040,
|
||||
rtc_s4 = 0x00000080,
|
||||
tmr_val_ext = 0x00000100,
|
||||
dck_cap = 0x00000200,
|
||||
reset_reg_sup = 0x00000400,
|
||||
sealed_case = 0x00000800,
|
||||
headless = 0x00001000,
|
||||
cpu_sw_slp = 0x00002000,
|
||||
pci_exp_wak = 0x00004000,
|
||||
use_plat_clock = 0x00008000,
|
||||
s4_rtc_sts_val = 0x00010000,
|
||||
remote_pwr_cap = 0x00020000,
|
||||
apic_cluster = 0x00040000,
|
||||
apic_physical = 0x00080000,
|
||||
hw_reduced_acpi = 0x00100000,
|
||||
low_pwr_s0_idle = 0x00200000
|
||||
};
|
||||
is_bitfield(acpi_fadt_flags);
|
||||
|
||||
struct acpi_fadt
|
||||
{
|
||||
TABLE_HEADER('FACP');
|
||||
TABLE_HEADER('FACP');
|
||||
|
||||
uint32_t facs;
|
||||
uint32_t dsdt;
|
||||
@@ -136,7 +136,7 @@ struct acpi_fadt
|
||||
acpi_gas reset_reg;
|
||||
uint8_t reset_value;
|
||||
|
||||
uint16_t arm_boot_arch;
|
||||
uint16_t arm_boot_arch;
|
||||
|
||||
uint8_t fadt_minor_version;
|
||||
|
||||
@@ -152,60 +152,60 @@ struct acpi_fadt
|
||||
acpi_gas x_gpe0_block;
|
||||
acpi_gas x_gpe1_block;
|
||||
|
||||
acpi_gas sleep_control_reg;
|
||||
acpi_gas sleep_status_reg;
|
||||
acpi_gas sleep_control_reg;
|
||||
acpi_gas sleep_status_reg;
|
||||
|
||||
uint64_t hypervisor_vendor_id;
|
||||
uint64_t hypervisor_vendor_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_xsdt
|
||||
{
|
||||
TABLE_HEADER('XSDT');
|
||||
acpi_table_header *headers[0];
|
||||
TABLE_HEADER('XSDT');
|
||||
acpi_table_header *headers[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_apic
|
||||
{
|
||||
TABLE_HEADER('APIC');
|
||||
uint32_t local_address;
|
||||
uint32_t flags;
|
||||
uint8_t controller_data[0];
|
||||
TABLE_HEADER('APIC');
|
||||
uint32_t local_address;
|
||||
uint32_t flags;
|
||||
uint8_t controller_data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_mcfg_entry
|
||||
{
|
||||
uint64_t base;
|
||||
uint16_t group;
|
||||
uint8_t bus_start;
|
||||
uint8_t bus_end;
|
||||
uint32_t reserved;
|
||||
uint64_t base;
|
||||
uint16_t group;
|
||||
uint8_t bus_start;
|
||||
uint8_t bus_end;
|
||||
uint32_t reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_mcfg
|
||||
{
|
||||
TABLE_HEADER('MCFG');
|
||||
uint64_t reserved;
|
||||
acpi_mcfg_entry entries[0];
|
||||
TABLE_HEADER('MCFG');
|
||||
uint64_t reserved;
|
||||
acpi_mcfg_entry entries[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_hpet
|
||||
{
|
||||
TABLE_HEADER('HPET');
|
||||
uint32_t hardware_id;
|
||||
acpi_gas base_address;
|
||||
uint8_t index;
|
||||
uint16_t periodic_min;
|
||||
uint8_t attributes;
|
||||
TABLE_HEADER('HPET');
|
||||
uint32_t hardware_id;
|
||||
acpi_gas base_address;
|
||||
uint8_t index;
|
||||
uint16_t periodic_min;
|
||||
uint8_t attributes;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_bgrt
|
||||
{
|
||||
TABLE_HEADER('BGRT');
|
||||
uint16_t version;
|
||||
uint8_t status;
|
||||
uint8_t type;
|
||||
uintptr_t address;
|
||||
uint32_t offset_x;
|
||||
uint32_t offset_y;
|
||||
TABLE_HEADER('BGRT');
|
||||
uint16_t version;
|
||||
uint8_t status;
|
||||
uint8_t type;
|
||||
uintptr_t address;
|
||||
uint32_t offset_x;
|
||||
uint32_t offset_y;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
@@ -26,286 +26,286 @@ static constexpr uint16_t lapic_timer_div = 0x03e0;
|
||||
static uint32_t
|
||||
apic_read(uint32_t volatile *apic, uint16_t offset)
|
||||
{
|
||||
return *(apic + offset/sizeof(uint32_t));
|
||||
return *(apic + offset/sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static void
|
||||
apic_write(uint32_t volatile *apic, uint16_t offset, uint32_t value)
|
||||
{
|
||||
log::debug(logs::apic, "LAPIC write: %x = %08lx", offset, value);
|
||||
*(apic + offset/sizeof(uint32_t)) = value;
|
||||
log::debug(logs::apic, "LAPIC write: %x = %08lx", offset, value);
|
||||
*(apic + offset/sizeof(uint32_t)) = value;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
ioapic_read(uint32_t volatile *base, uint8_t reg)
|
||||
{
|
||||
*base = reg;
|
||||
return *(base + 4);
|
||||
*base = reg;
|
||||
return *(base + 4);
|
||||
}
|
||||
|
||||
static void
|
||||
ioapic_write(uint32_t volatile *base, uint8_t reg, uint32_t value)
|
||||
{
|
||||
*base = reg;
|
||||
*(base + 4) = value;
|
||||
*base = reg;
|
||||
*(base + 4) = value;
|
||||
}
|
||||
|
||||
apic::apic(uintptr_t base) :
|
||||
m_base(memory::to_virtual<uint32_t>(base))
|
||||
m_base(memory::to_virtual<uint32_t>(base))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
lapic::lapic(uintptr_t base) :
|
||||
apic(base),
|
||||
m_divisor(0)
|
||||
apic(base),
|
||||
m_divisor(0)
|
||||
{
|
||||
apic_write(m_base, lapic_lvt_error, static_cast<uint32_t>(isr::isrAPICError));
|
||||
apic_write(m_base, lapic_spurious, static_cast<uint32_t>(isr::isrSpurious));
|
||||
apic_write(m_base, lapic_lvt_error, static_cast<uint32_t>(isr::isrAPICError));
|
||||
apic_write(m_base, lapic_spurious, static_cast<uint32_t>(isr::isrSpurious));
|
||||
}
|
||||
|
||||
uint8_t
|
||||
lapic::get_id()
|
||||
{
|
||||
return static_cast<uint8_t>(apic_read(m_base, lapic_id) >> 24);
|
||||
return static_cast<uint8_t>(apic_read(m_base, lapic_id) >> 24);
|
||||
}
|
||||
|
||||
void
|
||||
lapic::send_ipi(ipi mode, uint8_t vector, uint8_t dest)
|
||||
{
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode);
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode);
|
||||
|
||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
}
|
||||
|
||||
void
|
||||
lapic::send_ipi_broadcast(ipi mode, bool self, uint8_t vector)
|
||||
{
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
// Wait until the APIC is ready to send
|
||||
ipi_wait();
|
||||
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode) |
|
||||
(self ? 0 : (1 << 18)) |
|
||||
(1 << 19);
|
||||
uint32_t command =
|
||||
static_cast<uint32_t>(vector) |
|
||||
static_cast<uint32_t>(mode) |
|
||||
(self ? 0 : (1 << 18)) |
|
||||
(1 << 19);
|
||||
|
||||
apic_write(m_base, lapic_icr_high, 0);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
apic_write(m_base, lapic_icr_high, 0);
|
||||
apic_write(m_base, lapic_icr_low, command);
|
||||
}
|
||||
|
||||
void
|
||||
lapic::ipi_wait()
|
||||
{
|
||||
while (apic_read(m_base, lapic_icr_low) & (1<<12))
|
||||
asm volatile ("pause" : : : "memory");
|
||||
while (apic_read(m_base, lapic_icr_low) & (1<<12))
|
||||
asm volatile ("pause" : : : "memory");
|
||||
}
|
||||
|
||||
void
|
||||
lapic::calibrate_timer()
|
||||
{
|
||||
interrupts_disable();
|
||||
interrupts_disable();
|
||||
|
||||
log::info(logs::apic, "Calibrating APIC timer...");
|
||||
log::info(logs::apic, "Calibrating APIC timer...");
|
||||
|
||||
const uint32_t initial = -1u;
|
||||
enable_timer(isr::isrSpurious);
|
||||
set_divisor(1);
|
||||
apic_write(m_base, lapic_timer_init, initial);
|
||||
const uint32_t initial = -1u;
|
||||
enable_timer(isr::isrSpurious);
|
||||
set_divisor(1);
|
||||
apic_write(m_base, lapic_timer_init, initial);
|
||||
|
||||
uint64_t us = 20000;
|
||||
clock::get().spinwait(us);
|
||||
uint64_t us = 20000;
|
||||
clock::get().spinwait(us);
|
||||
|
||||
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
|
||||
uint64_t ticks_total = initial - remaining;
|
||||
s_ticks_per_us = ticks_total / us;
|
||||
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
|
||||
uint64_t ticks_total = initial - remaining;
|
||||
s_ticks_per_us = ticks_total / us;
|
||||
|
||||
log::info(logs::apic, "APIC timer ticks %d times per microsecond.", s_ticks_per_us);
|
||||
log::info(logs::apic, "APIC timer ticks %d times per microsecond.", s_ticks_per_us);
|
||||
|
||||
interrupts_enable();
|
||||
interrupts_enable();
|
||||
}
|
||||
|
||||
void
|
||||
lapic::set_divisor(uint8_t divisor)
|
||||
{
|
||||
uint32_t divbits = 0;
|
||||
uint32_t divbits = 0;
|
||||
|
||||
switch (divisor) {
|
||||
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::set_divisor");
|
||||
}
|
||||
switch (divisor) {
|
||||
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::set_divisor");
|
||||
}
|
||||
|
||||
apic_write(m_base, lapic_timer_div, divbits);
|
||||
m_divisor = divisor;
|
||||
apic_write(m_base, lapic_timer_div, divbits);
|
||||
m_divisor = divisor;
|
||||
}
|
||||
|
||||
void
|
||||
lapic::enable_timer(isr vector, bool repeat)
|
||||
{
|
||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||
if (repeat)
|
||||
lvte |= 0x20000;
|
||||
apic_write(m_base, lapic_lvt_timer, lvte);
|
||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||
if (repeat)
|
||||
lvte |= 0x20000;
|
||||
apic_write(m_base, lapic_lvt_timer, lvte);
|
||||
|
||||
log::debug(logs::apic, "Enabling APIC timer at isr %02x", vector);
|
||||
log::debug(logs::apic, "Enabling APIC timer at isr %02x", vector);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
lapic::reset_timer(uint64_t interval)
|
||||
{
|
||||
uint64_t remaining = ticks_to_us(apic_read(m_base, lapic_timer_cur));
|
||||
uint64_t ticks = us_to_ticks(interval);
|
||||
uint64_t remaining = ticks_to_us(apic_read(m_base, lapic_timer_cur));
|
||||
uint64_t ticks = us_to_ticks(interval);
|
||||
|
||||
int divisor = 1;
|
||||
while (ticks > 0xffffffffull) {
|
||||
ticks >>= 1;
|
||||
divisor <<= 1;
|
||||
}
|
||||
int divisor = 1;
|
||||
while (ticks > 0xffffffffull) {
|
||||
ticks >>= 1;
|
||||
divisor <<= 1;
|
||||
}
|
||||
|
||||
if (divisor != m_divisor)
|
||||
set_divisor(divisor);
|
||||
if (divisor != m_divisor)
|
||||
set_divisor(divisor);
|
||||
|
||||
apic_write(m_base, lapic_timer_init, ticks);
|
||||
return remaining;
|
||||
apic_write(m_base, lapic_timer_init, ticks);
|
||||
return remaining;
|
||||
}
|
||||
|
||||
void
|
||||
lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
|
||||
{
|
||||
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
|
||||
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
|
||||
|
||||
uint16_t off = num ? lapic_lvt_lint1 : lapic_lvt_lint0;
|
||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||
uint16_t off = num ? lapic_lvt_lint1 : lapic_lvt_lint0;
|
||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||
|
||||
uint16_t polarity = flags & 0x3;
|
||||
if (polarity == 3)
|
||||
lvte |= (1 << 13);
|
||||
uint16_t polarity = flags & 0x3;
|
||||
if (polarity == 3)
|
||||
lvte |= (1 << 13);
|
||||
|
||||
uint16_t trigger = (flags >> 2) & 0x3;
|
||||
if (trigger == 3)
|
||||
lvte |= (1 << 15);
|
||||
uint16_t trigger = (flags >> 2) & 0x3;
|
||||
if (trigger == 3)
|
||||
lvte |= (1 << 15);
|
||||
|
||||
apic_write(m_base, off, lvte);
|
||||
log::debug(logs::apic, "APIC LINT%d enabled as %s %d %s-triggered, active %s.",
|
||||
num, nmi ? "NMI" : "ISR", vector,
|
||||
polarity == 3 ? "level" : "edge",
|
||||
trigger == 3 ? "low" : "high");
|
||||
apic_write(m_base, off, lvte);
|
||||
log::debug(logs::apic, "APIC LINT%d enabled as %s %d %s-triggered, active %s.",
|
||||
num, nmi ? "NMI" : "ISR", vector,
|
||||
polarity == 3 ? "level" : "edge",
|
||||
trigger == 3 ? "low" : "high");
|
||||
}
|
||||
|
||||
void
|
||||
lapic::enable()
|
||||
{
|
||||
apic_write(m_base, lapic_spurious,
|
||||
apic_read(m_base, lapic_spurious) | 0x100);
|
||||
log::debug(logs::apic, "LAPIC enabled!");
|
||||
apic_write(m_base, lapic_spurious,
|
||||
apic_read(m_base, lapic_spurious) | 0x100);
|
||||
log::debug(logs::apic, "LAPIC enabled!");
|
||||
}
|
||||
|
||||
void
|
||||
lapic::disable()
|
||||
{
|
||||
apic_write(m_base, lapic_spurious,
|
||||
apic_read(m_base, lapic_spurious) & ~0x100);
|
||||
log::debug(logs::apic, "LAPIC disabled.");
|
||||
apic_write(m_base, lapic_spurious,
|
||||
apic_read(m_base, lapic_spurious) & ~0x100);
|
||||
log::debug(logs::apic, "LAPIC disabled.");
|
||||
}
|
||||
|
||||
|
||||
ioapic::ioapic(uintptr_t base, uint32_t base_gsi) :
|
||||
apic(base),
|
||||
m_base_gsi(base_gsi)
|
||||
apic(base),
|
||||
m_base_gsi(base_gsi)
|
||||
{
|
||||
uint32_t id = ioapic_read(m_base, 0);
|
||||
uint32_t version = ioapic_read(m_base, 1);
|
||||
uint32_t id = ioapic_read(m_base, 0);
|
||||
uint32_t version = ioapic_read(m_base, 1);
|
||||
|
||||
m_id = (id >> 24) & 0xff;
|
||||
m_version = version & 0xff;
|
||||
m_num_gsi = (version >> 16) & 0xff;
|
||||
log::debug(logs::apic, "IOAPIC %d loaded, version %d, GSIs %d-%d",
|
||||
m_id, m_version, base_gsi, base_gsi + (m_num_gsi - 1));
|
||||
m_id = (id >> 24) & 0xff;
|
||||
m_version = version & 0xff;
|
||||
m_num_gsi = (version >> 16) & 0xff;
|
||||
log::debug(logs::apic, "IOAPIC %d loaded, version %d, GSIs %d-%d",
|
||||
m_id, m_version, base_gsi, base_gsi + (m_num_gsi - 1));
|
||||
|
||||
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
||||
uint16_t flags = (i < 0x10) ? 0 : 0xf;
|
||||
isr vector = isr::irq00 + i;
|
||||
redirect(i, vector, flags, true);
|
||||
}
|
||||
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
||||
uint16_t flags = (i < 0x10) ? 0 : 0xf;
|
||||
isr vector = isr::irq00 + i;
|
||||
redirect(i, vector, flags, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
|
||||
{
|
||||
log::debug(logs::apic, "IOAPIC %d redirecting irq %3d to vector %3d [%04x]%s",
|
||||
m_id, irq, vector, flags, masked ? " (masked)" : "");
|
||||
log::debug(logs::apic, "IOAPIC %d redirecting irq %3d to vector %3d [%04x]%s",
|
||||
m_id, irq, vector, flags, masked ? " (masked)" : "");
|
||||
|
||||
uint64_t entry = static_cast<uint64_t>(vector);
|
||||
uint64_t entry = static_cast<uint64_t>(vector);
|
||||
|
||||
uint16_t polarity = flags & 0x3;
|
||||
if (polarity == 3)
|
||||
entry |= (1 << 13);
|
||||
uint16_t polarity = flags & 0x3;
|
||||
if (polarity == 3)
|
||||
entry |= (1 << 13);
|
||||
|
||||
uint16_t trigger = (flags >> 2) & 0x3;
|
||||
if (trigger == 3)
|
||||
entry |= (1 << 15);
|
||||
uint16_t trigger = (flags >> 2) & 0x3;
|
||||
if (trigger == 3)
|
||||
entry |= (1 << 15);
|
||||
|
||||
if (masked)
|
||||
entry |= (1 << 16);
|
||||
if (masked)
|
||||
entry |= (1 << 16);
|
||||
|
||||
ioapic_write(m_base, (2 * irq) + 0x10, static_cast<uint32_t>(entry & 0xffffffff));
|
||||
ioapic_write(m_base, (2 * irq) + 0x11, static_cast<uint32_t>(entry >> 32));
|
||||
ioapic_write(m_base, (2 * irq) + 0x10, static_cast<uint32_t>(entry & 0xffffffff));
|
||||
ioapic_write(m_base, (2 * irq) + 0x11, static_cast<uint32_t>(entry >> 32));
|
||||
}
|
||||
|
||||
void
|
||||
ioapic::mask(uint8_t irq, bool masked)
|
||||
{
|
||||
log::debug(logs::apic, "IOAPIC %d %smasking irq %3d",
|
||||
m_id, masked ? "" : "un", irq);
|
||||
log::debug(logs::apic, "IOAPIC %d %smasking irq %3d",
|
||||
m_id, masked ? "" : "un", irq);
|
||||
|
||||
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
|
||||
if (masked)
|
||||
entry |= (1 << 16);
|
||||
else
|
||||
entry &= ~(1 << 16);
|
||||
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
|
||||
if (masked)
|
||||
entry |= (1 << 16);
|
||||
else
|
||||
entry &= ~(1 << 16);
|
||||
|
||||
ioapic_write(m_base, (2 * irq) + 0x10, entry);
|
||||
ioapic_write(m_base, (2 * irq) + 0x10, entry);
|
||||
}
|
||||
|
||||
void
|
||||
ioapic::dump_redirs() const
|
||||
{
|
||||
log::debug(logs::apic, "IOAPIC %d redirections:", m_id);
|
||||
log::debug(logs::apic, "IOAPIC %d redirections:", m_id);
|
||||
|
||||
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
||||
uint64_t low = ioapic_read(m_base, 0x10 + (2 *i));
|
||||
uint64_t high = ioapic_read(m_base, 0x10 + (2 *i));
|
||||
uint64_t redir = low | (high << 32);
|
||||
if (redir == 0) continue;
|
||||
for (uint8_t i = 0; i < m_num_gsi; ++i) {
|
||||
uint64_t low = ioapic_read(m_base, 0x10 + (2 *i));
|
||||
uint64_t high = ioapic_read(m_base, 0x10 + (2 *i));
|
||||
uint64_t redir = low | (high << 32);
|
||||
if (redir == 0) continue;
|
||||
|
||||
uint8_t vector = redir & 0xff;
|
||||
uint8_t mode = (redir >> 8) & 0x7;
|
||||
uint8_t dest_mode = (redir >> 11) & 0x1;
|
||||
uint8_t polarity = (redir >> 13) & 0x1;
|
||||
uint8_t trigger = (redir >> 15) & 0x1;
|
||||
uint8_t mask = (redir >> 16) & 0x1;
|
||||
uint8_t dest = (redir >> 56) & 0xff;
|
||||
uint8_t vector = redir & 0xff;
|
||||
uint8_t mode = (redir >> 8) & 0x7;
|
||||
uint8_t dest_mode = (redir >> 11) & 0x1;
|
||||
uint8_t polarity = (redir >> 13) & 0x1;
|
||||
uint8_t trigger = (redir >> 15) & 0x1;
|
||||
uint8_t mask = (redir >> 16) & 0x1;
|
||||
uint8_t dest = (redir >> 56) & 0xff;
|
||||
|
||||
log::debug(logs::apic, " %2d: vec %3d %s active, %s-triggered %s dest %d: %x",
|
||||
m_base_gsi + i, vector,
|
||||
polarity ? "low" : "high",
|
||||
trigger ? "level" : "edge",
|
||||
mask ? "masked" : "",
|
||||
dest_mode,
|
||||
dest);
|
||||
}
|
||||
log::debug(logs::apic, " %2d: vec %3d %s active, %s-triggered %s dest %d: %x",
|
||||
m_base_gsi + i, vector,
|
||||
polarity ? "low" : "high",
|
||||
trigger ? "level" : "edge",
|
||||
mask ? "masked" : "",
|
||||
dest_mode,
|
||||
dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,137 +11,137 @@ enum class isr : uint8_t;
|
||||
class apic
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
/// \arg base Physical base address of the APIC's MMIO registers
|
||||
apic(uintptr_t base);
|
||||
/// Constructor
|
||||
/// \arg base Physical base address of the APIC's MMIO registers
|
||||
apic(uintptr_t base);
|
||||
|
||||
protected:
|
||||
uint32_t *m_base;
|
||||
uint32_t *m_base;
|
||||
};
|
||||
|
||||
/// Controller for processor-local APICs
|
||||
class lapic :
|
||||
public apic
|
||||
public apic
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
/// \arg base Physicl base address of the APIC's MMIO registers
|
||||
lapic(uintptr_t base);
|
||||
/// Constructor
|
||||
/// \arg base Physicl base address of the APIC's MMIO registers
|
||||
lapic(uintptr_t base);
|
||||
|
||||
/// Get the local APIC's ID
|
||||
uint8_t get_id();
|
||||
/// Get the local APIC's ID
|
||||
uint8_t get_id();
|
||||
|
||||
enum class ipi : uint32_t
|
||||
{
|
||||
// Delivery modes
|
||||
fixed = 0x0000,
|
||||
smi = 0x0200,
|
||||
nmi = 0x0400,
|
||||
init = 0x0500,
|
||||
startup = 0x0600,
|
||||
enum class ipi : uint32_t
|
||||
{
|
||||
// Delivery modes
|
||||
fixed = 0x0000,
|
||||
smi = 0x0200,
|
||||
nmi = 0x0400,
|
||||
init = 0x0500,
|
||||
startup = 0x0600,
|
||||
|
||||
// Flags
|
||||
deassert = 0x0000,
|
||||
assert = 0x4000,
|
||||
edge = 0x0000, ///< edge-triggered
|
||||
level = 0x8000, ///< level-triggered
|
||||
};
|
||||
// Flags
|
||||
deassert = 0x0000,
|
||||
assert = 0x4000,
|
||||
edge = 0x0000, ///< edge-triggered
|
||||
level = 0x8000, ///< level-triggered
|
||||
};
|
||||
|
||||
/// Send an inter-processor interrupt.
|
||||
/// \arg mode The sending mode
|
||||
/// \arg vector The interrupt vector
|
||||
/// \arg dest The APIC ID of the destination
|
||||
void send_ipi(ipi mode, uint8_t vector, uint8_t dest);
|
||||
/// Send an inter-processor interrupt.
|
||||
/// \arg mode The sending mode
|
||||
/// \arg vector The interrupt vector
|
||||
/// \arg dest The APIC ID of the destination
|
||||
void send_ipi(ipi mode, uint8_t vector, uint8_t dest);
|
||||
|
||||
/// Send an inter-processor broadcast interrupt to all other CPUs
|
||||
/// \arg mode The sending mode
|
||||
/// \arg self If true, include this CPU in the broadcast
|
||||
/// \arg vector The interrupt vector
|
||||
void send_ipi_broadcast(ipi mode, bool self, uint8_t vector);
|
||||
/// Send an inter-processor broadcast interrupt to all other CPUs
|
||||
/// \arg mode The sending mode
|
||||
/// \arg self If true, include this CPU in the broadcast
|
||||
/// \arg vector The interrupt vector
|
||||
void send_ipi_broadcast(ipi mode, bool self, uint8_t vector);
|
||||
|
||||
/// Wait for an IPI to finish sending. This is done automatically
|
||||
/// before sending another IPI with send_ipi().
|
||||
void ipi_wait();
|
||||
/// Wait for an IPI to finish sending. This is done automatically
|
||||
/// before sending another IPI with send_ipi().
|
||||
void ipi_wait();
|
||||
|
||||
/// Enable interrupts for the LAPIC timer.
|
||||
/// \arg vector Interrupt vector the timer should use
|
||||
/// \arg repeat If false, this timer is one-off, otherwise repeating
|
||||
void enable_timer(isr vector, bool repeat = true);
|
||||
/// Enable interrupts for the LAPIC timer.
|
||||
/// \arg vector Interrupt vector the timer should use
|
||||
/// \arg repeat If false, this timer is one-off, otherwise repeating
|
||||
void enable_timer(isr vector, bool repeat = true);
|
||||
|
||||
/// Reset the timer countdown.
|
||||
/// \arg interval The interval in us before an interrupt, or 0 to stop the timer
|
||||
/// \returns The interval in us that was remaining before reset
|
||||
uint32_t reset_timer(uint64_t interval);
|
||||
/// Reset the timer countdown.
|
||||
/// \arg interval The interval in us before an interrupt, or 0 to stop the timer
|
||||
/// \returns The interval in us that was remaining before reset
|
||||
uint32_t reset_timer(uint64_t interval);
|
||||
|
||||
/// Stop the timer.
|
||||
/// \returns The interval in us remaining before an interrupt was to happen
|
||||
inline uint32_t stop_timer() { return reset_timer(0); }
|
||||
/// Stop the timer.
|
||||
/// \returns The interval in us 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
|
||||
/// \arg nmi Whether this interrupt is NMI delivery mode
|
||||
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
||||
void enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags);
|
||||
/// Enable interrupts for the LAPIC LINT0 pin.
|
||||
/// \arg num Local interrupt number (0 or 1)
|
||||
/// \arg vector Interrupt vector LINT0 should use
|
||||
/// \arg nmi Whether this interrupt is NMI delivery mode
|
||||
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
||||
void enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags);
|
||||
|
||||
void enable(); ///< Enable servicing of interrupts
|
||||
void disable(); ///< Disable (temporarily) servicing of interrupts
|
||||
void enable(); ///< Enable servicing of interrupts
|
||||
void disable(); ///< Disable (temporarily) servicing of interrupts
|
||||
|
||||
/// Calibrate the timer speed against the clock
|
||||
void calibrate_timer();
|
||||
/// Calibrate the timer speed against the clock
|
||||
void calibrate_timer();
|
||||
|
||||
private:
|
||||
inline static uint64_t ticks_to_us(uint64_t ticks) { return ticks / s_ticks_per_us; }
|
||||
inline static uint64_t us_to_ticks(uint64_t interval) { return interval * s_ticks_per_us; }
|
||||
inline static uint64_t ticks_to_us(uint64_t ticks) { return ticks / s_ticks_per_us; }
|
||||
inline static uint64_t us_to_ticks(uint64_t interval) { return interval * s_ticks_per_us; }
|
||||
|
||||
void set_divisor(uint8_t divisor);
|
||||
void set_repeat(bool repeat);
|
||||
void set_divisor(uint8_t divisor);
|
||||
void set_repeat(bool repeat);
|
||||
|
||||
uint32_t m_divisor;
|
||||
static uint64_t s_ticks_per_us;
|
||||
uint32_t m_divisor;
|
||||
static uint64_t s_ticks_per_us;
|
||||
};
|
||||
|
||||
|
||||
/// Controller for I/O APICs
|
||||
class ioapic :
|
||||
public apic
|
||||
public apic
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
/// \arg base Physical base address of the APIC's MMIO registers
|
||||
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
|
||||
ioapic(uintptr_t base, uint32_t base_gsi);
|
||||
/// Constructor
|
||||
/// \arg base Physical base address of the APIC's MMIO registers
|
||||
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
|
||||
ioapic(uintptr_t base, uint32_t base_gsi);
|
||||
|
||||
uint32_t get_base_gsi() const { return m_base_gsi; }
|
||||
uint32_t get_num_gsi() const { return m_num_gsi; }
|
||||
uint32_t get_base_gsi() const { return m_base_gsi; }
|
||||
uint32_t get_num_gsi() const { return m_num_gsi; }
|
||||
|
||||
/// Set a redirection entry.
|
||||
/// TODO: pick CPU
|
||||
/// \arg source Source interrupt number
|
||||
/// \arg vector Interrupt vector that should be used
|
||||
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
||||
/// \arg masked Whether the iterrupt should be suppressed
|
||||
void redirect(uint8_t irq, isr vector, uint16_t flags, bool masked);
|
||||
/// Set a redirection entry.
|
||||
/// TODO: pick CPU
|
||||
/// \arg source Source interrupt number
|
||||
/// \arg vector Interrupt vector that should be used
|
||||
/// \arg flags Flags for mode/polarity (ACPI MPS INTI flags)
|
||||
/// \arg masked Whether the iterrupt should be suppressed
|
||||
void redirect(uint8_t irq, isr vector, uint16_t flags, bool masked);
|
||||
|
||||
/// Mask or unmask an interrupt to stop/start having it sent to the CPU
|
||||
/// \arg irq The IOAPIC-local irq number
|
||||
/// \arg masked Whether to suppress this interrupt
|
||||
void mask(uint8_t irq, bool masked);
|
||||
/// Mask or unmask an interrupt to stop/start having it sent to the CPU
|
||||
/// \arg irq The IOAPIC-local irq number
|
||||
/// \arg masked Whether to suppress this interrupt
|
||||
void mask(uint8_t irq, bool masked);
|
||||
|
||||
/// Mask all interrupts on this IOAPIC.
|
||||
void mask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, true); }
|
||||
/// Mask all interrupts on this IOAPIC.
|
||||
void mask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, true); }
|
||||
|
||||
/// Unmask all interrupts on this IOAPIC.
|
||||
void unmask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, false); }
|
||||
/// Unmask all interrupts on this IOAPIC.
|
||||
void unmask_all() { for(int i=0; i<m_num_gsi; ++i) mask(i, false); }
|
||||
|
||||
void dump_redirs() const;
|
||||
void dump_redirs() const;
|
||||
|
||||
private:
|
||||
uint32_t m_base_gsi;
|
||||
uint32_t m_num_gsi;
|
||||
uint32_t m_base_gsi;
|
||||
uint32_t m_num_gsi;
|
||||
|
||||
uint8_t m_id;
|
||||
uint8_t m_version;
|
||||
uint8_t m_id;
|
||||
uint8_t m_version;
|
||||
};
|
||||
|
||||
is_bitfield(lapic::ipi);
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
class block_device
|
||||
{
|
||||
public:
|
||||
virtual size_t read(size_t offset, size_t length, void *buffer) = 0;
|
||||
virtual size_t read(size_t offset, size_t length, void *buffer) = 0;
|
||||
};
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
clock * clock::s_instance = nullptr;
|
||||
|
||||
clock::clock(uint64_t rate, clock::source source_func, void *data) :
|
||||
m_rate(rate),
|
||||
m_data(data),
|
||||
m_source(source_func)
|
||||
m_rate(rate),
|
||||
m_data(data),
|
||||
m_source(source_func)
|
||||
{
|
||||
// TODO: make this atomic
|
||||
if (s_instance == nullptr)
|
||||
s_instance = this;
|
||||
update();
|
||||
// TODO: make this atomic
|
||||
if (s_instance == nullptr)
|
||||
s_instance = this;
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
clock::spinwait(uint64_t us) const
|
||||
{
|
||||
uint64_t when = value() + us;
|
||||
while (value() < when) asm ("pause");
|
||||
uint64_t when = value() + us;
|
||||
while (value() < when) asm ("pause");
|
||||
}
|
||||
|
||||
|
||||
@@ -7,38 +7,38 @@
|
||||
class clock
|
||||
{
|
||||
public:
|
||||
/// A source is a function that returns the current
|
||||
/// value of some clock source.
|
||||
using source = uint64_t (*)(void*);
|
||||
/// A source is a function that returns the current
|
||||
/// value of some clock source.
|
||||
using source = uint64_t (*)(void*);
|
||||
|
||||
/// Constructor.
|
||||
/// \arg rate Number of source ticks per us
|
||||
/// \arg source Function for the clock source
|
||||
/// \arg data Data to pass to the source function
|
||||
clock(uint64_t rate, source source_func, void *data);
|
||||
/// Constructor.
|
||||
/// \arg rate Number of source ticks per us
|
||||
/// \arg source Function for the clock source
|
||||
/// \arg data Data to pass to the source function
|
||||
clock(uint64_t rate, source source_func, void *data);
|
||||
|
||||
/// Get the current value of the clock.
|
||||
/// \returns Current value of the source, in us
|
||||
/// TODO: optimize divison by finding a multiply and
|
||||
/// shift value instead
|
||||
inline uint64_t value() const { return m_source(m_data) / m_rate; }
|
||||
/// Get the current value of the clock.
|
||||
/// \returns Current value of the source, in us
|
||||
/// TODO: optimize divison by finding a multiply and
|
||||
/// shift value instead
|
||||
inline uint64_t value() const { return m_source(m_data) / m_rate; }
|
||||
|
||||
/// Update the internal state via the source
|
||||
/// \returns Current value of the clock
|
||||
inline void update() { m_current = value(); }
|
||||
/// Update the internal state via the source
|
||||
/// \returns Current value of the clock
|
||||
inline void update() { m_current = value(); }
|
||||
|
||||
/// Wait in a tight loop
|
||||
/// \arg interval Time to wait, in us
|
||||
void spinwait(uint64_t us) const;
|
||||
/// Wait in a tight loop
|
||||
/// \arg interval Time to wait, in us
|
||||
void spinwait(uint64_t us) const;
|
||||
|
||||
/// Get the master clock
|
||||
static clock & get() { return *s_instance; }
|
||||
/// Get the master clock
|
||||
static clock & get() { return *s_instance; }
|
||||
|
||||
private:
|
||||
uint64_t m_current; ///< current us count
|
||||
uint64_t m_rate; ///< source ticks per us
|
||||
void *m_data;
|
||||
source m_source;
|
||||
uint64_t m_current; ///< current us count
|
||||
uint64_t m_rate; ///< source ticks per us
|
||||
void *m_data;
|
||||
source m_source;
|
||||
|
||||
static clock *s_instance;
|
||||
static clock *s_instance;
|
||||
};
|
||||
|
||||
@@ -14,73 +14,73 @@ console &g_console = __g_console_storage.value;
|
||||
|
||||
|
||||
console::console() :
|
||||
m_serial(nullptr)
|
||||
m_serial(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
console::console(serial_port *serial) :
|
||||
m_serial(serial)
|
||||
m_serial(serial)
|
||||
{
|
||||
if (m_serial) {
|
||||
const char *fgseq = "\x1b[2J";
|
||||
while (*fgseq)
|
||||
m_serial->write(*fgseq++);
|
||||
}
|
||||
if (m_serial) {
|
||||
const char *fgseq = "\x1b[2J";
|
||||
while (*fgseq)
|
||||
m_serial->write(*fgseq++);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
console::echo()
|
||||
{
|
||||
putc(m_serial->read());
|
||||
putc(m_serial->read());
|
||||
}
|
||||
|
||||
void
|
||||
console::set_color(uint8_t fg, uint8_t bg)
|
||||
{
|
||||
if (m_serial) {
|
||||
const char *fgseq = "\x1b[38;5;";
|
||||
while (*fgseq)
|
||||
m_serial->write(*fgseq++);
|
||||
if (fg >= 100) m_serial->write('0' + (fg/100));
|
||||
if (fg >= 10) m_serial->write('0' + (fg/10) % 10);
|
||||
m_serial->write('0' + fg % 10);
|
||||
m_serial->write('m');
|
||||
if (m_serial) {
|
||||
const char *fgseq = "\x1b[38;5;";
|
||||
while (*fgseq)
|
||||
m_serial->write(*fgseq++);
|
||||
if (fg >= 100) m_serial->write('0' + (fg/100));
|
||||
if (fg >= 10) m_serial->write('0' + (fg/10) % 10);
|
||||
m_serial->write('0' + fg % 10);
|
||||
m_serial->write('m');
|
||||
|
||||
const char *bgseq = "\x1b[48;5;";
|
||||
while (*bgseq)
|
||||
m_serial->write(*bgseq++);
|
||||
if (bg >= 100) m_serial->write('0' + (bg/100));
|
||||
if (bg >= 10) m_serial->write('0' + (bg/10) % 10);
|
||||
m_serial->write('0' + bg % 10);
|
||||
m_serial->write('m');
|
||||
}
|
||||
const char *bgseq = "\x1b[48;5;";
|
||||
while (*bgseq)
|
||||
m_serial->write(*bgseq++);
|
||||
if (bg >= 100) m_serial->write('0' + (bg/100));
|
||||
if (bg >= 10) m_serial->write('0' + (bg/10) % 10);
|
||||
m_serial->write('0' + bg % 10);
|
||||
m_serial->write('m');
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
console::puts(const char *message)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (message && *message) {
|
||||
n++;
|
||||
putc(*message++);
|
||||
}
|
||||
size_t n = 0;
|
||||
while (message && *message) {
|
||||
n++;
|
||||
putc(*message++);
|
||||
}
|
||||
|
||||
return n;
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
console::putc(char c)
|
||||
{
|
||||
if (m_serial) {
|
||||
if (c == '\n') m_serial->write('\r');
|
||||
m_serial->write(c);
|
||||
}
|
||||
if (m_serial) {
|
||||
if (c == '\n') m_serial->write('\r');
|
||||
m_serial->write(c);
|
||||
}
|
||||
}
|
||||
|
||||
void console::vprintf(const char *fmt, va_list args)
|
||||
{
|
||||
static const unsigned buf_size = 256;
|
||||
char buffer[buf_size];
|
||||
vsnprintf_(buffer, buf_size, fmt, args);
|
||||
puts(buffer);
|
||||
static const unsigned buf_size = 256;
|
||||
char buffer[buf_size];
|
||||
vsnprintf_(buffer, buf_size, fmt, args);
|
||||
puts(buffer);
|
||||
}
|
||||
|
||||
@@ -8,35 +8,35 @@ class serial_port;
|
||||
class console
|
||||
{
|
||||
public:
|
||||
console();
|
||||
console(serial_port *serial);
|
||||
console();
|
||||
console(serial_port *serial);
|
||||
|
||||
void set_color(uint8_t fg = 7, uint8_t bg = 0);
|
||||
void set_color(uint8_t fg = 7, uint8_t bg = 0);
|
||||
|
||||
void putc(char c);
|
||||
size_t puts(const char *message);
|
||||
void vprintf(const char *fmt, va_list args);
|
||||
void putc(char c);
|
||||
size_t puts(const char *message);
|
||||
void vprintf(const char *fmt, va_list args);
|
||||
|
||||
inline void printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
inline void printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void put_hex(T x, int width = 0, char pad = ' ');
|
||||
template <typename T>
|
||||
void put_hex(T x, int width = 0, char pad = ' ');
|
||||
|
||||
template <typename T>
|
||||
void put_dec(T x, int width = 0, char pad = ' ');
|
||||
template <typename T>
|
||||
void put_dec(T x, int width = 0, char pad = ' ');
|
||||
|
||||
void echo();
|
||||
void echo();
|
||||
|
||||
static console * get();
|
||||
static console * get();
|
||||
|
||||
private:
|
||||
serial_port *m_serial;
|
||||
serial_port *m_serial;
|
||||
};
|
||||
|
||||
extern console &g_console;
|
||||
@@ -48,37 +48,37 @@ extern const char digits[];
|
||||
template <typename T>
|
||||
void console::put_hex(T x, int width, char pad)
|
||||
{
|
||||
static const int chars = sizeof(x) * 2;
|
||||
char message[chars + 1];
|
||||
static const int chars = sizeof(x) * 2;
|
||||
char message[chars + 1];
|
||||
|
||||
int len = 1;
|
||||
for (int i = chars - 1; i >= 0; --i) {
|
||||
uint8_t nibble = (x >> (i*4)) & 0xf;
|
||||
if (nibble) len = len > i + 1 ? len : i + 1;
|
||||
message[chars - i - 1] = digits[nibble];
|
||||
}
|
||||
message[chars] = 0;
|
||||
int len = 1;
|
||||
for (int i = chars - 1; i >= 0; --i) {
|
||||
uint8_t nibble = (x >> (i*4)) & 0xf;
|
||||
if (nibble) len = len > i + 1 ? len : i + 1;
|
||||
message[chars - i - 1] = digits[nibble];
|
||||
}
|
||||
message[chars] = 0;
|
||||
|
||||
if (width > len) for(int i=0; i<(width-len); ++i) putc(pad);
|
||||
puts(message + (chars - len));
|
||||
if (-width > len) for(int i=0; i<(-width-len); ++i) putc(' ');
|
||||
if (width > len) for(int i=0; i<(width-len); ++i) putc(pad);
|
||||
puts(message + (chars - len));
|
||||
if (-width > len) for(int i=0; i<(-width-len); ++i) putc(' ');
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void console::put_dec(T x, int width, char pad)
|
||||
{
|
||||
static const int chars = sizeof(x) * 3;
|
||||
char message[chars + 1];
|
||||
char *p = message + chars;
|
||||
int length = 0;
|
||||
*p-- = 0;
|
||||
do {
|
||||
*p-- = digits[x % 10];
|
||||
x /= 10;
|
||||
length += 1;
|
||||
} while (x != 0);
|
||||
static const int chars = sizeof(x) * 3;
|
||||
char message[chars + 1];
|
||||
char *p = message + chars;
|
||||
int length = 0;
|
||||
*p-- = 0;
|
||||
do {
|
||||
*p-- = digits[x % 10];
|
||||
x /= 10;
|
||||
length += 1;
|
||||
} while (x != 0);
|
||||
|
||||
if (width > length) for(int i=0; i<(width-length); ++i) putc(pad);
|
||||
puts(++p);
|
||||
if (-width > length) for(int i=0; i<(-width-length); ++i) putc(' ');
|
||||
if (width > length) for(int i=0; i<(width-length); ++i) putc(pad);
|
||||
puts(++p);
|
||||
if (-width > length) for(int i=0; i<(-width-length); ++i) putc(' ');
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
using __exit_func = void (*)(void *);
|
||||
|
||||
extern "C" {
|
||||
void *__dso_handle __attribute__ ((__weak__));
|
||||
int __cxa_atexit(__exit_func, void *, void *);
|
||||
void __cxa_pure_virtual();
|
||||
void *__dso_handle __attribute__ ((__weak__));
|
||||
int __cxa_atexit(__exit_func, void *, void *);
|
||||
void __cxa_pure_virtual();
|
||||
}
|
||||
|
||||
|
||||
struct __exit_func_entry
|
||||
{
|
||||
__exit_func func;
|
||||
void *obj;
|
||||
void *dso;
|
||||
__exit_func func;
|
||||
void *obj;
|
||||
void *dso;
|
||||
};
|
||||
|
||||
static int __num_exit_funcs = 0;
|
||||
@@ -23,15 +23,15 @@ __exit_func_entry __exit_funcs[__max_exit_funcs];
|
||||
int
|
||||
__cxa_atexit(__exit_func f, void *o, void *dso)
|
||||
{
|
||||
int i = __num_exit_funcs++;
|
||||
if (i >= __max_exit_funcs) return -1;
|
||||
__exit_funcs[i].func = f;
|
||||
__exit_funcs[i].obj = o;
|
||||
__exit_funcs[i].dso = dso;
|
||||
return 0;
|
||||
int i = __num_exit_funcs++;
|
||||
if (i >= __max_exit_funcs) return -1;
|
||||
__exit_funcs[i].func = f;
|
||||
__exit_funcs[i].obj = o;
|
||||
__exit_funcs[i].dso = dso;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __cxa_pure_virtual()
|
||||
{
|
||||
kassert(0, "Pure virtual function call");
|
||||
kassert(0, "Pure virtual function call");
|
||||
}
|
||||
|
||||
@@ -18,20 +18,20 @@ cpu_data g_bsp_cpu_data;
|
||||
void
|
||||
cpu_validate()
|
||||
{
|
||||
cpu::cpu_id cpu;
|
||||
cpu::cpu_id cpu;
|
||||
|
||||
log::info(logs::boot, "CPU: %s", cpu.brand_name());
|
||||
log::debug(logs::boot, " Vendor is %s", cpu.vendor_id());
|
||||
log::info(logs::boot, "CPU: %s", cpu.brand_name());
|
||||
log::debug(logs::boot, " Vendor is %s", cpu.vendor_id());
|
||||
|
||||
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic());
|
||||
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
|
||||
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic());
|
||||
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
|
||||
|
||||
#define CPU_FEATURE_OPT(name, ...) \
|
||||
log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "yes" : "no");
|
||||
log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "yes" : "no");
|
||||
|
||||
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
|
||||
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
|
||||
kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name );
|
||||
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
|
||||
kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name );
|
||||
|
||||
#include "cpu/features.inc"
|
||||
#undef CPU_FEATURE_OPT
|
||||
@@ -41,32 +41,32 @@ cpu_validate()
|
||||
void
|
||||
cpu_early_init(cpu_data *cpu)
|
||||
{
|
||||
cpu->idt->install();
|
||||
cpu->gdt->install();
|
||||
cpu->idt->install();
|
||||
cpu->gdt->install();
|
||||
|
||||
// Install the GS base pointint to the cpu_data
|
||||
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
|
||||
// Install the GS base pointint to the cpu_data
|
||||
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
|
||||
|
||||
// Set the initial process as the kernel "process"
|
||||
extern process &g_kernel_process;
|
||||
cpu->process = &g_kernel_process;
|
||||
// Set the initial process as the kernel "process"
|
||||
extern process &g_kernel_process;
|
||||
cpu->process = &g_kernel_process;
|
||||
}
|
||||
|
||||
void
|
||||
cpu_init(cpu_data *cpu, bool bsp)
|
||||
{
|
||||
if (!bsp) {
|
||||
// The BSP already called cpu_early_init
|
||||
cpu_early_init(cpu);
|
||||
}
|
||||
if (!bsp) {
|
||||
// The BSP already called cpu_early_init
|
||||
cpu_early_init(cpu);
|
||||
}
|
||||
|
||||
// Set up the syscall MSRs
|
||||
syscall_enable();
|
||||
// Set up the syscall MSRs
|
||||
syscall_enable();
|
||||
|
||||
// Set up the page attributes table
|
||||
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||
pat = (pat & 0x00ffffffffffffffull) | (0x01ull << 56); // set PAT 7 to WC
|
||||
wrmsr(msr::ia32_pat, pat);
|
||||
// Set up the page attributes table
|
||||
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||
pat = (pat & 0x00ffffffffffffffull) | (0x01ull << 56); // set PAT 7 to WC
|
||||
wrmsr(msr::ia32_pat, pat);
|
||||
|
||||
cpu->idt->add_ist_entries();
|
||||
cpu->idt->add_ist_entries();
|
||||
}
|
||||
|
||||
@@ -12,32 +12,32 @@ class TSS;
|
||||
|
||||
struct cpu_state
|
||||
{
|
||||
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, rsp, ss;
|
||||
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, rsp, ss;
|
||||
};
|
||||
|
||||
/// Per-cpu state data. If you change this, remember to update the assembly
|
||||
/// version in 'tasking.inc'
|
||||
struct cpu_data
|
||||
{
|
||||
cpu_data *self;
|
||||
uint16_t id;
|
||||
uint16_t index;
|
||||
uint32_t reserved;
|
||||
uintptr_t rsp0;
|
||||
uintptr_t rsp3;
|
||||
TCB *tcb;
|
||||
thread *thread;
|
||||
process *process;
|
||||
IDT *idt;
|
||||
TSS *tss;
|
||||
GDT *gdt;
|
||||
cpu_data *self;
|
||||
uint16_t id;
|
||||
uint16_t index;
|
||||
uint32_t reserved;
|
||||
uintptr_t rsp0;
|
||||
uintptr_t rsp3;
|
||||
TCB *tcb;
|
||||
thread *thread;
|
||||
process *process;
|
||||
IDT *idt;
|
||||
TSS *tss;
|
||||
GDT *gdt;
|
||||
|
||||
// Members beyond this point do not appear in
|
||||
// the assembly version
|
||||
lapic *apic;
|
||||
// Members beyond this point do not appear in
|
||||
// the assembly version
|
||||
lapic *apic;
|
||||
};
|
||||
|
||||
extern "C" cpu_data * _current_gsbase();
|
||||
|
||||
@@ -11,63 +11,63 @@ size_t __counter_syscall_sysret = 0;
|
||||
void
|
||||
print_regs(const cpu_state ®s)
|
||||
{
|
||||
console *cons = console::get();
|
||||
cpu_data &cpu = current_cpu();
|
||||
console *cons = console::get();
|
||||
cpu_data &cpu = current_cpu();
|
||||
|
||||
uint64_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
uint64_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
|
||||
uintptr_t cr3 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
||||
uintptr_t cr3 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
||||
|
||||
cons->printf(" process: %llx", cpu.process->koid());
|
||||
cons->printf(" thread: %llx\n", cpu.thread->koid());
|
||||
cons->printf(" process: %llx", cpu.process->koid());
|
||||
cons->printf(" thread: %llx\n", cpu.thread->koid());
|
||||
|
||||
print_regL("rax", regs.rax);
|
||||
print_regM("rbx", regs.rbx);
|
||||
print_regR("rcx", regs.rcx);
|
||||
print_regL("rdx", regs.rdx);
|
||||
print_regM("rdi", regs.rdi);
|
||||
print_regR("rsi", regs.rsi);
|
||||
print_regL("rax", regs.rax);
|
||||
print_regM("rbx", regs.rbx);
|
||||
print_regR("rcx", regs.rcx);
|
||||
print_regL("rdx", regs.rdx);
|
||||
print_regM("rdi", regs.rdi);
|
||||
print_regR("rsi", regs.rsi);
|
||||
|
||||
cons->puts("\n");
|
||||
print_regL(" r8", regs.r8);
|
||||
print_regM(" r9", regs.r9);
|
||||
print_regR("r10", regs.r10);
|
||||
print_regL("r11", regs.r11);
|
||||
print_regM("r12", regs.r12);
|
||||
print_regR("r13", regs.r13);
|
||||
print_regL("r14", regs.r14);
|
||||
print_regM("r15", regs.r15);
|
||||
cons->puts("\n");
|
||||
print_regL(" r8", regs.r8);
|
||||
print_regM(" r9", regs.r9);
|
||||
print_regR("r10", regs.r10);
|
||||
print_regL("r11", regs.r11);
|
||||
print_regM("r12", regs.r12);
|
||||
print_regR("r13", regs.r13);
|
||||
print_regL("r14", regs.r14);
|
||||
print_regM("r15", regs.r15);
|
||||
|
||||
cons->puts("\n\n");
|
||||
print_regL("rbp", regs.rbp);
|
||||
print_regM("rsp", regs.rsp);
|
||||
print_regR("sp0", cpu.rsp0);
|
||||
cons->puts("\n\n");
|
||||
print_regL("rbp", regs.rbp);
|
||||
print_regM("rsp", regs.rsp);
|
||||
print_regR("sp0", cpu.rsp0);
|
||||
|
||||
print_regL("rip", regs.rip);
|
||||
print_regM("cr3", cr3);
|
||||
print_regR("cr2", cr2);
|
||||
print_regL("rip", regs.rip);
|
||||
print_regM("cr3", cr3);
|
||||
print_regR("cr2", cr2);
|
||||
|
||||
cons->puts("\n");
|
||||
cons->puts("\n");
|
||||
}
|
||||
|
||||
struct frame
|
||||
{
|
||||
frame *prev;
|
||||
uintptr_t return_addr;
|
||||
frame *prev;
|
||||
uintptr_t return_addr;
|
||||
};
|
||||
|
||||
void
|
||||
print_stack(const cpu_state ®s)
|
||||
{
|
||||
console *cons = console::get();
|
||||
console *cons = console::get();
|
||||
|
||||
cons->puts("\nStack:\n");
|
||||
uint64_t sp = regs.rsp;
|
||||
while (sp <= regs.rbp) {
|
||||
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
|
||||
sp += sizeof(uint64_t);
|
||||
}
|
||||
cons->puts("\nStack:\n");
|
||||
uint64_t sp = regs.rsp;
|
||||
while (sp <= regs.rbp) {
|
||||
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
|
||||
sp += sizeof(uint64_t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
struct cpu_state;
|
||||
|
||||
extern "C" {
|
||||
uintptr_t get_rsp();
|
||||
uintptr_t get_rip();
|
||||
uintptr_t get_caller();
|
||||
uintptr_t get_grandcaller();
|
||||
uintptr_t get_frame(int frame);
|
||||
uintptr_t get_gsbase();
|
||||
void _halt();
|
||||
uintptr_t get_rsp();
|
||||
uintptr_t get_rip();
|
||||
uintptr_t get_caller();
|
||||
uintptr_t get_grandcaller();
|
||||
uintptr_t get_frame(int frame);
|
||||
uintptr_t get_gsbase();
|
||||
void _halt();
|
||||
}
|
||||
|
||||
extern size_t __counter_syscall_enter;
|
||||
|
||||
@@ -23,417 +23,417 @@ device_manager device_manager::s_instance;
|
||||
|
||||
struct acpi1_rsdp
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
char signature[8];
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi2_rsdp
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum10;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
char signature[8];
|
||||
uint8_t checksum10;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
|
||||
uint32_t length;
|
||||
acpi_table_header *xsdt_address;
|
||||
uint8_t checksum20;
|
||||
uint8_t reserved[3];
|
||||
uint32_t length;
|
||||
acpi_table_header *xsdt_address;
|
||||
uint8_t checksum20;
|
||||
uint8_t reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
bool
|
||||
acpi_table_header::validate(uint32_t expected_type) const
|
||||
{
|
||||
if (kutil::checksum(this, length) != 0) return false;
|
||||
return !expected_type || (expected_type == type);
|
||||
if (kutil::checksum(this, length) != 0) return false;
|
||||
return !expected_type || (expected_type == type);
|
||||
}
|
||||
|
||||
|
||||
device_manager::device_manager() :
|
||||
m_lapic_base(0)
|
||||
m_lapic_base(0)
|
||||
{
|
||||
m_irqs.ensure_capacity(32);
|
||||
m_irqs.set_size(16);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
m_irqs[i] = nullptr;
|
||||
m_irqs.ensure_capacity(32);
|
||||
m_irqs.set_size(16);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
m_irqs[i] = nullptr;
|
||||
|
||||
m_irqs[2] = ignore_endpoint;
|
||||
m_irqs[2] = ignore_endpoint;
|
||||
}
|
||||
|
||||
template <typename T> static const T *
|
||||
check_get_table(const acpi_table_header *header)
|
||||
{
|
||||
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
|
||||
return reinterpret_cast<const T *>(header);
|
||||
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
|
||||
return reinterpret_cast<const T *>(header);
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::parse_acpi(const void *root_table)
|
||||
{
|
||||
kassert(root_table != 0, "ACPI root table pointer is null.");
|
||||
kassert(root_table != 0, "ACPI root table pointer is null.");
|
||||
|
||||
const acpi1_rsdp *acpi1 = memory::to_virtual(
|
||||
reinterpret_cast<const acpi1_rsdp *>(root_table));
|
||||
const acpi1_rsdp *acpi1 = memory::to_virtual(
|
||||
reinterpret_cast<const acpi1_rsdp *>(root_table));
|
||||
|
||||
for (int i = 0; i < sizeof(acpi1->signature); ++i)
|
||||
kassert(acpi1->signature[i] == expected_signature[i],
|
||||
"ACPI RSDP table signature mismatch");
|
||||
for (int i = 0; i < sizeof(acpi1->signature); ++i)
|
||||
kassert(acpi1->signature[i] == expected_signature[i],
|
||||
"ACPI RSDP table signature mismatch");
|
||||
|
||||
uint8_t sum = kutil::checksum(acpi1, sizeof(acpi1_rsdp), 0);
|
||||
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
|
||||
uint8_t sum = kutil::checksum(acpi1, sizeof(acpi1_rsdp), 0);
|
||||
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
|
||||
|
||||
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
|
||||
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
|
||||
|
||||
const acpi2_rsdp *acpi2 =
|
||||
reinterpret_cast<const acpi2_rsdp *>(acpi1);
|
||||
const acpi2_rsdp *acpi2 =
|
||||
reinterpret_cast<const acpi2_rsdp *>(acpi1);
|
||||
|
||||
sum = kutil::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
|
||||
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
||||
sum = kutil::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
|
||||
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
||||
|
||||
load_xsdt(memory::to_virtual(acpi2->xsdt_address));
|
||||
load_xsdt(memory::to_virtual(acpi2->xsdt_address));
|
||||
}
|
||||
|
||||
const device_manager::apic_nmi *
|
||||
device_manager::get_lapic_nmi(uint8_t id) const
|
||||
{
|
||||
for (const auto &nmi : m_nmis) {
|
||||
if (nmi.cpu == 0xff || nmi.cpu == id)
|
||||
return &nmi;
|
||||
}
|
||||
for (const auto &nmi : m_nmis) {
|
||||
if (nmi.cpu == 0xff || nmi.cpu == id)
|
||||
return &nmi;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const device_manager::irq_override *
|
||||
device_manager::get_irq_override(uint8_t irq) const
|
||||
{
|
||||
for (const auto &o : m_overrides)
|
||||
if (o.source == irq) return &o;
|
||||
for (const auto &o : m_overrides)
|
||||
if (o.source == irq) return &o;
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ioapic *
|
||||
device_manager::get_ioapic(int i)
|
||||
{
|
||||
return (i < m_ioapics.count()) ? &m_ioapics[i] : nullptr;
|
||||
return (i < m_ioapics.count()) ? &m_ioapics[i] : nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
put_sig(char *into, uint32_t type)
|
||||
{
|
||||
for (int j=0; j<4; ++j) into[j] = reinterpret_cast<char *>(&type)[j];
|
||||
for (int j=0; j<4; ++j) into[j] = reinterpret_cast<char *>(&type)[j];
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_xsdt(const acpi_table_header *header)
|
||||
{
|
||||
const auto *xsdt = check_get_table<acpi_xsdt>(header);
|
||||
const auto *xsdt = check_get_table<acpi_xsdt>(header);
|
||||
|
||||
char sig[5] = {0,0,0,0,0};
|
||||
log::info(logs::device, "ACPI 2.0+ tables loading");
|
||||
char sig[5] = {0,0,0,0,0};
|
||||
log::info(logs::device, "ACPI 2.0+ tables loading");
|
||||
|
||||
put_sig(sig, xsdt->header.type);
|
||||
log::debug(logs::device, " Found table %s", sig);
|
||||
put_sig(sig, xsdt->header.type);
|
||||
log::debug(logs::device, " Found table %s", sig);
|
||||
|
||||
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi_table_header *header =
|
||||
memory::to_virtual(xsdt->headers[i]);
|
||||
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi_table_header *header =
|
||||
memory::to_virtual(xsdt->headers[i]);
|
||||
|
||||
put_sig(sig, header->type);
|
||||
log::debug(logs::device, " Found table %s", sig);
|
||||
put_sig(sig, header->type);
|
||||
log::debug(logs::device, " Found table %s", sig);
|
||||
|
||||
kassert(header->validate(), "Table failed validation.");
|
||||
kassert(header->validate(), "Table failed validation.");
|
||||
|
||||
switch (header->type) {
|
||||
case acpi_apic::type_id:
|
||||
load_apic(header);
|
||||
break;
|
||||
switch (header->type) {
|
||||
case acpi_apic::type_id:
|
||||
load_apic(header);
|
||||
break;
|
||||
|
||||
case acpi_mcfg::type_id:
|
||||
load_mcfg(header);
|
||||
break;
|
||||
case acpi_mcfg::type_id:
|
||||
load_mcfg(header);
|
||||
break;
|
||||
|
||||
case acpi_hpet::type_id:
|
||||
load_hpet(header);
|
||||
break;
|
||||
case acpi_hpet::type_id:
|
||||
load_hpet(header);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_apic(const acpi_table_header *header)
|
||||
{
|
||||
const auto *apic = check_get_table<acpi_apic>(header);
|
||||
const auto *apic = check_get_table<acpi_apic>(header);
|
||||
|
||||
m_lapic_base = apic->local_address;
|
||||
m_lapic_base = apic->local_address;
|
||||
|
||||
size_t count = acpi_table_entries(apic, 1);
|
||||
uint8_t const *p = apic->controller_data;
|
||||
uint8_t const *end = p + count;
|
||||
size_t count = acpi_table_entries(apic, 1);
|
||||
uint8_t const *p = apic->controller_data;
|
||||
uint8_t const *end = p + count;
|
||||
|
||||
// Pass one: count objcts
|
||||
unsigned num_lapics = 0;
|
||||
unsigned num_ioapics = 0;
|
||||
unsigned num_overrides = 0;
|
||||
unsigned num_nmis = 0;
|
||||
while (p < end) {
|
||||
const uint8_t type = p[0];
|
||||
const uint8_t length = p[1];
|
||||
// Pass one: count objcts
|
||||
unsigned num_lapics = 0;
|
||||
unsigned num_ioapics = 0;
|
||||
unsigned num_overrides = 0;
|
||||
unsigned num_nmis = 0;
|
||||
while (p < end) {
|
||||
const uint8_t type = p[0];
|
||||
const uint8_t length = p[1];
|
||||
|
||||
switch (type) {
|
||||
case 0: ++num_lapics; break;
|
||||
case 1: ++num_ioapics; break;
|
||||
case 2: ++num_overrides; break;
|
||||
case 4: ++num_nmis; break;
|
||||
default: break;
|
||||
}
|
||||
switch (type) {
|
||||
case 0: ++num_lapics; break;
|
||||
case 1: ++num_ioapics; break;
|
||||
case 2: ++num_overrides; break;
|
||||
case 4: ++num_nmis; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
p += length;
|
||||
}
|
||||
p += length;
|
||||
}
|
||||
|
||||
m_apic_ids.set_capacity(num_lapics);
|
||||
m_ioapics.set_capacity(num_ioapics);
|
||||
m_overrides.set_capacity(num_overrides);
|
||||
m_nmis.set_capacity(num_nmis);
|
||||
m_apic_ids.set_capacity(num_lapics);
|
||||
m_ioapics.set_capacity(num_ioapics);
|
||||
m_overrides.set_capacity(num_overrides);
|
||||
m_nmis.set_capacity(num_nmis);
|
||||
|
||||
// Pass two: configure objects
|
||||
p = apic->controller_data;
|
||||
while (p < end) {
|
||||
const uint8_t type = p[0];
|
||||
const uint8_t length = p[1];
|
||||
// Pass two: configure objects
|
||||
p = apic->controller_data;
|
||||
while (p < end) {
|
||||
const uint8_t type = p[0];
|
||||
const uint8_t length = p[1];
|
||||
|
||||
switch (type) {
|
||||
case 0: { // Local APIC
|
||||
uint8_t uid = kutil::read_from<uint8_t>(p+2);
|
||||
uint8_t id = kutil::read_from<uint8_t>(p+3);
|
||||
m_apic_ids.append(id);
|
||||
switch (type) {
|
||||
case 0: { // Local APIC
|
||||
uint8_t uid = kutil::read_from<uint8_t>(p+2);
|
||||
uint8_t id = kutil::read_from<uint8_t>(p+3);
|
||||
m_apic_ids.append(id);
|
||||
|
||||
log::debug(logs::device, " Local APIC uid %x id %x", uid, id);
|
||||
}
|
||||
break;
|
||||
log::debug(logs::device, " Local APIC uid %x id %x", uid, id);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: { // I/O APIC
|
||||
uintptr_t base = kutil::read_from<uint32_t>(p+4);
|
||||
uint32_t base_gsi = kutil::read_from<uint32_t>(p+8);
|
||||
m_ioapics.emplace(base, base_gsi);
|
||||
case 1: { // I/O APIC
|
||||
uintptr_t base = kutil::read_from<uint32_t>(p+4);
|
||||
uint32_t base_gsi = kutil::read_from<uint32_t>(p+8);
|
||||
m_ioapics.emplace(base, base_gsi);
|
||||
|
||||
log::debug(logs::device, " IO APIC gsi %x base %x", base_gsi, base);
|
||||
}
|
||||
break;
|
||||
log::debug(logs::device, " IO APIC gsi %x base %x", base_gsi, base);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: { // Interrupt source override
|
||||
irq_override o;
|
||||
o.source = kutil::read_from<uint8_t>(p+3);
|
||||
o.gsi = kutil::read_from<uint32_t>(p+4);
|
||||
o.flags = kutil::read_from<uint16_t>(p+8);
|
||||
m_overrides.append(o);
|
||||
case 2: { // Interrupt source override
|
||||
irq_override o;
|
||||
o.source = kutil::read_from<uint8_t>(p+3);
|
||||
o.gsi = kutil::read_from<uint32_t>(p+4);
|
||||
o.flags = kutil::read_from<uint16_t>(p+8);
|
||||
m_overrides.append(o);
|
||||
|
||||
log::debug(logs::device, " Intr source override IRQ %d -> %d Pol %d Tri %d",
|
||||
o.source, o.gsi, (o.flags & 0x3), ((o.flags >> 2) & 0x3));
|
||||
}
|
||||
break;
|
||||
log::debug(logs::device, " Intr source override IRQ %d -> %d Pol %d Tri %d",
|
||||
o.source, o.gsi, (o.flags & 0x3), ((o.flags >> 2) & 0x3));
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: {// LAPIC NMI
|
||||
apic_nmi nmi;
|
||||
nmi.cpu = kutil::read_from<uint8_t>(p + 2);
|
||||
nmi.lint = kutil::read_from<uint8_t>(p + 5);
|
||||
nmi.flags = kutil::read_from<uint16_t>(p + 3);
|
||||
m_nmis.append(nmi);
|
||||
case 4: {// LAPIC NMI
|
||||
apic_nmi nmi;
|
||||
nmi.cpu = kutil::read_from<uint8_t>(p + 2);
|
||||
nmi.lint = kutil::read_from<uint8_t>(p + 5);
|
||||
nmi.flags = kutil::read_from<uint16_t>(p + 3);
|
||||
m_nmis.append(nmi);
|
||||
|
||||
log::debug(logs::device, " LAPIC NMI Proc %02x LINT%d Pol %d Tri %d",
|
||||
nmi.cpu, nmi.lint, nmi.flags & 0x3, (nmi.flags >> 2) & 0x3);
|
||||
}
|
||||
break;
|
||||
log::debug(logs::device, " LAPIC NMI Proc %02x LINT%d Pol %d Tri %d",
|
||||
nmi.cpu, nmi.lint, nmi.flags & 0x3, (nmi.flags >> 2) & 0x3);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log::debug(logs::device, " APIC entry type %d", type);
|
||||
}
|
||||
default:
|
||||
log::debug(logs::device, " APIC entry type %d", type);
|
||||
}
|
||||
|
||||
p += length;
|
||||
}
|
||||
p += length;
|
||||
}
|
||||
|
||||
m_ioapics[0].mask(3, false);
|
||||
m_ioapics[0].mask(4, false);
|
||||
m_ioapics[0].mask(3, false);
|
||||
m_ioapics[0].mask(4, false);
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_mcfg(const acpi_table_header *header)
|
||||
{
|
||||
const auto *mcfg = check_get_table<acpi_mcfg>(header);
|
||||
const auto *mcfg = check_get_table<acpi_mcfg>(header);
|
||||
|
||||
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
|
||||
m_pci.set_size(count);
|
||||
m_devices.set_capacity(16);
|
||||
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
|
||||
m_pci.set_size(count);
|
||||
m_devices.set_capacity(16);
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
|
||||
|
||||
m_pci[i].group = mcfge.group;
|
||||
m_pci[i].bus_start = mcfge.bus_start;
|
||||
m_pci[i].bus_end = mcfge.bus_end;
|
||||
m_pci[i].base = memory::to_virtual<uint32_t>(mcfge.base);
|
||||
m_pci[i].group = mcfge.group;
|
||||
m_pci[i].bus_start = mcfge.bus_start;
|
||||
m_pci[i].bus_end = mcfge.bus_end;
|
||||
m_pci[i].base = memory::to_virtual<uint32_t>(mcfge.base);
|
||||
|
||||
log::debug(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
|
||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||
}
|
||||
log::debug(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
|
||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||
}
|
||||
|
||||
probe_pci();
|
||||
probe_pci();
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_hpet(const acpi_table_header *header)
|
||||
{
|
||||
const auto *hpet = check_get_table<acpi_hpet>(header);
|
||||
const auto *hpet = check_get_table<acpi_hpet>(header);
|
||||
|
||||
log::debug(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
|
||||
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
|
||||
log::debug(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
|
||||
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
|
||||
|
||||
uint32_t hwid = hpet->hardware_id;
|
||||
uint8_t rev_id = hwid & 0xff;
|
||||
uint8_t comparators = (hwid >> 8) & 0x1f;
|
||||
uint8_t count_size_cap = (hwid >> 13) & 1;
|
||||
uint8_t legacy_replacement = (hwid >> 15) & 1;
|
||||
uint32_t pci_vendor_id = (hwid >> 16);
|
||||
uint32_t hwid = hpet->hardware_id;
|
||||
uint8_t rev_id = hwid & 0xff;
|
||||
uint8_t comparators = (hwid >> 8) & 0x1f;
|
||||
uint8_t count_size_cap = (hwid >> 13) & 1;
|
||||
uint8_t legacy_replacement = (hwid >> 15) & 1;
|
||||
uint32_t pci_vendor_id = (hwid >> 16);
|
||||
|
||||
log::debug(logs::device, " rev:%02d comparators:%02d count_size_cap:%1d legacy_repl:%1d",
|
||||
rev_id, comparators, count_size_cap, legacy_replacement);
|
||||
log::debug(logs::device, " pci vendor id: %04x", pci_vendor_id);
|
||||
log::debug(logs::device, " rev:%02d comparators:%02d count_size_cap:%1d legacy_repl:%1d",
|
||||
rev_id, comparators, count_size_cap, legacy_replacement);
|
||||
log::debug(logs::device, " pci vendor id: %04x", pci_vendor_id);
|
||||
|
||||
m_hpets.emplace(hpet->index,
|
||||
reinterpret_cast<uint64_t*>(hpet->base_address.address + ::memory::page_offset));
|
||||
m_hpets.emplace(hpet->index,
|
||||
reinterpret_cast<uint64_t*>(hpet->base_address.address + ::memory::page_offset));
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::probe_pci()
|
||||
{
|
||||
for (auto &pci : m_pci) {
|
||||
log::debug(logs::device, "Probing PCI group at base %016lx", pci.base);
|
||||
for (auto &pci : m_pci) {
|
||||
log::debug(logs::device, "Probing PCI group at base %016lx", pci.base);
|
||||
|
||||
for (int bus = pci.bus_start; bus <= pci.bus_end; ++bus) {
|
||||
for (int dev = 0; dev < 32; ++dev) {
|
||||
if (!pci.has_device(bus, dev, 0)) continue;
|
||||
for (int bus = pci.bus_start; bus <= pci.bus_end; ++bus) {
|
||||
for (int dev = 0; dev < 32; ++dev) {
|
||||
if (!pci.has_device(bus, dev, 0)) continue;
|
||||
|
||||
auto &d0 = m_devices.emplace(pci, bus, dev, 0);
|
||||
if (!d0.multi()) continue;
|
||||
auto &d0 = m_devices.emplace(pci, bus, dev, 0);
|
||||
if (!d0.multi()) continue;
|
||||
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
if (pci.has_device(bus, dev, i))
|
||||
m_devices.emplace(pci, bus, dev, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
if (pci.has_device(bus, dev, i))
|
||||
m_devices.emplace(pci, bus, dev, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
fake_clock_source(void*)
|
||||
{
|
||||
static uint64_t value = 0;
|
||||
return value++;
|
||||
static uint64_t value = 0;
|
||||
return value++;
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::init_drivers()
|
||||
{
|
||||
// Eventually this should be e.g. a lookup into a loadable driver list
|
||||
// for now, just look for AHCI devices
|
||||
/*
|
||||
for (auto &device : m_devices) {
|
||||
if (device.devclass() != 1 || device.subclass() != 6)
|
||||
continue;
|
||||
// Eventually this should be e.g. a lookup into a loadable driver list
|
||||
// for now, just look for AHCI devices
|
||||
/*
|
||||
for (auto &device : m_devices) {
|
||||
if (device.devclass() != 1 || device.subclass() != 6)
|
||||
continue;
|
||||
|
||||
if (device.progif() != 1) {
|
||||
log::warn(logs::device, "Found SATA device %d:%d:%d, but not an AHCI interface.",
|
||||
device.bus(), device.device(), device.function());
|
||||
}
|
||||
if (device.progif() != 1) {
|
||||
log::warn(logs::device, "Found SATA device %d:%d:%d, but not an AHCI interface.",
|
||||
device.bus(), device.device(), device.function());
|
||||
}
|
||||
|
||||
ahcid.register_device(&device);
|
||||
}
|
||||
*/
|
||||
clock *master_clock = nullptr;
|
||||
if (m_hpets.count() > 0) {
|
||||
hpet &h = m_hpets[0];
|
||||
h.enable();
|
||||
ahcid.register_device(&device);
|
||||
}
|
||||
*/
|
||||
clock *master_clock = nullptr;
|
||||
if (m_hpets.count() > 0) {
|
||||
hpet &h = m_hpets[0];
|
||||
h.enable();
|
||||
|
||||
// becomes the singleton
|
||||
master_clock = new clock(h.rate(), hpet_clock_source, &h);
|
||||
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
||||
} else {
|
||||
//TODO: Other clocks, APIC clock?
|
||||
master_clock = new clock(5000, fake_clock_source, nullptr);
|
||||
}
|
||||
// becomes the singleton
|
||||
master_clock = new clock(h.rate(), hpet_clock_source, &h);
|
||||
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
||||
} else {
|
||||
//TODO: Other clocks, APIC clock?
|
||||
master_clock = new clock(5000, fake_clock_source, nullptr);
|
||||
}
|
||||
|
||||
kassert(master_clock, "Failed to allocate master clock");
|
||||
kassert(master_clock, "Failed to allocate master clock");
|
||||
}
|
||||
|
||||
bool
|
||||
device_manager::dispatch_irq(unsigned irq)
|
||||
{
|
||||
if (irq == 4) {
|
||||
g_com1.handle_interrupt();
|
||||
return true;
|
||||
}
|
||||
if (irq == 4) {
|
||||
g_com1.handle_interrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (irq >= m_irqs.count())
|
||||
return false;
|
||||
if (irq >= m_irqs.count())
|
||||
return false;
|
||||
|
||||
endpoint *e = m_irqs[irq];
|
||||
if (!e || e == ignore_endpoint)
|
||||
return e == ignore_endpoint;
|
||||
endpoint *e = m_irqs[irq];
|
||||
if (!e || e == ignore_endpoint)
|
||||
return e == ignore_endpoint;
|
||||
|
||||
e->signal_irq(irq);
|
||||
return true;
|
||||
e->signal_irq(irq);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
device_manager::bind_irq(unsigned irq, endpoint *target)
|
||||
{
|
||||
// TODO: grow if under max size
|
||||
if (irq >= m_irqs.count())
|
||||
return false;
|
||||
// TODO: grow if under max size
|
||||
if (irq >= m_irqs.count())
|
||||
return false;
|
||||
|
||||
m_irqs[irq]= target;
|
||||
return true;
|
||||
m_irqs[irq]= target;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::unbind_irqs(endpoint *target)
|
||||
{
|
||||
const size_t count = m_irqs.count();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (m_irqs[i] == target)
|
||||
m_irqs[i] = nullptr;
|
||||
}
|
||||
const size_t count = m_irqs.count();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (m_irqs[i] == target)
|
||||
m_irqs[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||
{
|
||||
/*
|
||||
// TODO: find gaps to fill
|
||||
uint8_t irq = m_irqs.count();
|
||||
isr vector = isr::irq00 + irq;
|
||||
m_irqs.append({name, cb, data});
|
||||
/*
|
||||
// TODO: find gaps to fill
|
||||
uint8_t irq = m_irqs.count();
|
||||
isr vector = isr::irq00 + irq;
|
||||
m_irqs.append({name, cb, data});
|
||||
|
||||
log::debug(logs::device, "Allocating IRQ %02x to %s.", irq, name);
|
||||
log::debug(logs::device, "Allocating IRQ %02x to %s.", irq, name);
|
||||
|
||||
device.write_msi_regs(
|
||||
0xFEE00000,
|
||||
static_cast<uint16_t>(vector));
|
||||
*/
|
||||
return true;
|
||||
device.write_msi_regs(
|
||||
0xFEE00000,
|
||||
static_cast<uint16_t>(vector));
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::register_block_device(block_device *blockdev)
|
||||
{
|
||||
m_blockdevs.append(blockdev);
|
||||
m_blockdevs.append(blockdev);
|
||||
}
|
||||
|
||||
@@ -17,154 +17,154 @@ using irq_callback = void (*)(void *);
|
||||
class device_manager
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
device_manager();
|
||||
/// Constructor.
|
||||
device_manager();
|
||||
|
||||
/// Get the system global device manager.
|
||||
/// \returns A reference to the system device manager
|
||||
static device_manager & get() { return s_instance; }
|
||||
/// Get the system global device manager.
|
||||
/// \returns A reference to the system device manager
|
||||
static device_manager & get() { return s_instance; }
|
||||
|
||||
/// Get an IOAPIC
|
||||
/// \arg i Index of the requested IOAPIC
|
||||
/// \returns An object representing the given IOAPIC if it exists,
|
||||
/// otherwise nullptr.
|
||||
ioapic * get_ioapic(int i);
|
||||
/// Get an IOAPIC
|
||||
/// \arg i Index of the requested IOAPIC
|
||||
/// \returns An object representing the given IOAPIC if it exists,
|
||||
/// otherwise nullptr.
|
||||
ioapic * get_ioapic(int i);
|
||||
|
||||
/// Parse ACPI tables.
|
||||
/// \arg root_table Pointer to the ACPI RSDP
|
||||
void parse_acpi(const void *root_table);
|
||||
/// Parse ACPI tables.
|
||||
/// \arg root_table Pointer to the ACPI RSDP
|
||||
void parse_acpi(const void *root_table);
|
||||
|
||||
/// Intialize drivers for the current device list.
|
||||
void init_drivers();
|
||||
/// Intialize drivers for the current device list.
|
||||
void init_drivers();
|
||||
|
||||
/// Bind an IRQ to an endpoint
|
||||
/// \arg irq The IRQ number to bind
|
||||
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
||||
/// \returns True on success
|
||||
bool bind_irq(unsigned irq, endpoint *target);
|
||||
/// Bind an IRQ to an endpoint
|
||||
/// \arg irq The IRQ number to bind
|
||||
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
||||
/// \returns True on success
|
||||
bool bind_irq(unsigned irq, endpoint *target);
|
||||
|
||||
/// Remove IRQ bindings for an endpoint
|
||||
/// \arg target The endpoint to remove
|
||||
void unbind_irqs(endpoint *target);
|
||||
/// Remove IRQ bindings for an endpoint
|
||||
/// \arg target The endpoint to remove
|
||||
void unbind_irqs(endpoint *target);
|
||||
|
||||
/// Allocate an MSI IRQ for a device
|
||||
/// \arg name Name of the interrupt, for display to user
|
||||
/// \arg device Device this MSI is being allocated for
|
||||
/// \arg cb Callback to call when the interrupt is received
|
||||
/// \arg data Data to pass to the callback
|
||||
/// \returns True if an interrupt was allocated successfully
|
||||
bool allocate_msi(
|
||||
const char *name,
|
||||
pci_device &device,
|
||||
irq_callback cb,
|
||||
void *data);
|
||||
/// Allocate an MSI IRQ for a device
|
||||
/// \arg name Name of the interrupt, for display to user
|
||||
/// \arg device Device this MSI is being allocated for
|
||||
/// \arg cb Callback to call when the interrupt is received
|
||||
/// \arg data Data to pass to the callback
|
||||
/// \returns True if an interrupt was allocated successfully
|
||||
bool allocate_msi(
|
||||
const char *name,
|
||||
pci_device &device,
|
||||
irq_callback cb,
|
||||
void *data);
|
||||
|
||||
/// Dispatch an IRQ interrupt
|
||||
/// \arg irq The irq number of the interrupt
|
||||
/// \returns True if the interrupt was handled
|
||||
bool dispatch_irq(unsigned irq);
|
||||
/// Dispatch an IRQ interrupt
|
||||
/// \arg irq The irq number of the interrupt
|
||||
/// \returns True if the interrupt was handled
|
||||
bool dispatch_irq(unsigned irq);
|
||||
|
||||
struct apic_nmi
|
||||
{
|
||||
uint8_t cpu;
|
||||
uint8_t lint;
|
||||
uint16_t flags;
|
||||
};
|
||||
struct apic_nmi
|
||||
{
|
||||
uint8_t cpu;
|
||||
uint8_t lint;
|
||||
uint16_t flags;
|
||||
};
|
||||
|
||||
struct irq_override
|
||||
{
|
||||
uint8_t source;
|
||||
uint16_t flags;
|
||||
uint32_t gsi;
|
||||
};
|
||||
struct irq_override
|
||||
{
|
||||
uint8_t source;
|
||||
uint16_t flags;
|
||||
uint32_t gsi;
|
||||
};
|
||||
|
||||
/// Get the list of APIC ids for other CPUs
|
||||
inline const kutil::vector<uint8_t> & get_apic_ids() const { return m_apic_ids; }
|
||||
/// Get the list of APIC ids for other CPUs
|
||||
inline const kutil::vector<uint8_t> & get_apic_ids() const { return m_apic_ids; }
|
||||
|
||||
/// Get the LAPIC base address
|
||||
/// \returns The physical base address of the local apic registers
|
||||
uintptr_t get_lapic_base() const { return m_lapic_base; }
|
||||
/// Get the LAPIC base address
|
||||
/// \returns The physical base address of the local apic registers
|
||||
uintptr_t get_lapic_base() const { return m_lapic_base; }
|
||||
|
||||
/// Get the NMI mapping for the given local APIC
|
||||
/// \arg id ID of the local APIC
|
||||
/// \returns apic_nmi structure describing the NMI configuration,
|
||||
/// or null if no configuration was provided
|
||||
const apic_nmi * get_lapic_nmi(uint8_t id) const;
|
||||
/// Get the NMI mapping for the given local APIC
|
||||
/// \arg id ID of the local APIC
|
||||
/// \returns apic_nmi structure describing the NMI configuration,
|
||||
/// or null if no configuration was provided
|
||||
const apic_nmi * get_lapic_nmi(uint8_t id) const;
|
||||
|
||||
/// Get the IRQ source override for the given IRQ
|
||||
/// \arg irq IRQ number (not isr vector)
|
||||
/// \returns irq_override structure describing that IRQ's
|
||||
/// configuration, or null if no configuration was provided
|
||||
const irq_override * get_irq_override(uint8_t irq) const;
|
||||
/// Get the IRQ source override for the given IRQ
|
||||
/// \arg irq IRQ number (not isr vector)
|
||||
/// \returns irq_override structure describing that IRQ's
|
||||
/// configuration, or null if no configuration was provided
|
||||
const irq_override * get_irq_override(uint8_t irq) const;
|
||||
|
||||
/// Register the existance of a block device.
|
||||
/// \arg blockdev Pointer to the block device
|
||||
void register_block_device(block_device *blockdev);
|
||||
/// 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 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;
|
||||
}
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// Get an HPET device
|
||||
/// \arg i Index of the device to get
|
||||
/// \returns A pointer to the requested device, or nullptr
|
||||
inline hpet * get_hpet(unsigned i)
|
||||
{
|
||||
return i < m_hpets.count() ?
|
||||
&m_hpets[i] : nullptr;
|
||||
}
|
||||
/// Get an HPET device
|
||||
/// \arg i Index of the device to get
|
||||
/// \returns A pointer to the requested device, or nullptr
|
||||
inline hpet * get_hpet(unsigned i)
|
||||
{
|
||||
return i < m_hpets.count() ?
|
||||
&m_hpets[i] : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Parse the ACPI XSDT and load relevant sub-tables.
|
||||
/// \arg xsdt Pointer to the XSDT from the firmware
|
||||
void load_xsdt(const acpi_table_header *xsdt);
|
||||
/// Parse the ACPI XSDT and load relevant sub-tables.
|
||||
/// \arg xsdt Pointer to the XSDT from the firmware
|
||||
void load_xsdt(const acpi_table_header *xsdt);
|
||||
|
||||
/// Parse the ACPI MADT and initialize APICs from it.
|
||||
/// \arg apic Pointer to the MADT from the XSDT
|
||||
void load_apic(const acpi_table_header *apic);
|
||||
/// Parse the ACPI MADT and initialize APICs from it.
|
||||
/// \arg apic Pointer to the MADT from the XSDT
|
||||
void load_apic(const acpi_table_header *apic);
|
||||
|
||||
/// Parse the ACPI MCFG and initialize PCIe from it.
|
||||
/// \arg mcfg Pointer to the MCFG from the XSDT
|
||||
void load_mcfg(const acpi_table_header *mcfg);
|
||||
/// Parse the ACPI MCFG and initialize PCIe from it.
|
||||
/// \arg mcfg Pointer to the MCFG from the XSDT
|
||||
void load_mcfg(const acpi_table_header *mcfg);
|
||||
|
||||
/// Parse the ACPI HPET and initialize an HPET from it.
|
||||
/// \arg hpet Pointer to the HPET from the XSDT
|
||||
void load_hpet(const acpi_table_header *hpet);
|
||||
/// Parse the ACPI HPET and initialize an HPET from it.
|
||||
/// \arg hpet Pointer to the HPET from the XSDT
|
||||
void load_hpet(const acpi_table_header *hpet);
|
||||
|
||||
/// Probe the PCIe busses and add found devices to our
|
||||
/// device list. The device list is destroyed and rebuilt.
|
||||
void probe_pci();
|
||||
/// Probe the PCIe busses and add found devices to our
|
||||
/// device list. The device list is destroyed and rebuilt.
|
||||
void probe_pci();
|
||||
|
||||
/// Handle a bad IRQ. Called when an interrupt is dispatched
|
||||
/// that has no callback.
|
||||
void bad_irq(uint8_t irq);
|
||||
/// Handle a bad IRQ. Called when an interrupt is dispatched
|
||||
/// that has no callback.
|
||||
void bad_irq(uint8_t irq);
|
||||
|
||||
uintptr_t m_lapic_base;
|
||||
uintptr_t m_lapic_base;
|
||||
|
||||
kutil::vector<ioapic> m_ioapics;
|
||||
kutil::vector<hpet> m_hpets;
|
||||
kutil::vector<uint8_t> m_apic_ids;
|
||||
kutil::vector<apic_nmi> m_nmis;
|
||||
kutil::vector<irq_override> m_overrides;
|
||||
kutil::vector<ioapic> m_ioapics;
|
||||
kutil::vector<hpet> m_hpets;
|
||||
kutil::vector<uint8_t> m_apic_ids;
|
||||
kutil::vector<apic_nmi> m_nmis;
|
||||
kutil::vector<irq_override> m_overrides;
|
||||
|
||||
kutil::vector<pci_group> m_pci;
|
||||
kutil::vector<pci_device> m_devices;
|
||||
kutil::vector<pci_group> m_pci;
|
||||
kutil::vector<pci_device> m_devices;
|
||||
|
||||
kutil::vector<endpoint*> m_irqs;
|
||||
kutil::vector<endpoint*> m_irqs;
|
||||
|
||||
kutil::vector<block_device *> m_blockdevs;
|
||||
kutil::vector<block_device *> m_blockdevs;
|
||||
|
||||
static device_manager s_instance;
|
||||
static device_manager s_instance;
|
||||
|
||||
device_manager(const device_manager &) = delete;
|
||||
device_manager operator=(const device_manager &) = delete;
|
||||
device_manager(const device_manager &) = delete;
|
||||
device_manager operator=(const device_manager &) = delete;
|
||||
};
|
||||
|
||||
@@ -12,152 +12,152 @@ using memory::frame_size;
|
||||
frame_allocator &
|
||||
frame_allocator::get()
|
||||
{
|
||||
extern frame_allocator &g_frame_allocator;
|
||||
return g_frame_allocator;
|
||||
extern frame_allocator &g_frame_allocator;
|
||||
return g_frame_allocator;
|
||||
}
|
||||
|
||||
frame_allocator::frame_allocator(kernel::init::frame_block *frames, size_t count) :
|
||||
m_blocks {frames},
|
||||
m_count {count}
|
||||
m_blocks {frames},
|
||||
m_count {count}
|
||||
{
|
||||
}
|
||||
|
||||
inline unsigned
|
||||
bsf(uint64_t v)
|
||||
{
|
||||
asm ("tzcntq %q0, %q1" : "=r"(v) : "0"(v) : "cc");
|
||||
return v;
|
||||
asm ("tzcntq %q0, %q1" : "=r"(v) : "0"(v) : "cc");
|
||||
return v;
|
||||
}
|
||||
|
||||
size_t
|
||||
frame_allocator::allocate(size_t count, uintptr_t *address)
|
||||
{
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
|
||||
for (long i = m_count - 1; i >= 0; --i) {
|
||||
frame_block &block = m_blocks[i];
|
||||
for (long i = m_count - 1; i >= 0; --i) {
|
||||
frame_block &block = m_blocks[i];
|
||||
|
||||
if (!block.map1)
|
||||
continue;
|
||||
if (!block.map1)
|
||||
continue;
|
||||
|
||||
// Tree walk to find the first available page
|
||||
unsigned o1 = bsf(block.map1);
|
||||
// Tree walk to find the first available page
|
||||
unsigned o1 = bsf(block.map1);
|
||||
|
||||
uint64_t m2 = block.map2[o1];
|
||||
unsigned o2 = bsf(m2);
|
||||
uint64_t m2 = block.map2[o1];
|
||||
unsigned o2 = bsf(m2);
|
||||
|
||||
uint64_t m3 = block.bitmap[(o1 << 6) + o2];
|
||||
unsigned o3 = bsf(m3);
|
||||
uint64_t m3 = block.bitmap[(o1 << 6) + o2];
|
||||
unsigned o3 = bsf(m3);
|
||||
|
||||
unsigned frame = (o1 << 12) + (o2 << 6) + o3;
|
||||
unsigned frame = (o1 << 12) + (o2 << 6) + o3;
|
||||
|
||||
// See how many contiguous pages are here
|
||||
unsigned n = bsf(~m3 >> o3);
|
||||
if (n > count)
|
||||
n = count;
|
||||
// See how many contiguous pages are here
|
||||
unsigned n = bsf(~m3 >> o3);
|
||||
if (n > count)
|
||||
n = count;
|
||||
|
||||
*address = block.base + frame * frame_size;
|
||||
*address = block.base + frame * frame_size;
|
||||
|
||||
// Clear the bits to mark these pages allocated
|
||||
m3 &= ~(((1 << n) - 1) << o3);
|
||||
block.bitmap[(o1 << 6) + o2] = m3;
|
||||
if (!m3) {
|
||||
// if that was it for this group, clear the next level bit
|
||||
m2 &= ~(1 << o2);
|
||||
block.map2[o1] = m2;
|
||||
// Clear the bits to mark these pages allocated
|
||||
m3 &= ~(((1 << n) - 1) << o3);
|
||||
block.bitmap[(o1 << 6) + o2] = m3;
|
||||
if (!m3) {
|
||||
// if that was it for this group, clear the next level bit
|
||||
m2 &= ~(1 << o2);
|
||||
block.map2[o1] = m2;
|
||||
|
||||
if (!m2) {
|
||||
// if that was cleared too, update the top level
|
||||
block.map1 &= ~(1 << o1);
|
||||
}
|
||||
}
|
||||
if (!m2) {
|
||||
// if that was cleared too, update the top level
|
||||
block.map1 &= ~(1 << o1);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
kassert(false, "frame_allocator ran out of free frames!");
|
||||
return 0;
|
||||
kassert(false, "frame_allocator ran out of free frames!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
frame_allocator::free(uintptr_t address, size_t count)
|
||||
{
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
|
||||
kassert(address % frame_size == 0, "Trying to free a non page-aligned frame!");
|
||||
kassert(address % frame_size == 0, "Trying to free a non page-aligned frame!");
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
for (long i = 0; i < m_count; ++i) {
|
||||
frame_block &block = m_blocks[i];
|
||||
uintptr_t end = block.base + block.count * frame_size;
|
||||
for (long i = 0; i < m_count; ++i) {
|
||||
frame_block &block = m_blocks[i];
|
||||
uintptr_t end = block.base + block.count * frame_size;
|
||||
|
||||
if (address < block.base || address >= end)
|
||||
continue;
|
||||
if (address < block.base || address >= end)
|
||||
continue;
|
||||
|
||||
uint64_t frame = (address - block.base) >> 12;
|
||||
unsigned o1 = (frame >> 12) & 0x3f;
|
||||
unsigned o2 = (frame >> 6) & 0x3f;
|
||||
unsigned o3 = frame & 0x3f;
|
||||
uint64_t frame = (address - block.base) >> 12;
|
||||
unsigned o1 = (frame >> 12) & 0x3f;
|
||||
unsigned o2 = (frame >> 6) & 0x3f;
|
||||
unsigned o3 = frame & 0x3f;
|
||||
|
||||
while (count--) {
|
||||
block.map1 |= (1 << o1);
|
||||
block.map2[o1] |= (1 << o2);
|
||||
block.bitmap[o2] |= (1 << o3);
|
||||
if (++o3 == 64) {
|
||||
o3 = 0;
|
||||
if (++o2 == 64) {
|
||||
o2 = 0;
|
||||
++o1;
|
||||
kassert(o1 < 64, "Tried to free pages past the end of a block");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (count--) {
|
||||
block.map1 |= (1 << o1);
|
||||
block.map2[o1] |= (1 << o2);
|
||||
block.bitmap[o2] |= (1 << o3);
|
||||
if (++o3 == 64) {
|
||||
o3 = 0;
|
||||
if (++o2 == 64) {
|
||||
o2 = 0;
|
||||
++o1;
|
||||
kassert(o1 < 64, "Tried to free pages past the end of a block");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
frame_allocator::used(uintptr_t address, size_t count)
|
||||
{
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
|
||||
kassert(address % frame_size == 0, "Trying to mark a non page-aligned frame!");
|
||||
kassert(address % frame_size == 0, "Trying to mark a non page-aligned frame!");
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
for (long i = 0; i < m_count; ++i) {
|
||||
frame_block &block = m_blocks[i];
|
||||
uintptr_t end = block.base + block.count * frame_size;
|
||||
for (long i = 0; i < m_count; ++i) {
|
||||
frame_block &block = m_blocks[i];
|
||||
uintptr_t end = block.base + block.count * frame_size;
|
||||
|
||||
if (address < block.base || address >= end)
|
||||
continue;
|
||||
if (address < block.base || address >= end)
|
||||
continue;
|
||||
|
||||
uint64_t frame = (address - block.base) >> 12;
|
||||
unsigned o1 = (frame >> 12) & 0x3f;
|
||||
unsigned o2 = (frame >> 6) & 0x3f;
|
||||
unsigned o3 = frame & 0x3f;
|
||||
uint64_t frame = (address - block.base) >> 12;
|
||||
unsigned o1 = (frame >> 12) & 0x3f;
|
||||
unsigned o2 = (frame >> 6) & 0x3f;
|
||||
unsigned o3 = frame & 0x3f;
|
||||
|
||||
while (count--) {
|
||||
block.bitmap[o2] &= ~(1 << o3);
|
||||
if (!block.bitmap[o2]) {
|
||||
block.map2[o1] &= ~(1 << o2);
|
||||
while (count--) {
|
||||
block.bitmap[o2] &= ~(1 << o3);
|
||||
if (!block.bitmap[o2]) {
|
||||
block.map2[o1] &= ~(1 << o2);
|
||||
|
||||
if (!block.map2[o1]) {
|
||||
block.map1 &= ~(1 << o1);
|
||||
}
|
||||
}
|
||||
if (!block.map2[o1]) {
|
||||
block.map1 &= ~(1 << o1);
|
||||
}
|
||||
}
|
||||
|
||||
if (++o3 == 64) {
|
||||
o3 = 0;
|
||||
if (++o2 == 64) {
|
||||
o2 = 0;
|
||||
++o1;
|
||||
kassert(o1 < 64, "Tried to mark pages past the end of a block");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (++o3 == 64) {
|
||||
o3 = 0;
|
||||
if (++o2 == 64) {
|
||||
o2 = 0;
|
||||
++o1;
|
||||
kassert(o1 < 64, "Tried to mark pages past the end of a block");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,47 +7,47 @@
|
||||
|
||||
namespace kernel {
|
||||
namespace init {
|
||||
struct frame_block;
|
||||
struct frame_block;
|
||||
}}
|
||||
|
||||
/// Allocator for physical memory frames
|
||||
class frame_allocator
|
||||
{
|
||||
public:
|
||||
using frame_block = kernel::init::frame_block;
|
||||
using frame_block = kernel::init::frame_block;
|
||||
|
||||
/// Constructor
|
||||
/// \arg blocks The bootloader-supplied frame bitmap block list
|
||||
/// \arg count Number of entries in the block list
|
||||
frame_allocator(frame_block *frames, size_t count);
|
||||
/// Constructor
|
||||
/// \arg blocks The bootloader-supplied frame bitmap block list
|
||||
/// \arg count Number of entries in the block list
|
||||
frame_allocator(frame_block *frames, size_t count);
|
||||
|
||||
/// Get free frames from the free list. Only frames from the first free block
|
||||
/// are returned, so the number may be less than requested, but they will
|
||||
/// be contiguous.
|
||||
/// \arg count The maximum number of frames to get
|
||||
/// \arg address [out] The physical address of the first frame
|
||||
/// \returns The number of frames retrieved
|
||||
size_t allocate(size_t count, uintptr_t *address);
|
||||
/// Get free frames from the free list. Only frames from the first free block
|
||||
/// are returned, so the number may be less than requested, but they will
|
||||
/// be contiguous.
|
||||
/// \arg count The maximum number of frames to get
|
||||
/// \arg address [out] The physical address of the first frame
|
||||
/// \returns The number of frames retrieved
|
||||
size_t allocate(size_t count, uintptr_t *address);
|
||||
|
||||
/// Free previously allocated frames.
|
||||
/// \arg address The physical address of the first frame to free
|
||||
/// \arg count The number of frames to be freed
|
||||
void free(uintptr_t address, size_t count);
|
||||
/// Free previously allocated frames.
|
||||
/// \arg address The physical address of the first frame to free
|
||||
/// \arg count The number of frames to be freed
|
||||
void free(uintptr_t address, size_t count);
|
||||
|
||||
/// Mark frames as used
|
||||
/// \arg address The physical address of the first frame to free
|
||||
/// \arg count The number of frames to be freed
|
||||
void used(uintptr_t address, size_t count);
|
||||
/// Mark frames as used
|
||||
/// \arg address The physical address of the first frame to free
|
||||
/// \arg count The number of frames to be freed
|
||||
void used(uintptr_t address, size_t count);
|
||||
|
||||
/// Get the global frame allocator
|
||||
static frame_allocator & get();
|
||||
/// Get the global frame allocator
|
||||
static frame_allocator & get();
|
||||
|
||||
private:
|
||||
frame_block *m_blocks;
|
||||
size_t m_count;
|
||||
frame_block *m_blocks;
|
||||
size_t m_count;
|
||||
|
||||
kutil::spinlock m_lock;
|
||||
kutil::spinlock m_lock;
|
||||
|
||||
frame_allocator() = delete;
|
||||
frame_allocator(const frame_allocator &) = delete;
|
||||
frame_allocator() = delete;
|
||||
frame_allocator(const frame_allocator &) = delete;
|
||||
};
|
||||
|
||||
@@ -26,143 +26,143 @@ GDT &g_bsp_gdt = __g_bsp_gdt_storage.value;
|
||||
|
||||
|
||||
GDT::GDT(TSS *tss) :
|
||||
m_tss(tss)
|
||||
m_tss(tss)
|
||||
{
|
||||
kutil::memset(this, 0, sizeof(GDT));
|
||||
kutil::memset(this, 0, sizeof(GDT));
|
||||
|
||||
m_ptr.limit = sizeof(m_entries) - 1;
|
||||
m_ptr.base = &m_entries[0];
|
||||
m_ptr.limit = sizeof(m_entries) - 1;
|
||||
m_ptr.base = &m_entries[0];
|
||||
|
||||
// Kernel CS/SS - always 64bit
|
||||
set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute);
|
||||
set(kern_ss_index, 0, 0xfffff, true, type::read_write);
|
||||
// Kernel CS/SS - always 64bit
|
||||
set(kern_cs_index, 0, 0xfffff, true, type::read_write | type::execute);
|
||||
set(kern_ss_index, 0, 0xfffff, true, type::read_write);
|
||||
|
||||
// User CS32/SS/CS64 - layout expected by SYSRET
|
||||
set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute);
|
||||
set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write);
|
||||
set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute);
|
||||
// User CS32/SS/CS64 - layout expected by SYSRET
|
||||
set(user_cs32_index, 0, 0xfffff, false, type::ring3 | type::read_write | type::execute);
|
||||
set(user_ss_index, 0, 0xfffff, true, type::ring3 | type::read_write);
|
||||
set(user_cs64_index, 0, 0xfffff, true, type::ring3 | type::read_write | type::execute);
|
||||
|
||||
set_tss(tss);
|
||||
set_tss(tss);
|
||||
}
|
||||
|
||||
GDT &
|
||||
GDT::current()
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
return *cpu.gdt;
|
||||
cpu_data &cpu = current_cpu();
|
||||
return *cpu.gdt;
|
||||
}
|
||||
|
||||
void
|
||||
GDT::install() const
|
||||
{
|
||||
gdt_write(
|
||||
static_cast<const void*>(&m_ptr),
|
||||
kern_cs_index << 3,
|
||||
kern_ss_index << 3,
|
||||
tss_index << 3);
|
||||
gdt_write(
|
||||
static_cast<const void*>(&m_ptr),
|
||||
kern_cs_index << 3,
|
||||
kern_ss_index << 3,
|
||||
tss_index << 3);
|
||||
}
|
||||
|
||||
void
|
||||
GDT::set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t)
|
||||
{
|
||||
m_entries[i].limit_low = limit & 0xffff;
|
||||
m_entries[i].size = (limit >> 16) & 0xf;
|
||||
m_entries[i].size |= (is64 ? 0xa0 : 0xc0);
|
||||
m_entries[i].limit_low = limit & 0xffff;
|
||||
m_entries[i].size = (limit >> 16) & 0xf;
|
||||
m_entries[i].size |= (is64 ? 0xa0 : 0xc0);
|
||||
|
||||
m_entries[i].base_low = base & 0xffff;
|
||||
m_entries[i].base_mid = (base >> 16) & 0xff;
|
||||
m_entries[i].base_high = (base >> 24) & 0xff;
|
||||
m_entries[i].base_low = base & 0xffff;
|
||||
m_entries[i].base_mid = (base >> 16) & 0xff;
|
||||
m_entries[i].base_high = (base >> 24) & 0xff;
|
||||
|
||||
m_entries[i].type = t | type::system | type::present;
|
||||
m_entries[i].type = t | type::system | type::present;
|
||||
}
|
||||
|
||||
struct tss_descriptor
|
||||
{
|
||||
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;
|
||||
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));
|
||||
|
||||
void
|
||||
GDT::set_tss(TSS *tss)
|
||||
{
|
||||
tss_descriptor tssd;
|
||||
tss_descriptor tssd;
|
||||
|
||||
size_t limit = sizeof(TSS);
|
||||
tssd.limit_low = limit & 0xffff;
|
||||
tssd.size = (limit >> 16) & 0xf;
|
||||
size_t limit = sizeof(TSS);
|
||||
tssd.limit_low = limit & 0xffff;
|
||||
tssd.size = (limit >> 16) & 0xf;
|
||||
|
||||
uintptr_t base = reinterpret_cast<uintptr_t>(tss);
|
||||
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;
|
||||
uintptr_t base = reinterpret_cast<uintptr_t>(tss);
|
||||
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 =
|
||||
type::accessed |
|
||||
type::execute |
|
||||
type::ring3 |
|
||||
type::present;
|
||||
tssd.type =
|
||||
type::accessed |
|
||||
type::execute |
|
||||
type::ring3 |
|
||||
type::present;
|
||||
|
||||
kutil::memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
|
||||
kutil::memcpy(&m_entries[tss_index], &tssd, sizeof(tss_descriptor));
|
||||
}
|
||||
|
||||
void
|
||||
GDT::dump(unsigned index) const
|
||||
{
|
||||
console *cons = console::get();
|
||||
console *cons = console::get();
|
||||
|
||||
unsigned start = 0;
|
||||
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
||||
if (index != -1) {
|
||||
start = index;
|
||||
count = 1;
|
||||
} else {
|
||||
cons->printf(" GDT: loc:%lx size:%d\n", m_ptr.base, m_ptr.limit+1);
|
||||
}
|
||||
unsigned start = 0;
|
||||
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
||||
if (index != -1) {
|
||||
start = index;
|
||||
count = 1;
|
||||
} else {
|
||||
cons->printf(" GDT: loc:%lx size:%d\n", m_ptr.base, m_ptr.limit+1);
|
||||
}
|
||||
|
||||
const descriptor *gdt =
|
||||
reinterpret_cast<const descriptor *>(m_ptr.base);
|
||||
const descriptor *gdt =
|
||||
reinterpret_cast<const descriptor *>(m_ptr.base);
|
||||
|
||||
for (int i = start; i < start+count; ++i) {
|
||||
uint32_t base =
|
||||
(gdt[i].base_high << 24) |
|
||||
(gdt[i].base_mid << 16) |
|
||||
gdt[i].base_low;
|
||||
for (int i = start; i < start+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;
|
||||
uint32_t limit =
|
||||
static_cast<uint32_t>(gdt[i].size & 0x0f) << 16 |
|
||||
gdt[i].limit_low;
|
||||
|
||||
cons->printf(" %02d:", i);
|
||||
if (! (gdt[i].type && type::present)) {
|
||||
cons->puts(" Not Present\n");
|
||||
continue;
|
||||
}
|
||||
cons->printf(" %02d:", i);
|
||||
if (! (gdt[i].type && type::present)) {
|
||||
cons->puts(" Not Present\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
cons->printf(" Base %08x limit %05x ", base, limit);
|
||||
cons->printf(" Base %08x limit %05x ", base, limit);
|
||||
|
||||
switch (gdt[i].type & type::ring3) {
|
||||
case type::ring3: cons->puts("ring3"); break;
|
||||
case type::ring2: cons->puts("ring2"); break;
|
||||
case type::ring1: cons->puts("ring1"); break;
|
||||
default: cons->puts("ring0"); break;
|
||||
}
|
||||
switch (gdt[i].type & type::ring3) {
|
||||
case type::ring3: cons->puts("ring3"); break;
|
||||
case type::ring2: cons->puts("ring2"); break;
|
||||
case type::ring1: cons->puts("ring1"); break;
|
||||
default: cons->puts("ring0"); break;
|
||||
}
|
||||
|
||||
cons->printf(" %s %s %s %s %s %s %s\n",
|
||||
(gdt[i].type && type::accessed) ? "A" : " ",
|
||||
(gdt[i].type && type::read_write) ? "RW" : " ",
|
||||
(gdt[i].type && type::conforming) ? "C" : " ",
|
||||
(gdt[i].type && type::execute) ? "EX" : " ",
|
||||
(gdt[i].type && type::system) ? "S" : " ",
|
||||
(gdt[i].size & 0x80) ? "KB" : " B",
|
||||
(gdt[i].size & 0x60) == 0x20 ? "64" :
|
||||
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
|
||||
}
|
||||
cons->printf(" %s %s %s %s %s %s %s\n",
|
||||
(gdt[i].type && type::accessed) ? "A" : " ",
|
||||
(gdt[i].type && type::read_write) ? "RW" : " ",
|
||||
(gdt[i].type && type::conforming) ? "C" : " ",
|
||||
(gdt[i].type && type::execute) ? "EX" : " ",
|
||||
(gdt[i].type && type::system) ? "S" : " ",
|
||||
(gdt[i].size & 0x80) ? "KB" : " B",
|
||||
(gdt[i].size & 0x60) == 0x20 ? "64" :
|
||||
(gdt[i].size & 0x60) == 0x40 ? "32" : "16");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,58 +10,58 @@ class TSS;
|
||||
class GDT
|
||||
{
|
||||
public:
|
||||
GDT(TSS *tss);
|
||||
GDT(TSS *tss);
|
||||
|
||||
/// Get the currently running CPU's GDT
|
||||
static GDT & current();
|
||||
/// Get the currently running CPU's GDT
|
||||
static GDT & current();
|
||||
|
||||
/// Install this GDT to the current CPU
|
||||
void install() const;
|
||||
/// Install this GDT to the current CPU
|
||||
void install() const;
|
||||
|
||||
/// Get the addrss of the pointer
|
||||
inline const void * pointer() const { return static_cast<const void*>(&m_ptr); }
|
||||
/// Get the addrss of the pointer
|
||||
inline const void * pointer() const { return static_cast<const void*>(&m_ptr); }
|
||||
|
||||
/// Dump debug information about the GDT to the console.
|
||||
/// \arg index Which entry to print, or -1 for all entries
|
||||
void dump(unsigned index = -1) const;
|
||||
/// Dump debug information about the GDT to the console.
|
||||
/// \arg index Which entry to print, or -1 for all entries
|
||||
void dump(unsigned index = -1) const;
|
||||
|
||||
enum class type : uint8_t
|
||||
{
|
||||
accessed = 0x01,
|
||||
read_write = 0x02,
|
||||
conforming = 0x04,
|
||||
execute = 0x08,
|
||||
system = 0x10,
|
||||
ring1 = 0x20,
|
||||
ring2 = 0x40,
|
||||
ring3 = 0x60,
|
||||
present = 0x80
|
||||
};
|
||||
enum class type : uint8_t
|
||||
{
|
||||
accessed = 0x01,
|
||||
read_write = 0x02,
|
||||
conforming = 0x04,
|
||||
execute = 0x08,
|
||||
system = 0x10,
|
||||
ring1 = 0x20,
|
||||
ring2 = 0x40,
|
||||
ring3 = 0x60,
|
||||
present = 0x80
|
||||
};
|
||||
|
||||
private:
|
||||
void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t);
|
||||
void set_tss(TSS *tss);
|
||||
void set(uint8_t i, uint32_t base, uint64_t limit, bool is64, type t);
|
||||
void set_tss(TSS *tss);
|
||||
|
||||
struct descriptor
|
||||
{
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
type type;
|
||||
uint8_t size;
|
||||
uint8_t base_high;
|
||||
} __attribute__ ((packed, align(8)));
|
||||
struct descriptor
|
||||
{
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
type type;
|
||||
uint8_t size;
|
||||
uint8_t base_high;
|
||||
} __attribute__ ((packed, align(8)));
|
||||
|
||||
struct ptr
|
||||
{
|
||||
uint16_t limit;
|
||||
descriptor *base;
|
||||
} __attribute__ ((packed, align(4)));
|
||||
struct ptr
|
||||
{
|
||||
uint16_t limit;
|
||||
descriptor *base;
|
||||
} __attribute__ ((packed, align(4)));
|
||||
|
||||
descriptor m_entries[8];
|
||||
TSS *m_tss;
|
||||
descriptor m_entries[8];
|
||||
TSS *m_tss;
|
||||
|
||||
ptr m_ptr;
|
||||
ptr m_ptr;
|
||||
};
|
||||
|
||||
is_bitfield(GDT::type);
|
||||
|
||||
@@ -6,114 +6,114 @@
|
||||
#include "log.h"
|
||||
|
||||
namespace {
|
||||
inline uint64_t volatile *capabilities(uint64_t volatile *base) { return base; }
|
||||
inline uint64_t volatile *configuration(uint64_t volatile *base) { return base+2; }
|
||||
inline uint64_t volatile *interrupt_status(uint64_t volatile *base) { return base+4; }
|
||||
inline uint64_t volatile *counter_value(uint64_t volatile *base) { return base+30; }
|
||||
inline uint64_t volatile *capabilities(uint64_t volatile *base) { return base; }
|
||||
inline uint64_t volatile *configuration(uint64_t volatile *base) { return base+2; }
|
||||
inline uint64_t volatile *interrupt_status(uint64_t volatile *base) { return base+4; }
|
||||
inline uint64_t volatile *counter_value(uint64_t volatile *base) { return base+30; }
|
||||
|
||||
inline uint64_t volatile *timer_base(uint64_t volatile *base, unsigned i) { return base + 0x20 + (4*i); }
|
||||
inline uint64_t volatile *timer_config(uint64_t volatile *base, unsigned i) { return timer_base(base, i); }
|
||||
inline uint64_t volatile *timer_comparator(uint64_t volatile *base, unsigned i) { return timer_base(base, i) + 1; }
|
||||
inline uint64_t volatile *timer_base(uint64_t volatile *base, unsigned i) { return base + 0x20 + (4*i); }
|
||||
inline uint64_t volatile *timer_config(uint64_t volatile *base, unsigned i) { return timer_base(base, i); }
|
||||
inline uint64_t volatile *timer_comparator(uint64_t volatile *base, unsigned i) { return timer_base(base, i) + 1; }
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
hpet_irq_callback(void *hpet_ptr)
|
||||
{
|
||||
if (hpet_ptr)
|
||||
reinterpret_cast<hpet*>(hpet_ptr)->callback();
|
||||
if (hpet_ptr)
|
||||
reinterpret_cast<hpet*>(hpet_ptr)->callback();
|
||||
}
|
||||
|
||||
|
||||
hpet::hpet(uint8_t index, uint64_t *base) :
|
||||
m_index(index),
|
||||
m_base(base)
|
||||
m_index(index),
|
||||
m_base(base)
|
||||
{
|
||||
*configuration(m_base) = 0;
|
||||
*counter_value(m_base) = 0;
|
||||
*configuration(m_base) = 0;
|
||||
*counter_value(m_base) = 0;
|
||||
|
||||
uint64_t caps = *capabilities(base);
|
||||
uint64_t config = *configuration(base);
|
||||
m_timers = ((caps >> 8) & 0x1f) + 1;
|
||||
m_period = (caps >> 32);
|
||||
uint64_t caps = *capabilities(base);
|
||||
uint64_t config = *configuration(base);
|
||||
m_timers = ((caps >> 8) & 0x1f) + 1;
|
||||
m_period = (caps >> 32);
|
||||
|
||||
setup_timer_interrupts(0, 2, 1000, true);
|
||||
// bool installed = device_manager::get()
|
||||
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
|
||||
// kassert(installed, "Installing HPET IRQ handler");
|
||||
setup_timer_interrupts(0, 2, 1000, true);
|
||||
// bool installed = device_manager::get()
|
||||
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
|
||||
// kassert(installed, "Installing HPET IRQ handler");
|
||||
|
||||
log::debug(logs::timer, "HPET %d capabilities:", index);
|
||||
log::debug(logs::timer, " revision: %d", caps & 0xff);
|
||||
log::debug(logs::timer, " timers: %d", m_timers);
|
||||
log::debug(logs::timer, " bits: %d", ((caps >> 13) & 1) ? 64 : 32);
|
||||
log::debug(logs::timer, " LRR capable: %d", ((caps >> 15) & 1));
|
||||
log::debug(logs::timer, " period: %dns", m_period / 1000000);
|
||||
log::debug(logs::timer, " global enabled: %d", config & 1);
|
||||
log::debug(logs::timer, " LRR enable: %d", (config >> 1) & 1);
|
||||
log::debug(logs::timer, "HPET %d capabilities:", index);
|
||||
log::debug(logs::timer, " revision: %d", caps & 0xff);
|
||||
log::debug(logs::timer, " timers: %d", m_timers);
|
||||
log::debug(logs::timer, " bits: %d", ((caps >> 13) & 1) ? 64 : 32);
|
||||
log::debug(logs::timer, " LRR capable: %d", ((caps >> 15) & 1));
|
||||
log::debug(logs::timer, " period: %dns", m_period / 1000000);
|
||||
log::debug(logs::timer, " global enabled: %d", config & 1);
|
||||
log::debug(logs::timer, " LRR enable: %d", (config >> 1) & 1);
|
||||
|
||||
for (unsigned i = 0; i < m_timers; ++i) {
|
||||
disable_timer(i);
|
||||
uint64_t config = *timer_config(m_base, i);
|
||||
for (unsigned i = 0; i < m_timers; ++i) {
|
||||
disable_timer(i);
|
||||
uint64_t config = *timer_config(m_base, i);
|
||||
|
||||
log::debug(logs::timer, "HPET %d timer %d:", index, i);
|
||||
log::debug(logs::timer, " int type: %d", (config >> 1) & 1);
|
||||
log::debug(logs::timer, " int enable: %d", (config >> 2) & 1);
|
||||
log::debug(logs::timer, " timer type: %d", (config >> 3) & 1);
|
||||
log::debug(logs::timer, " periodic cap: %d", (config >> 4) & 1);
|
||||
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
|
||||
log::debug(logs::timer, " 32 mode: %d", (config >> 8) & 1);
|
||||
log::debug(logs::timer, " int route: %d", (config >> 9) & 0x1f);
|
||||
log::debug(logs::timer, " FSB enable: %d", (config >> 14) & 1);
|
||||
log::debug(logs::timer, " FSB capable: %d", (config >> 15) & 1);
|
||||
log::debug(logs::timer, " rotung cap: %08x", (config >> 32));
|
||||
}
|
||||
log::debug(logs::timer, "HPET %d timer %d:", index, i);
|
||||
log::debug(logs::timer, " int type: %d", (config >> 1) & 1);
|
||||
log::debug(logs::timer, " int enable: %d", (config >> 2) & 1);
|
||||
log::debug(logs::timer, " timer type: %d", (config >> 3) & 1);
|
||||
log::debug(logs::timer, " periodic cap: %d", (config >> 4) & 1);
|
||||
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
|
||||
log::debug(logs::timer, " 32 mode: %d", (config >> 8) & 1);
|
||||
log::debug(logs::timer, " int route: %d", (config >> 9) & 0x1f);
|
||||
log::debug(logs::timer, " FSB enable: %d", (config >> 14) & 1);
|
||||
log::debug(logs::timer, " FSB capable: %d", (config >> 15) & 1);
|
||||
log::debug(logs::timer, " rotung cap: %08x", (config >> 32));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
hpet::setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic)
|
||||
{
|
||||
constexpr uint64_t femto_per_us = 1000000000ull;
|
||||
*timer_comparator(m_base, timer) =
|
||||
*counter_value(m_base) +
|
||||
(interval * femto_per_us) / m_period;
|
||||
constexpr uint64_t femto_per_us = 1000000000ull;
|
||||
*timer_comparator(m_base, timer) =
|
||||
*counter_value(m_base) +
|
||||
(interval * femto_per_us) / m_period;
|
||||
|
||||
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
|
||||
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
|
||||
}
|
||||
|
||||
void
|
||||
hpet::enable_timer(unsigned timer)
|
||||
{
|
||||
*timer_config(m_base, timer) = *timer_config(m_base, timer) | (1 << 2);
|
||||
*timer_config(m_base, timer) = *timer_config(m_base, timer) | (1 << 2);
|
||||
}
|
||||
|
||||
void
|
||||
hpet::disable_timer(unsigned timer)
|
||||
{
|
||||
*timer_config(m_base, timer) = *timer_config(m_base, timer) & ~(1ull << 2);
|
||||
*timer_config(m_base, timer) = *timer_config(m_base, timer) & ~(1ull << 2);
|
||||
}
|
||||
|
||||
void
|
||||
hpet::callback()
|
||||
{
|
||||
log::debug(logs::timer, "HPET %d got irq", m_index);
|
||||
log::debug(logs::timer, "HPET %d got irq", m_index);
|
||||
}
|
||||
|
||||
void
|
||||
hpet::enable()
|
||||
{
|
||||
log::debug(logs::timer, "HPET %d enabling", m_index);
|
||||
*configuration(m_base) = (*configuration(m_base) & 0x3) | 1;
|
||||
log::debug(logs::timer, "HPET %d enabling", m_index);
|
||||
*configuration(m_base) = (*configuration(m_base) & 0x3) | 1;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
hpet::value() const
|
||||
{
|
||||
return *counter_value(m_base);
|
||||
return *counter_value(m_base);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
hpet_clock_source(void *data)
|
||||
{
|
||||
return reinterpret_cast<hpet*>(data)->value();
|
||||
return reinterpret_cast<hpet*>(data)->value();
|
||||
}
|
||||
|
||||
@@ -12,35 +12,35 @@ uint64_t hpet_clock_source(void *);
|
||||
class hpet
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg index The index of the HPET out of all HPETs
|
||||
/// \arg base The base address of the HPET for MMIO
|
||||
hpet(uint8_t index, uint64_t *base);
|
||||
/// Constructor.
|
||||
/// \arg index The index of the HPET out of all HPETs
|
||||
/// \arg base The base address of the HPET for MMIO
|
||||
hpet(uint8_t index, uint64_t *base);
|
||||
|
||||
/// Configure the timer and start it running.
|
||||
void enable();
|
||||
/// Configure the timer and start it running.
|
||||
void enable();
|
||||
|
||||
/// Get the timer rate in ticks per us
|
||||
inline uint64_t rate() const { return 1000000000/m_period; }
|
||||
/// Get the timer rate in ticks per us
|
||||
inline uint64_t rate() const { return 1000000000/m_period; }
|
||||
|
||||
/// Get the current timer value
|
||||
uint64_t value() const;
|
||||
/// Get the current timer value
|
||||
uint64_t value() const;
|
||||
|
||||
private:
|
||||
friend void hpet_irq_callback(void *);
|
||||
friend uint64_t hpet_clock_source(void *);
|
||||
friend void hpet_irq_callback(void *);
|
||||
friend uint64_t hpet_clock_source(void *);
|
||||
|
||||
void setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic);
|
||||
void enable_timer(unsigned timer);
|
||||
void disable_timer(unsigned timer);
|
||||
void setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic);
|
||||
void enable_timer(unsigned timer);
|
||||
void disable_timer(unsigned timer);
|
||||
|
||||
void callback();
|
||||
void callback();
|
||||
|
||||
uint8_t m_timers;
|
||||
uint8_t m_index;
|
||||
uint8_t m_timers;
|
||||
uint8_t m_index;
|
||||
|
||||
uint64_t m_period;
|
||||
volatile uint64_t *m_base;
|
||||
uint64_t m_period;
|
||||
volatile uint64_t *m_base;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "log.h"
|
||||
|
||||
extern "C" {
|
||||
void idt_write(const void *idt_ptr);
|
||||
void idt_write(const void *idt_ptr);
|
||||
|
||||
#define ISR(i, s, name) extern void name ();
|
||||
#define NISR(i, s, name)
|
||||
@@ -33,9 +33,9 @@ IDT::set_nmi_handler(uintptr_t address)
|
||||
|
||||
IDT::IDT()
|
||||
{
|
||||
kutil::memset(this, 0, sizeof(IDT));
|
||||
m_ptr.limit = sizeof(m_entries) - 1;
|
||||
m_ptr.base = &m_entries[0];
|
||||
kutil::memset(this, 0, sizeof(IDT));
|
||||
m_ptr.limit = sizeof(m_entries) - 1;
|
||||
m_ptr.base = &m_entries[0];
|
||||
|
||||
#define ISR(i, s, name) set(i, & name, 0x08, 0x8e);
|
||||
#define EISR(i, s, name) set(i, & name, 0x08, 0x8e);
|
||||
@@ -51,14 +51,14 @@ IDT::IDT()
|
||||
IDT &
|
||||
IDT::current()
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
return *cpu.idt;
|
||||
cpu_data &cpu = current_cpu();
|
||||
return *cpu.idt;
|
||||
}
|
||||
|
||||
void
|
||||
IDT::install() const
|
||||
{
|
||||
idt_write(static_cast<const void*>(&m_ptr));
|
||||
idt_write(static_cast<const void*>(&m_ptr));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -78,7 +78,7 @@ IDT::add_ist_entries()
|
||||
uint8_t
|
||||
IDT::used_ist_entries() const
|
||||
{
|
||||
constexpr uint8_t entries =
|
||||
constexpr uint8_t entries =
|
||||
|
||||
#define ISR(i, s, name) ((s) ? (1 << s) : 0) |
|
||||
#define NISR(i, s, name) ((s) ? (1 << s) : 0) |
|
||||
@@ -92,64 +92,64 @@ IDT::used_ist_entries() const
|
||||
|
||||
0;
|
||||
|
||||
return entries;
|
||||
return entries;
|
||||
}
|
||||
|
||||
void
|
||||
IDT::set(uint8_t i, void (*handler)(), uint16_t selector, uint8_t flags)
|
||||
{
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(handler);
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(handler);
|
||||
|
||||
m_entries[i].base_low = addr & 0xffff;
|
||||
m_entries[i].base_mid = (addr >> 16) & 0xffff;
|
||||
m_entries[i].base_high = (addr >> 32) & 0xffffffff;
|
||||
m_entries[i].selector = selector;
|
||||
m_entries[i].flags = flags;
|
||||
m_entries[i].ist = 0;
|
||||
m_entries[i].reserved = 0;
|
||||
m_entries[i].base_low = addr & 0xffff;
|
||||
m_entries[i].base_mid = (addr >> 16) & 0xffff;
|
||||
m_entries[i].base_high = (addr >> 32) & 0xffffffff;
|
||||
m_entries[i].selector = selector;
|
||||
m_entries[i].flags = flags;
|
||||
m_entries[i].ist = 0;
|
||||
m_entries[i].reserved = 0;
|
||||
}
|
||||
|
||||
void
|
||||
IDT::dump(unsigned index) const
|
||||
{
|
||||
unsigned start = 0;
|
||||
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
||||
if (index != -1) {
|
||||
start = index;
|
||||
count = 1;
|
||||
log::info(logs::boot, "IDT FOR INDEX %02x", index);
|
||||
} else {
|
||||
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", m_ptr.base, m_ptr.limit+1);
|
||||
}
|
||||
unsigned start = 0;
|
||||
unsigned count = (m_ptr.limit + 1) / sizeof(descriptor);
|
||||
if (index != -1) {
|
||||
start = index;
|
||||
count = 1;
|
||||
log::info(logs::boot, "IDT FOR INDEX %02x", index);
|
||||
} else {
|
||||
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", m_ptr.base, m_ptr.limit+1);
|
||||
}
|
||||
|
||||
const descriptor *idt =
|
||||
reinterpret_cast<const descriptor *>(m_ptr.base);
|
||||
const descriptor *idt =
|
||||
reinterpret_cast<const descriptor *>(m_ptr.base);
|
||||
|
||||
for (int i = start; i < start+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;
|
||||
for (int i = start; i < start+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;
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,62 +6,62 @@
|
||||
class IDT
|
||||
{
|
||||
public:
|
||||
IDT();
|
||||
IDT();
|
||||
|
||||
/// Get the currently running CPU's IDT
|
||||
static IDT & current();
|
||||
/// Get the currently running CPU's IDT
|
||||
static IDT & current();
|
||||
|
||||
/// Set the global NMI handler. Must happen before creating
|
||||
/// any IDTs.
|
||||
static void set_nmi_handler(uintptr_t address);
|
||||
|
||||
/// Install this IDT to the current CPU
|
||||
void install() const;
|
||||
/// Install this IDT to the current CPU
|
||||
void install() const;
|
||||
|
||||
/// Add the IST entries listed in the ISR table into the IDT.
|
||||
/// This can't be done until after memory is set up so the
|
||||
/// stacks can be created.
|
||||
void add_ist_entries();
|
||||
/// Add the IST entries listed in the ISR table into the IDT.
|
||||
/// This can't be done until after memory is set up so the
|
||||
/// stacks can be created.
|
||||
void add_ist_entries();
|
||||
|
||||
/// Get the IST entry used by an entry.
|
||||
/// \arg i Which IDT entry to look in
|
||||
/// \returns The IST index used by entry i, or 0 for none
|
||||
inline uint8_t get_ist(uint8_t i) const {
|
||||
return m_entries[i].ist;
|
||||
}
|
||||
/// Get the IST entry used by an entry.
|
||||
/// \arg i Which IDT entry to look in
|
||||
/// \returns The IST index used by entry i, or 0 for none
|
||||
inline uint8_t get_ist(uint8_t i) const {
|
||||
return m_entries[i].ist;
|
||||
}
|
||||
|
||||
/// Set the IST entry used by an entry.
|
||||
/// \arg i Which IDT entry to set
|
||||
/// \arg ist The IST index for entry i, or 0 for none
|
||||
void set_ist(uint8_t i, uint8_t ist) { m_entries[i].ist = ist; }
|
||||
/// Set the IST entry used by an entry.
|
||||
/// \arg i Which IDT entry to set
|
||||
/// \arg ist The IST index for entry i, or 0 for none
|
||||
void set_ist(uint8_t i, uint8_t ist) { m_entries[i].ist = ist; }
|
||||
|
||||
/// Get the IST entries that are used by this table, as a bitmap
|
||||
uint8_t used_ist_entries() const;
|
||||
/// Get the IST entries that are used by this table, as a bitmap
|
||||
uint8_t used_ist_entries() const;
|
||||
|
||||
/// Dump debug information about the IDT to the console.
|
||||
/// \arg index Which entry to print, or -1 for all entries
|
||||
void dump(unsigned index = -1) const;
|
||||
/// Dump debug information about the IDT to the console.
|
||||
/// \arg index Which entry to print, or -1 for all entries
|
||||
void dump(unsigned index = -1) const;
|
||||
|
||||
private:
|
||||
void set(uint8_t i, void (*handler)(), uint16_t selector, uint8_t flags);
|
||||
void set(uint8_t i, void (*handler)(), uint16_t selector, uint8_t flags);
|
||||
|
||||
struct descriptor
|
||||
{
|
||||
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, aligned(16)));
|
||||
struct 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, aligned(16)));
|
||||
|
||||
struct ptr
|
||||
{
|
||||
uint16_t limit;
|
||||
descriptor *base;
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
struct ptr
|
||||
{
|
||||
uint16_t limit;
|
||||
descriptor *base;
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
descriptor m_entries[256];
|
||||
ptr m_ptr;
|
||||
descriptor m_entries[256];
|
||||
ptr m_ptr;
|
||||
};
|
||||
|
||||
@@ -18,14 +18,14 @@ static const uint16_t PIC2 = 0xa0;
|
||||
constexpr uintptr_t apic_eoi_addr = 0xfee000b0 + ::memory::page_offset;
|
||||
|
||||
extern "C" {
|
||||
void isr_handler(cpu_state*);
|
||||
void irq_handler(cpu_state*);
|
||||
void isr_handler(cpu_state*);
|
||||
void irq_handler(cpu_state*);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
get_irq(unsigned vector)
|
||||
{
|
||||
switch (vector) {
|
||||
switch (vector) {
|
||||
#define ISR(i, s, name)
|
||||
#define EISR(i, s, name)
|
||||
#define NISR(i, s, name)
|
||||
@@ -36,51 +36,51 @@ get_irq(unsigned vector)
|
||||
#undef EISR
|
||||
#undef ISR
|
||||
|
||||
default: return 0xff;
|
||||
}
|
||||
default: return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
disable_legacy_pic()
|
||||
{
|
||||
// Mask all interrupts
|
||||
outb(PIC2+1, 0xfc);
|
||||
outb(PIC1+1, 0xff);
|
||||
// Mask all interrupts
|
||||
outb(PIC2+1, 0xfc);
|
||||
outb(PIC1+1, 0xff);
|
||||
|
||||
// Start initialization sequence
|
||||
outb(PIC1, 0x11); io_wait();
|
||||
outb(PIC2, 0x11); io_wait();
|
||||
// Start initialization sequence
|
||||
outb(PIC1, 0x11); io_wait();
|
||||
outb(PIC2, 0x11); io_wait();
|
||||
|
||||
// Remap into ignore ISRs
|
||||
outb(PIC1+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
|
||||
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore8)); io_wait();
|
||||
// Remap into ignore ISRs
|
||||
outb(PIC1+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
|
||||
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore8)); io_wait();
|
||||
|
||||
// Tell PICs about each other
|
||||
outb(PIC1+1, 0x04); io_wait();
|
||||
outb(PIC2+1, 0x02); io_wait();
|
||||
// Tell PICs about each other
|
||||
outb(PIC1+1, 0x04); io_wait();
|
||||
outb(PIC2+1, 0x02); io_wait();
|
||||
}
|
||||
|
||||
void
|
||||
isr_handler(cpu_state *regs)
|
||||
{
|
||||
uint8_t vector = regs->interrupt & 0xff;
|
||||
uint8_t vector = regs->interrupt & 0xff;
|
||||
if ((vector & 0xf0) == 0xf0) {
|
||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear out the IST for this vector so we just keep using
|
||||
// this stack
|
||||
IDT &idt = IDT::current();
|
||||
uint8_t old_ist = idt.get_ist(vector);
|
||||
if (old_ist)
|
||||
idt.set_ist(vector, 0);
|
||||
// Clear out the IST for this vector so we just keep using
|
||||
// this stack
|
||||
IDT &idt = IDT::current();
|
||||
uint8_t old_ist = idt.get_ist(vector);
|
||||
if (old_ist)
|
||||
idt.set_ist(vector, 0);
|
||||
|
||||
char message[200];
|
||||
|
||||
switch (static_cast<isr>(vector)) {
|
||||
switch (static_cast<isr>(vector)) {
|
||||
|
||||
case isr::isrDebug:
|
||||
case isr::isrDebug:
|
||||
asm volatile ("mov %%dr0, %%r8" ::: "r8");
|
||||
asm volatile ("mov %%dr1, %%r9" ::: "r9");
|
||||
asm volatile ("mov %%dr2, %%r10" ::: "r10");
|
||||
@@ -90,15 +90,15 @@ isr_handler(cpu_state *regs)
|
||||
asm volatile ("mov %%dr6, %%r14" ::: "r14");
|
||||
asm volatile ("mov %%dr7, %%r15" ::: "r15");
|
||||
kassert(false, "Debug exception");
|
||||
break;
|
||||
break;
|
||||
|
||||
case isr::isrDoubleFault:
|
||||
case isr::isrDoubleFault:
|
||||
kassert(false, "Double fault");
|
||||
break;
|
||||
break;
|
||||
|
||||
case isr::isrGPFault:
|
||||
case isr::isrGPFault:
|
||||
if (regs->errorcode & 0xfff0) {
|
||||
int index = (regs->errorcode & 0xffff) >> 4;
|
||||
int index = (regs->errorcode & 0xffff) >> 4;
|
||||
int ti = (regs->errorcode & 0x07) >> 1;
|
||||
char const *table =
|
||||
(ti & 1) ? "IDT" :
|
||||
@@ -114,20 +114,20 @@ isr_handler(cpu_state *regs)
|
||||
kassert(false, message);
|
||||
break;
|
||||
|
||||
case isr::isrPageFault: {
|
||||
uintptr_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
case isr::isrPageFault: {
|
||||
uintptr_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
|
||||
bool user = cr2 < memory::kernel_offset;
|
||||
vm_space::fault_type ft =
|
||||
static_cast<vm_space::fault_type>(regs->errorcode);
|
||||
bool user = cr2 < memory::kernel_offset;
|
||||
vm_space::fault_type ft =
|
||||
static_cast<vm_space::fault_type>(regs->errorcode);
|
||||
|
||||
vm_space &space = user
|
||||
? process::current().space()
|
||||
: vm_space::kernel_space();
|
||||
vm_space &space = user
|
||||
? process::current().space()
|
||||
: vm_space::kernel_space();
|
||||
|
||||
if (cr2 && space.handle_fault(cr2, ft))
|
||||
break;
|
||||
if (cr2 && space.handle_fault(cr2, ft))
|
||||
break;
|
||||
|
||||
snprintf(message, sizeof(message),
|
||||
"Page fault: %016llx%s%s%s%s%s", cr2,
|
||||
@@ -137,42 +137,42 @@ isr_handler(cpu_state *regs)
|
||||
(regs->errorcode & 0x08) ? " reserved" : "",
|
||||
(regs->errorcode & 0x10) ? " ip" : "");
|
||||
kassert(false, message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case isr::isrTimer:
|
||||
scheduler::get().schedule();
|
||||
break;
|
||||
case isr::isrTimer:
|
||||
scheduler::get().schedule();
|
||||
break;
|
||||
|
||||
case isr::isrLINT0:
|
||||
case isr::isrLINT1:
|
||||
break;
|
||||
case isr::isrLINT0:
|
||||
case isr::isrLINT1:
|
||||
break;
|
||||
|
||||
case isr::isrSpurious:
|
||||
// No EOI for the spurious interrupt
|
||||
return;
|
||||
case isr::isrSpurious:
|
||||
// No EOI for the spurious interrupt
|
||||
return;
|
||||
|
||||
default:
|
||||
default:
|
||||
snprintf(message, sizeof(message), "Unknown interrupt 0x%x", regs->interrupt);
|
||||
kassert(false, message);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the IST for this vector to what it was
|
||||
if (old_ist)
|
||||
idt.set_ist(vector, old_ist);
|
||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||
// Return the IST for this vector to what it was
|
||||
if (old_ist)
|
||||
idt.set_ist(vector, old_ist);
|
||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||
}
|
||||
|
||||
void
|
||||
irq_handler(cpu_state *regs)
|
||||
{
|
||||
uint8_t irq = get_irq(regs->interrupt);
|
||||
if (! device_manager::get().dispatch_irq(irq)) {
|
||||
uint8_t irq = get_irq(regs->interrupt);
|
||||
if (! device_manager::get().dispatch_irq(irq)) {
|
||||
char message[100];
|
||||
snprintf(message, sizeof(message),
|
||||
"Unknown IRQ: %d (vec 0x%x)", irq, regs->interrupt);
|
||||
kassert(false, message);
|
||||
}
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||
*reinterpret_cast<uint32_t *>(apic_eoi_addr) = 0;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ enum class isr : uint8_t
|
||||
#undef NISR
|
||||
#undef ISR
|
||||
|
||||
_zero = 0
|
||||
_zero = 0
|
||||
};
|
||||
|
||||
/// Helper operator to add an offset to an isr vector
|
||||
@@ -26,11 +26,11 @@ constexpr isr operator+(const isr &lhs, int rhs) {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
/// Set the CPU interrupt enable flag (sti)
|
||||
void interrupts_enable();
|
||||
/// Set the CPU interrupt enable flag (sti)
|
||||
void interrupts_enable();
|
||||
|
||||
/// Set the CPU interrupt disable flag (cli)
|
||||
void interrupts_disable();
|
||||
/// Set the CPU interrupt disable flag (cli)
|
||||
void interrupts_disable();
|
||||
}
|
||||
|
||||
/// Disable the legacy PIC
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
uint8_t
|
||||
inb(uint16_t port)
|
||||
{
|
||||
uint8_t val;
|
||||
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||
return val;
|
||||
uint8_t val;
|
||||
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||
return val;
|
||||
}
|
||||
|
||||
void
|
||||
outb(uint16_t port, uint8_t val)
|
||||
{
|
||||
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
|
||||
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
|
||||
}
|
||||
|
||||
void
|
||||
io_wait(unsigned times)
|
||||
{
|
||||
for (unsigned i = 0; i < times; ++i)
|
||||
outb(0x80, 0);
|
||||
for (unsigned i = 0; i < times; ++i)
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
@@ -19,73 +19,73 @@ static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09};
|
||||
static void
|
||||
output_log(log::area_t area, log::level severity, const char *message)
|
||||
{
|
||||
auto *cons = console::get();
|
||||
cons->set_color(level_colors[static_cast<int>(severity)]);
|
||||
cons->printf("%7s %5s: %s\n",
|
||||
g_logger.area_name(area),
|
||||
g_logger.level_name(severity),
|
||||
message);
|
||||
cons->set_color();
|
||||
auto *cons = console::get();
|
||||
cons->set_color(level_colors[static_cast<int>(severity)]);
|
||||
cons->printf("%7s %5s: %s\n",
|
||||
g_logger.area_name(area),
|
||||
g_logger.level_name(severity),
|
||||
message);
|
||||
cons->set_color();
|
||||
}
|
||||
|
||||
static void
|
||||
log_flush()
|
||||
{
|
||||
system &sys = system::get();
|
||||
sys.assert_signal(j6_signal_system_has_log);
|
||||
system &sys = system::get();
|
||||
sys.assert_signal(j6_signal_system_has_log);
|
||||
}
|
||||
|
||||
void
|
||||
logger_task()
|
||||
{
|
||||
auto *cons = console::get();
|
||||
auto *cons = console::get();
|
||||
|
||||
log::info(logs::task, "Starting kernel logger task");
|
||||
g_logger.set_immediate(nullptr);
|
||||
g_logger.set_flush(log_flush);
|
||||
log::info(logs::task, "Starting kernel logger task");
|
||||
g_logger.set_immediate(nullptr);
|
||||
g_logger.set_flush(log_flush);
|
||||
|
||||
thread &self = thread::current();
|
||||
system &sys = system::get();
|
||||
thread &self = thread::current();
|
||||
system &sys = system::get();
|
||||
|
||||
size_t buffer_size = 1;
|
||||
uint8_t *buffer = nullptr;
|
||||
size_t buffer_size = 1;
|
||||
uint8_t *buffer = nullptr;
|
||||
|
||||
while (true) {
|
||||
size_t size = g_logger.get_entry(buffer, buffer_size);
|
||||
if (size > buffer_size) {
|
||||
while (size > buffer_size) buffer_size *= 2;
|
||||
kutil::kfree(buffer);
|
||||
buffer = reinterpret_cast<uint8_t*>(kutil::kalloc(buffer_size));
|
||||
kassert(buffer, "Could not allocate logger task buffer");
|
||||
continue;
|
||||
}
|
||||
while (true) {
|
||||
size_t size = g_logger.get_entry(buffer, buffer_size);
|
||||
if (size > buffer_size) {
|
||||
while (size > buffer_size) buffer_size *= 2;
|
||||
kutil::kfree(buffer);
|
||||
buffer = reinterpret_cast<uint8_t*>(kutil::kalloc(buffer_size));
|
||||
kassert(buffer, "Could not allocate logger task buffer");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(size) {
|
||||
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
||||
buffer[ent->bytes] = 0;
|
||||
if(size) {
|
||||
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
||||
buffer[ent->bytes] = 0;
|
||||
|
||||
cons->set_color(level_colors[static_cast<int>(ent->severity)]);
|
||||
cons->printf("%7s %5s: %s\n",
|
||||
g_logger.area_name(ent->area),
|
||||
g_logger.level_name(ent->severity),
|
||||
ent->message);
|
||||
cons->set_color();
|
||||
}
|
||||
cons->set_color(level_colors[static_cast<int>(ent->severity)]);
|
||||
cons->printf("%7s %5s: %s\n",
|
||||
g_logger.area_name(ent->area),
|
||||
g_logger.level_name(ent->severity),
|
||||
ent->message);
|
||||
cons->set_color();
|
||||
}
|
||||
|
||||
if (!g_logger.has_log()) {
|
||||
sys.deassert_signal(j6_signal_system_has_log);
|
||||
sys.add_blocked_thread(&self);
|
||||
self.wait_on_signals(j6_signal_system_has_log);
|
||||
}
|
||||
}
|
||||
if (!g_logger.has_log()) {
|
||||
sys.deassert_signal(j6_signal_system_has_log);
|
||||
sys.add_blocked_thread(&self);
|
||||
self.wait_on_signals(j6_signal_system_has_log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logger_init()
|
||||
{
|
||||
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), output_log);
|
||||
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), output_log);
|
||||
}
|
||||
|
||||
void logger_clear_immediate()
|
||||
{
|
||||
g_logger.set_immediate(nullptr);
|
||||
g_logger.set_immediate(nullptr);
|
||||
}
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
void kernel_main(kernel::init::args *args);
|
||||
void (*__ctors)(void);
|
||||
void (*__ctors_end)(void);
|
||||
void long_ap_startup(cpu_data *cpu);
|
||||
void ap_startup();
|
||||
void ap_idle();
|
||||
void init_ap_trampoline(void*, cpu_data *, void (*)());
|
||||
void kernel_main(kernel::init::args *args);
|
||||
void (*__ctors)(void);
|
||||
void (*__ctors_end)(void);
|
||||
void long_ap_startup(cpu_data *cpu);
|
||||
void ap_startup();
|
||||
void ap_idle();
|
||||
void init_ap_trampoline(void*, cpu_data *, void (*)());
|
||||
}
|
||||
|
||||
using namespace kernel;
|
||||
@@ -57,23 +57,23 @@ unsigned start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4);
|
||||
void
|
||||
init_console()
|
||||
{
|
||||
serial_port *com1 = new (&g_com1) serial_port(COM1);
|
||||
console *cons = new (&g_console) console(com1);
|
||||
serial_port *com1 = new (&g_com1) serial_port(COM1);
|
||||
console *cons = new (&g_console) console(com1);
|
||||
|
||||
cons->set_color(0x21, 0x00);
|
||||
cons->puts("jsix OS ");
|
||||
cons->set_color(0x08, 0x00);
|
||||
cons->puts(GIT_VERSION " booting...\n");
|
||||
cons->set_color(0x21, 0x00);
|
||||
cons->puts("jsix OS ");
|
||||
cons->set_color(0x08, 0x00);
|
||||
cons->puts(GIT_VERSION " booting...\n");
|
||||
}
|
||||
|
||||
void
|
||||
run_constructors()
|
||||
{
|
||||
void (**p)(void) = &__ctors;
|
||||
while (p < &__ctors_end) {
|
||||
void (*ctor)(void) = *p++;
|
||||
if (ctor) ctor();
|
||||
}
|
||||
void (**p)(void) = &__ctors;
|
||||
while (p < &__ctors_end) {
|
||||
void (*ctor)(void) = *p++;
|
||||
if (ctor) ctor();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -84,230 +84,230 @@ kernel_main(init::args *args)
|
||||
kutil::assert::symbol_table = args->symbol_table | memory::page_offset;
|
||||
}
|
||||
|
||||
init_console();
|
||||
logger_init();
|
||||
init_console();
|
||||
logger_init();
|
||||
|
||||
cpu_validate();
|
||||
cpu_validate();
|
||||
|
||||
kassert(args->magic == init::args_magic,
|
||||
kassert(args->magic == init::args_magic,
|
||||
"Bad kernel args magic number");
|
||||
|
||||
log::debug(logs::boot, "jsix init args are at: %016lx", args);
|
||||
log::debug(logs::boot, " Memory map is at: %016lx", args->mem_map);
|
||||
log::debug(logs::boot, "ACPI root table is at: %016lx", args->acpi_table);
|
||||
log::debug(logs::boot, "Runtime service is at: %016lx", args->runtime_services);
|
||||
log::debug(logs::boot, " Kernel PML4 is at: %016lx", args->pml4);
|
||||
log::debug(logs::boot, "jsix init args are at: %016lx", args);
|
||||
log::debug(logs::boot, " Memory map is at: %016lx", args->mem_map);
|
||||
log::debug(logs::boot, "ACPI root table is at: %016lx", args->acpi_table);
|
||||
log::debug(logs::boot, "Runtime service is at: %016lx", args->runtime_services);
|
||||
log::debug(logs::boot, " Kernel PML4 is at: %016lx", args->pml4);
|
||||
|
||||
uint64_t cr0, cr4;
|
||||
asm ("mov %%cr0, %0" : "=r"(cr0));
|
||||
asm ("mov %%cr4, %0" : "=r"(cr4));
|
||||
uint64_t efer = rdmsr(msr::ia32_efer);
|
||||
log::debug(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0, cr4, efer);
|
||||
uint64_t cr0, cr4;
|
||||
asm ("mov %%cr0, %0" : "=r"(cr0));
|
||||
asm ("mov %%cr4, %0" : "=r"(cr4));
|
||||
uint64_t efer = rdmsr(msr::ia32_efer);
|
||||
log::debug(logs::boot, "Control regs: cr0:%lx cr4:%lx efer:%lx", cr0, cr4, efer);
|
||||
|
||||
extern IDT &g_bsp_idt;
|
||||
extern TSS &g_bsp_tss;
|
||||
extern GDT &g_bsp_gdt;
|
||||
extern cpu_data g_bsp_cpu_data;
|
||||
extern uintptr_t idle_stack_end;
|
||||
extern IDT &g_bsp_idt;
|
||||
extern TSS &g_bsp_tss;
|
||||
extern GDT &g_bsp_gdt;
|
||||
extern cpu_data g_bsp_cpu_data;
|
||||
extern uintptr_t idle_stack_end;
|
||||
|
||||
cpu_data *cpu = &g_bsp_cpu_data;
|
||||
kutil::memset(cpu, 0, sizeof(cpu_data));
|
||||
cpu_data *cpu = &g_bsp_cpu_data;
|
||||
kutil::memset(cpu, 0, sizeof(cpu_data));
|
||||
|
||||
cpu->self = cpu;
|
||||
cpu->idt = new (&g_bsp_idt) IDT;
|
||||
cpu->tss = new (&g_bsp_tss) TSS;
|
||||
cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss};
|
||||
cpu->rsp0 = idle_stack_end;
|
||||
cpu_early_init(cpu);
|
||||
cpu->self = cpu;
|
||||
cpu->idt = new (&g_bsp_idt) IDT;
|
||||
cpu->tss = new (&g_bsp_tss) TSS;
|
||||
cpu->gdt = new (&g_bsp_gdt) GDT {cpu->tss};
|
||||
cpu->rsp0 = idle_stack_end;
|
||||
cpu_early_init(cpu);
|
||||
|
||||
disable_legacy_pic();
|
||||
disable_legacy_pic();
|
||||
|
||||
memory_initialize_pre_ctors(*args);
|
||||
run_constructors();
|
||||
memory_initialize_post_ctors(*args);
|
||||
memory_initialize_pre_ctors(*args);
|
||||
run_constructors();
|
||||
memory_initialize_post_ctors(*args);
|
||||
|
||||
cpu->tss->create_ist_stacks(cpu->idt->used_ist_entries());
|
||||
cpu->tss->create_ist_stacks(cpu->idt->used_ist_entries());
|
||||
|
||||
syscall_initialize();
|
||||
syscall_initialize();
|
||||
|
||||
device_manager &devices = device_manager::get();
|
||||
devices.parse_acpi(args->acpi_table);
|
||||
device_manager &devices = device_manager::get();
|
||||
devices.parse_acpi(args->acpi_table);
|
||||
|
||||
// Need the local APIC to get the BSP's id
|
||||
uintptr_t apic_base = devices.get_lapic_base();
|
||||
// Need the local APIC to get the BSP's id
|
||||
uintptr_t apic_base = devices.get_lapic_base();
|
||||
|
||||
lapic *apic = new lapic(apic_base);
|
||||
apic->enable();
|
||||
lapic *apic = new lapic(apic_base);
|
||||
apic->enable();
|
||||
|
||||
cpu->id = apic->get_id();
|
||||
cpu->apic = apic;
|
||||
cpu->id = apic->get_id();
|
||||
cpu->apic = apic;
|
||||
|
||||
cpu_init(cpu, true);
|
||||
cpu_init(cpu, true);
|
||||
|
||||
devices.init_drivers();
|
||||
apic->calibrate_timer();
|
||||
devices.init_drivers();
|
||||
apic->calibrate_timer();
|
||||
|
||||
const auto &apic_ids = devices.get_apic_ids();
|
||||
unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4);
|
||||
const auto &apic_ids = devices.get_apic_ids();
|
||||
unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4);
|
||||
|
||||
interrupts_enable();
|
||||
g_com1.handle_interrupt();
|
||||
interrupts_enable();
|
||||
g_com1.handle_interrupt();
|
||||
|
||||
/*
|
||||
block_device *disk = devices->get_block_device(0);
|
||||
if (disk) {
|
||||
for (int i=0; i<1; ++i) {
|
||||
uint8_t buf[512];
|
||||
kutil::memset(buf, 0, 512);
|
||||
/*
|
||||
block_device *disk = devices->get_block_device(0);
|
||||
if (disk) {
|
||||
for (int i=0; i<1; ++i) {
|
||||
uint8_t buf[512];
|
||||
kutil::memset(buf, 0, 512);
|
||||
|
||||
kassert(disk->read(0x200, sizeof(buf), buf),
|
||||
"Disk read returned 0");
|
||||
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++);
|
||||
}
|
||||
cons->putc('\n');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn(logs::boot, "No block devices present.");
|
||||
}
|
||||
*/
|
||||
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');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn(logs::boot, "No block devices present.");
|
||||
}
|
||||
*/
|
||||
|
||||
scheduler *sched = new scheduler {num_cpus};
|
||||
scheduler_ready = true;
|
||||
scheduler *sched = new scheduler {num_cpus};
|
||||
scheduler_ready = true;
|
||||
|
||||
// Load the init server
|
||||
load_init_server(*args->init, args->modules);
|
||||
// Load the init server
|
||||
load_init_server(*args->init, args->modules);
|
||||
|
||||
sched->create_kernel_task(logger_task, scheduler::max_priority/2, true);
|
||||
sched->start();
|
||||
sched->create_kernel_task(logger_task, scheduler::max_priority/2, true);
|
||||
sched->start();
|
||||
}
|
||||
|
||||
unsigned
|
||||
start_aps(lapic &apic, const kutil::vector<uint8_t> &ids, void *kpml4)
|
||||
{
|
||||
using memory::frame_size;
|
||||
using memory::kernel_stack_pages;
|
||||
using memory::frame_size;
|
||||
using memory::kernel_stack_pages;
|
||||
|
||||
extern size_t ap_startup_code_size;
|
||||
extern process &g_kernel_process;
|
||||
extern vm_area_guarded &g_kernel_stacks;
|
||||
extern size_t ap_startup_code_size;
|
||||
extern process &g_kernel_process;
|
||||
extern vm_area_guarded &g_kernel_stacks;
|
||||
|
||||
clock &clk = clock::get();
|
||||
clock &clk = clock::get();
|
||||
|
||||
ap_startup_count = 1; // BSP processor
|
||||
log::info(logs::boot, "Starting %d other CPUs", ids.count() - 1);
|
||||
ap_startup_count = 1; // BSP processor
|
||||
log::info(logs::boot, "Starting %d other CPUs", ids.count() - 1);
|
||||
|
||||
// Since we're using address space outside kernel space, make sure
|
||||
// the kernel's vm_space is used
|
||||
cpu_data &bsp = current_cpu();
|
||||
bsp.process = &g_kernel_process;
|
||||
// Since we're using address space outside kernel space, make sure
|
||||
// the kernel's vm_space is used
|
||||
cpu_data &bsp = current_cpu();
|
||||
bsp.process = &g_kernel_process;
|
||||
|
||||
uint16_t index = bsp.index;
|
||||
uint16_t index = bsp.index;
|
||||
|
||||
// Copy the startup code somwhere the real mode trampoline can run
|
||||
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
|
||||
uint8_t vector = addr >> 12;
|
||||
vm_area *vma = new vm_area_fixed(addr, 0x1000, vm_flags::write);
|
||||
vm_space::kernel_space().add(addr, vma);
|
||||
kutil::memcpy(
|
||||
reinterpret_cast<void*>(addr),
|
||||
reinterpret_cast<void*>(&ap_startup),
|
||||
ap_startup_code_size);
|
||||
// Copy the startup code somwhere the real mode trampoline can run
|
||||
uintptr_t addr = 0x8000; // TODO: find a valid address, rewrite addresses
|
||||
uint8_t vector = addr >> 12;
|
||||
vm_area *vma = new vm_area_fixed(addr, 0x1000, vm_flags::write);
|
||||
vm_space::kernel_space().add(addr, vma);
|
||||
kutil::memcpy(
|
||||
reinterpret_cast<void*>(addr),
|
||||
reinterpret_cast<void*>(&ap_startup),
|
||||
ap_startup_code_size);
|
||||
|
||||
// AP idle stacks need less room than normal stacks, so pack multiple
|
||||
// into a normal stack area
|
||||
static constexpr size_t idle_stack_bytes = 2048; // 2KiB is generous
|
||||
static constexpr size_t full_stack_bytes = kernel_stack_pages * frame_size;
|
||||
static constexpr size_t idle_stacks_per = full_stack_bytes / idle_stack_bytes;
|
||||
// AP idle stacks need less room than normal stacks, so pack multiple
|
||||
// into a normal stack area
|
||||
static constexpr size_t idle_stack_bytes = 2048; // 2KiB is generous
|
||||
static constexpr size_t full_stack_bytes = kernel_stack_pages * frame_size;
|
||||
static constexpr size_t idle_stacks_per = full_stack_bytes / idle_stack_bytes;
|
||||
|
||||
uint8_t ist_entries = IDT::current().used_ist_entries();
|
||||
uint8_t ist_entries = IDT::current().used_ist_entries();
|
||||
|
||||
size_t free_stack_count = 0;
|
||||
uintptr_t stack_area_start = 0;
|
||||
size_t free_stack_count = 0;
|
||||
uintptr_t stack_area_start = 0;
|
||||
|
||||
lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert;
|
||||
apic.send_ipi_broadcast(mode, false, 0);
|
||||
lapic::ipi mode = lapic::ipi::init | lapic::ipi::level | lapic::ipi::assert;
|
||||
apic.send_ipi_broadcast(mode, false, 0);
|
||||
|
||||
for (uint8_t id : ids) {
|
||||
if (id == bsp.id) continue;
|
||||
for (uint8_t id : ids) {
|
||||
if (id == bsp.id) continue;
|
||||
|
||||
// Set up the CPU data structures
|
||||
IDT *idt = new IDT;
|
||||
TSS *tss = new TSS;
|
||||
GDT *gdt = new GDT {tss};
|
||||
cpu_data *cpu = new cpu_data;
|
||||
kutil::memset(cpu, 0, sizeof(cpu_data));
|
||||
// Set up the CPU data structures
|
||||
IDT *idt = new IDT;
|
||||
TSS *tss = new TSS;
|
||||
GDT *gdt = new GDT {tss};
|
||||
cpu_data *cpu = new cpu_data;
|
||||
kutil::memset(cpu, 0, sizeof(cpu_data));
|
||||
|
||||
cpu->self = cpu;
|
||||
cpu->id = id;
|
||||
cpu->index = ++index;
|
||||
cpu->idt = idt;
|
||||
cpu->tss = tss;
|
||||
cpu->gdt = gdt;
|
||||
cpu->self = cpu;
|
||||
cpu->id = id;
|
||||
cpu->index = ++index;
|
||||
cpu->idt = idt;
|
||||
cpu->tss = tss;
|
||||
cpu->gdt = gdt;
|
||||
|
||||
tss->create_ist_stacks(ist_entries);
|
||||
tss->create_ist_stacks(ist_entries);
|
||||
|
||||
// Set up the CPU's idle task stack
|
||||
if (free_stack_count == 0) {
|
||||
stack_area_start = g_kernel_stacks.get_section();
|
||||
free_stack_count = idle_stacks_per;
|
||||
}
|
||||
// Set up the CPU's idle task stack
|
||||
if (free_stack_count == 0) {
|
||||
stack_area_start = g_kernel_stacks.get_section();
|
||||
free_stack_count = idle_stacks_per;
|
||||
}
|
||||
|
||||
uintptr_t stack_end = stack_area_start + free_stack_count-- * idle_stack_bytes;
|
||||
stack_end -= 2 * sizeof(void*); // Null frame
|
||||
*reinterpret_cast<uint64_t*>(stack_end) = 0; // pre-fault the page
|
||||
cpu->rsp0 = stack_end;
|
||||
uintptr_t stack_end = stack_area_start + free_stack_count-- * idle_stack_bytes;
|
||||
stack_end -= 2 * sizeof(void*); // Null frame
|
||||
*reinterpret_cast<uint64_t*>(stack_end) = 0; // pre-fault the page
|
||||
cpu->rsp0 = stack_end;
|
||||
|
||||
// Set up the trampoline with this CPU's data
|
||||
init_ap_trampoline(kpml4, cpu, ap_idle);
|
||||
// Set up the trampoline with this CPU's data
|
||||
init_ap_trampoline(kpml4, cpu, ap_idle);
|
||||
|
||||
// Kick it off!
|
||||
size_t current_count = ap_startup_count;
|
||||
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
|
||||
// Kick it off!
|
||||
size_t current_count = ap_startup_count;
|
||||
log::debug(logs::boot, "Starting AP %d: stack %llx", cpu->index, stack_end);
|
||||
|
||||
lapic::ipi startup = lapic::ipi::startup | lapic::ipi::assert;
|
||||
lapic::ipi startup = lapic::ipi::startup | lapic::ipi::assert;
|
||||
|
||||
apic.send_ipi(startup, vector, id);
|
||||
for (unsigned i = 0; i < 20; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(20);
|
||||
}
|
||||
apic.send_ipi(startup, vector, id);
|
||||
for (unsigned i = 0; i < 20; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(20);
|
||||
}
|
||||
|
||||
// If the CPU already incremented ap_startup_count, it's done
|
||||
if (ap_startup_count > current_count)
|
||||
continue;
|
||||
// If the CPU already incremented ap_startup_count, it's done
|
||||
if (ap_startup_count > current_count)
|
||||
continue;
|
||||
|
||||
// Send the second SIPI (intel recommends this)
|
||||
apic.send_ipi(startup, vector, id);
|
||||
for (unsigned i = 0; i < 100; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(100);
|
||||
}
|
||||
// Send the second SIPI (intel recommends this)
|
||||
apic.send_ipi(startup, vector, id);
|
||||
for (unsigned i = 0; i < 100; ++i) {
|
||||
if (ap_startup_count > current_count) break;
|
||||
clk.spinwait(100);
|
||||
}
|
||||
|
||||
log::warn(logs::boot, "No response from AP %d within timeout", id);
|
||||
}
|
||||
log::warn(logs::boot, "No response from AP %d within timeout", id);
|
||||
}
|
||||
|
||||
log::info(logs::boot, "%d CPUs running", ap_startup_count);
|
||||
vm_space::kernel_space().remove(vma);
|
||||
return ap_startup_count;
|
||||
log::info(logs::boot, "%d CPUs running", ap_startup_count);
|
||||
vm_space::kernel_space().remove(vma);
|
||||
return ap_startup_count;
|
||||
}
|
||||
|
||||
void
|
||||
long_ap_startup(cpu_data *cpu)
|
||||
{
|
||||
cpu_init(cpu, false);
|
||||
++ap_startup_count;
|
||||
while (!scheduler_ready) asm ("pause");
|
||||
cpu_init(cpu, false);
|
||||
++ap_startup_count;
|
||||
while (!scheduler_ready) asm ("pause");
|
||||
|
||||
uintptr_t apic_base =
|
||||
device_manager::get().get_lapic_base();
|
||||
cpu->apic = new lapic(apic_base);
|
||||
cpu->apic->enable();
|
||||
uintptr_t apic_base =
|
||||
device_manager::get().get_lapic_base();
|
||||
cpu->apic = new lapic(apic_base);
|
||||
cpu->apic->enable();
|
||||
|
||||
scheduler::get().start();
|
||||
scheduler::get().start();
|
||||
}
|
||||
|
||||
@@ -52,10 +52,10 @@ static kutil::no_construct<vm_area_guarded> __g_kernel_stacks_storage;
|
||||
vm_area_guarded &g_kernel_stacks = __g_kernel_stacks_storage.value;
|
||||
|
||||
vm_area_guarded g_kernel_buffers {
|
||||
memory::buffers_start,
|
||||
memory::kernel_buffer_pages,
|
||||
memory::kernel_max_buffers,
|
||||
vm_flags::write};
|
||||
memory::buffers_start,
|
||||
memory::kernel_buffer_pages,
|
||||
memory::kernel_max_buffers,
|
||||
vm_flags::write};
|
||||
|
||||
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
|
||||
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
|
||||
@@ -63,149 +63,149 @@ void operator delete (void *p) noexcept { return g_kernel_heap.free(p); }
|
||||
void operator delete [] (void *p) noexcept { return g_kernel_heap.free(p); }
|
||||
|
||||
namespace kutil {
|
||||
void * kalloc(size_t size) { return g_kernel_heap.allocate(size); }
|
||||
void kfree(void *p) { return g_kernel_heap.free(p); }
|
||||
void * kalloc(size_t size) { return g_kernel_heap.allocate(size); }
|
||||
void kfree(void *p) { return g_kernel_heap.free(p); }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
uintptr_t
|
||||
get_physical_page(T *p) {
|
||||
return memory::page_align_down(reinterpret_cast<uintptr_t>(p));
|
||||
return memory::page_align_down(reinterpret_cast<uintptr_t>(p));
|
||||
}
|
||||
|
||||
void
|
||||
memory_initialize_pre_ctors(init::args &kargs)
|
||||
{
|
||||
using kernel::init::frame_block;
|
||||
using kernel::init::frame_block;
|
||||
|
||||
page_table *kpml4 = static_cast<page_table*>(kargs.pml4);
|
||||
page_table *kpml4 = static_cast<page_table*>(kargs.pml4);
|
||||
|
||||
new (&g_kernel_heap) kutil::heap_allocator {heap_start, kernel_max_heap};
|
||||
new (&g_kernel_heap) kutil::heap_allocator {heap_start, kernel_max_heap};
|
||||
|
||||
frame_block *blocks = reinterpret_cast<frame_block*>(memory::bitmap_start);
|
||||
new (&g_frame_allocator) frame_allocator {blocks, kargs.frame_blocks.count};
|
||||
frame_block *blocks = reinterpret_cast<frame_block*>(memory::bitmap_start);
|
||||
new (&g_frame_allocator) frame_allocator {blocks, kargs.frame_blocks.count};
|
||||
|
||||
// Mark all the things the bootloader allocated for us as used
|
||||
allocation_register *reg = kargs.allocations;
|
||||
while (reg) {
|
||||
for (auto &alloc : reg->entries)
|
||||
if (alloc.type != init::allocation_type::none)
|
||||
g_frame_allocator.used(alloc.address, alloc.count);
|
||||
reg = reg->next;
|
||||
}
|
||||
// Mark all the things the bootloader allocated for us as used
|
||||
allocation_register *reg = kargs.allocations;
|
||||
while (reg) {
|
||||
for (auto &alloc : reg->entries)
|
||||
if (alloc.type != init::allocation_type::none)
|
||||
g_frame_allocator.used(alloc.address, alloc.count);
|
||||
reg = reg->next;
|
||||
}
|
||||
|
||||
process *kp = process::create_kernel_process(kpml4);
|
||||
vm_space &vm = kp->space();
|
||||
process *kp = process::create_kernel_process(kpml4);
|
||||
vm_space &vm = kp->space();
|
||||
|
||||
vm_area *heap = new (&g_kernel_heap_area)
|
||||
vm_area_untracked(kernel_max_heap, vm_flags::write);
|
||||
vm_area *heap = new (&g_kernel_heap_area)
|
||||
vm_area_untracked(kernel_max_heap, vm_flags::write);
|
||||
|
||||
vm.add(heap_start, heap);
|
||||
vm.add(heap_start, heap);
|
||||
|
||||
vm_area *stacks = new (&g_kernel_stacks) vm_area_guarded {
|
||||
memory::stacks_start,
|
||||
memory::kernel_stack_pages,
|
||||
memory::kernel_max_stacks,
|
||||
vm_flags::write};
|
||||
vm.add(memory::stacks_start, &g_kernel_stacks);
|
||||
vm_area *stacks = new (&g_kernel_stacks) vm_area_guarded {
|
||||
memory::stacks_start,
|
||||
memory::kernel_stack_pages,
|
||||
memory::kernel_max_stacks,
|
||||
vm_flags::write};
|
||||
vm.add(memory::stacks_start, &g_kernel_stacks);
|
||||
|
||||
// Clean out any remaning bootloader page table entries
|
||||
for (unsigned i = 0; i < memory::pml4e_kernel; ++i)
|
||||
kpml4->entries[i] = 0;
|
||||
// Clean out any remaning bootloader page table entries
|
||||
for (unsigned i = 0; i < memory::pml4e_kernel; ++i)
|
||||
kpml4->entries[i] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
memory_initialize_post_ctors(init::args &kargs)
|
||||
{
|
||||
vm_space &vm = vm_space::kernel_space();
|
||||
vm.add(memory::buffers_start, &g_kernel_buffers);
|
||||
vm_space &vm = vm_space::kernel_space();
|
||||
vm.add(memory::buffers_start, &g_kernel_buffers);
|
||||
|
||||
g_frame_allocator.free(
|
||||
get_physical_page(kargs.page_tables.pointer),
|
||||
kargs.page_tables.count);
|
||||
g_frame_allocator.free(
|
||||
get_physical_page(kargs.page_tables.pointer),
|
||||
kargs.page_tables.count);
|
||||
}
|
||||
|
||||
static void
|
||||
log_mtrrs()
|
||||
{
|
||||
uint64_t mtrrcap = rdmsr(msr::ia32_mtrrcap);
|
||||
uint64_t mtrrdeftype = rdmsr(msr::ia32_mtrrdeftype);
|
||||
unsigned vcap = mtrrcap & 0xff;
|
||||
log::debug(logs::boot, "MTRRs: vcap=%d %s %s def=%02x %s %s",
|
||||
vcap,
|
||||
(mtrrcap & (1<< 8)) ? "fix" : "",
|
||||
(mtrrcap & (1<<10)) ? "wc" : "",
|
||||
mtrrdeftype & 0xff,
|
||||
(mtrrdeftype & (1<<10)) ? "fe" : "",
|
||||
(mtrrdeftype & (1<<11)) ? "enabled" : ""
|
||||
);
|
||||
uint64_t mtrrcap = rdmsr(msr::ia32_mtrrcap);
|
||||
uint64_t mtrrdeftype = rdmsr(msr::ia32_mtrrdeftype);
|
||||
unsigned vcap = mtrrcap & 0xff;
|
||||
log::debug(logs::boot, "MTRRs: vcap=%d %s %s def=%02x %s %s",
|
||||
vcap,
|
||||
(mtrrcap & (1<< 8)) ? "fix" : "",
|
||||
(mtrrcap & (1<<10)) ? "wc" : "",
|
||||
mtrrdeftype & 0xff,
|
||||
(mtrrdeftype & (1<<10)) ? "fe" : "",
|
||||
(mtrrdeftype & (1<<11)) ? "enabled" : ""
|
||||
);
|
||||
|
||||
for (unsigned i = 0; i < vcap; ++i) {
|
||||
uint64_t base = rdmsr(find_mtrr(msr::ia32_mtrrphysbase, i));
|
||||
uint64_t mask = rdmsr(find_mtrr(msr::ia32_mtrrphysmask, i));
|
||||
log::debug(logs::boot, " vcap[%2d] base:%016llx mask:%016llx type:%02x %s", i,
|
||||
(base & ~0xfffull),
|
||||
(mask & ~0xfffull),
|
||||
(base & 0xff),
|
||||
(mask & (1<<11)) ? "valid" : "");
|
||||
}
|
||||
for (unsigned i = 0; i < vcap; ++i) {
|
||||
uint64_t base = rdmsr(find_mtrr(msr::ia32_mtrrphysbase, i));
|
||||
uint64_t mask = rdmsr(find_mtrr(msr::ia32_mtrrphysmask, i));
|
||||
log::debug(logs::boot, " vcap[%2d] base:%016llx mask:%016llx type:%02x %s", i,
|
||||
(base & ~0xfffull),
|
||||
(mask & ~0xfffull),
|
||||
(base & 0xff),
|
||||
(mask & (1<<11)) ? "valid" : "");
|
||||
}
|
||||
|
||||
msr mtrr_fixed[] = {
|
||||
msr::ia32_mtrrfix64k_00000,
|
||||
msr::ia32_mtrrfix16k_80000,
|
||||
msr::ia32_mtrrfix16k_a0000,
|
||||
msr::ia32_mtrrfix4k_c0000,
|
||||
msr::ia32_mtrrfix4k_c8000,
|
||||
msr::ia32_mtrrfix4k_d0000,
|
||||
msr::ia32_mtrrfix4k_d8000,
|
||||
msr::ia32_mtrrfix4k_e0000,
|
||||
msr::ia32_mtrrfix4k_e8000,
|
||||
msr::ia32_mtrrfix4k_f0000,
|
||||
msr::ia32_mtrrfix4k_f8000,
|
||||
};
|
||||
msr mtrr_fixed[] = {
|
||||
msr::ia32_mtrrfix64k_00000,
|
||||
msr::ia32_mtrrfix16k_80000,
|
||||
msr::ia32_mtrrfix16k_a0000,
|
||||
msr::ia32_mtrrfix4k_c0000,
|
||||
msr::ia32_mtrrfix4k_c8000,
|
||||
msr::ia32_mtrrfix4k_d0000,
|
||||
msr::ia32_mtrrfix4k_d8000,
|
||||
msr::ia32_mtrrfix4k_e0000,
|
||||
msr::ia32_mtrrfix4k_e8000,
|
||||
msr::ia32_mtrrfix4k_f0000,
|
||||
msr::ia32_mtrrfix4k_f8000,
|
||||
};
|
||||
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
uint64_t v = rdmsr(mtrr_fixed[i]);
|
||||
log::debug(logs::boot, " fixed[%2d] %02x %02x %02x %02x %02x %02x %02x %02x", i,
|
||||
((v << 0) & 0xff), ((v << 8) & 0xff), ((v << 16) & 0xff), ((v << 24) & 0xff),
|
||||
((v << 32) & 0xff), ((v << 40) & 0xff), ((v << 48) & 0xff), ((v << 56) & 0xff));
|
||||
}
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
uint64_t v = rdmsr(mtrr_fixed[i]);
|
||||
log::debug(logs::boot, " fixed[%2d] %02x %02x %02x %02x %02x %02x %02x %02x", i,
|
||||
((v << 0) & 0xff), ((v << 8) & 0xff), ((v << 16) & 0xff), ((v << 24) & 0xff),
|
||||
((v << 32) & 0xff), ((v << 40) & 0xff), ((v << 48) & 0xff), ((v << 56) & 0xff));
|
||||
}
|
||||
|
||||
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||
static const char *pat_names[] = {"UC ","WC ","XX ","XX ","WT ","WP ","WB ","UC-"};
|
||||
log::debug(logs::boot, " PAT: 0:%s 1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s",
|
||||
pat_names[(pat >> (0*8)) & 7], pat_names[(pat >> (1*8)) & 7],
|
||||
pat_names[(pat >> (2*8)) & 7], pat_names[(pat >> (3*8)) & 7],
|
||||
pat_names[(pat >> (4*8)) & 7], pat_names[(pat >> (5*8)) & 7],
|
||||
pat_names[(pat >> (6*8)) & 7], pat_names[(pat >> (7*8)) & 7]);
|
||||
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||
static const char *pat_names[] = {"UC ","WC ","XX ","XX ","WT ","WP ","WB ","UC-"};
|
||||
log::debug(logs::boot, " PAT: 0:%s 1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s",
|
||||
pat_names[(pat >> (0*8)) & 7], pat_names[(pat >> (1*8)) & 7],
|
||||
pat_names[(pat >> (2*8)) & 7], pat_names[(pat >> (3*8)) & 7],
|
||||
pat_names[(pat >> (4*8)) & 7], pat_names[(pat >> (5*8)) & 7],
|
||||
pat_names[(pat >> (6*8)) & 7], pat_names[(pat >> (7*8)) & 7]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
load_init_server(init::program &program, uintptr_t modules_address)
|
||||
{
|
||||
process *p = new process;
|
||||
p->add_handle(&system::get());
|
||||
process *p = new process;
|
||||
p->add_handle(&system::get());
|
||||
|
||||
vm_space &space = p->space();
|
||||
for (const auto § : program.sections) {
|
||||
vm_flags flags =
|
||||
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
||||
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||
vm_space &space = p->space();
|
||||
for (const auto § : program.sections) {
|
||||
vm_flags flags =
|
||||
((sect.type && section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
||||
((sect.type && section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||
|
||||
vm_area *vma = new vm_area_fixed(sect.phys_addr, sect.size, flags);
|
||||
space.add(sect.virt_addr, vma);
|
||||
}
|
||||
vm_area *vma = new vm_area_fixed(sect.phys_addr, sect.size, flags);
|
||||
space.add(sect.virt_addr, vma);
|
||||
}
|
||||
|
||||
uint64_t iopl = (3ull << 12);
|
||||
uint64_t iopl = (3ull << 12);
|
||||
|
||||
thread *main = p->create_thread();
|
||||
main->add_thunk_user(program.entrypoint, 0, iopl);
|
||||
main->set_state(thread::state::ready);
|
||||
thread *main = p->create_thread();
|
||||
main->add_thunk_user(program.entrypoint, 0, iopl);
|
||||
main->set_state(thread::state::ready);
|
||||
|
||||
// Hacky: No process exists to have created a stack for init; it needs to create
|
||||
// its own stack. We take advantage of that to use rsp to pass it the init modules
|
||||
// address.
|
||||
auto *tcb = main->tcb();
|
||||
tcb->rsp3 = modules_address;
|
||||
// Hacky: No process exists to have created a stack for init; it needs to create
|
||||
// its own stack. We take advantage of that to use rsp to pass it the init modules
|
||||
// address.
|
||||
auto *tcb = main->tcb();
|
||||
tcb->rsp3 = modules_address;
|
||||
}
|
||||
|
||||
@@ -3,22 +3,22 @@
|
||||
msr
|
||||
find_mtrr(msr type, unsigned index)
|
||||
{
|
||||
return static_cast<msr>(static_cast<uint32_t>(type) + (2 * index));
|
||||
return static_cast<msr>(static_cast<uint32_t>(type) + (2 * index));
|
||||
}
|
||||
|
||||
uint64_t
|
||||
rdmsr(msr addr)
|
||||
{
|
||||
uint32_t low, high;
|
||||
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
|
||||
return (static_cast<uint64_t>(high) << 32) | low;
|
||||
uint32_t low, high;
|
||||
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
|
||||
return (static_cast<uint64_t>(high) << 32) | low;
|
||||
}
|
||||
|
||||
void
|
||||
wrmsr(msr addr, uint64_t value)
|
||||
{
|
||||
uint32_t low = value & 0xffffffff;
|
||||
uint32_t high = value >> 32;
|
||||
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
|
||||
uint32_t low = value & 0xffffffff;
|
||||
uint32_t high = value >> 32;
|
||||
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,34 +6,34 @@
|
||||
|
||||
enum class msr : uint32_t
|
||||
{
|
||||
ia32_mtrrcap = 0x000000fe,
|
||||
ia32_mtrrdeftype = 0x000002ff,
|
||||
ia32_mtrrcap = 0x000000fe,
|
||||
ia32_mtrrdeftype = 0x000002ff,
|
||||
|
||||
ia32_mtrrphysbase = 0x00000200,
|
||||
ia32_mtrrphysmask = 0x00000201,
|
||||
ia32_mtrrphysbase = 0x00000200,
|
||||
ia32_mtrrphysmask = 0x00000201,
|
||||
|
||||
ia32_mtrrfix64k_00000 = 0x00000250,
|
||||
ia32_mtrrfix64k_00000 = 0x00000250,
|
||||
|
||||
ia32_mtrrfix16k_80000 = 0x00000258,
|
||||
ia32_mtrrfix16k_a0000 = 0x00000259,
|
||||
ia32_mtrrfix16k_80000 = 0x00000258,
|
||||
ia32_mtrrfix16k_a0000 = 0x00000259,
|
||||
|
||||
ia32_mtrrfix4k_c0000 = 0x00000268,
|
||||
ia32_mtrrfix4k_c8000 = 0x00000269,
|
||||
ia32_mtrrfix4k_d0000 = 0x0000026A,
|
||||
ia32_mtrrfix4k_d8000 = 0x0000026B,
|
||||
ia32_mtrrfix4k_e0000 = 0x0000026C,
|
||||
ia32_mtrrfix4k_e8000 = 0x0000026D,
|
||||
ia32_mtrrfix4k_f0000 = 0x0000026E,
|
||||
ia32_mtrrfix4k_f8000 = 0x0000026F,
|
||||
ia32_mtrrfix4k_c0000 = 0x00000268,
|
||||
ia32_mtrrfix4k_c8000 = 0x00000269,
|
||||
ia32_mtrrfix4k_d0000 = 0x0000026A,
|
||||
ia32_mtrrfix4k_d8000 = 0x0000026B,
|
||||
ia32_mtrrfix4k_e0000 = 0x0000026C,
|
||||
ia32_mtrrfix4k_e8000 = 0x0000026D,
|
||||
ia32_mtrrfix4k_f0000 = 0x0000026E,
|
||||
ia32_mtrrfix4k_f8000 = 0x0000026F,
|
||||
|
||||
ia32_pat = 0x00000277,
|
||||
ia32_efer = 0xc0000080,
|
||||
ia32_star = 0xc0000081,
|
||||
ia32_lstar = 0xc0000082,
|
||||
ia32_fmask = 0xc0000084,
|
||||
ia32_pat = 0x00000277,
|
||||
ia32_efer = 0xc0000080,
|
||||
ia32_star = 0xc0000081,
|
||||
ia32_lstar = 0xc0000082,
|
||||
ia32_fmask = 0xc0000084,
|
||||
|
||||
ia32_gs_base = 0xc0000101,
|
||||
ia32_kernel_gs_base = 0xc0000102
|
||||
ia32_gs_base = 0xc0000101,
|
||||
ia32_kernel_gs_base = 0xc0000102
|
||||
};
|
||||
|
||||
/// Find the msr for MTRR physical base or mask
|
||||
|
||||
@@ -9,82 +9,82 @@ extern vm_area_guarded g_kernel_buffers;
|
||||
constexpr size_t buffer_bytes = memory::kernel_buffer_pages * memory::frame_size;
|
||||
|
||||
channel::channel() :
|
||||
m_len(0),
|
||||
m_data(g_kernel_buffers.get_section()),
|
||||
m_buffer(reinterpret_cast<uint8_t*>(m_data), buffer_bytes),
|
||||
kobject(kobject::type::channel, j6_signal_channel_can_send)
|
||||
m_len(0),
|
||||
m_data(g_kernel_buffers.get_section()),
|
||||
m_buffer(reinterpret_cast<uint8_t*>(m_data), buffer_bytes),
|
||||
kobject(kobject::type::channel, j6_signal_channel_can_send)
|
||||
{
|
||||
}
|
||||
|
||||
channel::~channel()
|
||||
{
|
||||
if (!closed()) close();
|
||||
if (!closed()) close();
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel::enqueue(size_t *len, const void *data)
|
||||
{
|
||||
// TODO: Make this thread safe!
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
// TODO: Make this thread safe!
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
|
||||
if (!len || !*len)
|
||||
return j6_err_invalid_arg;
|
||||
if (!len || !*len)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
if (m_buffer.free_space() == 0)
|
||||
return j6_err_not_ready;
|
||||
if (m_buffer.free_space() == 0)
|
||||
return j6_err_not_ready;
|
||||
|
||||
void *buffer = nullptr;
|
||||
size_t avail = m_buffer.reserve(*len, &buffer);
|
||||
*len = *len > avail ? avail : *len;
|
||||
void *buffer = nullptr;
|
||||
size_t avail = m_buffer.reserve(*len, &buffer);
|
||||
*len = *len > avail ? avail : *len;
|
||||
|
||||
kutil::memcpy(buffer, data, *len);
|
||||
m_buffer.commit(*len);
|
||||
kutil::memcpy(buffer, data, *len);
|
||||
m_buffer.commit(*len);
|
||||
|
||||
assert_signal(j6_signal_channel_can_recv);
|
||||
if (m_buffer.free_space() == 0)
|
||||
deassert_signal(j6_signal_channel_can_send);
|
||||
assert_signal(j6_signal_channel_can_recv);
|
||||
if (m_buffer.free_space() == 0)
|
||||
deassert_signal(j6_signal_channel_can_send);
|
||||
|
||||
return j6_status_ok;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel::dequeue(size_t *len, void *data)
|
||||
{
|
||||
// TODO: Make this thread safe!
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
// TODO: Make this thread safe!
|
||||
if (closed())
|
||||
return j6_status_closed;
|
||||
|
||||
if (!len || !*len)
|
||||
return j6_err_invalid_arg;
|
||||
if (!len || !*len)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
if (m_buffer.size() == 0)
|
||||
return j6_err_not_ready;
|
||||
if (m_buffer.size() == 0)
|
||||
return j6_err_not_ready;
|
||||
|
||||
void *buffer = nullptr;
|
||||
size_t avail = m_buffer.get_block(&buffer);
|
||||
*len = *len > avail ? avail : *len;
|
||||
void *buffer = nullptr;
|
||||
size_t avail = m_buffer.get_block(&buffer);
|
||||
*len = *len > avail ? avail : *len;
|
||||
|
||||
kutil::memcpy(data, buffer, *len);
|
||||
m_buffer.consume(*len);
|
||||
kutil::memcpy(data, buffer, *len);
|
||||
m_buffer.consume(*len);
|
||||
|
||||
assert_signal(j6_signal_channel_can_send);
|
||||
if (m_buffer.size() == 0)
|
||||
deassert_signal(j6_signal_channel_can_recv);
|
||||
assert_signal(j6_signal_channel_can_send);
|
||||
if (m_buffer.size() == 0)
|
||||
deassert_signal(j6_signal_channel_can_recv);
|
||||
|
||||
return j6_status_ok;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
void
|
||||
channel::close()
|
||||
{
|
||||
kobject::close();
|
||||
g_kernel_buffers.return_section(m_data);
|
||||
kobject::close();
|
||||
g_kernel_buffers.return_section(m_data);
|
||||
}
|
||||
|
||||
void
|
||||
channel::on_no_handles()
|
||||
{
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -8,42 +8,42 @@
|
||||
|
||||
/// Channels are bi-directional means of sending messages
|
||||
class channel :
|
||||
public kobject
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
channel();
|
||||
virtual ~channel();
|
||||
channel();
|
||||
virtual ~channel();
|
||||
|
||||
static constexpr kobject::type type = kobject::type::channel;
|
||||
static constexpr kobject::type type = kobject::type::channel;
|
||||
|
||||
/// Check if the channel has space for a message to be sent
|
||||
inline bool can_send() const { return check_signal(j6_signal_channel_can_send); }
|
||||
/// Check if the channel has space for a message to be sent
|
||||
inline bool can_send() const { return check_signal(j6_signal_channel_can_send); }
|
||||
|
||||
/// Check if the channel has a message wiating already
|
||||
inline bool can_receive() const { return check_signal(j6_signal_channel_can_recv); }
|
||||
/// Check if the channel has a message wiating already
|
||||
inline bool can_receive() const { return check_signal(j6_signal_channel_can_recv); }
|
||||
|
||||
/// Put a message into the channel
|
||||
/// \arg len [in] Bytes in data buffer [out] number of bytes written
|
||||
/// \arg data Pointer to the message data
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t enqueue(size_t *len, const void *data);
|
||||
/// Put a message into the channel
|
||||
/// \arg len [in] Bytes in data buffer [out] number of bytes written
|
||||
/// \arg data Pointer to the message data
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t enqueue(size_t *len, const void *data);
|
||||
|
||||
/// Get a message from the channel, copied into a provided buffer
|
||||
/// \arg len On input, the size of the provided buffer. On output,
|
||||
/// the size of the message copied into the buffer.
|
||||
/// \arg data Pointer to the buffer
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t dequeue(size_t *len, void *data);
|
||||
/// Get a message from the channel, copied into a provided buffer
|
||||
/// \arg len On input, the size of the provided buffer. On output,
|
||||
/// the size of the message copied into the buffer.
|
||||
/// \arg data Pointer to the buffer
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t dequeue(size_t *len, void *data);
|
||||
|
||||
/// Mark this channel as closed, all future calls to enqueue or
|
||||
/// dequeue messages will fail with j6_status_closed.
|
||||
virtual void close() override;
|
||||
/// Mark this channel as closed, all future calls to enqueue or
|
||||
/// dequeue messages will fail with j6_status_closed.
|
||||
virtual void close() override;
|
||||
|
||||
protected:
|
||||
virtual void on_no_handles() override;
|
||||
virtual void on_no_handles() override;
|
||||
|
||||
private:
|
||||
size_t m_len;
|
||||
uintptr_t m_data;
|
||||
kutil::bip_buffer m_buffer;
|
||||
size_t m_len;
|
||||
uintptr_t m_data;
|
||||
kutil::bip_buffer m_buffer;
|
||||
};
|
||||
|
||||
@@ -5,128 +5,128 @@
|
||||
#include "vm_space.h"
|
||||
|
||||
endpoint::endpoint() :
|
||||
kobject {kobject::type::endpoint}
|
||||
kobject {kobject::type::endpoint}
|
||||
{}
|
||||
|
||||
endpoint::~endpoint()
|
||||
{
|
||||
if (!check_signal(j6_signal_closed))
|
||||
close();
|
||||
if (!check_signal(j6_signal_closed))
|
||||
close();
|
||||
}
|
||||
|
||||
void
|
||||
endpoint::close()
|
||||
{
|
||||
kobject::close();
|
||||
kobject::close();
|
||||
|
||||
for (auto &data : m_blocked) {
|
||||
if (data.th)
|
||||
data.th->wake_on_result(this, j6_status_closed);
|
||||
}
|
||||
for (auto &data : m_blocked) {
|
||||
if (data.th)
|
||||
data.th->wake_on_result(this, j6_status_closed);
|
||||
}
|
||||
|
||||
device_manager::get().unbind_irqs(this);
|
||||
device_manager::get().unbind_irqs(this);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint::send(j6_tag_t tag, size_t len, void *data)
|
||||
{
|
||||
thread_data sender = { &thread::current(), data };
|
||||
sender.len = len;
|
||||
sender.tag = tag;
|
||||
thread_data sender = { &thread::current(), data };
|
||||
sender.len = len;
|
||||
sender.tag = tag;
|
||||
|
||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||
assert_signal(j6_signal_endpoint_can_recv);
|
||||
m_blocked.append(sender);
|
||||
sender.th->wait_on_object(this);
|
||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||
assert_signal(j6_signal_endpoint_can_recv);
|
||||
m_blocked.append(sender);
|
||||
sender.th->wait_on_object(this);
|
||||
|
||||
// we woke up having already finished the send
|
||||
// because it happened in the receiver
|
||||
return sender.th->get_wait_result();
|
||||
}
|
||||
// we woke up having already finished the send
|
||||
// because it happened in the receiver
|
||||
return sender.th->get_wait_result();
|
||||
}
|
||||
|
||||
thread_data receiver = m_blocked.pop_front();
|
||||
if (m_blocked.count() == 0)
|
||||
deassert_signal(j6_signal_endpoint_can_send);
|
||||
thread_data receiver = m_blocked.pop_front();
|
||||
if (m_blocked.count() == 0)
|
||||
deassert_signal(j6_signal_endpoint_can_send);
|
||||
|
||||
j6_status_t status = do_message_copy(sender, receiver);
|
||||
j6_status_t status = do_message_copy(sender, receiver);
|
||||
|
||||
receiver.th->wake_on_result(this, status);
|
||||
return status;
|
||||
receiver.th->wake_on_result(this, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint::receive(j6_tag_t *tag, size_t *len, void *data)
|
||||
{
|
||||
thread_data receiver = { &thread::current(), data };
|
||||
receiver.tag_p = tag;
|
||||
receiver.len_p = len;
|
||||
thread_data receiver = { &thread::current(), data };
|
||||
receiver.tag_p = tag;
|
||||
receiver.len_p = len;
|
||||
|
||||
if (!check_signal(j6_signal_endpoint_can_recv)) {
|
||||
assert_signal(j6_signal_endpoint_can_send);
|
||||
m_blocked.append(receiver);
|
||||
receiver.th->wait_on_object(this);
|
||||
if (!check_signal(j6_signal_endpoint_can_recv)) {
|
||||
assert_signal(j6_signal_endpoint_can_send);
|
||||
m_blocked.append(receiver);
|
||||
receiver.th->wait_on_object(this);
|
||||
|
||||
// we woke up having already finished the recv
|
||||
// because it happened in the sender
|
||||
return receiver.th->get_wait_result();
|
||||
}
|
||||
// we woke up having already finished the recv
|
||||
// because it happened in the sender
|
||||
return receiver.th->get_wait_result();
|
||||
}
|
||||
|
||||
thread_data sender = m_blocked.pop_front();
|
||||
if (m_blocked.count() == 0)
|
||||
deassert_signal(j6_signal_endpoint_can_recv);
|
||||
thread_data sender = m_blocked.pop_front();
|
||||
if (m_blocked.count() == 0)
|
||||
deassert_signal(j6_signal_endpoint_can_recv);
|
||||
|
||||
// TODO: don't pop sender on some errors
|
||||
j6_status_t status = do_message_copy(sender, receiver);
|
||||
// TODO: don't pop sender on some errors
|
||||
j6_status_t status = do_message_copy(sender, receiver);
|
||||
|
||||
if (sender.th)
|
||||
sender.th->wake_on_result(this, status);
|
||||
if (sender.th)
|
||||
sender.th->wake_on_result(this, status);
|
||||
|
||||
return status;
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
endpoint::signal_irq(unsigned irq)
|
||||
{
|
||||
j6_tag_t tag = j6_tag_from_irq(irq);
|
||||
j6_tag_t tag = j6_tag_from_irq(irq);
|
||||
|
||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||
assert_signal(j6_signal_endpoint_can_recv);
|
||||
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||
assert_signal(j6_signal_endpoint_can_recv);
|
||||
|
||||
for (auto &blocked : m_blocked)
|
||||
if (blocked.tag == tag)
|
||||
return;
|
||||
for (auto &blocked : m_blocked)
|
||||
if (blocked.tag == tag)
|
||||
return;
|
||||
|
||||
thread_data sender = { nullptr, nullptr };
|
||||
sender.tag = tag;
|
||||
m_blocked.append(sender);
|
||||
return;
|
||||
}
|
||||
thread_data sender = { nullptr, nullptr };
|
||||
sender.tag = tag;
|
||||
m_blocked.append(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
thread_data receiver = m_blocked.pop_front();
|
||||
if (m_blocked.count() == 0)
|
||||
deassert_signal(j6_signal_endpoint_can_send);
|
||||
thread_data receiver = m_blocked.pop_front();
|
||||
if (m_blocked.count() == 0)
|
||||
deassert_signal(j6_signal_endpoint_can_send);
|
||||
|
||||
*receiver.len_p = 0;
|
||||
*receiver.tag_p = tag;
|
||||
receiver.th->wake_on_result(this, j6_status_ok);
|
||||
*receiver.len_p = 0;
|
||||
*receiver.tag_p = tag;
|
||||
receiver.th->wake_on_result(this, j6_status_ok);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver)
|
||||
{
|
||||
if (sender.len > *receiver.len_p)
|
||||
return j6_err_insufficient;
|
||||
if (sender.len > *receiver.len_p)
|
||||
return j6_err_insufficient;
|
||||
|
||||
if (sender.len) {
|
||||
vm_space &source = sender.th->parent().space();
|
||||
vm_space &dest = receiver.th->parent().space();
|
||||
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
||||
}
|
||||
if (sender.len) {
|
||||
vm_space &source = sender.th->parent().space();
|
||||
vm_space &dest = receiver.th->parent().space();
|
||||
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
||||
}
|
||||
|
||||
*receiver.len_p = sender.len;
|
||||
*receiver.tag_p = sender.tag;
|
||||
*receiver.len_p = sender.len;
|
||||
*receiver.tag_p = sender.tag;
|
||||
|
||||
// TODO: this will not work if non-contiguous pages are mapped!!
|
||||
// TODO: this will not work if non-contiguous pages are mapped!!
|
||||
|
||||
return j6_status_ok;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -7,59 +7,59 @@
|
||||
|
||||
/// Endpoints are objects that enable synchronous message-passing IPC
|
||||
class endpoint :
|
||||
public kobject
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
endpoint();
|
||||
virtual ~endpoint();
|
||||
endpoint();
|
||||
virtual ~endpoint();
|
||||
|
||||
static constexpr kobject::type type = kobject::type::endpoint;
|
||||
static constexpr kobject::type type = kobject::type::endpoint;
|
||||
|
||||
/// Close the endpoint, waking all waiting processes with an error
|
||||
virtual void close() override;
|
||||
/// Close the endpoint, waking all waiting processes with an error
|
||||
virtual void close() override;
|
||||
|
||||
/// Check if the endpoint has space for a message to be sent
|
||||
inline bool can_send() const { return check_signal(j6_signal_endpoint_can_send); }
|
||||
/// Check if the endpoint has space for a message to be sent
|
||||
inline bool can_send() const { return check_signal(j6_signal_endpoint_can_send); }
|
||||
|
||||
/// Check if the endpoint has a message wiating already
|
||||
inline bool can_receive() const { return check_signal(j6_signal_endpoint_can_recv); }
|
||||
/// Check if the endpoint has a message wiating already
|
||||
inline bool can_receive() const { return check_signal(j6_signal_endpoint_can_recv); }
|
||||
|
||||
/// Send a message to a thread waiting to receive on this endpoint. If no threads
|
||||
/// are currently trying to receive, block the current thread.
|
||||
/// \arg tag The application-specified message tag
|
||||
/// \arg len The size in bytes of the message
|
||||
/// \arg data The message data
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t send(j6_tag_t tag, size_t len, void *data);
|
||||
/// Send a message to a thread waiting to receive on this endpoint. If no threads
|
||||
/// are currently trying to receive, block the current thread.
|
||||
/// \arg tag The application-specified message tag
|
||||
/// \arg len The size in bytes of the message
|
||||
/// \arg data The message data
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t send(j6_tag_t tag, size_t len, void *data);
|
||||
|
||||
/// Receive a message from a thread waiting to send on this endpoint. If no threads
|
||||
/// are currently trying to send, block the current thread.
|
||||
/// \arg tag [in] The sender-specified message tag
|
||||
/// \arg len [in] The size in bytes of the buffer [out] Number of bytes in the message
|
||||
/// \arg data Buffer for copying message data into
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t receive(j6_tag_t *tag, size_t *len, void *data);
|
||||
/// Receive a message from a thread waiting to send on this endpoint. If no threads
|
||||
/// are currently trying to send, block the current thread.
|
||||
/// \arg tag [in] The sender-specified message tag
|
||||
/// \arg len [in] The size in bytes of the buffer [out] Number of bytes in the message
|
||||
/// \arg data Buffer for copying message data into
|
||||
/// \returns j6_status_ok on success
|
||||
j6_status_t receive(j6_tag_t *tag, size_t *len, void *data);
|
||||
|
||||
/// Give the listener on the endpoint a message that a bound IRQ has been signalled
|
||||
/// \arg irq The IRQ that caused this signal
|
||||
void signal_irq(unsigned irq);
|
||||
/// Give the listener on the endpoint a message that a bound IRQ has been signalled
|
||||
/// \arg irq The IRQ that caused this signal
|
||||
void signal_irq(unsigned irq);
|
||||
|
||||
private:
|
||||
struct thread_data
|
||||
{
|
||||
thread *th;
|
||||
void *data;
|
||||
union {
|
||||
j6_tag_t *tag_p;
|
||||
j6_tag_t tag;
|
||||
};
|
||||
union {
|
||||
size_t *len_p;
|
||||
size_t len;
|
||||
};
|
||||
};
|
||||
struct thread_data
|
||||
{
|
||||
thread *th;
|
||||
void *data;
|
||||
union {
|
||||
j6_tag_t *tag_p;
|
||||
j6_tag_t tag;
|
||||
};
|
||||
union {
|
||||
size_t *len_p;
|
||||
size_t len;
|
||||
};
|
||||
};
|
||||
|
||||
j6_status_t do_message_copy(const thread_data &sender, thread_data &receiver);
|
||||
j6_status_t do_message_copy(const thread_data &sender, thread_data &receiver);
|
||||
|
||||
kutil::vector<thread_data> m_blocked;
|
||||
kutil::vector<thread_data> m_blocked;
|
||||
};
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#include "objects/kobject.h"
|
||||
|
||||
class event :
|
||||
public kobject
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
event() :
|
||||
kobject(type::event) {}
|
||||
event() :
|
||||
kobject(type::event) {}
|
||||
|
||||
static constexpr kobject::type type = kobject::type::event;
|
||||
static constexpr kobject::type type = kobject::type::event;
|
||||
};
|
||||
|
||||
@@ -8,67 +8,67 @@
|
||||
static j6_koid_t next_koids [static_cast<size_t>(kobject::type::max)] = { 0 };
|
||||
|
||||
kobject::kobject(type t, j6_signal_t signals) :
|
||||
m_koid(koid_generate(t)),
|
||||
m_signals(signals),
|
||||
m_handle_count(0)
|
||||
m_koid(koid_generate(t)),
|
||||
m_signals(signals),
|
||||
m_handle_count(0)
|
||||
{}
|
||||
|
||||
kobject::~kobject()
|
||||
{
|
||||
for (auto *t : m_blocked_threads)
|
||||
t->wake_on_result(this, j6_status_destroyed);
|
||||
for (auto *t : m_blocked_threads)
|
||||
t->wake_on_result(this, j6_status_destroyed);
|
||||
}
|
||||
|
||||
j6_koid_t
|
||||
kobject::koid_generate(type t)
|
||||
{
|
||||
kassert(t < type::max, "Object type out of bounds");
|
||||
uint64_t type_int = static_cast<uint64_t>(t);
|
||||
return (type_int << 48) | next_koids[type_int]++;
|
||||
kassert(t < type::max, "Object type out of bounds");
|
||||
uint64_t type_int = static_cast<uint64_t>(t);
|
||||
return (type_int << 48) | next_koids[type_int]++;
|
||||
}
|
||||
|
||||
kobject::type
|
||||
kobject::koid_type(j6_koid_t koid)
|
||||
{
|
||||
return static_cast<type>((koid >> 48) & 0xffffull);
|
||||
return static_cast<type>((koid >> 48) & 0xffffull);
|
||||
}
|
||||
|
||||
void
|
||||
kobject::assert_signal(j6_signal_t s)
|
||||
{
|
||||
m_signals |= s;
|
||||
notify_signal_observers();
|
||||
m_signals |= s;
|
||||
notify_signal_observers();
|
||||
}
|
||||
|
||||
void
|
||||
kobject::deassert_signal(j6_signal_t s)
|
||||
{
|
||||
m_signals &= ~s;
|
||||
m_signals &= ~s;
|
||||
}
|
||||
|
||||
void
|
||||
kobject::notify_signal_observers()
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < m_blocked_threads.count()) {
|
||||
thread *t = m_blocked_threads[i];
|
||||
size_t i = 0;
|
||||
while (i < m_blocked_threads.count()) {
|
||||
thread *t = m_blocked_threads[i];
|
||||
|
||||
if (t->wake_on_signals(this, m_signals)) {
|
||||
m_blocked_threads.remove_swap_at(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (t->wake_on_signals(this, m_signals)) {
|
||||
m_blocked_threads.remove_swap_at(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kobject::close()
|
||||
{
|
||||
assert_signal(j6_signal_closed);
|
||||
assert_signal(j6_signal_closed);
|
||||
}
|
||||
|
||||
void
|
||||
kobject::on_no_handles()
|
||||
{
|
||||
assert_signal(j6_signal_no_handles);
|
||||
assert_signal(j6_signal_no_handles);
|
||||
}
|
||||
|
||||
@@ -13,88 +13,88 @@ class thread;
|
||||
class kobject
|
||||
{
|
||||
public:
|
||||
/// Types of kernel objects.
|
||||
enum class type : uint16_t
|
||||
{
|
||||
/// Types of kernel objects.
|
||||
enum class type : uint16_t
|
||||
{
|
||||
#define OBJECT_TYPE( name, val ) name = val,
|
||||
#include "j6/tables/object_types.inc"
|
||||
#undef OBJECT_TYPE
|
||||
|
||||
max
|
||||
};
|
||||
max
|
||||
};
|
||||
|
||||
kobject(type t, j6_signal_t signals = 0ull);
|
||||
virtual ~kobject();
|
||||
kobject(type t, j6_signal_t signals = 0ull);
|
||||
virtual ~kobject();
|
||||
|
||||
/// Generate a new koid for a given type
|
||||
/// \arg t The object type
|
||||
/// \returns A new unique koid
|
||||
static j6_koid_t koid_generate(type t);
|
||||
/// Generate a new koid for a given type
|
||||
/// \arg t The object type
|
||||
/// \returns A new unique koid
|
||||
static j6_koid_t koid_generate(type t);
|
||||
|
||||
/// Get the kobject type from a given koid
|
||||
/// \arg koid An existing koid
|
||||
/// \returns The object type for the koid
|
||||
static type koid_type(j6_koid_t koid);
|
||||
/// Get the kobject type from a given koid
|
||||
/// \arg koid An existing koid
|
||||
/// \returns The object type for the koid
|
||||
static type koid_type(j6_koid_t koid);
|
||||
|
||||
/// Get this object's type
|
||||
inline type get_type() const { return koid_type(m_koid); }
|
||||
/// Get this object's type
|
||||
inline type get_type() const { return koid_type(m_koid); }
|
||||
|
||||
/// Get this object's koid
|
||||
inline j6_koid_t koid() const { return m_koid; }
|
||||
/// Get this object's koid
|
||||
inline j6_koid_t koid() const { return m_koid; }
|
||||
|
||||
/// Set the given signals active on this object
|
||||
/// \arg s The set of signals to assert
|
||||
void assert_signal(j6_signal_t s);
|
||||
/// Set the given signals active on this object
|
||||
/// \arg s The set of signals to assert
|
||||
void assert_signal(j6_signal_t s);
|
||||
|
||||
/// Clear the given signals on this object
|
||||
/// \arg s The set of signals to deassert
|
||||
void deassert_signal(j6_signal_t s);
|
||||
/// Clear the given signals on this object
|
||||
/// \arg s The set of signals to deassert
|
||||
void deassert_signal(j6_signal_t s);
|
||||
|
||||
/// Check if the given signals are set on this object
|
||||
/// \arg s The set of signals to check
|
||||
inline bool check_signal(j6_signal_t s) const { return (m_signals & s) == s; }
|
||||
/// Check if the given signals are set on this object
|
||||
/// \arg s The set of signals to check
|
||||
inline bool check_signal(j6_signal_t s) const { return (m_signals & s) == s; }
|
||||
|
||||
/// Get the current object signal state
|
||||
inline j6_signal_t signals() const { return m_signals; }
|
||||
/// Get the current object signal state
|
||||
inline j6_signal_t signals() const { return m_signals; }
|
||||
|
||||
/// Increment the handle refcount
|
||||
inline void handle_retain() { ++m_handle_count; }
|
||||
/// Increment the handle refcount
|
||||
inline void handle_retain() { ++m_handle_count; }
|
||||
|
||||
/// Decrement the handle refcount
|
||||
inline void handle_release() {
|
||||
if (--m_handle_count == 0) on_no_handles();
|
||||
}
|
||||
/// Decrement the handle refcount
|
||||
inline void handle_release() {
|
||||
if (--m_handle_count == 0) on_no_handles();
|
||||
}
|
||||
|
||||
/// Add the given thread to the list of threads waiting on this object.
|
||||
inline void add_blocked_thread(thread *t) { m_blocked_threads.append(t); }
|
||||
/// Add the given thread to the list of threads waiting on this object.
|
||||
inline void add_blocked_thread(thread *t) { m_blocked_threads.append(t); }
|
||||
|
||||
/// Remove the given thread from the list of threads waiting on this object.
|
||||
inline void remove_blocked_thread(thread *t) { m_blocked_threads.remove_swap(t); }
|
||||
/// Remove the given thread from the list of threads waiting on this object.
|
||||
inline void remove_blocked_thread(thread *t) { m_blocked_threads.remove_swap(t); }
|
||||
|
||||
/// Perform any cleanup actions necessary to mark this object closed
|
||||
virtual void close();
|
||||
/// Perform any cleanup actions necessary to mark this object closed
|
||||
virtual void close();
|
||||
|
||||
/// Check if this object has been closed
|
||||
inline bool closed() const { return check_signal(j6_signal_closed); }
|
||||
/// Check if this object has been closed
|
||||
inline bool closed() const { return check_signal(j6_signal_closed); }
|
||||
|
||||
protected:
|
||||
/// Interface for subclasses to handle when all handles are closed. Subclasses
|
||||
/// should either call the base version, or assert j6_signal_no_handles.
|
||||
virtual void on_no_handles();
|
||||
/// Interface for subclasses to handle when all handles are closed. Subclasses
|
||||
/// should either call the base version, or assert j6_signal_no_handles.
|
||||
virtual void on_no_handles();
|
||||
|
||||
private:
|
||||
kobject() = delete;
|
||||
kobject(const kobject &other) = delete;
|
||||
kobject(const kobject &&other) = delete;
|
||||
kobject() = delete;
|
||||
kobject(const kobject &other) = delete;
|
||||
kobject(const kobject &&other) = delete;
|
||||
|
||||
/// Notifiy observers of this object
|
||||
/// \arg result The result to pass to the observers
|
||||
void notify_signal_observers();
|
||||
/// Notifiy observers of this object
|
||||
/// \arg result The result to pass to the observers
|
||||
void notify_signal_observers();
|
||||
|
||||
j6_koid_t m_koid;
|
||||
j6_signal_t m_signals;
|
||||
uint16_t m_handle_count;
|
||||
j6_koid_t m_koid;
|
||||
j6_signal_t m_signals;
|
||||
uint16_t m_handle_count;
|
||||
|
||||
protected:
|
||||
kutil::vector<thread*> m_blocked_threads;
|
||||
kutil::vector<thread*> m_blocked_threads;
|
||||
};
|
||||
|
||||
@@ -14,27 +14,27 @@ process &g_kernel_process = __g_kernel_process_storage.value;
|
||||
|
||||
|
||||
process::process() :
|
||||
kobject {kobject::type::process},
|
||||
m_next_handle {1},
|
||||
m_state {state::running}
|
||||
kobject {kobject::type::process},
|
||||
m_next_handle {1},
|
||||
m_state {state::running}
|
||||
{
|
||||
j6_handle_t self = add_handle(this);
|
||||
kassert(self == self_handle(), "Process self-handle is not 1");
|
||||
j6_handle_t self = add_handle(this);
|
||||
kassert(self == self_handle(), "Process self-handle is not 1");
|
||||
}
|
||||
|
||||
// The "kernel process"-only constructor
|
||||
process::process(page_table *kpml4) :
|
||||
kobject {kobject::type::process},
|
||||
m_space {kpml4},
|
||||
m_next_handle {self_handle()+1},
|
||||
m_state {state::running}
|
||||
kobject {kobject::type::process},
|
||||
m_space {kpml4},
|
||||
m_next_handle {self_handle()+1},
|
||||
m_state {state::running}
|
||||
{
|
||||
}
|
||||
|
||||
process::~process()
|
||||
{
|
||||
for (auto &it : m_handles)
|
||||
if (it.val) it.val->handle_release();
|
||||
for (auto &it : m_handles)
|
||||
if (it.val) it.val->handle_release();
|
||||
}
|
||||
|
||||
process & process::current() { return *current_cpu().process; }
|
||||
@@ -43,115 +43,115 @@ process & process::kernel_process() { return g_kernel_process; }
|
||||
process *
|
||||
process::create_kernel_process(page_table *pml4)
|
||||
{
|
||||
return new (&g_kernel_process) process {pml4};
|
||||
return new (&g_kernel_process) process {pml4};
|
||||
}
|
||||
|
||||
void
|
||||
process::exit(int32_t code)
|
||||
{
|
||||
// TODO: make this thread-safe
|
||||
m_state = state::exited;
|
||||
m_return_code = code;
|
||||
close();
|
||||
// TODO: make this thread-safe
|
||||
m_state = state::exited;
|
||||
m_return_code = code;
|
||||
close();
|
||||
|
||||
for (auto *thread : m_threads) {
|
||||
thread->exit(code);
|
||||
}
|
||||
for (auto *thread : m_threads) {
|
||||
thread->exit(code);
|
||||
}
|
||||
|
||||
if (this == current_cpu().process)
|
||||
scheduler::get().schedule();
|
||||
if (this == current_cpu().process)
|
||||
scheduler::get().schedule();
|
||||
}
|
||||
|
||||
void
|
||||
process::update()
|
||||
{
|
||||
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
||||
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
||||
|
||||
size_t i = 0;
|
||||
uint32_t status = 0;
|
||||
while (i < m_threads.count()) {
|
||||
thread *th = m_threads[i];
|
||||
if (th->has_state(thread::state::exited)) {
|
||||
status = th->m_return_code;
|
||||
m_threads.remove_swap_at(i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
size_t i = 0;
|
||||
uint32_t status = 0;
|
||||
while (i < m_threads.count()) {
|
||||
thread *th = m_threads[i];
|
||||
if (th->has_state(thread::state::exited)) {
|
||||
status = th->m_return_code;
|
||||
m_threads.remove_swap_at(i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (m_threads.count() == 0) {
|
||||
// TODO: What really is the return code in this case?
|
||||
exit(status);
|
||||
}
|
||||
if (m_threads.count() == 0) {
|
||||
// TODO: What really is the return code in this case?
|
||||
exit(status);
|
||||
}
|
||||
}
|
||||
|
||||
thread *
|
||||
process::create_thread(uint8_t priority, bool user)
|
||||
{
|
||||
if (priority == default_priority)
|
||||
priority = scheduler::default_priority;
|
||||
if (priority == default_priority)
|
||||
priority = scheduler::default_priority;
|
||||
|
||||
thread *th = new thread(*this, priority);
|
||||
kassert(th, "Failed to create thread!");
|
||||
thread *th = new thread(*this, priority);
|
||||
kassert(th, "Failed to create thread!");
|
||||
|
||||
if (user) {
|
||||
uintptr_t stack_top = stacks_top - (m_threads.count() * stack_size);
|
||||
if (user) {
|
||||
uintptr_t stack_top = stacks_top - (m_threads.count() * stack_size);
|
||||
|
||||
vm_flags flags = vm_flags::zero|vm_flags::write;
|
||||
vm_area *vma = new vm_area_open(stack_size, flags);
|
||||
m_space.add(stack_top - stack_size, vma);
|
||||
vm_flags flags = vm_flags::zero|vm_flags::write;
|
||||
vm_area *vma = new vm_area_open(stack_size, flags);
|
||||
m_space.add(stack_top - stack_size, vma);
|
||||
|
||||
// Space for null frame - because the page gets zeroed on
|
||||
// allocation, just pointing rsp here does the trick
|
||||
th->tcb()->rsp3 = stack_top - 2 * sizeof(uint64_t);
|
||||
}
|
||||
// Space for null frame - because the page gets zeroed on
|
||||
// allocation, just pointing rsp here does the trick
|
||||
th->tcb()->rsp3 = stack_top - 2 * sizeof(uint64_t);
|
||||
}
|
||||
|
||||
m_threads.append(th);
|
||||
scheduler::get().add_thread(th->tcb());
|
||||
return th;
|
||||
m_threads.append(th);
|
||||
scheduler::get().add_thread(th->tcb());
|
||||
return th;
|
||||
}
|
||||
|
||||
bool
|
||||
process::thread_exited(thread *th)
|
||||
{
|
||||
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
||||
uint32_t status = th->m_return_code;
|
||||
m_threads.remove_swap(th);
|
||||
remove_handle(th->self_handle());
|
||||
delete th;
|
||||
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
||||
uint32_t status = th->m_return_code;
|
||||
m_threads.remove_swap(th);
|
||||
remove_handle(th->self_handle());
|
||||
delete th;
|
||||
|
||||
// TODO: delete the thread's stack VMA
|
||||
// TODO: delete the thread's stack VMA
|
||||
|
||||
if (m_threads.count() == 0) {
|
||||
exit(status);
|
||||
return true;
|
||||
}
|
||||
if (m_threads.count() == 0) {
|
||||
exit(status);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
j6_handle_t
|
||||
process::add_handle(kobject *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return j6_handle_invalid;
|
||||
if (!obj)
|
||||
return j6_handle_invalid;
|
||||
|
||||
obj->handle_retain();
|
||||
j6_handle_t handle = m_next_handle++;
|
||||
m_handles.insert(handle, obj);
|
||||
return handle;
|
||||
obj->handle_retain();
|
||||
j6_handle_t handle = m_next_handle++;
|
||||
m_handles.insert(handle, obj);
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool
|
||||
process::remove_handle(j6_handle_t handle)
|
||||
{
|
||||
kobject *obj = m_handles.find(handle);
|
||||
if (obj) obj->handle_release();
|
||||
return m_handles.erase(handle);
|
||||
kobject *obj = m_handles.find(handle);
|
||||
if (obj) obj->handle_release();
|
||||
return m_handles.erase(handle);
|
||||
}
|
||||
|
||||
kobject *
|
||||
process::lookup_handle(j6_handle_t handle)
|
||||
{
|
||||
return m_handles.find(handle);
|
||||
return m_handles.find(handle);
|
||||
}
|
||||
|
||||
@@ -9,89 +9,89 @@
|
||||
#include "vm_space.h"
|
||||
|
||||
class process :
|
||||
public kobject
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
/// Top of memory area where thread stacks are allocated
|
||||
constexpr static uintptr_t stacks_top = 0x0000800000000000;
|
||||
/// Top of memory area where thread stacks are allocated
|
||||
constexpr static uintptr_t stacks_top = 0x0000800000000000;
|
||||
|
||||
/// Size of userspace thread stacks
|
||||
constexpr static size_t stack_size = 0x4000000; // 64MiB
|
||||
/// Size of userspace thread stacks
|
||||
constexpr static size_t stack_size = 0x4000000; // 64MiB
|
||||
|
||||
/// Value that represents default priority
|
||||
constexpr static uint8_t default_priority = 0xff;
|
||||
/// Value that represents default priority
|
||||
constexpr static uint8_t default_priority = 0xff;
|
||||
|
||||
/// Constructor.
|
||||
process();
|
||||
/// Constructor.
|
||||
process();
|
||||
|
||||
/// Destructor.
|
||||
virtual ~process();
|
||||
/// Destructor.
|
||||
virtual ~process();
|
||||
|
||||
static constexpr kobject::type type = kobject::type::process;
|
||||
static constexpr kobject::type type = kobject::type::process;
|
||||
|
||||
/// Get the currently executing process.
|
||||
static process & current();
|
||||
/// Get the currently executing process.
|
||||
static process & current();
|
||||
|
||||
/// Terminate this process.
|
||||
/// \arg code The return code to exit with.
|
||||
void exit(int32_t code);
|
||||
/// Terminate this process.
|
||||
/// \arg code The return code to exit with.
|
||||
void exit(int32_t code);
|
||||
|
||||
/// Update internal bookkeeping about threads.
|
||||
void update();
|
||||
/// Update internal bookkeeping about threads.
|
||||
void update();
|
||||
|
||||
/// Get the process' virtual memory space
|
||||
vm_space & space() { return m_space; }
|
||||
/// Get the process' virtual memory space
|
||||
vm_space & space() { return m_space; }
|
||||
|
||||
/// Create a new thread in this process
|
||||
/// \args priority The new thread's scheduling priority
|
||||
/// \args user If true, create a userspace stack for this thread
|
||||
/// \returns The newly created thread object
|
||||
thread * create_thread(uint8_t priorty = default_priority, bool user = true);
|
||||
/// Create a new thread in this process
|
||||
/// \args priority The new thread's scheduling priority
|
||||
/// \args user If true, create a userspace stack for this thread
|
||||
/// \returns The newly created thread object
|
||||
thread * create_thread(uint8_t priorty = default_priority, bool user = true);
|
||||
|
||||
/// Start tracking an object with a handle.
|
||||
/// \args obj The object this handle refers to
|
||||
/// \returns The new handle for this object
|
||||
j6_handle_t add_handle(kobject *obj);
|
||||
/// Start tracking an object with a handle.
|
||||
/// \args obj The object this handle refers to
|
||||
/// \returns The new handle for this object
|
||||
j6_handle_t add_handle(kobject *obj);
|
||||
|
||||
/// Stop tracking an object with a handle.
|
||||
/// \args handle The handle that refers to the object
|
||||
/// \returns True if the handle was removed
|
||||
bool remove_handle(j6_handle_t handle);
|
||||
/// Stop tracking an object with a handle.
|
||||
/// \args handle The handle that refers to the object
|
||||
/// \returns True if the handle was removed
|
||||
bool remove_handle(j6_handle_t handle);
|
||||
|
||||
/// Lookup an object for a handle
|
||||
/// \args handle The handle to the object
|
||||
/// \returns Pointer to the object, or null if not found
|
||||
kobject * lookup_handle(j6_handle_t handle);
|
||||
/// Lookup an object for a handle
|
||||
/// \args handle The handle to the object
|
||||
/// \returns Pointer to the object, or null if not found
|
||||
kobject * lookup_handle(j6_handle_t handle);
|
||||
|
||||
/// Inform the process of an exited thread
|
||||
/// \args th The thread which has exited
|
||||
/// \returns True if this thread ending has ended the process
|
||||
bool thread_exited(thread *th);
|
||||
/// Inform the process of an exited thread
|
||||
/// \args th The thread which has exited
|
||||
/// \returns True if this thread ending has ended the process
|
||||
bool thread_exited(thread *th);
|
||||
|
||||
/// Get the handle for this process to refer to itself
|
||||
inline j6_handle_t self_handle() const { return 1; }
|
||||
/// Get the handle for this process to refer to itself
|
||||
inline j6_handle_t self_handle() const { return 1; }
|
||||
|
||||
/// Get the process object that owns kernel threads and the
|
||||
/// kernel address space
|
||||
static process & kernel_process();
|
||||
/// Get the process object that owns kernel threads and the
|
||||
/// kernel address space
|
||||
static process & kernel_process();
|
||||
|
||||
/// Create the special kernel process that owns kernel tasks
|
||||
/// \arg pml4 The kernel-only pml4
|
||||
/// \returns The kernel process object
|
||||
static process * create_kernel_process(page_table *pml4);
|
||||
/// Create the special kernel process that owns kernel tasks
|
||||
/// \arg pml4 The kernel-only pml4
|
||||
/// \returns The kernel process object
|
||||
static process * create_kernel_process(page_table *pml4);
|
||||
|
||||
private:
|
||||
// This constructor is called by create_kernel_process
|
||||
process(page_table *kpml4);
|
||||
// This constructor is called by create_kernel_process
|
||||
process(page_table *kpml4);
|
||||
|
||||
int32_t m_return_code;
|
||||
int32_t m_return_code;
|
||||
|
||||
vm_space m_space;
|
||||
vm_space m_space;
|
||||
|
||||
kutil::vector<thread*> m_threads;
|
||||
kutil::map<j6_handle_t, kobject*> m_handles;
|
||||
j6_handle_t m_next_handle;
|
||||
kutil::vector<thread*> m_threads;
|
||||
kutil::map<j6_handle_t, kobject*> m_handles;
|
||||
j6_handle_t m_next_handle;
|
||||
|
||||
enum class state : uint8_t { running, exited };
|
||||
state m_state;
|
||||
enum class state : uint8_t { running, exited };
|
||||
state m_state;
|
||||
};
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
#include "objects/kobject.h"
|
||||
|
||||
class system :
|
||||
public kobject
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
static constexpr kobject::type type = kobject::type::event;
|
||||
static constexpr kobject::type type = kobject::type::event;
|
||||
|
||||
inline static system & get() { return s_instance; }
|
||||
inline static system & get() { return s_instance; }
|
||||
|
||||
private:
|
||||
static system s_instance;
|
||||
system() : kobject(type::system) {}
|
||||
static system s_instance;
|
||||
system() : kobject(type::system) {}
|
||||
};
|
||||
|
||||
@@ -12,35 +12,35 @@ static constexpr j6_signal_t thread_default_signals = 0;
|
||||
extern vm_area_guarded &g_kernel_stacks;
|
||||
|
||||
thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
||||
kobject(kobject::type::thread, thread_default_signals),
|
||||
m_parent(parent),
|
||||
m_state(state::loading),
|
||||
m_wait_type(wait_type::none),
|
||||
m_wait_data(0),
|
||||
m_wait_obj(0)
|
||||
kobject(kobject::type::thread, thread_default_signals),
|
||||
m_parent(parent),
|
||||
m_state(state::loading),
|
||||
m_wait_type(wait_type::none),
|
||||
m_wait_data(0),
|
||||
m_wait_obj(0)
|
||||
{
|
||||
parent.space().initialize_tcb(m_tcb);
|
||||
m_tcb.priority = pri;
|
||||
parent.space().initialize_tcb(m_tcb);
|
||||
m_tcb.priority = pri;
|
||||
|
||||
if (!rsp0)
|
||||
setup_kernel_stack();
|
||||
else
|
||||
m_tcb.rsp0 = rsp0;
|
||||
if (!rsp0)
|
||||
setup_kernel_stack();
|
||||
else
|
||||
m_tcb.rsp0 = rsp0;
|
||||
|
||||
m_self_handle = parent.add_handle(this);
|
||||
m_self_handle = parent.add_handle(this);
|
||||
}
|
||||
|
||||
thread::~thread()
|
||||
{
|
||||
g_kernel_stacks.return_section(m_tcb.kernel_stack);
|
||||
g_kernel_stacks.return_section(m_tcb.kernel_stack);
|
||||
}
|
||||
|
||||
thread *
|
||||
thread::from_tcb(TCB *tcb)
|
||||
{
|
||||
static ptrdiff_t offset =
|
||||
-1 * static_cast<ptrdiff_t>(offsetof(thread, m_tcb));
|
||||
return reinterpret_cast<thread*>(kutil::offset_pointer(tcb, offset));
|
||||
static ptrdiff_t offset =
|
||||
-1 * static_cast<ptrdiff_t>(offsetof(thread, m_tcb));
|
||||
return reinterpret_cast<thread*>(kutil::offset_pointer(tcb, offset));
|
||||
}
|
||||
|
||||
thread & thread::current() { return *current_cpu().thread; }
|
||||
@@ -50,176 +50,176 @@ inline void schedule_if_current(thread *t) { if (t == current_cpu().thread) sche
|
||||
void
|
||||
thread::wait_on_signals(j6_signal_t signals)
|
||||
{
|
||||
m_wait_type = wait_type::signal;
|
||||
m_wait_data = signals;
|
||||
clear_state(state::ready);
|
||||
m_wait_type = wait_type::signal;
|
||||
m_wait_data = signals;
|
||||
clear_state(state::ready);
|
||||
|
||||
schedule_if_current(this);
|
||||
schedule_if_current(this);
|
||||
}
|
||||
|
||||
void
|
||||
thread::wait_on_time(uint64_t t)
|
||||
{
|
||||
m_wait_type = wait_type::time;
|
||||
m_wait_data = t;
|
||||
clear_state(state::ready);
|
||||
m_wait_type = wait_type::time;
|
||||
m_wait_data = t;
|
||||
clear_state(state::ready);
|
||||
|
||||
schedule_if_current(this);
|
||||
schedule_if_current(this);
|
||||
}
|
||||
|
||||
void
|
||||
thread::wait_on_object(kobject *o)
|
||||
{
|
||||
m_wait_type = wait_type::object;
|
||||
m_wait_data = reinterpret_cast<uint64_t>(o);
|
||||
clear_state(state::ready);
|
||||
m_wait_type = wait_type::object;
|
||||
m_wait_data = reinterpret_cast<uint64_t>(o);
|
||||
clear_state(state::ready);
|
||||
|
||||
schedule_if_current(this);
|
||||
schedule_if_current(this);
|
||||
}
|
||||
|
||||
bool
|
||||
thread::wake_on_signals(kobject *obj, j6_signal_t signals)
|
||||
{
|
||||
if (m_wait_type != wait_type::signal ||
|
||||
(signals & m_wait_data) == 0)
|
||||
return false;
|
||||
if (m_wait_type != wait_type::signal ||
|
||||
(signals & m_wait_data) == 0)
|
||||
return false;
|
||||
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = j6_status_ok;
|
||||
m_wait_data = signals;
|
||||
m_wait_obj = obj->koid();
|
||||
set_state(state::ready);
|
||||
return true;
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = j6_status_ok;
|
||||
m_wait_data = signals;
|
||||
m_wait_obj = obj->koid();
|
||||
set_state(state::ready);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
thread::wake_on_time(uint64_t now)
|
||||
{
|
||||
if (m_wait_type != wait_type::time ||
|
||||
now < m_wait_data)
|
||||
return false;
|
||||
if (m_wait_type != wait_type::time ||
|
||||
now < m_wait_data)
|
||||
return false;
|
||||
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = j6_status_ok;
|
||||
m_wait_data = now;
|
||||
m_wait_obj = 0;
|
||||
set_state(state::ready);
|
||||
return true;
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = j6_status_ok;
|
||||
m_wait_data = now;
|
||||
m_wait_obj = 0;
|
||||
set_state(state::ready);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
thread::wake_on_object(kobject *o)
|
||||
{
|
||||
if (m_wait_type != wait_type::object ||
|
||||
reinterpret_cast<uint64_t>(o) != m_wait_data)
|
||||
return false;
|
||||
if (m_wait_type != wait_type::object ||
|
||||
reinterpret_cast<uint64_t>(o) != m_wait_data)
|
||||
return false;
|
||||
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = j6_status_ok;
|
||||
m_wait_obj = o->koid();
|
||||
set_state(state::ready);
|
||||
return true;
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = j6_status_ok;
|
||||
m_wait_obj = o->koid();
|
||||
set_state(state::ready);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
thread::wake_on_result(kobject *obj, j6_status_t result)
|
||||
{
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = result;
|
||||
m_wait_data = 0;
|
||||
m_wait_obj = obj->koid();
|
||||
set_state(state::ready);
|
||||
m_wait_type = wait_type::none;
|
||||
m_wait_result = result;
|
||||
m_wait_data = 0;
|
||||
m_wait_obj = obj->koid();
|
||||
set_state(state::ready);
|
||||
}
|
||||
|
||||
void
|
||||
thread::exit(int32_t code)
|
||||
{
|
||||
m_return_code = code;
|
||||
set_state(state::exited);
|
||||
clear_state(state::ready);
|
||||
close();
|
||||
m_return_code = code;
|
||||
set_state(state::exited);
|
||||
clear_state(state::ready);
|
||||
close();
|
||||
|
||||
schedule_if_current(this);
|
||||
schedule_if_current(this);
|
||||
}
|
||||
|
||||
void
|
||||
thread::add_thunk_kernel(uintptr_t rip)
|
||||
{
|
||||
// This adds just enough values to the top of the
|
||||
// kernel stack to come out of task_switch correctly
|
||||
// and start executing at rip (still in kernel mode)
|
||||
// This adds just enough values to the top of the
|
||||
// kernel stack to come out of task_switch correctly
|
||||
// and start executing at rip (still in kernel mode)
|
||||
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||
|
||||
stack[6] = rip; // return rip
|
||||
stack[5] = m_tcb.rsp0; // rbp
|
||||
stack[4] = 0xbbbbbbbb; // rbx
|
||||
stack[3] = 0x12121212; // r12
|
||||
stack[2] = 0x13131313; // r13
|
||||
stack[1] = 0x14141414; // r14
|
||||
stack[0] = 0x15151515; // r15
|
||||
stack[6] = rip; // return rip
|
||||
stack[5] = m_tcb.rsp0; // rbp
|
||||
stack[4] = 0xbbbbbbbb; // rbx
|
||||
stack[3] = 0x12121212; // r12
|
||||
stack[2] = 0x13131313; // r13
|
||||
stack[1] = 0x14141414; // r14
|
||||
stack[0] = 0x15151515; // r15
|
||||
}
|
||||
|
||||
void
|
||||
thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
|
||||
{
|
||||
// This sets up the stack to:
|
||||
// a) come out of task_switch and return to rip0 (default is the
|
||||
// kernel/user trampoline) (via add_thunk_kernel) - if this is
|
||||
// changed, it needs to end up at the trampoline with the stack
|
||||
// as it was
|
||||
// b) come out of the kernel/user trampoline and start executing
|
||||
// in user mode at rip
|
||||
// This sets up the stack to:
|
||||
// a) come out of task_switch and return to rip0 (default is the
|
||||
// kernel/user trampoline) (via add_thunk_kernel) - if this is
|
||||
// changed, it needs to end up at the trampoline with the stack
|
||||
// as it was
|
||||
// b) come out of the kernel/user trampoline and start executing
|
||||
// in user mode at rip
|
||||
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 8;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||
flags |= 0x200;
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 8;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||
flags |= 0x200;
|
||||
|
||||
stack[7] = rip3; // return rip in rcx
|
||||
stack[6] = m_tcb.rsp3; // rbp
|
||||
stack[5] = 0xbbbbbbbb; // rbx
|
||||
stack[4] = flags; // r11 sets RFLAGS
|
||||
stack[3] = 0x12121212; // r12
|
||||
stack[2] = 0x13131313; // r13
|
||||
stack[1] = 0x14141414; // r14
|
||||
stack[0] = 0x15151515; // r15
|
||||
stack[7] = rip3; // return rip in rcx
|
||||
stack[6] = m_tcb.rsp3; // rbp
|
||||
stack[5] = 0xbbbbbbbb; // rbx
|
||||
stack[4] = flags; // r11 sets RFLAGS
|
||||
stack[3] = 0x12121212; // r12
|
||||
stack[2] = 0x13131313; // r13
|
||||
stack[1] = 0x14141414; // r14
|
||||
stack[0] = 0x15151515; // r15
|
||||
|
||||
static const uintptr_t trampoline =
|
||||
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
||||
add_thunk_kernel(rip0 ? rip0 : trampoline);
|
||||
static const uintptr_t trampoline =
|
||||
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
||||
add_thunk_kernel(rip0 ? rip0 : trampoline);
|
||||
}
|
||||
|
||||
void
|
||||
thread::setup_kernel_stack()
|
||||
{
|
||||
using memory::frame_size;
|
||||
using memory::kernel_stack_pages;
|
||||
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||
using memory::frame_size;
|
||||
using memory::kernel_stack_pages;
|
||||
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||
|
||||
constexpr unsigned null_frame_entries = 2;
|
||||
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||
constexpr unsigned null_frame_entries = 2;
|
||||
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||
|
||||
uintptr_t stack_addr = g_kernel_stacks.get_section();
|
||||
uintptr_t stack_end = stack_addr + stack_bytes;
|
||||
uintptr_t stack_addr = g_kernel_stacks.get_section();
|
||||
uintptr_t stack_end = stack_addr + stack_bytes;
|
||||
|
||||
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_end - null_frame_size);
|
||||
for (unsigned i = 0; i < null_frame_entries; ++i)
|
||||
null_frame[i] = 0;
|
||||
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_end - null_frame_size);
|
||||
for (unsigned i = 0; i < null_frame_entries; ++i)
|
||||
null_frame[i] = 0;
|
||||
|
||||
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
||||
stack_addr, stack_bytes);
|
||||
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
||||
stack_addr, stack_bytes);
|
||||
|
||||
m_tcb.kernel_stack = stack_addr;
|
||||
m_tcb.rsp0 = reinterpret_cast<uintptr_t>(null_frame);
|
||||
m_tcb.rsp = m_tcb.rsp0;
|
||||
m_tcb.kernel_stack = stack_addr;
|
||||
m_tcb.rsp0 = reinterpret_cast<uintptr_t>(null_frame);
|
||||
m_tcb.rsp = m_tcb.rsp0;
|
||||
}
|
||||
|
||||
thread *
|
||||
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0)
|
||||
{
|
||||
thread *idle = new thread(kernel, pri, rsp0);
|
||||
idle->set_state(state::constant);
|
||||
idle->set_state(state::ready);
|
||||
return idle;
|
||||
thread *idle = new thread(kernel, pri, rsp0);
|
||||
idle->set_state(state::constant);
|
||||
idle->set_state(state::ready);
|
||||
return idle;
|
||||
}
|
||||
|
||||
@@ -10,179 +10,179 @@ class process;
|
||||
|
||||
struct TCB
|
||||
{
|
||||
// Data used by assembly task control routines. If you change any of these,
|
||||
// be sure to change the assembly definitions in 'tasking.inc'
|
||||
uintptr_t rsp;
|
||||
uintptr_t rsp0;
|
||||
uintptr_t rsp3;
|
||||
uintptr_t pml4;
|
||||
// Data used by assembly task control routines. If you change any of these,
|
||||
// be sure to change the assembly definitions in 'tasking.inc'
|
||||
uintptr_t rsp;
|
||||
uintptr_t rsp0;
|
||||
uintptr_t rsp3;
|
||||
uintptr_t pml4;
|
||||
|
||||
uint8_t priority;
|
||||
// note: 3 bytes padding
|
||||
uint8_t priority;
|
||||
// note: 3 bytes padding
|
||||
|
||||
// TODO: move state into TCB?
|
||||
// TODO: move state into TCB?
|
||||
|
||||
uintptr_t kernel_stack;
|
||||
uintptr_t kernel_stack;
|
||||
|
||||
uint32_t time_left;
|
||||
uint64_t last_ran;
|
||||
uint32_t time_left;
|
||||
uint64_t last_ran;
|
||||
};
|
||||
|
||||
using tcb_list = kutil::linked_list<TCB>;
|
||||
using tcb_node = tcb_list::item_type;
|
||||
|
||||
class thread :
|
||||
public kobject
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
enum class wait_type : uint8_t { none, signal, time, object };
|
||||
enum class state : uint8_t {
|
||||
ready = 0x01,
|
||||
loading = 0x02,
|
||||
exited = 0x04,
|
||||
constant = 0x80,
|
||||
none = 0x00
|
||||
};
|
||||
enum class wait_type : uint8_t { none, signal, time, object };
|
||||
enum class state : uint8_t {
|
||||
ready = 0x01,
|
||||
loading = 0x02,
|
||||
exited = 0x04,
|
||||
constant = 0x80,
|
||||
none = 0x00
|
||||
};
|
||||
|
||||
/// Get the pointer to the thread object containing this TCB
|
||||
static thread * from_tcb(TCB *tcb);
|
||||
/// Get the pointer to the thread object containing this TCB
|
||||
static thread * from_tcb(TCB *tcb);
|
||||
|
||||
/// Destructor
|
||||
virtual ~thread();
|
||||
/// Destructor
|
||||
virtual ~thread();
|
||||
|
||||
static constexpr kobject::type type = kobject::type::thread;
|
||||
static constexpr kobject::type type = kobject::type::thread;
|
||||
|
||||
/// Get the currently executing thread.
|
||||
static thread & current();
|
||||
/// Get the currently executing thread.
|
||||
static thread & current();
|
||||
|
||||
/// Get the `ready` state of the thread.
|
||||
/// \returns True if the thread is ready to execute.
|
||||
inline bool ready() const { return has_state(state::ready); }
|
||||
/// Get the `ready` state of the thread.
|
||||
/// \returns True if the thread is ready to execute.
|
||||
inline bool ready() const { return has_state(state::ready); }
|
||||
|
||||
/// Get the `loading` state of the thread.
|
||||
/// \returns True if the thread has not finished loading.
|
||||
inline bool loading() const { return has_state(state::loading); }
|
||||
/// Get the `loading` state of the thread.
|
||||
/// \returns True if the thread has not finished loading.
|
||||
inline bool loading() const { return has_state(state::loading); }
|
||||
|
||||
/// Get the `constant` state of the thread.
|
||||
/// \returns True if the thread has constant priority.
|
||||
inline bool constant() const { return has_state(state::constant); }
|
||||
/// Get the `constant` state of the thread.
|
||||
/// \returns True if the thread has constant priority.
|
||||
inline bool constant() const { return has_state(state::constant); }
|
||||
|
||||
/// Get the thread priority.
|
||||
inline uint8_t priority() const { return m_tcb.priority; }
|
||||
/// Get the thread priority.
|
||||
inline uint8_t priority() const { return m_tcb.priority; }
|
||||
|
||||
/// Set the thread priority.
|
||||
/// \arg p The new thread priority
|
||||
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
||||
/// Set the thread priority.
|
||||
/// \arg p The new thread priority
|
||||
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
||||
|
||||
/// Block the thread, waiting an object's signals.
|
||||
/// \arg signals Mask of signals to wait for
|
||||
void wait_on_signals(j6_signal_t signals);
|
||||
/// Block the thread, waiting an object's signals.
|
||||
/// \arg signals Mask of signals to wait for
|
||||
void wait_on_signals(j6_signal_t signals);
|
||||
|
||||
/// Block the thread, waiting for a given clock value
|
||||
/// \arg t Clock value to wait for
|
||||
void wait_on_time(uint64_t t);
|
||||
/// Block the thread, waiting for a given clock value
|
||||
/// \arg t Clock value to wait for
|
||||
void wait_on_time(uint64_t t);
|
||||
|
||||
/// Block the thread, waiting on the given object
|
||||
/// \arg o The ojbect that should wake this thread
|
||||
void wait_on_object(kobject *o);
|
||||
/// Block the thread, waiting on the given object
|
||||
/// \arg o The ojbect that should wake this thread
|
||||
void wait_on_object(kobject *o);
|
||||
|
||||
/// Wake the thread if it is waiting on signals.
|
||||
/// \arg obj Object that changed signals
|
||||
/// \arg signals Signal state of the object
|
||||
/// \returns True if this action unblocked the thread
|
||||
bool wake_on_signals(kobject *obj, j6_signal_t signals);
|
||||
/// Wake the thread if it is waiting on signals.
|
||||
/// \arg obj Object that changed signals
|
||||
/// \arg signals Signal state of the object
|
||||
/// \returns True if this action unblocked the thread
|
||||
bool wake_on_signals(kobject *obj, j6_signal_t signals);
|
||||
|
||||
/// Wake the thread if it is waiting on the clock.
|
||||
/// \arg now Current clock value
|
||||
/// \returns True if this action unblocked the thread
|
||||
bool wake_on_time(uint64_t now);
|
||||
/// Wake the thread if it is waiting on the clock.
|
||||
/// \arg now Current clock value
|
||||
/// \returns True if this action unblocked the thread
|
||||
bool wake_on_time(uint64_t now);
|
||||
|
||||
/// Wake the thread if it is waiting on the given object.
|
||||
/// \arg o Object trying to wake the thread
|
||||
/// \returns True if this action unblocked the thread
|
||||
bool wake_on_object(kobject *o);
|
||||
/// Wake the thread if it is waiting on the given object.
|
||||
/// \arg o Object trying to wake the thread
|
||||
/// \returns True if this action unblocked the thread
|
||||
bool wake_on_object(kobject *o);
|
||||
|
||||
/// Wake the thread with a given result code.
|
||||
/// \arg obj Object that changed signals
|
||||
/// \arg result Result code to return to the thread
|
||||
void wake_on_result(kobject *obj, j6_status_t result);
|
||||
/// Wake the thread with a given result code.
|
||||
/// \arg obj Object that changed signals
|
||||
/// \arg result Result code to return to the thread
|
||||
void wake_on_result(kobject *obj, j6_status_t result);
|
||||
|
||||
/// Get the result status code from the last blocking operation
|
||||
j6_status_t get_wait_result() const { return m_wait_result; }
|
||||
/// Get the result status code from the last blocking operation
|
||||
j6_status_t get_wait_result() const { return m_wait_result; }
|
||||
|
||||
/// Get the current blocking opreation's wait data
|
||||
uint64_t get_wait_data() const { return m_wait_data; }
|
||||
/// Get the current blocking opreation's wait data
|
||||
uint64_t get_wait_data() const { return m_wait_data; }
|
||||
|
||||
/// Get the current blocking operation's wait ojbect (as a handle)
|
||||
j6_koid_t get_wait_object() const { return m_wait_obj; }
|
||||
j6_koid_t get_wait_object() const { return m_wait_obj; }
|
||||
|
||||
inline bool has_state(state s) const {
|
||||
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
||||
}
|
||||
inline bool has_state(state s) const {
|
||||
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
||||
}
|
||||
|
||||
inline void set_state(state s) {
|
||||
m_state = static_cast<state>(static_cast<uint8_t>(m_state) | static_cast<uint8_t>(s));
|
||||
}
|
||||
inline void set_state(state s) {
|
||||
m_state = static_cast<state>(static_cast<uint8_t>(m_state) | static_cast<uint8_t>(s));
|
||||
}
|
||||
|
||||
inline void clear_state(state s) {
|
||||
m_state = static_cast<state>(static_cast<uint8_t>(m_state) & ~static_cast<uint8_t>(s));
|
||||
}
|
||||
inline void clear_state(state s) {
|
||||
m_state = static_cast<state>(static_cast<uint8_t>(m_state) & ~static_cast<uint8_t>(s));
|
||||
}
|
||||
|
||||
inline tcb_node * tcb() { return &m_tcb; }
|
||||
inline process & parent() { return m_parent; }
|
||||
inline tcb_node * tcb() { return &m_tcb; }
|
||||
inline process & parent() { return m_parent; }
|
||||
|
||||
/// Terminate this thread.
|
||||
/// \arg code The return code to exit with.
|
||||
void exit(int32_t code);
|
||||
/// Terminate this thread.
|
||||
/// \arg code The return code to exit with.
|
||||
void exit(int32_t code);
|
||||
|
||||
/// Add a stack header that returns to the given address in kernel space.
|
||||
/// \arg rip The address to return to, must be kernel space
|
||||
void add_thunk_kernel(uintptr_t rip);
|
||||
/// Add a stack header that returns to the given address in kernel space.
|
||||
/// \arg rip The address to return to, must be kernel space
|
||||
void add_thunk_kernel(uintptr_t rip);
|
||||
|
||||
/// Add a stack header that returns to the given address in user space
|
||||
/// via a function in kernel space.
|
||||
/// \arg rip3 The user space address to return to
|
||||
/// \arg rip0 The kernel function to pass through, optional
|
||||
/// \arg flags Extra RFLAGS values to set, optional
|
||||
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0);
|
||||
/// Add a stack header that returns to the given address in user space
|
||||
/// via a function in kernel space.
|
||||
/// \arg rip3 The user space address to return to
|
||||
/// \arg rip0 The kernel function to pass through, optional
|
||||
/// \arg flags Extra RFLAGS values to set, optional
|
||||
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0);
|
||||
|
||||
/// Get the handle representing this thread to its process
|
||||
j6_handle_t self_handle() const { return m_self_handle; }
|
||||
/// Get the handle representing this thread to its process
|
||||
j6_handle_t self_handle() const { return m_self_handle; }
|
||||
|
||||
/// Create the kernel idle thread
|
||||
/// \arg kernel The process object that owns kernel tasks
|
||||
/// \arg pri The idle thread priority value
|
||||
/// \arg rsp The existing stack for the idle thread
|
||||
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
||||
/// Create the kernel idle thread
|
||||
/// \arg kernel The process object that owns kernel tasks
|
||||
/// \arg pri The idle thread priority value
|
||||
/// \arg rsp The existing stack for the idle thread
|
||||
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
||||
|
||||
private:
|
||||
thread() = delete;
|
||||
thread(const thread &other) = delete;
|
||||
thread(const thread &&other) = delete;
|
||||
friend class process;
|
||||
thread() = delete;
|
||||
thread(const thread &other) = delete;
|
||||
thread(const thread &&other) = delete;
|
||||
friend class process;
|
||||
|
||||
/// Constructor. Used when a kernel stack already exists.
|
||||
/// \arg parent The process which owns this thread
|
||||
/// \arg pri Initial priority level of this thread
|
||||
/// \arg rsp0 The existing kernel stack rsp, 0 for none
|
||||
thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0);
|
||||
/// Constructor. Used when a kernel stack already exists.
|
||||
/// \arg parent The process which owns this thread
|
||||
/// \arg pri Initial priority level of this thread
|
||||
/// \arg rsp0 The existing kernel stack rsp, 0 for none
|
||||
thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0);
|
||||
|
||||
/// Set up a new empty kernel stack for this thread.
|
||||
void setup_kernel_stack();
|
||||
/// Set up a new empty kernel stack for this thread.
|
||||
void setup_kernel_stack();
|
||||
|
||||
tcb_node m_tcb;
|
||||
tcb_node m_tcb;
|
||||
|
||||
process &m_parent;
|
||||
process &m_parent;
|
||||
|
||||
state m_state;
|
||||
wait_type m_wait_type;
|
||||
// There should be 1 byte of padding here
|
||||
state m_state;
|
||||
wait_type m_wait_type;
|
||||
// There should be 1 byte of padding here
|
||||
|
||||
int32_t m_return_code;
|
||||
int32_t m_return_code;
|
||||
|
||||
uint64_t m_wait_data;
|
||||
j6_status_t m_wait_result;
|
||||
j6_koid_t m_wait_obj;
|
||||
uint64_t m_wait_data;
|
||||
j6_status_t m_wait_result;
|
||||
j6_koid_t m_wait_obj;
|
||||
|
||||
j6_handle_t m_self_handle;
|
||||
j6_handle_t m_self_handle;
|
||||
};
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
using memory::frame_size;
|
||||
|
||||
vm_area::vm_area(size_t size, vm_flags flags) :
|
||||
m_size {size},
|
||||
m_flags {flags},
|
||||
m_spaces {m_vector_static, 0, static_size},
|
||||
kobject {kobject::type::vma}
|
||||
m_size {size},
|
||||
m_flags {flags},
|
||||
m_spaces {m_vector_static, 0, static_size},
|
||||
kobject {kobject::type::vma}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -19,50 +19,50 @@ vm_area::~vm_area() {}
|
||||
bool
|
||||
vm_area::add_to(vm_space *space)
|
||||
{
|
||||
for (auto *s : m_spaces) {
|
||||
if (s == space)
|
||||
return true;
|
||||
}
|
||||
m_spaces.append(space);
|
||||
return true;
|
||||
for (auto *s : m_spaces) {
|
||||
if (s == space)
|
||||
return true;
|
||||
}
|
||||
m_spaces.append(space);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area::remove_from(vm_space *space)
|
||||
{
|
||||
m_spaces.remove_swap(space);
|
||||
return
|
||||
!m_spaces.count() &&
|
||||
!(m_flags && vm_flags::mmio);
|
||||
m_spaces.remove_swap(space);
|
||||
return
|
||||
!m_spaces.count() &&
|
||||
!(m_flags && vm_flags::mmio);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::on_no_handles()
|
||||
{
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
}
|
||||
|
||||
size_t
|
||||
vm_area::resize(size_t size)
|
||||
{
|
||||
if (can_resize(size))
|
||||
m_size = size;
|
||||
return m_size;
|
||||
if (can_resize(size))
|
||||
m_size = size;
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area::can_resize(size_t size)
|
||||
{
|
||||
for (auto *space : m_spaces)
|
||||
if (!space->can_resize(*this, size))
|
||||
return false;
|
||||
return true;
|
||||
for (auto *space : m_spaces)
|
||||
if (!space->can_resize(*this, size))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
vm_area_fixed::vm_area_fixed(uintptr_t start, size_t size, vm_flags flags) :
|
||||
m_start {start},
|
||||
vm_area {size, flags}
|
||||
m_start {start},
|
||||
vm_area {size, flags}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,22 +70,22 @@ vm_area_fixed::~vm_area_fixed() {}
|
||||
|
||||
size_t vm_area_fixed::resize(size_t size)
|
||||
{
|
||||
// Not resizable
|
||||
return m_size;
|
||||
// Not resizable
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
{
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
|
||||
phys = m_start + offset;
|
||||
return true;
|
||||
phys = m_start + offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
vm_area_untracked::vm_area_untracked(size_t size, vm_flags flags) :
|
||||
vm_area {size, flags}
|
||||
vm_area {size, flags}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -94,24 +94,24 @@ vm_area_untracked::~vm_area_untracked() {}
|
||||
bool
|
||||
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
{
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
|
||||
return frame_allocator::get().allocate(1, &phys);
|
||||
return frame_allocator::get().allocate(1, &phys);
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_untracked::add_to(vm_space *space)
|
||||
{
|
||||
if (!m_spaces.count())
|
||||
return vm_area::add_to(space);
|
||||
return m_spaces[0] == space;
|
||||
if (!m_spaces.count())
|
||||
return vm_area::add_to(space);
|
||||
return m_spaces[0] == space;
|
||||
}
|
||||
|
||||
|
||||
vm_area_open::vm_area_open(size_t size, vm_flags flags) :
|
||||
m_mapped {nullptr},
|
||||
vm_area {size, flags}
|
||||
m_mapped {nullptr},
|
||||
vm_area {size, flags}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -120,15 +120,15 @@ vm_area_open::~vm_area_open() {}
|
||||
bool
|
||||
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
{
|
||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||
}
|
||||
|
||||
|
||||
vm_area_guarded::vm_area_guarded(uintptr_t start, size_t buf_pages, size_t size, vm_flags flags) :
|
||||
m_start {start},
|
||||
m_pages {buf_pages},
|
||||
m_next {memory::frame_size},
|
||||
vm_area_untracked {size, flags}
|
||||
m_start {start},
|
||||
m_pages {buf_pages},
|
||||
m_next {memory::frame_size},
|
||||
vm_area_untracked {size, flags}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -137,33 +137,33 @@ vm_area_guarded::~vm_area_guarded() {}
|
||||
uintptr_t
|
||||
vm_area_guarded::get_section()
|
||||
{
|
||||
if (m_cache.count() > 0) {
|
||||
return m_cache.pop();
|
||||
}
|
||||
if (m_cache.count() > 0) {
|
||||
return m_cache.pop();
|
||||
}
|
||||
|
||||
uintptr_t addr = m_next;
|
||||
m_next += (m_pages + 1) * memory::frame_size;
|
||||
return m_start + addr;
|
||||
uintptr_t addr = m_next;
|
||||
m_next += (m_pages + 1) * memory::frame_size;
|
||||
return m_start + addr;
|
||||
}
|
||||
|
||||
void
|
||||
vm_area_guarded::return_section(uintptr_t addr)
|
||||
{
|
||||
m_cache.append(addr);
|
||||
m_cache.append(addr);
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
{
|
||||
if (offset > m_next)
|
||||
return false;
|
||||
if (offset > m_next)
|
||||
return false;
|
||||
|
||||
// make sure this isn't in a guard page. (sections are
|
||||
// m_pages big plus 1 leading guard page, so page 0 is
|
||||
// invalid)
|
||||
if ((offset >> 12) % (m_pages+1) == 0)
|
||||
return false;
|
||||
// make sure this isn't in a guard page. (sections are
|
||||
// m_pages big plus 1 leading guard page, so page 0 is
|
||||
// invalid)
|
||||
if ((offset >> 12) % (m_pages+1) == 0)
|
||||
return false;
|
||||
|
||||
return vm_area_untracked::get_page(offset, phys);
|
||||
return vm_area_untracked::get_page(offset, phys);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,158 +20,158 @@ enum class vm_flags : uint32_t
|
||||
#define VM_FLAG(name, v) name = v,
|
||||
#include "j6/tables/vm_flags.inc"
|
||||
#undef VM_FLAG
|
||||
driver_mask = 0x000fffff, ///< flags allowed via syscall for drivers
|
||||
user_mask = 0x0000ffff, ///< flags allowed via syscall for non-drivers
|
||||
driver_mask = 0x000fffff, ///< flags allowed via syscall for drivers
|
||||
user_mask = 0x0000ffff, ///< flags allowed via syscall for non-drivers
|
||||
};
|
||||
|
||||
|
||||
/// Virtual memory areas allow control over memory allocation
|
||||
class vm_area :
|
||||
public kobject
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
static constexpr kobject::type type = kobject::type::vma;
|
||||
static constexpr kobject::type type = kobject::type::vma;
|
||||
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area(size_t size, vm_flags flags = vm_flags::none);
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area(size_t size, vm_flags flags = vm_flags::none);
|
||||
|
||||
virtual ~vm_area();
|
||||
virtual ~vm_area();
|
||||
|
||||
/// Get the current virtual size of the memory area
|
||||
inline size_t size() const { return m_size; }
|
||||
/// Get the current virtual size of the memory area
|
||||
inline size_t size() const { return m_size; }
|
||||
|
||||
/// Get the flags set for this area
|
||||
inline vm_flags flags() const { return m_flags; }
|
||||
/// Get the flags set for this area
|
||||
inline vm_flags flags() const { return m_flags; }
|
||||
|
||||
/// Track that this area was added to a vm_space
|
||||
/// \arg space The space to add this area to
|
||||
/// \returns False if this area cannot be added
|
||||
virtual bool add_to(vm_space *space);
|
||||
/// Track that this area was added to a vm_space
|
||||
/// \arg space The space to add this area to
|
||||
/// \returns False if this area cannot be added
|
||||
virtual bool add_to(vm_space *space);
|
||||
|
||||
/// Track that this area was removed frm a vm_space
|
||||
/// \arg space The space that is removing this area
|
||||
/// \returns True if the removing space should free the pages
|
||||
/// mapped for this area
|
||||
virtual bool remove_from(vm_space *space);
|
||||
/// Track that this area was removed frm a vm_space
|
||||
/// \arg space The space that is removing this area
|
||||
/// \returns True if the removing space should free the pages
|
||||
/// mapped for this area
|
||||
virtual bool remove_from(vm_space *space);
|
||||
|
||||
/// Change the virtual size of the memory area. This may cause
|
||||
/// deallocation if the new size is smaller than the current size.
|
||||
/// Note that if resizing is unsuccessful, the previous size will
|
||||
/// be returned.
|
||||
/// \arg size The desired new virtual size
|
||||
/// \returns The new virtual size
|
||||
virtual size_t resize(size_t size);
|
||||
/// Change the virtual size of the memory area. This may cause
|
||||
/// deallocation if the new size is smaller than the current size.
|
||||
/// Note that if resizing is unsuccessful, the previous size will
|
||||
/// be returned.
|
||||
/// \arg size The desired new virtual size
|
||||
/// \returns The new virtual size
|
||||
virtual size_t resize(size_t size);
|
||||
|
||||
/// Get the physical page for the given offset
|
||||
/// \arg offset The offset into the VMA
|
||||
/// \arg phys [out] Receives the physical page address, if any
|
||||
/// \returns True if there should be a page at the given offset
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) = 0;
|
||||
/// Get the physical page for the given offset
|
||||
/// \arg offset The offset into the VMA
|
||||
/// \arg phys [out] Receives the physical page address, if any
|
||||
/// \returns True if there should be a page at the given offset
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) = 0;
|
||||
|
||||
protected:
|
||||
virtual void on_no_handles() override;
|
||||
bool can_resize(size_t size);
|
||||
virtual void on_no_handles() override;
|
||||
bool can_resize(size_t size);
|
||||
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
kutil::vector<vm_space*> m_spaces;
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
kutil::vector<vm_space*> m_spaces;
|
||||
|
||||
// Initial static space for m_spaces - most areas will never grow
|
||||
// beyond this size, so avoid allocations
|
||||
static constexpr size_t static_size = 2;
|
||||
vm_space *m_vector_static[static_size];
|
||||
// Initial static space for m_spaces - most areas will never grow
|
||||
// beyond this size, so avoid allocations
|
||||
static constexpr size_t static_size = 2;
|
||||
vm_space *m_vector_static[static_size];
|
||||
};
|
||||
|
||||
|
||||
/// A shareable but non-allocatable memory area of contiguous physical
|
||||
/// addresses (like mmio)
|
||||
class vm_area_fixed :
|
||||
public vm_area
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg start Starting physical address of this area
|
||||
/// \arg size Size of the physical memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_fixed(uintptr_t start, size_t size, vm_flags flags = vm_flags::none);
|
||||
virtual ~vm_area_fixed();
|
||||
/// Constructor.
|
||||
/// \arg start Starting physical address of this area
|
||||
/// \arg size Size of the physical memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_fixed(uintptr_t start, size_t size, vm_flags flags = vm_flags::none);
|
||||
virtual ~vm_area_fixed();
|
||||
|
||||
virtual size_t resize(size_t size) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual size_t resize(size_t size) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
|
||||
private:
|
||||
uintptr_t m_start;
|
||||
uintptr_t m_start;
|
||||
};
|
||||
|
||||
|
||||
/// Area that allows open allocation
|
||||
class vm_area_open :
|
||||
public vm_area
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_open(size_t size, vm_flags flags);
|
||||
virtual ~vm_area_open();
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_open(size_t size, vm_flags flags);
|
||||
virtual ~vm_area_open();
|
||||
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
|
||||
private:
|
||||
page_tree *m_mapped;
|
||||
page_tree *m_mapped;
|
||||
};
|
||||
|
||||
|
||||
/// Area that does not track its allocations and thus cannot be shared
|
||||
class vm_area_untracked :
|
||||
public vm_area
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_untracked(size_t size, vm_flags flags);
|
||||
virtual ~vm_area_untracked();
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_untracked(size_t size, vm_flags flags);
|
||||
virtual ~vm_area_untracked();
|
||||
|
||||
virtual bool add_to(vm_space *space) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool add_to(vm_space *space) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
};
|
||||
|
||||
|
||||
/// Area split into standard-sized segments, separated by guard pages.
|
||||
/// Based on vm_area_untracked, can not be shared.
|
||||
class vm_area_guarded :
|
||||
public vm_area_untracked
|
||||
public vm_area_untracked
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg start Initial address where this area is mapped
|
||||
/// \arg sec_pages Pages in an individual section
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_guarded(
|
||||
uintptr_t start,
|
||||
size_t sec_pages,
|
||||
size_t size,
|
||||
vm_flags flags);
|
||||
/// Constructor.
|
||||
/// \arg start Initial address where this area is mapped
|
||||
/// \arg sec_pages Pages in an individual section
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_guarded(
|
||||
uintptr_t start,
|
||||
size_t sec_pages,
|
||||
size_t size,
|
||||
vm_flags flags);
|
||||
|
||||
virtual ~vm_area_guarded();
|
||||
virtual ~vm_area_guarded();
|
||||
|
||||
/// Get an available section in this area
|
||||
uintptr_t get_section();
|
||||
/// Get an available section in this area
|
||||
uintptr_t get_section();
|
||||
|
||||
/// Return a section address to the available pool
|
||||
void return_section(uintptr_t addr);
|
||||
/// Return a section address to the available pool
|
||||
void return_section(uintptr_t addr);
|
||||
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
|
||||
private:
|
||||
kutil::vector<uintptr_t> m_cache;
|
||||
uintptr_t m_start;
|
||||
size_t m_pages;
|
||||
uintptr_t m_next;
|
||||
kutil::vector<uintptr_t> m_cache;
|
||||
uintptr_t m_start;
|
||||
size_t m_pages;
|
||||
uintptr_t m_next;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -14,21 +14,21 @@ constexpr size_t page_table::entry_sizes[4];
|
||||
|
||||
|
||||
constexpr page_table::flag table_flags =
|
||||
page_table::flag::present |
|
||||
page_table::flag::write;
|
||||
page_table::flag::present |
|
||||
page_table::flag::write;
|
||||
|
||||
|
||||
page_table::iterator::iterator(uintptr_t virt, page_table *pml4) :
|
||||
m_table {pml4, 0, 0, 0}
|
||||
m_table {pml4, 0, 0, 0}
|
||||
{
|
||||
for (unsigned i = 0; i < D; ++i)
|
||||
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
|
||||
for (unsigned i = 0; i < D; ++i)
|
||||
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
|
||||
}
|
||||
|
||||
page_table::iterator::iterator(const page_table::iterator &o)
|
||||
{
|
||||
kutil::memcpy(&m_table, &o.m_table, sizeof(m_table));
|
||||
kutil::memcpy(&m_index, &o.m_index, sizeof(m_index));
|
||||
kutil::memcpy(&m_table, &o.m_table, sizeof(m_table));
|
||||
kutil::memcpy(&m_index, &o.m_index, sizeof(m_index));
|
||||
}
|
||||
|
||||
inline static level to_lv(unsigned i) { return static_cast<level>(i); }
|
||||
@@ -37,136 +37,136 @@ inline static unsigned to_un(level i) { return static_cast<unsigned>(i); }
|
||||
uintptr_t
|
||||
page_table::iterator::start(level l) const
|
||||
{
|
||||
uintptr_t address = 0;
|
||||
uintptr_t address = 0;
|
||||
|
||||
for (level i = level::pml4; i <= l; ++i)
|
||||
address |= static_cast<uintptr_t>(index(i)) << (12 + 9*(3-unsigned(i)));
|
||||
for (level i = level::pml4; i <= l; ++i)
|
||||
address |= static_cast<uintptr_t>(index(i)) << (12 + 9*(3-unsigned(i)));
|
||||
|
||||
// canonicalize the address
|
||||
if (address & (1ull<<47))
|
||||
address |= (0xffffull<<48);
|
||||
// canonicalize the address
|
||||
if (address & (1ull<<47))
|
||||
address |= (0xffffull<<48);
|
||||
|
||||
return address;
|
||||
return address;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
page_table::iterator::end(level l) const
|
||||
{
|
||||
kassert(l != level::pml4, "Called end() with level::pml4");
|
||||
kassert(l != level::pml4, "Called end() with level::pml4");
|
||||
|
||||
uintptr_t address = 0;
|
||||
uintptr_t address = 0;
|
||||
|
||||
for (level i = level::pml4; i < l; ++i) {
|
||||
uintptr_t idx = index(i) +
|
||||
((i == l) ? 1 : 0);
|
||||
address |= idx << (12 + 9*(3-unsigned(i)));
|
||||
}
|
||||
for (level i = level::pml4; i < l; ++i) {
|
||||
uintptr_t idx = index(i) +
|
||||
((i == l) ? 1 : 0);
|
||||
address |= idx << (12 + 9*(3-unsigned(i)));
|
||||
}
|
||||
|
||||
// canonicalize the address
|
||||
if (address & (1ull<<47))
|
||||
address |= (0xffffull<<48);
|
||||
// canonicalize the address
|
||||
if (address & (1ull<<47))
|
||||
address |= (0xffffull<<48);
|
||||
|
||||
return address;
|
||||
return address;
|
||||
}
|
||||
|
||||
level
|
||||
page_table::iterator::align() const
|
||||
{
|
||||
for (int i = 4; i > 0; --i)
|
||||
if (m_index[i-1]) return level(i);
|
||||
return level::pml4;
|
||||
for (int i = 4; i > 0; --i)
|
||||
if (m_index[i-1]) return level(i);
|
||||
return level::pml4;
|
||||
}
|
||||
|
||||
page_table::level
|
||||
page_table::iterator::depth() const
|
||||
{
|
||||
for (level i = level::pml4; i < level::page; ++i)
|
||||
if (!(entry(i) & 1)) return i;
|
||||
return level::pt;
|
||||
for (level i = level::pml4; i < level::page; ++i)
|
||||
if (!(entry(i) & 1)) return i;
|
||||
return level::pt;
|
||||
}
|
||||
|
||||
void
|
||||
page_table::iterator::next(level l)
|
||||
{
|
||||
kassert(l != level::pml4, "Called next() with level::pml4");
|
||||
kassert(l <= level::page, "Called next() with too deep level");
|
||||
kassert(l != level::pml4, "Called next() with level::pml4");
|
||||
kassert(l <= level::page, "Called next() with too deep level");
|
||||
|
||||
for (level i = l; i < level::page; ++i)
|
||||
index(i) = 0;
|
||||
for (level i = l; i < level::page; ++i)
|
||||
index(i) = 0;
|
||||
|
||||
while (++index(--l) == 512) {
|
||||
kassert(to_un(l), "iterator ran off the end of memory");
|
||||
index(l) = 0;
|
||||
}
|
||||
while (++index(--l) == 512) {
|
||||
kassert(to_un(l), "iterator ran off the end of memory");
|
||||
index(l) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
page_table::iterator::operator!=(const iterator &o) const
|
||||
{
|
||||
for (unsigned i = 0; i < D; ++i)
|
||||
if (o.m_index[i] != m_index[i])
|
||||
return true;
|
||||
for (unsigned i = 0; i < D; ++i)
|
||||
if (o.m_index[i] != m_index[i])
|
||||
return true;
|
||||
|
||||
return o.m_table[0] != m_table[0];
|
||||
return o.m_table[0] != m_table[0];
|
||||
}
|
||||
|
||||
bool
|
||||
page_table::iterator::check_table(level l) const
|
||||
{
|
||||
// We're only dealing with D levels of paging, and
|
||||
// there must always be a PML4.
|
||||
if (l == level::pml4 || l > level::pt)
|
||||
return l == level::pml4;
|
||||
// We're only dealing with D levels of paging, and
|
||||
// there must always be a PML4.
|
||||
if (l == level::pml4 || l > level::pt)
|
||||
return l == level::pml4;
|
||||
|
||||
uint64_t parent = entry(l - 1);
|
||||
if (parent & 1) {
|
||||
table(l) = reinterpret_cast<page_table*>(page_offset | (parent & ~0xfffull));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
uint64_t parent = entry(l - 1);
|
||||
if (parent & 1) {
|
||||
table(l) = reinterpret_cast<page_table*>(page_offset | (parent & ~0xfffull));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
page_table::iterator::ensure_table(level l)
|
||||
{
|
||||
// We're only dealing with D levels of paging, and
|
||||
// there must always be a PML4.
|
||||
if (l == level::pml4 || l > level::pt) return;
|
||||
if (check_table(l)) return;
|
||||
// We're only dealing with D levels of paging, and
|
||||
// there must always be a PML4.
|
||||
if (l == level::pml4 || l > level::pt) return;
|
||||
if (check_table(l)) return;
|
||||
|
||||
page_table *table = page_table::get_table_page();
|
||||
uintptr_t phys = reinterpret_cast<uintptr_t>(table) & ~page_offset;
|
||||
page_table *table = page_table::get_table_page();
|
||||
uintptr_t phys = reinterpret_cast<uintptr_t>(table) & ~page_offset;
|
||||
|
||||
uint64_t &parent = entry(l - 1);
|
||||
flag flags = table_flags;
|
||||
if (m_index[0] < memory::pml4e_kernel)
|
||||
flags |= flag::user;
|
||||
uint64_t &parent = entry(l - 1);
|
||||
flag flags = table_flags;
|
||||
if (m_index[0] < memory::pml4e_kernel)
|
||||
flags |= flag::user;
|
||||
|
||||
m_table[unsigned(l)] = table;
|
||||
parent = (phys & ~0xfffull) | flags;
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
m_table[unsigned(l)] = table;
|
||||
parent = (phys & ~0xfffull) | flags;
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
page_table *
|
||||
page_table::get(int i, uint16_t *flags) const
|
||||
{
|
||||
uint64_t entry = entries[i];
|
||||
uint64_t entry = entries[i];
|
||||
|
||||
if ((entry & 0x1) == 0)
|
||||
return nullptr;
|
||||
if ((entry & 0x1) == 0)
|
||||
return nullptr;
|
||||
|
||||
if (flags)
|
||||
*flags = entry & 0xfffull;
|
||||
if (flags)
|
||||
*flags = entry & 0xfffull;
|
||||
|
||||
return reinterpret_cast<page_table *>((entry & ~0xfffull) + page_offset);
|
||||
return reinterpret_cast<page_table *>((entry & ~0xfffull) + page_offset);
|
||||
}
|
||||
|
||||
void
|
||||
page_table::set(int i, page_table *p, uint16_t flags)
|
||||
{
|
||||
entries[i] =
|
||||
(reinterpret_cast<uint64_t>(p) - page_offset) |
|
||||
(flags & 0xfff);
|
||||
entries[i] =
|
||||
(reinterpret_cast<uint64_t>(p) - page_offset) |
|
||||
(flags & 0xfff);
|
||||
}
|
||||
|
||||
struct free_page_header { free_page_header *next; };
|
||||
@@ -174,105 +174,105 @@ struct free_page_header { free_page_header *next; };
|
||||
page_table *
|
||||
page_table::get_table_page()
|
||||
{
|
||||
if (!s_cache_count)
|
||||
fill_table_page_cache();
|
||||
if (!s_cache_count)
|
||||
fill_table_page_cache();
|
||||
|
||||
free_page_header *page = s_page_cache;
|
||||
s_page_cache = s_page_cache->next;
|
||||
--s_cache_count;
|
||||
free_page_header *page = s_page_cache;
|
||||
s_page_cache = s_page_cache->next;
|
||||
--s_cache_count;
|
||||
|
||||
kutil::memset(page, 0, memory::frame_size);
|
||||
return reinterpret_cast<page_table*>(page);
|
||||
kutil::memset(page, 0, memory::frame_size);
|
||||
return reinterpret_cast<page_table*>(page);
|
||||
}
|
||||
|
||||
void
|
||||
page_table::free_table_page(page_table *pt)
|
||||
{
|
||||
free_page_header *page =
|
||||
reinterpret_cast<free_page_header*>(pt);
|
||||
page->next = s_page_cache;
|
||||
s_page_cache = page->next;
|
||||
++s_cache_count;
|
||||
free_page_header *page =
|
||||
reinterpret_cast<free_page_header*>(pt);
|
||||
page->next = s_page_cache;
|
||||
s_page_cache = page->next;
|
||||
++s_cache_count;
|
||||
}
|
||||
|
||||
void
|
||||
page_table::fill_table_page_cache()
|
||||
{
|
||||
constexpr size_t min_pages = 16;
|
||||
constexpr size_t min_pages = 16;
|
||||
|
||||
frame_allocator &fa = frame_allocator::get();
|
||||
while (s_cache_count < min_pages) {
|
||||
uintptr_t phys = 0;
|
||||
size_t n = fa.allocate(min_pages - s_cache_count, &phys);
|
||||
frame_allocator &fa = frame_allocator::get();
|
||||
while (s_cache_count < min_pages) {
|
||||
uintptr_t phys = 0;
|
||||
size_t n = fa.allocate(min_pages - s_cache_count, &phys);
|
||||
|
||||
free_page_header *start =
|
||||
memory::to_virtual<free_page_header>(phys);
|
||||
free_page_header *start =
|
||||
memory::to_virtual<free_page_header>(phys);
|
||||
|
||||
for (int i = 0; i < n - 1; ++i)
|
||||
kutil::offset_pointer(start, i * memory::frame_size)
|
||||
->next = kutil::offset_pointer(start, (i+1) * memory::frame_size);
|
||||
for (int i = 0; i < n - 1; ++i)
|
||||
kutil::offset_pointer(start, i * memory::frame_size)
|
||||
->next = kutil::offset_pointer(start, (i+1) * memory::frame_size);
|
||||
|
||||
free_page_header *end =
|
||||
kutil::offset_pointer(start, (n-1) * memory::frame_size);
|
||||
free_page_header *end =
|
||||
kutil::offset_pointer(start, (n-1) * memory::frame_size);
|
||||
|
||||
end->next = s_page_cache;
|
||||
s_page_cache = start;
|
||||
s_cache_count += n;
|
||||
}
|
||||
end->next = s_page_cache;
|
||||
s_page_cache = start;
|
||||
s_cache_count += n;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
page_table::free(page_table::level l)
|
||||
{
|
||||
unsigned last = l == level::pml4
|
||||
? memory::pml4e_kernel
|
||||
: memory::table_entries;
|
||||
unsigned last = l == level::pml4
|
||||
? memory::pml4e_kernel
|
||||
: memory::table_entries;
|
||||
|
||||
frame_allocator &fa = frame_allocator::get();
|
||||
for (unsigned i = 0; i < last; ++i) {
|
||||
if (!is_present(i)) continue;
|
||||
if (is_page(l, i)) {
|
||||
size_t count = memory::page_count(entry_sizes[unsigned(l)]);
|
||||
fa.free(entries[i] & ~0xfffull, count);
|
||||
} else {
|
||||
get(i)->free(l + 1);
|
||||
}
|
||||
}
|
||||
frame_allocator &fa = frame_allocator::get();
|
||||
for (unsigned i = 0; i < last; ++i) {
|
||||
if (!is_present(i)) continue;
|
||||
if (is_page(l, i)) {
|
||||
size_t count = memory::page_count(entry_sizes[unsigned(l)]);
|
||||
fa.free(entries[i] & ~0xfffull, count);
|
||||
} else {
|
||||
get(i)->free(l + 1);
|
||||
}
|
||||
}
|
||||
|
||||
free_table_page(this);
|
||||
free_table_page(this);
|
||||
}
|
||||
|
||||
void
|
||||
page_table::dump(page_table::level lvl, bool recurse)
|
||||
{
|
||||
console *cons = console::get();
|
||||
console *cons = console::get();
|
||||
|
||||
cons->printf("\nLevel %d page table @ %lx:\n", lvl, this);
|
||||
for (int i=0; i<memory::table_entries; ++i) {
|
||||
uint64_t ent = entries[i];
|
||||
cons->printf("\nLevel %d page table @ %lx:\n", lvl, this);
|
||||
for (int i=0; i<memory::table_entries; ++i) {
|
||||
uint64_t ent = entries[i];
|
||||
|
||||
if ((ent & 0x1) == 0)
|
||||
cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent);
|
||||
if ((ent & 0x1) == 0)
|
||||
cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent);
|
||||
|
||||
else if ((lvl == level::pdp || lvl == level::pd) && (ent & 0x80) == 0x80)
|
||||
cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||
else if ((lvl == level::pdp || lvl == level::pd) && (ent & 0x80) == 0x80)
|
||||
cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||
|
||||
else if (lvl == level::pt)
|
||||
cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||
else if (lvl == level::pt)
|
||||
cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull);
|
||||
|
||||
else
|
||||
cons->printf(" %3d: %016lx -> Level %d table at %016lx\n",
|
||||
i, ent, deeper(lvl), (ent & ~0xfffull) + page_offset);
|
||||
}
|
||||
else
|
||||
cons->printf(" %3d: %016lx -> Level %d table at %016lx\n",
|
||||
i, ent, deeper(lvl), (ent & ~0xfffull) + page_offset);
|
||||
}
|
||||
|
||||
if (lvl != level::pt && recurse) {
|
||||
for (int i=0; i<=memory::table_entries; ++i) {
|
||||
if (is_large_page(lvl, i))
|
||||
continue;
|
||||
if (lvl != level::pt && recurse) {
|
||||
for (int i=0; i<=memory::table_entries; ++i) {
|
||||
if (is_large_page(lvl, i))
|
||||
continue;
|
||||
|
||||
page_table *next = get(i);
|
||||
if (next)
|
||||
next->dump(deeper(lvl), true);
|
||||
}
|
||||
}
|
||||
page_table *next = get(i);
|
||||
if (next)
|
||||
next->dump(deeper(lvl), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,184 +11,184 @@ struct free_page_header;
|
||||
/// Struct to allow easy accessing of a memory page being used as a page table.
|
||||
struct page_table
|
||||
{
|
||||
/// Enum representing the table levels in 4-level paging
|
||||
enum class level : unsigned { pml4, pdp, pd, pt, page };
|
||||
/// Enum representing the table levels in 4-level paging
|
||||
enum class level : unsigned { pml4, pdp, pd, pt, page };
|
||||
|
||||
/// Page entry flags
|
||||
enum class flag : uint64_t
|
||||
{
|
||||
none = 0x0000,
|
||||
present = 0x0001, /// Entry is present in the table
|
||||
write = 0x0002, /// Section may be written
|
||||
user = 0x0004, /// User-accessible
|
||||
pat0 = 0x0008, /// PAT selector bit 0
|
||||
pat1 = 0x0010, /// PAT selector bit 1
|
||||
accessed = 0x0020, /// Entry has been accessed
|
||||
dirty = 0x0040, /// Page has been written to
|
||||
page = 0x0080, /// Entry is a large page
|
||||
pat2 = 0x0080, /// PAT selector bit 2 on PT entries
|
||||
global = 0x0100, /// Entry is not PCID-specific
|
||||
pat2_lg = 0x1000, /// PAT selector bit 2 on large/huge pages
|
||||
/// Page entry flags
|
||||
enum class flag : uint64_t
|
||||
{
|
||||
none = 0x0000,
|
||||
present = 0x0001, /// Entry is present in the table
|
||||
write = 0x0002, /// Section may be written
|
||||
user = 0x0004, /// User-accessible
|
||||
pat0 = 0x0008, /// PAT selector bit 0
|
||||
pat1 = 0x0010, /// PAT selector bit 1
|
||||
accessed = 0x0020, /// Entry has been accessed
|
||||
dirty = 0x0040, /// Page has been written to
|
||||
page = 0x0080, /// Entry is a large page
|
||||
pat2 = 0x0080, /// PAT selector bit 2 on PT entries
|
||||
global = 0x0100, /// Entry is not PCID-specific
|
||||
pat2_lg = 0x1000, /// PAT selector bit 2 on large/huge pages
|
||||
|
||||
wb = none,
|
||||
wt = pat0,
|
||||
uc_ = pat1,
|
||||
uc = pat0 | pat1,
|
||||
wc = pat0 | pat1 | pat2,
|
||||
wc_lg = pat0 | pat1 | pat2_lg,
|
||||
};
|
||||
wb = none,
|
||||
wt = pat0,
|
||||
uc_ = pat1,
|
||||
uc = pat0 | pat1,
|
||||
wc = pat0 | pat1 | pat2,
|
||||
wc_lg = pat0 | pat1 | pat2_lg,
|
||||
};
|
||||
|
||||
/// Helper for getting the next level value
|
||||
inline static level deeper(level l) {
|
||||
return static_cast<level>(static_cast<unsigned>(l) + 1);
|
||||
}
|
||||
/// Helper for getting the next level value
|
||||
inline static level deeper(level l) {
|
||||
return static_cast<level>(static_cast<unsigned>(l) + 1);
|
||||
}
|
||||
|
||||
static constexpr size_t entry_sizes[] = {
|
||||
0x8000000000, // PML4 entry: 512 GiB
|
||||
0x40000000, // PDP entry: 1 GiB
|
||||
0x200000, // PD entry: 2 MiB
|
||||
0x1000}; // PT entry: 4 KiB
|
||||
static constexpr size_t entry_sizes[] = {
|
||||
0x8000000000, // PML4 entry: 512 GiB
|
||||
0x40000000, // PDP entry: 1 GiB
|
||||
0x200000, // PD entry: 2 MiB
|
||||
0x1000}; // PT entry: 4 KiB
|
||||
|
||||
/// Iterator over page table entries.
|
||||
class iterator
|
||||
{
|
||||
/// The number of levels
|
||||
static constexpr unsigned D = 4;
|
||||
/// Iterator over page table entries.
|
||||
class iterator
|
||||
{
|
||||
/// The number of levels
|
||||
static constexpr unsigned D = 4;
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg virt Virtual address this iterator is starting at
|
||||
/// \arg pml4 Root of the page tables to iterate
|
||||
iterator(uintptr_t virt, page_table *pml4);
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg virt Virtual address this iterator is starting at
|
||||
/// \arg pml4 Root of the page tables to iterate
|
||||
iterator(uintptr_t virt, page_table *pml4);
|
||||
|
||||
/// Copy constructor.
|
||||
iterator(const iterator &o);
|
||||
/// Copy constructor.
|
||||
iterator(const iterator &o);
|
||||
|
||||
/// Get the starting address of pages mapped by the current table
|
||||
/// of level l.
|
||||
uintptr_t start(level l) const;
|
||||
/// Get the starting address of pages mapped by the current table
|
||||
/// of level l.
|
||||
uintptr_t start(level l) const;
|
||||
|
||||
/// Get the ending address of pages mapped by the current table
|
||||
/// of level l.
|
||||
uintptr_t end(level l) const;
|
||||
/// Get the ending address of pages mapped by the current table
|
||||
/// of level l.
|
||||
uintptr_t end(level l) const;
|
||||
|
||||
/// Get the widest table type the current address is aligned to
|
||||
level align() const;
|
||||
/// Get the widest table type the current address is aligned to
|
||||
level align() const;
|
||||
|
||||
/// Get the current virtual address
|
||||
inline uintptr_t vaddress() const { return start(level::pt); }
|
||||
/// Get the current virtual address
|
||||
inline uintptr_t vaddress() const { return start(level::pt); }
|
||||
|
||||
/// Get the nth page table of the current address
|
||||
inline page_table *& table(level l) const { return m_table[unsigned(l)]; }
|
||||
/// Get the nth page table of the current address
|
||||
inline page_table *& table(level l) const { return m_table[unsigned(l)]; }
|
||||
|
||||
/// Get the index in the nth page table of the current address
|
||||
inline uint16_t & index(level l) { return m_index[unsigned(l)]; }
|
||||
/// Get the index in the nth page table of the current address
|
||||
inline uint16_t & index(level l) { return m_index[unsigned(l)]; }
|
||||
|
||||
/// Get the index in the nth page table of the current address
|
||||
inline uint16_t index(level l) const { return m_index[unsigned(l)]; }
|
||||
/// Get the index in the nth page table of the current address
|
||||
inline uint16_t index(level l) const { return m_index[unsigned(l)]; }
|
||||
|
||||
/// Get the current table entry of the table at the given level.
|
||||
inline uint64_t entry(level l) const {
|
||||
for (unsigned i = 1; i <= unsigned(l); ++i)
|
||||
if (!check_table(level(i))) return 0;
|
||||
return table(l)->entries[index(l)];
|
||||
}
|
||||
/// Get the current table entry of the table at the given level.
|
||||
inline uint64_t entry(level l) const {
|
||||
for (unsigned i = 1; i <= unsigned(l); ++i)
|
||||
if (!check_table(level(i))) return 0;
|
||||
return table(l)->entries[index(l)];
|
||||
}
|
||||
|
||||
/// Get a *non-const* reference to the current table entry of
|
||||
/// the table at the given level.
|
||||
inline uint64_t & entry(level l) {
|
||||
for (unsigned i = 1; i <= unsigned(l); ++i) ensure_table(level(i));
|
||||
return table(l)->entries[index(l)];
|
||||
}
|
||||
/// Get a *non-const* reference to the current table entry of
|
||||
/// the table at the given level.
|
||||
inline uint64_t & entry(level l) {
|
||||
for (unsigned i = 1; i <= unsigned(l); ++i) ensure_table(level(i));
|
||||
return table(l)->entries[index(l)];
|
||||
}
|
||||
|
||||
/// Get the depth of tables that actually exist for the current address
|
||||
level depth() const;
|
||||
/// Get the depth of tables that actually exist for the current address
|
||||
level depth() const;
|
||||
|
||||
/// Increment iteration to the next entry aligned to the given level
|
||||
void next(level l);
|
||||
/// Increment iteration to the next entry aligned to the given level
|
||||
void next(level l);
|
||||
|
||||
/// Increment iteration to the next entry at the deepest level
|
||||
inline void increment() { next(level::page); }
|
||||
/// Increment iteration to the next entry at the deepest level
|
||||
inline void increment() { next(level::page); }
|
||||
|
||||
inline uint64_t & operator*() { return entry(level::pt); }
|
||||
inline iterator & operator++() { increment(); return *this; }
|
||||
inline iterator operator++(int) { iterator old {*this}; increment(); return old; }
|
||||
inline uint64_t & operator*() { return entry(level::pt); }
|
||||
inline iterator & operator++() { increment(); return *this; }
|
||||
inline iterator operator++(int) { iterator old {*this}; increment(); return old; }
|
||||
|
||||
bool operator!=(const iterator &o) const;
|
||||
bool operator!=(const iterator &o) const;
|
||||
|
||||
bool check_table(level l) const;
|
||||
void ensure_table(level l);
|
||||
bool check_table(level l) const;
|
||||
void ensure_table(level l);
|
||||
|
||||
private:
|
||||
// The table array is mutable because we might update it with existing
|
||||
// tables; a 'view switch'. therefore, be careful not to modify table
|
||||
// contents in const functions.
|
||||
mutable page_table *m_table[D];
|
||||
uint16_t m_index[D];
|
||||
};
|
||||
private:
|
||||
// The table array is mutable because we might update it with existing
|
||||
// tables; a 'view switch'. therefore, be careful not to modify table
|
||||
// contents in const functions.
|
||||
mutable page_table *m_table[D];
|
||||
uint16_t m_index[D];
|
||||
};
|
||||
|
||||
/// Allocate a page for a page table, or pull one from the cache
|
||||
/// \returns An empty page, mapped in the linear offset area
|
||||
static page_table * get_table_page();
|
||||
/// Allocate a page for a page table, or pull one from the cache
|
||||
/// \returns An empty page, mapped in the linear offset area
|
||||
static page_table * get_table_page();
|
||||
|
||||
/// Return a page table's page to the page cache.
|
||||
/// \arg pt The page to be returned
|
||||
static void free_table_page(page_table *pt);
|
||||
/// Return a page table's page to the page cache.
|
||||
/// \arg pt The page to be returned
|
||||
static void free_table_page(page_table *pt);
|
||||
|
||||
// Ensure the page table page cache has a minimum number of pages
|
||||
// in it.
|
||||
static void fill_table_page_cache();
|
||||
// Ensure the page table page cache has a minimum number of pages
|
||||
// in it.
|
||||
static void fill_table_page_cache();
|
||||
|
||||
static free_page_header *s_page_cache; ///< Cache of free pages to use for tables
|
||||
static size_t s_cache_count; ///< Number of pages in s_page_cache
|
||||
static free_page_header *s_page_cache; ///< Cache of free pages to use for tables
|
||||
static size_t s_cache_count; ///< Number of pages in s_page_cache
|
||||
|
||||
/// Get an entry in the page table as a page_table pointer
|
||||
/// \arg i Index of the entry in this page table
|
||||
/// \arg flags [out] If set, this will receive the entry's flags
|
||||
/// \returns The corresponding entry, offset into page-offset memory
|
||||
page_table * get(int i, uint16_t *flags = nullptr) const;
|
||||
/// Get an entry in the page table as a page_table pointer
|
||||
/// \arg i Index of the entry in this page table
|
||||
/// \arg flags [out] If set, this will receive the entry's flags
|
||||
/// \returns The corresponding entry, offset into page-offset memory
|
||||
page_table * get(int i, uint16_t *flags = nullptr) const;
|
||||
|
||||
/// Set an entry in the page table as a page_table pointer
|
||||
/// \arg i Index of the entry in this page table
|
||||
/// \arg flags The flags for the entry
|
||||
void set(int i, page_table *p, uint16_t flags);
|
||||
/// Set an entry in the page table as a page_table pointer
|
||||
/// \arg i Index of the entry in this page table
|
||||
/// \arg flags The flags for the entry
|
||||
void set(int i, page_table *p, uint16_t flags);
|
||||
|
||||
/// Check if the given entry represents a large or huge page
|
||||
inline bool is_large_page(level l, int i) const {
|
||||
return (l == level::pdp || l == level::pd) && (entries[i] & 0x80) == 0x80;
|
||||
}
|
||||
/// Check if the given entry represents a large or huge page
|
||||
inline bool is_large_page(level l, int i) const {
|
||||
return (l == level::pdp || l == level::pd) && (entries[i] & 0x80) == 0x80;
|
||||
}
|
||||
|
||||
/// Check if the given entry is marked as present in the table
|
||||
inline bool is_present(int i) const { return (entries[i] & 0x1) == 0x1; }
|
||||
/// Check if the given entry is marked as present in the table
|
||||
inline bool is_present(int i) const { return (entries[i] & 0x1) == 0x1; }
|
||||
|
||||
/// Check if the given entry represents a page (of any size)
|
||||
inline bool is_page(level l, int i) const { return (l == level::pt) || is_large_page(l, i); }
|
||||
/// Check if the given entry represents a page (of any size)
|
||||
inline bool is_page(level l, int i) const { return (l == level::pt) || is_large_page(l, i); }
|
||||
|
||||
/// Free this page table and all resources it references
|
||||
/// \arg l The level of this page table
|
||||
void free(level l);
|
||||
/// Free this page table and all resources it references
|
||||
/// \arg l The level of this page table
|
||||
void free(level l);
|
||||
|
||||
/// Print this table to the debug console.
|
||||
void dump(level lvl = level::pml4, bool recurse = true);
|
||||
/// Print this table to the debug console.
|
||||
void dump(level lvl = level::pml4, bool recurse = true);
|
||||
|
||||
uint64_t entries[memory::table_entries];
|
||||
uint64_t entries[memory::table_entries];
|
||||
};
|
||||
|
||||
|
||||
inline page_table::level operator+(page_table::level a, unsigned b) {
|
||||
return static_cast<page_table::level>(static_cast<unsigned>(a) + b);
|
||||
return static_cast<page_table::level>(static_cast<unsigned>(a) + b);
|
||||
}
|
||||
|
||||
inline page_table::level operator-(page_table::level a, unsigned b) {
|
||||
return static_cast<page_table::level>(static_cast<unsigned>(a) - b);
|
||||
return static_cast<page_table::level>(static_cast<unsigned>(a) - b);
|
||||
}
|
||||
|
||||
inline bool operator>(page_table::level a, page_table::level b) {
|
||||
return static_cast<unsigned>(a) > static_cast<unsigned>(b);
|
||||
return static_cast<unsigned>(a) > static_cast<unsigned>(b);
|
||||
}
|
||||
|
||||
inline bool operator<(page_table::level a, page_table::level b) {
|
||||
return static_cast<unsigned>(a) < static_cast<unsigned>(b);
|
||||
return static_cast<unsigned>(a) < static_cast<unsigned>(b);
|
||||
}
|
||||
|
||||
inline page_table::level& operator++(page_table::level& l) { l = l + 1; return l; }
|
||||
|
||||
@@ -19,134 +19,134 @@ static constexpr unsigned max_level = 5;
|
||||
static constexpr unsigned bits_per_level = 6;
|
||||
|
||||
inline uint64_t to_word(uint64_t base, uint64_t level, uint64_t flags = 0) {
|
||||
// Clear out the non-appropriate bits for this level
|
||||
base &= (~0x3full << (level*bits_per_level));
|
||||
// Clear out the non-appropriate bits for this level
|
||||
base &= (~0x3full << (level*bits_per_level));
|
||||
|
||||
return
|
||||
(base & 0x3ffffffffff) |
|
||||
((level & 0x7) << 42) |
|
||||
((flags & 0x7ffff) << 45);
|
||||
return
|
||||
(base & 0x3ffffffffff) |
|
||||
((level & 0x7) << 42) |
|
||||
((flags & 0x7ffff) << 45);
|
||||
}
|
||||
|
||||
inline uint64_t to_base(uint64_t word) {
|
||||
return word & 0x3ffffffffff;
|
||||
return word & 0x3ffffffffff;
|
||||
}
|
||||
|
||||
inline uint64_t to_level(uint64_t word) {
|
||||
return (word >> 42) & 0x3f;
|
||||
return (word >> 42) & 0x3f;
|
||||
}
|
||||
|
||||
inline uint64_t to_flags(uint64_t word) {
|
||||
return (word >> 45);
|
||||
return (word >> 45);
|
||||
}
|
||||
|
||||
inline bool contains(uint64_t page_off, uint64_t word, uint8_t &index) {
|
||||
uint64_t base = to_base(word);
|
||||
uint64_t bits = to_level(word) * bits_per_level;
|
||||
index = (page_off >> bits) & 0x3f;
|
||||
return (page_off & (~0x3full << bits)) == base;
|
||||
uint64_t base = to_base(word);
|
||||
uint64_t bits = to_level(word) * bits_per_level;
|
||||
index = (page_off >> bits) & 0x3f;
|
||||
return (page_off & (~0x3full << bits)) == base;
|
||||
}
|
||||
|
||||
inline uint64_t index_for(uint64_t page_off, uint8_t level) {
|
||||
return (page_off >> (level*bits_per_level)) & 0x3f;
|
||||
return (page_off >> (level*bits_per_level)) & 0x3f;
|
||||
}
|
||||
|
||||
page_tree::page_tree(uint64_t base, uint8_t level) :
|
||||
m_base {to_word(base, level)}
|
||||
m_base {to_word(base, level)}
|
||||
{
|
||||
kutil::memset(m_entries, 0, sizeof(m_entries));
|
||||
kutil::memset(m_entries, 0, sizeof(m_entries));
|
||||
}
|
||||
|
||||
bool
|
||||
page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page)
|
||||
{
|
||||
uint64_t page_off = offset >> 12; // change to pagewise offset
|
||||
page_tree const *node = root;
|
||||
while (node) {
|
||||
uint8_t level = to_level(node->m_base);
|
||||
uint8_t index = 0;
|
||||
if (!contains(page_off, node->m_base, index))
|
||||
return false;
|
||||
uint64_t page_off = offset >> 12; // change to pagewise offset
|
||||
page_tree const *node = root;
|
||||
while (node) {
|
||||
uint8_t level = to_level(node->m_base);
|
||||
uint8_t index = 0;
|
||||
if (!contains(page_off, node->m_base, index))
|
||||
return false;
|
||||
|
||||
if (!level) {
|
||||
uintptr_t entry = node->m_entries[index].entry;
|
||||
page = entry & ~1ull; // bit 0 marks 'present'
|
||||
return (entry & 1);
|
||||
}
|
||||
if (!level) {
|
||||
uintptr_t entry = node->m_entries[index].entry;
|
||||
page = entry & ~1ull; // bit 0 marks 'present'
|
||||
return (entry & 1);
|
||||
}
|
||||
|
||||
node = node->m_entries[index].child;
|
||||
}
|
||||
node = node->m_entries[index].child;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page)
|
||||
{
|
||||
uint64_t page_off = offset >> 12; // change to pagewise offset
|
||||
page_tree *level0 = nullptr;
|
||||
uint64_t page_off = offset >> 12; // change to pagewise offset
|
||||
page_tree *level0 = nullptr;
|
||||
|
||||
if (!root) {
|
||||
// There's no root yet, just make a level0 and make it
|
||||
// the root.
|
||||
level0 = new page_tree(page_off, 0);
|
||||
root = level0;
|
||||
} else {
|
||||
// Find or insert an existing level0
|
||||
page_tree **parent = &root;
|
||||
page_tree *node = root;
|
||||
uint8_t parent_level = max_level + 1;
|
||||
if (!root) {
|
||||
// There's no root yet, just make a level0 and make it
|
||||
// the root.
|
||||
level0 = new page_tree(page_off, 0);
|
||||
root = level0;
|
||||
} else {
|
||||
// Find or insert an existing level0
|
||||
page_tree **parent = &root;
|
||||
page_tree *node = root;
|
||||
uint8_t parent_level = max_level + 1;
|
||||
|
||||
while (node) {
|
||||
uint8_t level = to_level(node->m_base);
|
||||
uint8_t index = 0;
|
||||
if (!contains(page_off, node->m_base, index)) {
|
||||
// We found a valid parent but the slot where this node should
|
||||
// go contains another node. Insert an intermediate parent of
|
||||
// this node and a new level0 into the parent.
|
||||
uint64_t other = to_base(node->m_base);
|
||||
uint8_t lcl = parent_level;
|
||||
while (index_for(page_off, lcl) == index_for(other, lcl))
|
||||
--lcl;
|
||||
while (node) {
|
||||
uint8_t level = to_level(node->m_base);
|
||||
uint8_t index = 0;
|
||||
if (!contains(page_off, node->m_base, index)) {
|
||||
// We found a valid parent but the slot where this node should
|
||||
// go contains another node. Insert an intermediate parent of
|
||||
// this node and a new level0 into the parent.
|
||||
uint64_t other = to_base(node->m_base);
|
||||
uint8_t lcl = parent_level;
|
||||
while (index_for(page_off, lcl) == index_for(other, lcl))
|
||||
--lcl;
|
||||
|
||||
page_tree *inter = new page_tree(page_off, lcl);
|
||||
inter->m_entries[index_for(other, lcl)].child = node;
|
||||
*parent = inter;
|
||||
page_tree *inter = new page_tree(page_off, lcl);
|
||||
inter->m_entries[index_for(other, lcl)].child = node;
|
||||
*parent = inter;
|
||||
|
||||
level0 = new page_tree(page_off, 0);
|
||||
inter->m_entries[index_for(page_off, lcl)].child = level0;
|
||||
break;
|
||||
}
|
||||
level0 = new page_tree(page_off, 0);
|
||||
inter->m_entries[index_for(page_off, lcl)].child = level0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
level0 = node;
|
||||
break;
|
||||
}
|
||||
if (!level) {
|
||||
level0 = node;
|
||||
break;
|
||||
}
|
||||
|
||||
parent = &node->m_entries[index].child;
|
||||
node = *parent;
|
||||
}
|
||||
parent = &node->m_entries[index].child;
|
||||
node = *parent;
|
||||
}
|
||||
|
||||
kassert( node || parent, "Both node and parent were null in find_or_add");
|
||||
kassert( node || parent, "Both node and parent were null in find_or_add");
|
||||
|
||||
if (!node) {
|
||||
// We found a parent with an empty spot where this node should
|
||||
// be. Insert a new level0 there.
|
||||
level0 = new page_tree(page_off, 0);
|
||||
*parent = level0;
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
// We found a parent with an empty spot where this node should
|
||||
// be. Insert a new level0 there.
|
||||
level0 = new page_tree(page_off, 0);
|
||||
*parent = level0;
|
||||
}
|
||||
}
|
||||
|
||||
kassert(level0, "Got through find_or_add without a level0");
|
||||
uint8_t index = index_for(page_off, 0);
|
||||
uint64_t &ent = level0->m_entries[index].entry;
|
||||
if (!(ent & 1)) {
|
||||
// No entry for this page exists, so make one
|
||||
if (!frame_allocator::get().allocate(1, &ent))
|
||||
return false;
|
||||
ent |= 1;
|
||||
}
|
||||
kassert(level0, "Got through find_or_add without a level0");
|
||||
uint8_t index = index_for(page_off, 0);
|
||||
uint64_t &ent = level0->m_entries[index].entry;
|
||||
if (!(ent & 1)) {
|
||||
// No entry for this page exists, so make one
|
||||
if (!frame_allocator::get().allocate(1, &ent))
|
||||
return false;
|
||||
ent |= 1;
|
||||
}
|
||||
|
||||
page = ent & ~0xfffull;
|
||||
return true;
|
||||
page = ent & ~0xfffull;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,32 +8,32 @@
|
||||
class page_tree
|
||||
{
|
||||
public:
|
||||
/// Get the physical address of the page at the given offset.
|
||||
/// \arg root The root node of the tree
|
||||
/// \arg offset Offset into the VMA, in bytes
|
||||
/// \arg page [out] Receives the page physical address, if found
|
||||
/// \returns True if a page was found
|
||||
static bool find(const page_tree *root, uint64_t offset, uintptr_t &page);
|
||||
/// Get the physical address of the page at the given offset.
|
||||
/// \arg root The root node of the tree
|
||||
/// \arg offset Offset into the VMA, in bytes
|
||||
/// \arg page [out] Receives the page physical address, if found
|
||||
/// \returns True if a page was found
|
||||
static bool find(const page_tree *root, uint64_t offset, uintptr_t &page);
|
||||
|
||||
/// Get the physical address of the page at the given offset. If one does
|
||||
/// not exist yet, allocate a page, insert it, and return that.
|
||||
/// \arg root [inout] The root node of the tree. This pointer may be updated.
|
||||
/// \arg offset Offset into the VMA, in bytes
|
||||
/// \arg page [out] Receives the page physical address, if found
|
||||
/// \returns True if a page was found
|
||||
static bool find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page);
|
||||
/// Get the physical address of the page at the given offset. If one does
|
||||
/// not exist yet, allocate a page, insert it, and return that.
|
||||
/// \arg root [inout] The root node of the tree. This pointer may be updated.
|
||||
/// \arg offset Offset into the VMA, in bytes
|
||||
/// \arg page [out] Receives the page physical address, if found
|
||||
/// \returns True if a page was found
|
||||
static bool find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page);
|
||||
|
||||
private:
|
||||
page_tree(uint64_t base, uint8_t level);
|
||||
page_tree(uint64_t base, uint8_t level);
|
||||
|
||||
/// Stores the page offset of the start of this node's pages in bits 0:41
|
||||
/// and the depth of tree this node represents in bits 42:44 (0-7)
|
||||
uint64_t m_base;
|
||||
/// Stores the page offset of the start of this node's pages in bits 0:41
|
||||
/// and the depth of tree this node represents in bits 42:44 (0-7)
|
||||
uint64_t m_base;
|
||||
|
||||
/// For a level 0 node, the entries area all physical page addresses.
|
||||
/// Other nodes contain pointers to child tree nodes.
|
||||
union {
|
||||
uintptr_t entry;
|
||||
page_tree *child;
|
||||
} m_entries[64];
|
||||
/// For a level 0 node, the entries area all physical page addresses.
|
||||
/// Other nodes contain pointers to child tree nodes.
|
||||
union {
|
||||
uintptr_t entry;
|
||||
page_tree *child;
|
||||
} m_entries[64];
|
||||
};
|
||||
|
||||
@@ -13,8 +13,8 @@ class symbol_table;
|
||||
|
||||
struct frame
|
||||
{
|
||||
frame *prev;
|
||||
uintptr_t return_addr;
|
||||
frame *prev;
|
||||
uintptr_t return_addr;
|
||||
};
|
||||
|
||||
void print_header(serial_port &out, const char *message);
|
||||
|
||||
@@ -14,7 +14,7 @@ void panic_handler(
|
||||
panic::symbol_table syms(symbol_data);
|
||||
|
||||
panic::frame const *fp = nullptr;
|
||||
asm ( "mov %%rbp, %0" : "=r" (fp) );
|
||||
asm ( "mov %%rbp, %0" : "=r" (fp) );
|
||||
|
||||
print_header(com1, message);
|
||||
print_callstack(com1, syms, fp);
|
||||
|
||||
@@ -21,22 +21,22 @@ inline void outb(uint16_t port, uint8_t val) {
|
||||
}
|
||||
|
||||
inline uint8_t inb(uint16_t port) {
|
||||
uint8_t val;
|
||||
asm ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||
return val;
|
||||
uint8_t val;
|
||||
asm ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
serial_port::serial_port(uint16_t port) :
|
||||
m_port(port)
|
||||
m_port(port)
|
||||
{
|
||||
outb(port + IER, 0x00); // Disable all interrupts
|
||||
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
||||
outb(port + DLH, 0x00); // Divisor high byte
|
||||
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
||||
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||
outb(port + IER, 0x00); // Disable all interrupts
|
||||
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
||||
outb(port + DLH, 0x00); // Divisor high byte
|
||||
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
||||
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||
}
|
||||
|
||||
inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; }
|
||||
|
||||
@@ -8,14 +8,14 @@ namespace panic {
|
||||
class serial_port
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg port The IO address of the serial port
|
||||
serial_port(uint16_t port);
|
||||
/// Constructor.
|
||||
/// \arg port The IO address of the serial port
|
||||
serial_port(uint16_t port);
|
||||
|
||||
void write(const char *s);
|
||||
void write(const char *s);
|
||||
|
||||
private:
|
||||
uint16_t m_port;
|
||||
uint16_t m_port;
|
||||
};
|
||||
|
||||
constexpr uint16_t COM1 = 0x03f8;
|
||||
|
||||
@@ -16,13 +16,13 @@ symbol_table::symbol_table(const void *data) :
|
||||
const char *
|
||||
symbol_table::find_symbol(uintptr_t addr) const
|
||||
{
|
||||
// TODO: binary search
|
||||
for (auto &e : m_entries) {
|
||||
if (addr >= e.address && addr < e.address + e.size)
|
||||
// TODO: binary search
|
||||
for (auto &e : m_entries) {
|
||||
if (addr >= e.address && addr < e.address + e.size)
|
||||
return reinterpret_cast<const char*>(m_data) + e.name;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace panic
|
||||
|
||||
@@ -8,22 +8,22 @@ namespace panic {
|
||||
class symbol_table
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg data Pointer to the start of the symbol_table data.
|
||||
symbol_table(const void *data);
|
||||
/// Constructor.
|
||||
/// \arg data Pointer to the start of the symbol_table data.
|
||||
symbol_table(const void *data);
|
||||
|
||||
/// Find the name of the symbol at the given address
|
||||
/// \args addr Address to search for
|
||||
/// \returns Name of the symbol if found, or null
|
||||
const char * find_symbol(uintptr_t addr) const;
|
||||
/// Find the name of the symbol at the given address
|
||||
/// \args addr Address to search for
|
||||
/// \returns Name of the symbol if found, or null
|
||||
const char * find_symbol(uintptr_t addr) const;
|
||||
|
||||
private:
|
||||
struct entry
|
||||
{
|
||||
uintptr_t address;
|
||||
struct entry
|
||||
{
|
||||
uintptr_t address;
|
||||
size_t size;
|
||||
uintptr_t name;
|
||||
};
|
||||
uintptr_t name;
|
||||
};
|
||||
|
||||
const void *m_data;
|
||||
counted<entry const> m_entries;
|
||||
|
||||
@@ -6,174 +6,174 @@
|
||||
|
||||
struct pci_cap_msi
|
||||
{
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
uint16_t control;
|
||||
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;
|
||||
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;
|
||||
uint16_t reserved;
|
||||
uint32_t mask;
|
||||
uint32_t pending;
|
||||
pci_cap::type id;
|
||||
uint8_t next;
|
||||
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');
|
||||
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),
|
||||
m_bus_addr(0),
|
||||
m_vendor(0),
|
||||
m_device(0),
|
||||
m_class(0),
|
||||
m_subclass(0),
|
||||
m_progif(0),
|
||||
m_revision(0),
|
||||
m_irq(isr::isrIgnoreF),
|
||||
m_header_type(0)
|
||||
m_base(nullptr),
|
||||
m_bus_addr(0),
|
||||
m_vendor(0),
|
||||
m_device(0),
|
||||
m_class(0),
|
||||
m_subclass(0),
|
||||
m_progif(0),
|
||||
m_revision(0),
|
||||
m_irq(isr::isrIgnoreF),
|
||||
m_header_type(0)
|
||||
{
|
||||
}
|
||||
|
||||
pci_device::pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func) :
|
||||
m_base(group.base_for(bus, device, func)),
|
||||
m_msi(nullptr),
|
||||
m_bus_addr(bus_addr(bus, device, func)),
|
||||
m_irq(isr::isrIgnoreF)
|
||||
m_base(group.base_for(bus, device, func)),
|
||||
m_msi(nullptr),
|
||||
m_bus_addr(bus_addr(bus, device, func)),
|
||||
m_irq(isr::isrIgnoreF)
|
||||
{
|
||||
m_vendor = m_base[0] & 0xffff;
|
||||
m_device = (m_base[0] >> 16) & 0xffff;
|
||||
m_vendor = m_base[0] & 0xffff;
|
||||
m_device = (m_base[0] >> 16) & 0xffff;
|
||||
|
||||
m_revision = m_base[2] & 0xff;
|
||||
m_progif = (m_base[2] >> 8) & 0xff;
|
||||
m_subclass = (m_base[2] >> 16) & 0xff;
|
||||
m_class = (m_base[2] >> 24) & 0xff;
|
||||
m_revision = m_base[2] & 0xff;
|
||||
m_progif = (m_base[2] >> 8) & 0xff;
|
||||
m_subclass = (m_base[2] >> 16) & 0xff;
|
||||
m_class = (m_base[2] >> 24) & 0xff;
|
||||
|
||||
m_header_type = (m_base[3] >> 16) & 0x7f;
|
||||
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
|
||||
m_header_type = (m_base[3] >> 16) & 0x7f;
|
||||
m_multi = ((m_base[3] >> 16) & 0x80) == 0x80;
|
||||
|
||||
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
|
||||
*command |= 0x400; // Mask old INTx style interrupts
|
||||
uint16_t *command = reinterpret_cast<uint16_t *>(&m_base[1]);
|
||||
*command |= 0x400; // Mask old INTx style interrupts
|
||||
|
||||
uint16_t *status = command + 1;
|
||||
uint16_t *status = command + 1;
|
||||
|
||||
log::info(logs::device, "Found PCIe device at %02d:%02d:%d of type %x.%x.%x id %04x:%04x",
|
||||
bus, device, func, m_class, m_subclass, m_progif, m_vendor, m_device);
|
||||
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 (*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 (*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
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pci_device::get_bar(unsigned i)
|
||||
{
|
||||
if (m_header_type == 0) {
|
||||
kassert(i < 6, "Requested BAR >5 for PCI device");
|
||||
} else if (m_header_type == 1) {
|
||||
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
||||
} else {
|
||||
kassert(0, "Requested BAR for other PCI device type");
|
||||
}
|
||||
if (m_header_type == 0) {
|
||||
kassert(i < 6, "Requested BAR >5 for PCI device");
|
||||
} else if (m_header_type == 1) {
|
||||
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
||||
} else {
|
||||
kassert(0, "Requested BAR for other PCI device type");
|
||||
}
|
||||
|
||||
return m_base[4+i];
|
||||
return m_base[4+i];
|
||||
}
|
||||
|
||||
void
|
||||
pci_device::set_bar(unsigned i, uint32_t val)
|
||||
{
|
||||
if (m_header_type == 0) {
|
||||
kassert(i < 6, "Requested BAR >5 for PCI device");
|
||||
} else if (m_header_type == 1) {
|
||||
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
||||
} else {
|
||||
kassert(0, "Requested BAR for other PCI device type");
|
||||
}
|
||||
if (m_header_type == 0) {
|
||||
kassert(i < 6, "Requested BAR >5 for PCI device");
|
||||
} else if (m_header_type == 1) {
|
||||
kassert(i < 2, "Requested BAR >1 for PCI bridge");
|
||||
} else {
|
||||
kassert(0, "Requested BAR for other PCI device type");
|
||||
}
|
||||
|
||||
m_base[4+i] = val;
|
||||
m_base[4+i] = val;
|
||||
}
|
||||
|
||||
void
|
||||
pci_device::write_msi_regs(uintptr_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);
|
||||
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");
|
||||
}
|
||||
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);
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
pci_group::has_device(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
|
||||
return (*base_for(bus, device, func) & 0xffff) != 0xffff;
|
||||
}
|
||||
|
||||
182
src/kernel/pci.h
182
src/kernel/pci.h
@@ -10,14 +10,14 @@ enum class isr : uint8_t;
|
||||
|
||||
struct pci_cap
|
||||
{
|
||||
enum class type : uint8_t
|
||||
{
|
||||
msi = 0x05,
|
||||
msix = 0x11
|
||||
};
|
||||
enum class type : uint8_t
|
||||
{
|
||||
msi = 0x05,
|
||||
msix = 0x11
|
||||
};
|
||||
|
||||
type id;
|
||||
uint8_t next;
|
||||
type id;
|
||||
uint8_t next;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
@@ -25,117 +25,117 @@ struct pci_cap
|
||||
class pci_device
|
||||
{
|
||||
public:
|
||||
/// Default constructor creates an empty object.
|
||||
pci_device();
|
||||
/// Default constructor creates an empty object.
|
||||
pci_device();
|
||||
|
||||
/// Constructor
|
||||
/// \arg group The group of this device's bus
|
||||
/// \arg bus The bus number this device is on
|
||||
/// \arg device The device number of this device
|
||||
/// \arg func The function number of this device
|
||||
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
|
||||
/// Constructor
|
||||
/// \arg group The group of this device's bus
|
||||
/// \arg bus The bus number this device is on
|
||||
/// \arg device The device number of this device
|
||||
/// \arg func The function number of this device
|
||||
pci_device(pci_group &group, uint8_t bus, uint8_t device, uint8_t func);
|
||||
|
||||
/// Check if this device is multi-function.
|
||||
/// \returns True if this device is multi-function
|
||||
inline bool multi() const { return m_multi; }
|
||||
/// Check if this device is multi-function.
|
||||
/// \returns True if this device is multi-function
|
||||
inline bool multi() const { return m_multi; }
|
||||
|
||||
/// Get the bus number this device is on.
|
||||
/// \returns The bus number
|
||||
inline uint8_t bus() const { return (m_bus_addr >> 8); }
|
||||
/// Get the bus number this device is on.
|
||||
/// \returns The bus number
|
||||
inline uint8_t bus() const { return (m_bus_addr >> 8); }
|
||||
|
||||
/// Get the device number of this device on its bus
|
||||
/// \returns The device number
|
||||
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
|
||||
/// Get the device number of this device on its bus
|
||||
/// \returns The device number
|
||||
inline uint8_t device() const { return (m_bus_addr >> 3) & 0x1f; }
|
||||
|
||||
/// Get the function number of this device on its device
|
||||
/// \returns The function number
|
||||
inline uint8_t function() const { return m_bus_addr & 0x7; }
|
||||
/// Get the function number of this device on its device
|
||||
/// \returns The function number
|
||||
inline uint8_t function() const { return m_bus_addr & 0x7; }
|
||||
|
||||
/// Get the device class
|
||||
/// \returns The PCI device class
|
||||
inline uint8_t devclass() const { return m_class; }
|
||||
/// Get the device class
|
||||
/// \returns The PCI device class
|
||||
inline uint8_t devclass() const { return m_class; }
|
||||
|
||||
/// Get the device subclass
|
||||
/// \returns The PCI device subclass
|
||||
inline uint8_t subclass() const { return m_subclass; }
|
||||
/// Get the device subclass
|
||||
/// \returns The PCI device subclass
|
||||
inline uint8_t subclass() const { return m_subclass; }
|
||||
|
||||
/// Get the device program interface
|
||||
/// \returns The PCI device program interface
|
||||
inline uint8_t progif() const { return m_progif; }
|
||||
/// Get the device program interface
|
||||
/// \returns The PCI device program interface
|
||||
inline uint8_t progif() const { return m_progif; }
|
||||
|
||||
/// Read one of the device's Base Address Registers
|
||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||
/// \returns The contents of the BAR
|
||||
uint32_t get_bar(unsigned i);
|
||||
/// Read one of the device's Base Address Registers
|
||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||
/// \returns The contents of the BAR
|
||||
uint32_t get_bar(unsigned i);
|
||||
|
||||
/// Write one of the device's Base Address Registers
|
||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||
/// \arg val The value to write
|
||||
void set_bar(unsigned i, uint32_t val);
|
||||
/// Write one of the device's Base Address Registers
|
||||
/// \arg i Which BAR to read (up to 5 for non-bridges)
|
||||
/// \arg val The value to write
|
||||
void set_bar(unsigned i, uint32_t val);
|
||||
|
||||
/// Write to the MSI registers
|
||||
/// \arg addr The address to write to the MSI address registers
|
||||
/// \arg data The value to write to the MSI data register
|
||||
void write_msi_regs(uintptr_t addr, uint16_t data);
|
||||
/// Write to the MSI registers
|
||||
/// \arg addr The address to write to the MSI address registers
|
||||
/// \arg data The value to write to the MSI data register
|
||||
void write_msi_regs(uintptr_t addr, uint16_t data);
|
||||
|
||||
/// Get a bus address, given the bus/device/function numbers.
|
||||
/// \arg bus Number of the bus
|
||||
/// \arg device Index of the device on the bus
|
||||
/// \arg func The function number within the device
|
||||
/// \returns The computed bus_addr
|
||||
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return bus << 8 | device << 3 | func;
|
||||
}
|
||||
/// Get a bus address, given the bus/device/function numbers.
|
||||
/// \arg bus Number of the bus
|
||||
/// \arg device Index of the device on the bus
|
||||
/// \arg func The function number within the device
|
||||
/// \returns The computed bus_addr
|
||||
static inline uint16_t bus_addr(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return bus << 8 | device << 3 | func;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t *m_base;
|
||||
pci_cap *m_msi;
|
||||
uint32_t *m_base;
|
||||
pci_cap *m_msi;
|
||||
|
||||
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
||||
uint16_t m_bus_addr;
|
||||
/// Bus address: 15:8 bus, 7:3 device, 2:0 device
|
||||
uint16_t m_bus_addr;
|
||||
|
||||
uint16_t m_vendor;
|
||||
uint16_t m_device;
|
||||
uint16_t m_vendor;
|
||||
uint16_t m_device;
|
||||
|
||||
uint8_t m_class;
|
||||
uint8_t m_subclass;
|
||||
uint8_t m_progif;
|
||||
uint8_t m_revision;
|
||||
bool m_multi;
|
||||
uint8_t m_class;
|
||||
uint8_t m_subclass;
|
||||
uint8_t m_progif;
|
||||
uint8_t m_revision;
|
||||
bool m_multi;
|
||||
|
||||
// Might as well cache these to fill out the struct align
|
||||
isr m_irq;
|
||||
uint8_t m_header_type;
|
||||
// Might as well cache these to fill out the struct align
|
||||
isr m_irq;
|
||||
uint8_t m_header_type;
|
||||
};
|
||||
|
||||
|
||||
/// Represents data about a PCI bus group from the ACPI MCFG
|
||||
struct pci_group
|
||||
{
|
||||
uint16_t group;
|
||||
uint16_t bus_start;
|
||||
uint16_t bus_end;
|
||||
uint16_t group;
|
||||
uint16_t bus_start;
|
||||
uint16_t bus_end;
|
||||
|
||||
uint32_t *base;
|
||||
uint32_t *base;
|
||||
|
||||
/// Get the base address of the MMIO configuration registers for a device
|
||||
/// \arg bus The bus number of the device (relative to this group)
|
||||
/// \arg device The device number on the given bus
|
||||
/// \arg func The function number on the device
|
||||
/// \returns A pointer to the memory-mapped configuration registers
|
||||
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return kutil::offset_pointer(base,
|
||||
pci_device::bus_addr(bus, device, func) << 12);
|
||||
}
|
||||
/// Get the base address of the MMIO configuration registers for a device
|
||||
/// \arg bus The bus number of the device (relative to this group)
|
||||
/// \arg device The device number on the given bus
|
||||
/// \arg func The function number on the device
|
||||
/// \returns A pointer to the memory-mapped configuration registers
|
||||
inline uint32_t * base_for(uint8_t bus, uint8_t device, uint8_t func)
|
||||
{
|
||||
return kutil::offset_pointer(base,
|
||||
pci_device::bus_addr(bus, device, func) << 12);
|
||||
}
|
||||
|
||||
/// Check if the given device function is present.
|
||||
/// \arg bus The bus number of the device (relative to this group)
|
||||
/// \arg device The device number on the given bus
|
||||
/// \arg func The function number on the device
|
||||
/// \returns True if the device function is present
|
||||
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
|
||||
/// Check if the given device function is present.
|
||||
/// \arg bus The bus number of the device (relative to this group)
|
||||
/// \arg device The device number on the given bus
|
||||
/// \arg func The function number on the device
|
||||
/// \returns True if the device function is present
|
||||
bool has_device(uint8_t bus, uint8_t device, uint8_t func);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -31,304 +31,304 @@ scheduler *scheduler::s_instance = nullptr;
|
||||
|
||||
struct run_queue
|
||||
{
|
||||
tcb_node *current = nullptr;
|
||||
tcb_list ready[scheduler::num_priorities];
|
||||
tcb_list blocked;
|
||||
tcb_node *current = nullptr;
|
||||
tcb_list ready[scheduler::num_priorities];
|
||||
tcb_list blocked;
|
||||
|
||||
uint64_t last_promotion = 0;
|
||||
uint64_t last_steal = 0;
|
||||
kutil::spinlock lock;
|
||||
uint64_t last_promotion = 0;
|
||||
uint64_t last_steal = 0;
|
||||
kutil::spinlock lock;
|
||||
};
|
||||
|
||||
scheduler::scheduler(unsigned cpus) :
|
||||
m_next_pid {1},
|
||||
m_clock {0}
|
||||
m_next_pid {1},
|
||||
m_clock {0}
|
||||
{
|
||||
kassert(!s_instance, "Created multiple schedulers!");
|
||||
if (!s_instance)
|
||||
s_instance = this;
|
||||
kassert(!s_instance, "Created multiple schedulers!");
|
||||
if (!s_instance)
|
||||
s_instance = this;
|
||||
|
||||
m_run_queues.set_size(cpus);
|
||||
m_run_queues.set_size(cpus);
|
||||
}
|
||||
|
||||
scheduler::~scheduler()
|
||||
{
|
||||
// Not truly necessary - if the scheduler is going away, the whole
|
||||
// system is probably going down. But let's be clean.
|
||||
if (s_instance == this)
|
||||
s_instance = nullptr;
|
||||
// Not truly necessary - if the scheduler is going away, the whole
|
||||
// system is probably going down. But let's be clean.
|
||||
if (s_instance == this)
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) {
|
||||
rsp -= size;
|
||||
T *p = reinterpret_cast<T*>(rsp);
|
||||
rsp &= ~(sizeof(uint64_t)-1); // Align the stack
|
||||
return p;
|
||||
rsp -= size;
|
||||
T *p = reinterpret_cast<T*>(rsp);
|
||||
rsp &= ~(sizeof(uint64_t)-1); // Align the stack
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant)
|
||||
{
|
||||
thread *th = process::kernel_process().create_thread(priority, false);
|
||||
auto *tcb = th->tcb();
|
||||
thread *th = process::kernel_process().create_thread(priority, false);
|
||||
auto *tcb = th->tcb();
|
||||
|
||||
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(task));
|
||||
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(task));
|
||||
|
||||
tcb->time_left = quantum(priority);
|
||||
if (constant)
|
||||
th->set_state(thread::state::constant);
|
||||
tcb->time_left = quantum(priority);
|
||||
if (constant)
|
||||
th->set_state(thread::state::constant);
|
||||
|
||||
th->set_state(thread::state::ready);
|
||||
th->set_state(thread::state::ready);
|
||||
|
||||
log::debug(logs::task, "Creating kernel task: thread %llx pri %d", th->koid(), tcb->priority);
|
||||
log::debug(logs::task, " RSP0 %016lx", tcb->rsp0);
|
||||
log::debug(logs::task, " RSP %016lx", tcb->rsp);
|
||||
log::debug(logs::task, " PML4 %016lx", tcb->pml4);
|
||||
log::debug(logs::task, "Creating kernel task: thread %llx pri %d", th->koid(), tcb->priority);
|
||||
log::debug(logs::task, " RSP0 %016lx", tcb->rsp0);
|
||||
log::debug(logs::task, " RSP %016lx", tcb->rsp);
|
||||
log::debug(logs::task, " PML4 %016lx", tcb->pml4);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
scheduler::quantum(int priority)
|
||||
{
|
||||
return quantum_micros << priority;
|
||||
return quantum_micros << priority;
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::start()
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
run_queue &queue = m_run_queues[cpu.index];
|
||||
cpu_data &cpu = current_cpu();
|
||||
run_queue &queue = m_run_queues[cpu.index];
|
||||
|
||||
{
|
||||
kutil::scoped_lock lock {queue.lock};
|
||||
{
|
||||
kutil::scoped_lock lock {queue.lock};
|
||||
|
||||
process *kp = &process::kernel_process();
|
||||
thread *idle = thread::create_idle_thread(*kp, max_priority, cpu.rsp0);
|
||||
process *kp = &process::kernel_process();
|
||||
thread *idle = thread::create_idle_thread(*kp, max_priority, cpu.rsp0);
|
||||
|
||||
auto *tcb = idle->tcb();
|
||||
cpu.process = kp;
|
||||
cpu.thread = idle;
|
||||
cpu.tcb = tcb;
|
||||
auto *tcb = idle->tcb();
|
||||
cpu.process = kp;
|
||||
cpu.thread = idle;
|
||||
cpu.tcb = tcb;
|
||||
|
||||
queue.current = tcb;
|
||||
}
|
||||
queue.current = tcb;
|
||||
}
|
||||
|
||||
cpu.apic->enable_timer(isr::isrTimer, false);
|
||||
cpu.apic->reset_timer(10);
|
||||
cpu.apic->enable_timer(isr::isrTimer, false);
|
||||
cpu.apic->reset_timer(10);
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::add_thread(TCB *t)
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
run_queue &queue = m_run_queues[cpu.index];
|
||||
kutil::scoped_lock lock {queue.lock};
|
||||
cpu_data &cpu = current_cpu();
|
||||
run_queue &queue = m_run_queues[cpu.index];
|
||||
kutil::scoped_lock lock {queue.lock};
|
||||
|
||||
queue.blocked.push_back(static_cast<tcb_node*>(t));
|
||||
t->time_left = quantum(t->priority);
|
||||
queue.blocked.push_back(static_cast<tcb_node*>(t));
|
||||
t->time_left = quantum(t->priority);
|
||||
}
|
||||
|
||||
void scheduler::prune(run_queue &queue, uint64_t now)
|
||||
{
|
||||
// Find processes that are ready or have exited and
|
||||
// move them to the appropriate lists.
|
||||
auto *tcb = queue.blocked.front();
|
||||
while (tcb) {
|
||||
thread *th = thread::from_tcb(tcb);
|
||||
uint8_t priority = tcb->priority;
|
||||
// Find processes that are ready or have exited and
|
||||
// move them to the appropriate lists.
|
||||
auto *tcb = queue.blocked.front();
|
||||
while (tcb) {
|
||||
thread *th = thread::from_tcb(tcb);
|
||||
uint8_t priority = tcb->priority;
|
||||
|
||||
bool ready = th->has_state(thread::state::ready);
|
||||
bool exited = th->has_state(thread::state::exited);
|
||||
bool constant = th->has_state(thread::state::constant);
|
||||
bool current = tcb == queue.current;
|
||||
bool ready = th->has_state(thread::state::ready);
|
||||
bool exited = th->has_state(thread::state::exited);
|
||||
bool constant = th->has_state(thread::state::constant);
|
||||
bool current = tcb == queue.current;
|
||||
|
||||
ready |= th->wake_on_time(now);
|
||||
ready |= th->wake_on_time(now);
|
||||
|
||||
auto *remove = tcb;
|
||||
tcb = tcb->next();
|
||||
if (!exited && !ready)
|
||||
continue;
|
||||
auto *remove = tcb;
|
||||
tcb = tcb->next();
|
||||
if (!exited && !ready)
|
||||
continue;
|
||||
|
||||
if (exited) {
|
||||
// If the current thread has exited, wait until the next call
|
||||
// to prune() to delete it, because we may be deleting our current
|
||||
// page tables
|
||||
if (current) continue;
|
||||
if (exited) {
|
||||
// If the current thread has exited, wait until the next call
|
||||
// to prune() to delete it, because we may be deleting our current
|
||||
// page tables
|
||||
if (current) continue;
|
||||
|
||||
queue.blocked.remove(remove);
|
||||
process &p = th->parent();
|
||||
queue.blocked.remove(remove);
|
||||
process &p = th->parent();
|
||||
|
||||
// thread_exited deletes the thread, and returns true if the process
|
||||
// should also now be deleted
|
||||
if(!current && p.thread_exited(th))
|
||||
delete &p;
|
||||
} else {
|
||||
queue.blocked.remove(remove);
|
||||
log::debug(logs::sched, "Prune: readying unblocked thread %llx", th->koid());
|
||||
queue.ready[remove->priority].push_back(remove);
|
||||
}
|
||||
}
|
||||
// thread_exited deletes the thread, and returns true if the process
|
||||
// should also now be deleted
|
||||
if(!current && p.thread_exited(th))
|
||||
delete &p;
|
||||
} else {
|
||||
queue.blocked.remove(remove);
|
||||
log::debug(logs::sched, "Prune: readying unblocked thread %llx", th->koid());
|
||||
queue.ready[remove->priority].push_back(remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::check_promotions(run_queue &queue, uint64_t now)
|
||||
{
|
||||
for (auto &pri_list : queue.ready) {
|
||||
for (auto *tcb : pri_list) {
|
||||
const thread *th = thread::from_tcb(queue.current);
|
||||
const bool constant = th->has_state(thread::state::constant);
|
||||
if (constant)
|
||||
continue;
|
||||
for (auto &pri_list : queue.ready) {
|
||||
for (auto *tcb : pri_list) {
|
||||
const thread *th = thread::from_tcb(queue.current);
|
||||
const bool constant = th->has_state(thread::state::constant);
|
||||
if (constant)
|
||||
continue;
|
||||
|
||||
const uint64_t age = now - tcb->last_ran;
|
||||
const uint8_t priority = tcb->priority;
|
||||
const uint64_t age = now - tcb->last_ran;
|
||||
const uint8_t priority = tcb->priority;
|
||||
|
||||
bool stale =
|
||||
age > quantum(priority) * 2 &&
|
||||
tcb->priority > promote_limit &&
|
||||
!constant;
|
||||
bool stale =
|
||||
age > quantum(priority) * 2 &&
|
||||
tcb->priority > promote_limit &&
|
||||
!constant;
|
||||
|
||||
if (stale) {
|
||||
// If the thread is stale, promote it
|
||||
queue.ready[priority].remove(tcb);
|
||||
tcb->priority -= 1;
|
||||
tcb->time_left = quantum(tcb->priority);
|
||||
queue.ready[tcb->priority].push_back(tcb);
|
||||
log::info(logs::sched, "Scheduler promoting thread %llx, priority %d",
|
||||
th->koid(), tcb->priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stale) {
|
||||
// If the thread is stale, promote it
|
||||
queue.ready[priority].remove(tcb);
|
||||
tcb->priority -= 1;
|
||||
tcb->time_left = quantum(tcb->priority);
|
||||
queue.ready[tcb->priority].push_back(tcb);
|
||||
log::info(logs::sched, "Scheduler promoting thread %llx, priority %d",
|
||||
th->koid(), tcb->priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queue.last_promotion = now;
|
||||
queue.last_promotion = now;
|
||||
}
|
||||
|
||||
static size_t
|
||||
balance_lists(tcb_list &to, tcb_list &from)
|
||||
{
|
||||
size_t to_len = to.length();
|
||||
size_t from_len = from.length();
|
||||
size_t to_len = to.length();
|
||||
size_t from_len = from.length();
|
||||
|
||||
// Only steal from the rich, don't be Dennis Moore
|
||||
if (from_len <= to_len)
|
||||
return 0;
|
||||
// Only steal from the rich, don't be Dennis Moore
|
||||
if (from_len <= to_len)
|
||||
return 0;
|
||||
|
||||
size_t steal = (from_len - to_len) / 2;
|
||||
for (size_t i = 0; i < steal; ++i)
|
||||
to.push_front(from.pop_front());
|
||||
return steal;
|
||||
size_t steal = (from_len - to_len) / 2;
|
||||
for (size_t i = 0; i < steal; ++i)
|
||||
to.push_front(from.pop_front());
|
||||
return steal;
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::steal_work(cpu_data &cpu)
|
||||
{
|
||||
// Lock this cpu's queue for the whole time while we modify it
|
||||
run_queue &my_queue = m_run_queues[cpu.index];
|
||||
kutil::scoped_lock my_queue_lock {my_queue.lock};
|
||||
// Lock this cpu's queue for the whole time while we modify it
|
||||
run_queue &my_queue = m_run_queues[cpu.index];
|
||||
kutil::scoped_lock my_queue_lock {my_queue.lock};
|
||||
|
||||
const unsigned count = m_run_queues.count();
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
if (i == cpu.index) continue;
|
||||
const unsigned count = m_run_queues.count();
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
if (i == cpu.index) continue;
|
||||
|
||||
run_queue &other_queue = m_run_queues[i];
|
||||
kutil::scoped_lock other_queue_lock {other_queue.lock};
|
||||
run_queue &other_queue = m_run_queues[i];
|
||||
kutil::scoped_lock other_queue_lock {other_queue.lock};
|
||||
|
||||
size_t stolen = 0;
|
||||
size_t stolen = 0;
|
||||
|
||||
// Don't steal from max_priority, that's the idle thread
|
||||
for (unsigned pri = 0; pri < max_priority; ++pri)
|
||||
stolen += balance_lists(my_queue.ready[pri], other_queue.ready[pri]);
|
||||
// Don't steal from max_priority, that's the idle thread
|
||||
for (unsigned pri = 0; pri < max_priority; ++pri)
|
||||
stolen += balance_lists(my_queue.ready[pri], other_queue.ready[pri]);
|
||||
|
||||
stolen += balance_lists(my_queue.blocked, other_queue.blocked);
|
||||
stolen += balance_lists(my_queue.blocked, other_queue.blocked);
|
||||
|
||||
if (stolen)
|
||||
log::debug(logs::sched, "CPU%02x stole %2d tasks from CPU%02x",
|
||||
cpu.index, stolen, i);
|
||||
}
|
||||
if (stolen)
|
||||
log::debug(logs::sched, "CPU%02x stole %2d tasks from CPU%02x",
|
||||
cpu.index, stolen, i);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::schedule()
|
||||
{
|
||||
cpu_data &cpu = current_cpu();
|
||||
run_queue &queue = m_run_queues[cpu.index];
|
||||
lapic &apic = *cpu.apic;
|
||||
uint32_t remaining = apic.stop_timer();
|
||||
cpu_data &cpu = current_cpu();
|
||||
run_queue &queue = m_run_queues[cpu.index];
|
||||
lapic &apic = *cpu.apic;
|
||||
uint32_t remaining = apic.stop_timer();
|
||||
|
||||
// Only one CPU can be stealing at a time
|
||||
if (m_steal_turn == cpu.index &&
|
||||
m_clock - queue.last_steal > steal_frequency) {
|
||||
steal_work(cpu);
|
||||
queue.last_steal = m_clock;
|
||||
m_steal_turn = (m_steal_turn + 1) % m_run_queues.count();
|
||||
}
|
||||
// Only one CPU can be stealing at a time
|
||||
if (m_steal_turn == cpu.index &&
|
||||
m_clock - queue.last_steal > steal_frequency) {
|
||||
steal_work(cpu);
|
||||
queue.last_steal = m_clock;
|
||||
m_steal_turn = (m_steal_turn + 1) % m_run_queues.count();
|
||||
}
|
||||
|
||||
// We need to explicitly lock/unlock here instead of
|
||||
// using a scoped lock, because the scope doesn't "end"
|
||||
// for the current thread until it gets scheduled again
|
||||
kutil::spinlock::waiter waiter;
|
||||
queue.lock.acquire(&waiter);
|
||||
// We need to explicitly lock/unlock here instead of
|
||||
// using a scoped lock, because the scope doesn't "end"
|
||||
// for the current thread until it gets scheduled again
|
||||
kutil::spinlock::waiter waiter;
|
||||
queue.lock.acquire(&waiter);
|
||||
|
||||
queue.current->time_left = remaining;
|
||||
thread *th = thread::from_tcb(queue.current);
|
||||
uint8_t priority = queue.current->priority;
|
||||
const bool constant = th->has_state(thread::state::constant);
|
||||
queue.current->time_left = remaining;
|
||||
thread *th = thread::from_tcb(queue.current);
|
||||
uint8_t priority = queue.current->priority;
|
||||
const bool constant = th->has_state(thread::state::constant);
|
||||
|
||||
if (remaining == 0) {
|
||||
if (priority < max_priority && !constant) {
|
||||
// Process used its whole timeslice, demote it
|
||||
++queue.current->priority;
|
||||
log::debug(logs::sched, "Scheduler demoting thread %llx, priority %d",
|
||||
th->koid(), queue.current->priority);
|
||||
}
|
||||
queue.current->time_left = quantum(queue.current->priority);
|
||||
} else if (remaining > 0) {
|
||||
// Process gave up CPU, give it a small bonus to its
|
||||
// remaining timeslice.
|
||||
uint32_t bonus = quantum(priority) >> 4;
|
||||
queue.current->time_left += bonus;
|
||||
}
|
||||
if (remaining == 0) {
|
||||
if (priority < max_priority && !constant) {
|
||||
// Process used its whole timeslice, demote it
|
||||
++queue.current->priority;
|
||||
log::debug(logs::sched, "Scheduler demoting thread %llx, priority %d",
|
||||
th->koid(), queue.current->priority);
|
||||
}
|
||||
queue.current->time_left = quantum(queue.current->priority);
|
||||
} else if (remaining > 0) {
|
||||
// Process gave up CPU, give it a small bonus to its
|
||||
// remaining timeslice.
|
||||
uint32_t bonus = quantum(priority) >> 4;
|
||||
queue.current->time_left += bonus;
|
||||
}
|
||||
|
||||
if (th->has_state(thread::state::ready)) {
|
||||
queue.ready[queue.current->priority].push_back(queue.current);
|
||||
} else {
|
||||
queue.blocked.push_back(queue.current);
|
||||
}
|
||||
if (th->has_state(thread::state::ready)) {
|
||||
queue.ready[queue.current->priority].push_back(queue.current);
|
||||
} else {
|
||||
queue.blocked.push_back(queue.current);
|
||||
}
|
||||
|
||||
clock::get().update();
|
||||
prune(queue, ++m_clock);
|
||||
if (m_clock - queue.last_promotion > promote_frequency)
|
||||
check_promotions(queue, m_clock);
|
||||
clock::get().update();
|
||||
prune(queue, ++m_clock);
|
||||
if (m_clock - queue.last_promotion > promote_frequency)
|
||||
check_promotions(queue, m_clock);
|
||||
|
||||
priority = 0;
|
||||
while (queue.ready[priority].empty()) {
|
||||
++priority;
|
||||
kassert(priority < num_priorities, "All runlists are empty");
|
||||
}
|
||||
priority = 0;
|
||||
while (queue.ready[priority].empty()) {
|
||||
++priority;
|
||||
kassert(priority < num_priorities, "All runlists are empty");
|
||||
}
|
||||
|
||||
queue.current->last_ran = m_clock;
|
||||
queue.current->last_ran = m_clock;
|
||||
|
||||
auto *next = queue.ready[priority].pop_front();
|
||||
next->last_ran = m_clock;
|
||||
apic.reset_timer(next->time_left);
|
||||
auto *next = queue.ready[priority].pop_front();
|
||||
next->last_ran = m_clock;
|
||||
apic.reset_timer(next->time_left);
|
||||
|
||||
if (next == queue.current) {
|
||||
queue.lock.release(&waiter);
|
||||
return;
|
||||
}
|
||||
if (next == queue.current) {
|
||||
queue.lock.release(&waiter);
|
||||
return;
|
||||
}
|
||||
|
||||
thread *next_thread = thread::from_tcb(next);
|
||||
thread *next_thread = thread::from_tcb(next);
|
||||
|
||||
cpu.thread = next_thread;
|
||||
cpu.process = &next_thread->parent();
|
||||
queue.current = next;
|
||||
cpu.thread = next_thread;
|
||||
cpu.process = &next_thread->parent();
|
||||
queue.current = next;
|
||||
|
||||
log::debug(logs::sched, "CPU%02x switching threads %llx->%llx",
|
||||
cpu.index, th->koid(), next_thread->koid());
|
||||
log::debug(logs::sched, " priority %d time left %d @ %lld.",
|
||||
next->priority, next->time_left, m_clock);
|
||||
log::debug(logs::sched, " PML4 %llx", next->pml4);
|
||||
log::debug(logs::sched, "CPU%02x switching threads %llx->%llx",
|
||||
cpu.index, th->koid(), next_thread->koid());
|
||||
log::debug(logs::sched, " priority %d time left %d @ %lld.",
|
||||
next->priority, next->time_left, m_clock);
|
||||
log::debug(logs::sched, " PML4 %llx", next->pml4);
|
||||
|
||||
queue.lock.release(&waiter);
|
||||
task_switch(queue.current);
|
||||
queue.lock.release(&waiter);
|
||||
task_switch(queue.current);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace kernel {
|
||||
namespace args {
|
||||
struct program;
|
||||
struct program;
|
||||
}}
|
||||
|
||||
struct cpu_data;
|
||||
@@ -22,82 +22,82 @@ struct run_queue;
|
||||
class scheduler
|
||||
{
|
||||
public:
|
||||
/// Total number of priority levels
|
||||
static const uint8_t num_priorities = 8;
|
||||
/// Total number of priority levels
|
||||
static const uint8_t num_priorities = 8;
|
||||
|
||||
/// Maximum (least urgent/interactive) priority
|
||||
static const uint8_t max_priority = num_priorities - 1;
|
||||
/// Maximum (least urgent/interactive) priority
|
||||
static const uint8_t max_priority = num_priorities - 1;
|
||||
|
||||
/// Default priority on process creation
|
||||
static const uint8_t default_priority = 1;
|
||||
/// Default priority on process creation
|
||||
static const uint8_t default_priority = 1;
|
||||
|
||||
/// Lowest (most urgent) priority achieved via promotion
|
||||
static const uint8_t promote_limit = 1;
|
||||
/// Lowest (most urgent) priority achieved via promotion
|
||||
static const uint8_t promote_limit = 1;
|
||||
|
||||
/// How long the base timer quantum is, in us
|
||||
static const uint64_t quantum_micros = 500;
|
||||
/// How long the base timer quantum is, in us
|
||||
static const uint64_t quantum_micros = 500;
|
||||
|
||||
/// How many quanta a process gets before being rescheduled
|
||||
static const uint16_t process_quanta = 10;
|
||||
/// How many quanta a process gets before being rescheduled
|
||||
static const uint16_t process_quanta = 10;
|
||||
|
||||
/// Constructor.
|
||||
/// \arg cpus The number of CPUs to schedule for
|
||||
scheduler(unsigned cpus);
|
||||
~scheduler();
|
||||
/// Constructor.
|
||||
/// \arg cpus The number of CPUs to schedule for
|
||||
scheduler(unsigned cpus);
|
||||
~scheduler();
|
||||
|
||||
/// Create a new process from a program image in memory.
|
||||
/// \arg program The descriptor of the pogram in memory
|
||||
/// \returns The main thread of the loaded process
|
||||
thread * load_process(kernel::args::program &program);
|
||||
/// Create a new process from a program image in memory.
|
||||
/// \arg program The descriptor of the pogram in memory
|
||||
/// \returns The main thread of the loaded process
|
||||
thread * load_process(kernel::args::program &program);
|
||||
|
||||
/// Create a new kernel task
|
||||
/// \arg proc Function to run as a kernel task
|
||||
/// \arg priority Priority to start the process with
|
||||
/// \arg constant True if this task cannot be promoted/demoted
|
||||
void create_kernel_task(
|
||||
void (*task)(),
|
||||
uint8_t priority,
|
||||
bool constant = false);
|
||||
/// Create a new kernel task
|
||||
/// \arg proc Function to run as a kernel task
|
||||
/// \arg priority Priority to start the process with
|
||||
/// \arg constant True if this task cannot be promoted/demoted
|
||||
void create_kernel_task(
|
||||
void (*task)(),
|
||||
uint8_t priority,
|
||||
bool constant = false);
|
||||
|
||||
/// Get the quantum for a given priority.
|
||||
static uint32_t quantum(int priority);
|
||||
/// Get the quantum for a given priority.
|
||||
static uint32_t quantum(int priority);
|
||||
|
||||
/// Start the scheduler working. This may involve starting
|
||||
/// timer interrupts or other preemption methods.
|
||||
void start();
|
||||
/// Start the scheduler working. This may involve starting
|
||||
/// timer interrupts or other preemption methods.
|
||||
void start();
|
||||
|
||||
/// Run the scheduler, possibly switching to a new task
|
||||
void schedule();
|
||||
/// Run the scheduler, possibly switching to a new task
|
||||
void schedule();
|
||||
|
||||
/// Start scheduling a new thread.
|
||||
/// \arg t The new thread's TCB
|
||||
void add_thread(TCB *t);
|
||||
/// Start scheduling a new thread.
|
||||
/// \arg t The new thread's TCB
|
||||
void add_thread(TCB *t);
|
||||
|
||||
/// Get a reference to the scheduler
|
||||
/// \returns A reference to the global system scheduler
|
||||
static scheduler & get() { return *s_instance; }
|
||||
/// Get a reference to the scheduler
|
||||
/// \returns A reference to the global system scheduler
|
||||
static scheduler & get() { return *s_instance; }
|
||||
|
||||
private:
|
||||
friend class process;
|
||||
friend class process;
|
||||
|
||||
static constexpr uint64_t promote_frequency = 10;
|
||||
static constexpr uint64_t steal_frequency = 10;
|
||||
static constexpr uint64_t promote_frequency = 10;
|
||||
static constexpr uint64_t steal_frequency = 10;
|
||||
|
||||
void prune(run_queue &queue, uint64_t now);
|
||||
void check_promotions(run_queue &queue, uint64_t now);
|
||||
void steal_work(cpu_data &cpu);
|
||||
void prune(run_queue &queue, uint64_t now);
|
||||
void check_promotions(run_queue &queue, uint64_t now);
|
||||
void steal_work(cpu_data &cpu);
|
||||
|
||||
uint32_t m_next_pid;
|
||||
uint32_t m_tick_count;
|
||||
uint32_t m_next_pid;
|
||||
uint32_t m_tick_count;
|
||||
|
||||
process *m_kernel_process;
|
||||
process *m_kernel_process;
|
||||
|
||||
kutil::vector<run_queue> m_run_queues;
|
||||
kutil::vector<run_queue> m_run_queues;
|
||||
|
||||
// TODO: lol a real clock
|
||||
uint64_t m_clock = 0;
|
||||
// TODO: lol a real clock
|
||||
uint64_t m_clock = 0;
|
||||
|
||||
unsigned m_steal_turn = 0;
|
||||
static scheduler *s_instance;
|
||||
unsigned m_steal_turn = 0;
|
||||
static scheduler *s_instance;
|
||||
};
|
||||
|
||||
|
||||
@@ -31,28 +31,28 @@ uint8_t com1_out_buffer[4096*4];
|
||||
uint8_t com1_in_buffer[512];
|
||||
|
||||
serial_port::serial_port() :
|
||||
m_writing(false),
|
||||
m_port(0)
|
||||
m_writing(false),
|
||||
m_port(0)
|
||||
{
|
||||
}
|
||||
|
||||
serial_port::serial_port(uint16_t port) :
|
||||
m_writing(false),
|
||||
m_port(port),
|
||||
m_out_buffer(com1_out_buffer, sizeof(com1_out_buffer)),
|
||||
m_in_buffer(com1_in_buffer, sizeof(com1_in_buffer))
|
||||
m_writing(false),
|
||||
m_port(port),
|
||||
m_out_buffer(com1_out_buffer, sizeof(com1_out_buffer)),
|
||||
m_in_buffer(com1_in_buffer, sizeof(com1_in_buffer))
|
||||
{
|
||||
outb(port + IER, 0x00); // Disable all interrupts
|
||||
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
||||
outb(port + DLH, 0x00); // Divisor high byte
|
||||
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
||||
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||
outb(port + IER, 0x03); // Enable interrupts
|
||||
outb(port + IER, 0x00); // Disable all interrupts
|
||||
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
||||
outb(port + DLH, 0x00); // Divisor high byte
|
||||
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
||||
outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||
outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||
outb(port + IER, 0x03); // Enable interrupts
|
||||
|
||||
// Clear out pending interrupts
|
||||
handle_interrupt();
|
||||
// Clear out pending interrupts
|
||||
handle_interrupt();
|
||||
}
|
||||
|
||||
inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; }
|
||||
@@ -61,102 +61,102 @@ inline bool write_ready(uint16_t port) { return (inb(port + LSR) & 0x20) != 0; }
|
||||
void
|
||||
serial_port::handle_interrupt()
|
||||
{
|
||||
interrupts_disable();
|
||||
uint8_t iir = inb(m_port + IIR);
|
||||
interrupts_disable();
|
||||
uint8_t iir = inb(m_port + IIR);
|
||||
|
||||
while ((iir & 1) == 0) {
|
||||
uint8_t reg = 0;
|
||||
switch ((iir>>1) & 0x3) {
|
||||
case 0: // Modem status change
|
||||
reg = inb(m_port + MSR);
|
||||
handle_error(MSR, reg);
|
||||
break;
|
||||
while ((iir & 1) == 0) {
|
||||
uint8_t reg = 0;
|
||||
switch ((iir>>1) & 0x3) {
|
||||
case 0: // Modem status change
|
||||
reg = inb(m_port + MSR);
|
||||
handle_error(MSR, reg);
|
||||
break;
|
||||
|
||||
case 1: // Transmit buffer empty
|
||||
do_write();
|
||||
break;
|
||||
case 1: // Transmit buffer empty
|
||||
do_write();
|
||||
break;
|
||||
|
||||
case 2: // Received data available
|
||||
do_read();
|
||||
break;
|
||||
case 2: // Received data available
|
||||
do_read();
|
||||
break;
|
||||
|
||||
case 3: // Line status change
|
||||
reg = inb(m_port + LSR);
|
||||
handle_error(LSR, reg);
|
||||
break;
|
||||
}
|
||||
case 3: // Line status change
|
||||
reg = inb(m_port + LSR);
|
||||
handle_error(LSR, reg);
|
||||
break;
|
||||
}
|
||||
|
||||
iir = inb(m_port + IIR);
|
||||
}
|
||||
interrupts_enable();
|
||||
iir = inb(m_port + IIR);
|
||||
}
|
||||
interrupts_enable();
|
||||
}
|
||||
|
||||
void
|
||||
serial_port::do_read()
|
||||
{
|
||||
size_t used = 0;
|
||||
uint8_t *data = nullptr;
|
||||
size_t avail = m_in_buffer.reserve(fifo_size, reinterpret_cast<void**>(&data));
|
||||
size_t used = 0;
|
||||
uint8_t *data = nullptr;
|
||||
size_t avail = m_in_buffer.reserve(fifo_size, reinterpret_cast<void**>(&data));
|
||||
|
||||
while (used < avail && read_ready(m_port)) {
|
||||
*data++ = inb(m_port);
|
||||
used++;
|
||||
}
|
||||
while (used < avail && read_ready(m_port)) {
|
||||
*data++ = inb(m_port);
|
||||
used++;
|
||||
}
|
||||
|
||||
m_in_buffer.commit(used);
|
||||
m_in_buffer.commit(used);
|
||||
}
|
||||
|
||||
void
|
||||
serial_port::do_write()
|
||||
{
|
||||
uint8_t *data = nullptr;
|
||||
uint8_t tmp[fifo_size];
|
||||
uint8_t *data = nullptr;
|
||||
uint8_t tmp[fifo_size];
|
||||
|
||||
size_t n = m_out_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||
size_t n = m_out_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||
|
||||
m_writing = (n > 0);
|
||||
if (!m_writing)
|
||||
return;
|
||||
m_writing = (n > 0);
|
||||
if (!m_writing)
|
||||
return;
|
||||
|
||||
if (n > fifo_size)
|
||||
n = fifo_size;
|
||||
if (n > fifo_size)
|
||||
n = fifo_size;
|
||||
|
||||
kutil::memcpy(tmp, data, n);
|
||||
m_out_buffer.consume(n);
|
||||
kutil::memcpy(tmp, data, n);
|
||||
m_out_buffer.consume(n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
outb(m_port, data[i]);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
outb(m_port, data[i]);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
serial_port::handle_error(uint16_t reg, uint8_t value)
|
||||
{
|
||||
kassert(false, "serial line error");
|
||||
kassert(false, "serial line error");
|
||||
}
|
||||
|
||||
char
|
||||
serial_port::read()
|
||||
{
|
||||
uint8_t *data = nullptr;
|
||||
size_t n = m_in_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||
if (!n) return 0;
|
||||
char c = *data;
|
||||
m_in_buffer.consume(1);
|
||||
return c;
|
||||
uint8_t *data = nullptr;
|
||||
size_t n = m_in_buffer.get_block(reinterpret_cast<void**>(&data));
|
||||
if (!n) return 0;
|
||||
char c = *data;
|
||||
m_in_buffer.consume(1);
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
serial_port::write(char c)
|
||||
{
|
||||
uint8_t *data = nullptr;
|
||||
size_t avail = m_out_buffer.reserve(1, reinterpret_cast<void**>(&data));
|
||||
if (!avail)
|
||||
return;
|
||||
*data = c;
|
||||
m_out_buffer.commit(1);
|
||||
uint8_t *data = nullptr;
|
||||
size_t avail = m_out_buffer.reserve(1, reinterpret_cast<void**>(&data));
|
||||
if (!avail)
|
||||
return;
|
||||
*data = c;
|
||||
m_out_buffer.commit(1);
|
||||
|
||||
if (!m_writing)
|
||||
do_write();
|
||||
if (!m_writing)
|
||||
do_write();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
class serial_port
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg port The IO address of the serial port
|
||||
serial_port(uint16_t port);
|
||||
/// Constructor.
|
||||
/// \arg port The IO address of the serial port
|
||||
serial_port(uint16_t port);
|
||||
|
||||
serial_port();
|
||||
serial_port();
|
||||
|
||||
void write(char c);
|
||||
char read();
|
||||
void write(char c);
|
||||
char read();
|
||||
|
||||
void handle_interrupt();
|
||||
void handle_interrupt();
|
||||
|
||||
private:
|
||||
bool m_writing;
|
||||
uint16_t m_port;
|
||||
kutil::bip_buffer m_out_buffer;
|
||||
kutil::bip_buffer m_in_buffer;
|
||||
bool m_writing;
|
||||
uint16_t m_port;
|
||||
kutil::bip_buffer m_out_buffer;
|
||||
kutil::bip_buffer m_in_buffer;
|
||||
|
||||
void do_read();
|
||||
void do_write();
|
||||
void handle_error(uint16_t reg, uint8_t value);
|
||||
void do_read();
|
||||
void do_write();
|
||||
void handle_error(uint16_t reg, uint8_t value);
|
||||
};
|
||||
|
||||
extern serial_port &g_com1;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "syscall.h"
|
||||
|
||||
extern "C" {
|
||||
void syscall_invalid(uint64_t call);
|
||||
void syscall_invalid(uint64_t call);
|
||||
}
|
||||
|
||||
uintptr_t syscall_registry[256] __attribute__((section(".syscall_registry")));
|
||||
@@ -18,34 +18,34 @@ static constexpr size_t num_syscalls = sizeof(syscall_registry) / sizeof(syscall
|
||||
void
|
||||
syscall_invalid(uint64_t call)
|
||||
{
|
||||
console *cons = console::get();
|
||||
cons->set_color(9);
|
||||
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
||||
console *cons = console::get();
|
||||
cons->set_color(9);
|
||||
cons->printf("\nReceived unknown syscall: %02x\n", call);
|
||||
|
||||
cons->printf(" Known syscalls:\n");
|
||||
cons->printf(" invalid %016lx\n", syscall_invalid);
|
||||
cons->printf(" Known syscalls:\n");
|
||||
cons->printf(" invalid %016lx\n", syscall_invalid);
|
||||
|
||||
for (unsigned i = 0; i < num_syscalls; ++i) {
|
||||
const char *name = syscall_names[i];
|
||||
uintptr_t handler = syscall_registry[i];
|
||||
if (name)
|
||||
cons->printf(" %02x %10s %016lx\n", i, name, handler);
|
||||
}
|
||||
for (unsigned i = 0; i < num_syscalls; ++i) {
|
||||
const char *name = syscall_names[i];
|
||||
uintptr_t handler = syscall_registry[i];
|
||||
if (name)
|
||||
cons->printf(" %02x %10s %016lx\n", i, name, handler);
|
||||
}
|
||||
|
||||
cons->set_color();
|
||||
_halt();
|
||||
cons->set_color();
|
||||
_halt();
|
||||
}
|
||||
|
||||
void
|
||||
syscall_initialize()
|
||||
{
|
||||
kutil::memset(&syscall_registry, 0, sizeof(syscall_registry));
|
||||
kutil::memset(&syscall_names, 0, sizeof(syscall_names));
|
||||
kutil::memset(&syscall_registry, 0, sizeof(syscall_registry));
|
||||
kutil::memset(&syscall_names, 0, sizeof(syscall_names));
|
||||
|
||||
#define SYSCALL(id, name, result, ...) \
|
||||
syscall_registry[id] = reinterpret_cast<uintptr_t>(syscalls::name); \
|
||||
syscall_names[id] = #name; \
|
||||
log::debug(logs::syscall, "Enabling syscall 0x%02x as " #name , id);
|
||||
syscall_registry[id] = reinterpret_cast<uintptr_t>(syscalls::name); \
|
||||
syscall_names[id] = #name; \
|
||||
log::debug(logs::syscall, "Enabling syscall 0x%02x as " #name , id);
|
||||
#include "j6/tables/syscalls.inc"
|
||||
#undef SYSCALL
|
||||
}
|
||||
|
||||
@@ -9,24 +9,24 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
channel_create(j6_handle_t *handle)
|
||||
{
|
||||
construct_handle<channel>(handle);
|
||||
return j6_status_ok;
|
||||
construct_handle<channel>(handle);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel_send(j6_handle_t handle, size_t *len, void *data)
|
||||
{
|
||||
channel *c = get_handle<channel>(handle);
|
||||
if (!c) return j6_err_invalid_arg;
|
||||
return c->enqueue(len, data);
|
||||
channel *c = get_handle<channel>(handle);
|
||||
if (!c) return j6_err_invalid_arg;
|
||||
return c->enqueue(len, data);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
channel_receive(j6_handle_t handle, size_t *len, void *data)
|
||||
{
|
||||
channel *c = get_handle<channel>(handle);
|
||||
if (!c) return j6_err_invalid_arg;
|
||||
return c->dequeue(len, data);
|
||||
channel *c = get_handle<channel>(handle);
|
||||
if (!c) return j6_err_invalid_arg;
|
||||
return c->dequeue(len, data);
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
@@ -10,58 +10,58 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
endpoint_create(j6_handle_t *handle)
|
||||
{
|
||||
construct_handle<endpoint>(handle);
|
||||
return j6_status_ok;
|
||||
construct_handle<endpoint>(handle);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_send(j6_handle_t handle, j6_tag_t tag, size_t len, void *data)
|
||||
{
|
||||
if (tag & j6_tag_system_flag)
|
||||
return j6_err_invalid_arg;
|
||||
if (tag & j6_tag_system_flag)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
return e->send(tag, len, data);
|
||||
return e->send(tag, len, data);
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_receive(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||
{
|
||||
if (!tag || !len || (*len && !data))
|
||||
return j6_err_invalid_arg;
|
||||
if (!tag || !len || (*len && !data))
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
j6_tag_t out_tag = j6_tag_invalid;
|
||||
size_t out_len = *len;
|
||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||
*tag = out_tag;
|
||||
*len = out_len;
|
||||
return s;
|
||||
j6_tag_t out_tag = j6_tag_invalid;
|
||||
size_t out_len = *len;
|
||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||
*tag = out_tag;
|
||||
*len = out_len;
|
||||
return s;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
endpoint_sendrecv(j6_handle_t handle, j6_tag_t *tag, size_t *len, void *data)
|
||||
{
|
||||
if (!tag || (*tag & j6_tag_system_flag))
|
||||
return j6_err_invalid_arg;
|
||||
if (!tag || (*tag & j6_tag_system_flag))
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
endpoint *e = get_handle<endpoint>(handle);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
j6_status_t status = e->send(*tag, *len, data);
|
||||
if (status != j6_status_ok)
|
||||
return status;
|
||||
j6_status_t status = e->send(*tag, *len, data);
|
||||
if (status != j6_status_ok)
|
||||
return status;
|
||||
|
||||
j6_tag_t out_tag = j6_tag_invalid;
|
||||
size_t out_len = *len;
|
||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||
*tag = out_tag;
|
||||
*len = out_len;
|
||||
return s;
|
||||
j6_tag_t out_tag = j6_tag_invalid;
|
||||
size_t out_len = *len;
|
||||
j6_status_t s = e->receive(&out_tag, &out_len, data);
|
||||
*tag = out_tag;
|
||||
*len = out_len;
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
@@ -11,38 +11,38 @@ namespace syscalls {
|
||||
template <typename T, typename... Args>
|
||||
T * construct_handle(j6_handle_t *handle, Args... args)
|
||||
{
|
||||
process &p = process::current();
|
||||
T *o = new T {args...};
|
||||
*handle = p.add_handle(o);
|
||||
return o;
|
||||
process &p = process::current();
|
||||
T *o = new T {args...};
|
||||
*handle = p.add_handle(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T * get_handle(j6_handle_t handle)
|
||||
{
|
||||
process &p = process::current();
|
||||
kobject *o = p.lookup_handle(handle);
|
||||
if (!o || o->get_type() != T::type)
|
||||
return nullptr;
|
||||
return static_cast<T*>(o);
|
||||
process &p = process::current();
|
||||
kobject *o = p.lookup_handle(handle);
|
||||
if (!o || o->get_type() != T::type)
|
||||
return nullptr;
|
||||
return static_cast<T*>(o);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline kobject * get_handle<kobject>(j6_handle_t handle)
|
||||
{
|
||||
process &p = process::current();
|
||||
return p.lookup_handle(handle);
|
||||
process &p = process::current();
|
||||
return p.lookup_handle(handle);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T * remove_handle(j6_handle_t handle)
|
||||
{
|
||||
T *o = get_handle<T>(handle);
|
||||
if (o) {
|
||||
process &p = process::current();
|
||||
p.remove_handle(handle);
|
||||
}
|
||||
return o;
|
||||
T *o = get_handle<T>(handle);
|
||||
if (o) {
|
||||
process &p = process::current();
|
||||
p.remove_handle(handle);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,39 +11,39 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
object_koid(j6_handle_t handle, j6_koid_t *koid)
|
||||
{
|
||||
if (koid == nullptr)
|
||||
return j6_err_invalid_arg;
|
||||
if (koid == nullptr)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
*koid = obj->koid();
|
||||
return j6_status_ok;
|
||||
*koid = obj->koid();
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
|
||||
{
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
j6_signal_t current = obj->signals();
|
||||
if ((current & mask) != 0) {
|
||||
*sigs = current;
|
||||
return j6_status_ok;
|
||||
}
|
||||
j6_signal_t current = obj->signals();
|
||||
if ((current & mask) != 0) {
|
||||
*sigs = current;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
thread &th = thread::current();
|
||||
obj->add_blocked_thread(&th);
|
||||
th.wait_on_signals(mask);
|
||||
thread &th = thread::current();
|
||||
obj->add_blocked_thread(&th);
|
||||
th.wait_on_signals(mask);
|
||||
|
||||
j6_status_t result = th.get_wait_result();
|
||||
if (result == j6_status_ok) {
|
||||
*sigs = th.get_wait_data();
|
||||
}
|
||||
return result;
|
||||
j6_status_t result = th.get_wait_result();
|
||||
if (result == j6_status_ok) {
|
||||
*sigs = th.get_wait_data();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
@@ -70,14 +70,14 @@ object_wait_many(j6_handle_t *handles, uint32_t count, j6_signal_t mask, j6_hand
|
||||
objects.append(obj);
|
||||
}
|
||||
|
||||
thread &th = thread::current();
|
||||
thread &th = thread::current();
|
||||
for (auto *obj : objects)
|
||||
obj->add_blocked_thread(&th);
|
||||
|
||||
th.wait_on_signals(mask);
|
||||
th.wait_on_signals(mask);
|
||||
|
||||
j6_status_t result = th.get_wait_result();
|
||||
if (result != j6_status_ok)
|
||||
j6_status_t result = th.get_wait_result();
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
*handle = j6_handle_invalid;
|
||||
@@ -98,26 +98,26 @@ object_wait_many(j6_handle_t *handles, uint32_t count, j6_signal_t mask, j6_hand
|
||||
j6_status_t
|
||||
object_signal(j6_handle_t handle, j6_signal_t signals)
|
||||
{
|
||||
if ((signals & j6_signal_user_mask) != signals)
|
||||
return j6_err_invalid_arg;
|
||||
if ((signals & j6_signal_user_mask) != signals)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
obj->assert_signal(signals);
|
||||
return j6_status_ok;
|
||||
obj->assert_signal(signals);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
object_close(j6_handle_t handle)
|
||||
{
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
kobject *obj = get_handle<kobject>(handle);
|
||||
if (!obj)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
obj->close();
|
||||
return j6_status_ok;
|
||||
obj->close();
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
@@ -10,50 +10,50 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
process_create(j6_handle_t *handle)
|
||||
{
|
||||
process *child = construct_handle<process>(handle);
|
||||
log::debug(logs::task, "Process %llx created", child->koid());
|
||||
return j6_status_ok;
|
||||
process *child = construct_handle<process>(handle);
|
||||
log::debug(logs::task, "Process %llx created", child->koid());
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
process_start(j6_handle_t handle, uintptr_t entrypoint, j6_handle_t *handles, size_t handle_count)
|
||||
{
|
||||
process &p = process::current();
|
||||
process *c = get_handle<process>(handle);
|
||||
if (handle_count && !handles)
|
||||
return j6_err_invalid_arg;
|
||||
process &p = process::current();
|
||||
process *c = get_handle<process>(handle);
|
||||
if (handle_count && !handles)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
for (size_t i = 0; i < handle_count; ++i) {
|
||||
kobject *o = p.lookup_handle(handles[i]);
|
||||
if (o) c->add_handle(o);
|
||||
}
|
||||
for (size_t i = 0; i < handle_count; ++i) {
|
||||
kobject *o = p.lookup_handle(handles[i]);
|
||||
if (o) c->add_handle(o);
|
||||
}
|
||||
|
||||
return j6_err_nyi;
|
||||
return j6_err_nyi;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
process_kill(j6_handle_t handle)
|
||||
{
|
||||
process &p = process::current();
|
||||
process *c = get_handle<process>(handle);
|
||||
if (!c) return j6_err_invalid_arg;
|
||||
process &p = process::current();
|
||||
process *c = get_handle<process>(handle);
|
||||
if (!c) return j6_err_invalid_arg;
|
||||
|
||||
log::debug(logs::task, "Process %llx killed by process %llx", c->koid(), p.koid());
|
||||
c->exit(-1u);
|
||||
log::debug(logs::task, "Process %llx killed by process %llx", c->koid(), p.koid());
|
||||
c->exit(-1u);
|
||||
|
||||
return j6_status_ok;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
process_exit(int32_t status)
|
||||
{
|
||||
process &p = process::current();
|
||||
log::debug(logs::task, "Process %llx exiting with code %d", p.koid(), status);
|
||||
process &p = process::current();
|
||||
log::debug(logs::task, "Process %llx exiting with code %d", p.koid(), status);
|
||||
|
||||
p.exit(status);
|
||||
p.exit(status);
|
||||
|
||||
log::error(logs::task, "returned to exit syscall");
|
||||
return j6_err_unexpected;
|
||||
log::error(logs::task, "returned to exit syscall");
|
||||
return j6_err_unexpected;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
@@ -17,64 +17,64 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
system_log(const char *message)
|
||||
{
|
||||
if (message == nullptr)
|
||||
return j6_err_invalid_arg;
|
||||
if (message == nullptr)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
thread &th = thread::current();
|
||||
log::info(logs::syscall, "Message[%llx]: %s", th.koid(), message);
|
||||
return j6_status_ok;
|
||||
thread &th = thread::current();
|
||||
log::info(logs::syscall, "Message[%llx]: %s", th.koid(), message);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
system_noop()
|
||||
{
|
||||
thread &th = thread::current();
|
||||
log::debug(logs::syscall, "Thread %llx called noop syscall.", th.koid());
|
||||
return j6_status_ok;
|
||||
thread &th = thread::current();
|
||||
log::debug(logs::syscall, "Thread %llx called noop syscall.", th.koid());
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
system_get_log(j6_handle_t sys, void *buffer, size_t *size)
|
||||
{
|
||||
if (!size || (*size && !buffer))
|
||||
return j6_err_invalid_arg;
|
||||
if (!size || (*size && !buffer))
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
size_t orig_size = *size;
|
||||
*size = g_logger.get_entry(buffer, *size);
|
||||
if (!g_logger.has_log())
|
||||
system::get().deassert_signal(j6_signal_system_has_log);
|
||||
size_t orig_size = *size;
|
||||
*size = g_logger.get_entry(buffer, *size);
|
||||
if (!g_logger.has_log())
|
||||
system::get().deassert_signal(j6_signal_system_has_log);
|
||||
|
||||
return (*size > orig_size) ? j6_err_insufficient : j6_status_ok;
|
||||
return (*size > orig_size) ? j6_err_insufficient : j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
system_bind_irq(j6_handle_t sys, j6_handle_t endp, unsigned irq)
|
||||
{
|
||||
// TODO: check capabilities on sys handle
|
||||
endpoint *e = get_handle<endpoint>(endp);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
// TODO: check capabilities on sys handle
|
||||
endpoint *e = get_handle<endpoint>(endp);
|
||||
if (!e) return j6_err_invalid_arg;
|
||||
|
||||
if (device_manager::get().bind_irq(irq, e))
|
||||
return j6_status_ok;
|
||||
if (device_manager::get().bind_irq(irq, e))
|
||||
return j6_status_ok;
|
||||
|
||||
return j6_err_invalid_arg;
|
||||
return j6_err_invalid_arg;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
system_map_phys(j6_handle_t sys, j6_handle_t *vma_handle, uintptr_t phys_addr, size_t size, uint32_t flags)
|
||||
{
|
||||
// TODO: check capabilities on sys handle
|
||||
if (!vma_handle) return j6_err_invalid_arg;
|
||||
// TODO: check capabilities on sys handle
|
||||
if (!vma_handle) return j6_err_invalid_arg;
|
||||
|
||||
// TODO: check to see if frames are already used? How would that collide with
|
||||
// the bootloader's allocated pages already being marked used?
|
||||
if (!(flags & vm_flags::mmio))
|
||||
frame_allocator::get().used(phys_addr, memory::page_count(size));
|
||||
// TODO: check to see if frames are already used? How would that collide with
|
||||
// the bootloader's allocated pages already being marked used?
|
||||
if (!(flags & vm_flags::mmio))
|
||||
frame_allocator::get().used(phys_addr, memory::page_count(size));
|
||||
|
||||
vm_flags vmf = (static_cast<vm_flags>(flags) & vm_flags::driver_mask);
|
||||
construct_handle<vm_area_fixed>(vma_handle, phys_addr, size, vmf);
|
||||
vm_flags vmf = (static_cast<vm_flags>(flags) & vm_flags::driver_mask);
|
||||
construct_handle<vm_area_fixed>(vma_handle, phys_addr, size, vmf);
|
||||
|
||||
return j6_status_ok;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
@@ -10,48 +10,48 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
thread_create(void *rip, j6_handle_t *handle)
|
||||
{
|
||||
thread &parent = thread::current();
|
||||
process &p = parent.parent();
|
||||
thread &parent = thread::current();
|
||||
process &p = parent.parent();
|
||||
|
||||
thread *child = p.create_thread();
|
||||
child->add_thunk_user(reinterpret_cast<uintptr_t>(rip));
|
||||
*handle = child->self_handle();
|
||||
child->clear_state(thread::state::loading);
|
||||
child->set_state(thread::state::ready);
|
||||
thread *child = p.create_thread();
|
||||
child->add_thunk_user(reinterpret_cast<uintptr_t>(rip));
|
||||
*handle = child->self_handle();
|
||||
child->clear_state(thread::state::loading);
|
||||
child->set_state(thread::state::ready);
|
||||
|
||||
log::debug(logs::task, "Thread %llx spawned new thread %llx, handle %d",
|
||||
parent.koid(), child->koid(), *handle);
|
||||
log::debug(logs::task, "Thread %llx spawned new thread %llx, handle %d",
|
||||
parent.koid(), child->koid(), *handle);
|
||||
|
||||
return j6_status_ok;
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
thread_exit(int32_t status)
|
||||
{
|
||||
thread &th = thread::current();
|
||||
log::debug(logs::task, "Thread %llx exiting with code %d", th.koid(), status);
|
||||
th.exit(status);
|
||||
thread &th = thread::current();
|
||||
log::debug(logs::task, "Thread %llx exiting with code %d", th.koid(), status);
|
||||
th.exit(status);
|
||||
|
||||
log::error(logs::task, "returned to exit syscall");
|
||||
return j6_err_unexpected;
|
||||
log::error(logs::task, "returned to exit syscall");
|
||||
return j6_err_unexpected;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
thread_pause()
|
||||
{
|
||||
thread &th = thread::current();
|
||||
th.wait_on_signals(-1ull);
|
||||
return j6_status_ok;
|
||||
thread &th = thread::current();
|
||||
th.wait_on_signals(-1ull);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
thread_sleep(uint64_t til)
|
||||
{
|
||||
thread &th = thread::current();
|
||||
log::debug(logs::task, "Thread %llx sleeping until %llu", th.koid(), til);
|
||||
thread &th = thread::current();
|
||||
log::debug(logs::task, "Thread %llx sleeping until %llu", th.koid(), til);
|
||||
|
||||
th.wait_on_time(til);
|
||||
return j6_status_ok;
|
||||
th.wait_on_time(til);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
@@ -13,57 +13,57 @@ namespace syscalls {
|
||||
j6_status_t
|
||||
vma_create(j6_handle_t *handle, size_t size, uint32_t flags)
|
||||
{
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
construct_handle<vm_area_open>(handle, size, f);
|
||||
return j6_status_ok;
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
construct_handle<vm_area_open>(handle, size, f);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
vma_create_map(j6_handle_t *handle, size_t size, uintptr_t base, uint32_t flags)
|
||||
{
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
vm_area *a = construct_handle<vm_area_open>(handle, size, f);
|
||||
process::current().space().add(base, a);
|
||||
return j6_status_ok;
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
vm_area *a = construct_handle<vm_area_open>(handle, size, f);
|
||||
process::current().space().add(base, a);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
vma_map(j6_handle_t handle, j6_handle_t proc, uintptr_t base)
|
||||
{
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
|
||||
process *p = get_handle<process>(proc);
|
||||
if (!p) return j6_err_invalid_arg;
|
||||
process *p = get_handle<process>(proc);
|
||||
if (!p) return j6_err_invalid_arg;
|
||||
|
||||
p->space().add(base, a);
|
||||
return j6_status_ok;
|
||||
p->space().add(base, a);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
vma_unmap(j6_handle_t handle, j6_handle_t proc)
|
||||
{
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
|
||||
process *p = get_handle<process>(proc);
|
||||
if (!p) return j6_err_invalid_arg;
|
||||
process *p = get_handle<process>(proc);
|
||||
if (!p) return j6_err_invalid_arg;
|
||||
|
||||
p->space().remove(a);
|
||||
return j6_status_ok;
|
||||
p->space().remove(a);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
vma_resize(j6_handle_t handle, size_t *size)
|
||||
{
|
||||
if (!size)
|
||||
return j6_err_invalid_arg;
|
||||
if (!size)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
|
||||
*size = a->resize(*size);
|
||||
return j6_status_ok;
|
||||
*size = a->resize(*size);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,52 +17,52 @@ TSS &g_bsp_tss = __g_bsp_tss_storage.value;
|
||||
|
||||
TSS::TSS()
|
||||
{
|
||||
kutil::memset(this, 0, sizeof(TSS));
|
||||
m_iomap_offset = sizeof(TSS);
|
||||
kutil::memset(this, 0, sizeof(TSS));
|
||||
m_iomap_offset = sizeof(TSS);
|
||||
}
|
||||
|
||||
TSS &
|
||||
TSS::current()
|
||||
{
|
||||
return *current_cpu().tss;
|
||||
return *current_cpu().tss;
|
||||
}
|
||||
|
||||
uintptr_t &
|
||||
TSS::ring_stack(unsigned ring)
|
||||
{
|
||||
kassert(ring < 3, "Bad ring passed to TSS::ring_stack.");
|
||||
return m_rsp[ring];
|
||||
kassert(ring < 3, "Bad ring passed to TSS::ring_stack.");
|
||||
return m_rsp[ring];
|
||||
}
|
||||
|
||||
uintptr_t &
|
||||
TSS::ist_stack(unsigned ist)
|
||||
{
|
||||
kassert(ist > 0 && ist < 7, "Bad ist passed to TSS::ist_stack.");
|
||||
return m_ist[ist];
|
||||
kassert(ist > 0 && ist < 7, "Bad ist passed to TSS::ist_stack.");
|
||||
return m_ist[ist];
|
||||
}
|
||||
|
||||
void
|
||||
TSS::create_ist_stacks(uint8_t ist_entries)
|
||||
{
|
||||
extern vm_area_guarded &g_kernel_stacks;
|
||||
using memory::frame_size;
|
||||
using memory::kernel_stack_pages;
|
||||
constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||
extern vm_area_guarded &g_kernel_stacks;
|
||||
using memory::frame_size;
|
||||
using memory::kernel_stack_pages;
|
||||
constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||
|
||||
for (unsigned ist = 1; ist < 8; ++ist) {
|
||||
if (!(ist_entries & (1 << ist))) continue;
|
||||
for (unsigned ist = 1; ist < 8; ++ist) {
|
||||
if (!(ist_entries & (1 << ist))) continue;
|
||||
|
||||
// Two zero entries at the top for the null frame
|
||||
uintptr_t stack_bottom = g_kernel_stacks.get_section();
|
||||
uintptr_t stack_top = stack_bottom + stack_bytes - 2 * sizeof(uintptr_t);
|
||||
// Two zero entries at the top for the null frame
|
||||
uintptr_t stack_bottom = g_kernel_stacks.get_section();
|
||||
uintptr_t stack_top = stack_bottom + stack_bytes - 2 * sizeof(uintptr_t);
|
||||
|
||||
log::debug(logs::memory, "Created IST stack at %016lx size 0x%lx",
|
||||
stack_bottom, stack_bytes);
|
||||
log::debug(logs::memory, "Created IST stack at %016lx size 0x%lx",
|
||||
stack_bottom, stack_bytes);
|
||||
|
||||
// Pre-realize these stacks, they're no good if they page fault
|
||||
for (unsigned i = 0; i < kernel_stack_pages; ++i)
|
||||
*reinterpret_cast<uint64_t*>(stack_bottom + i * frame_size) = 0;
|
||||
// Pre-realize these stacks, they're no good if they page fault
|
||||
for (unsigned i = 0; i < kernel_stack_pages; ++i)
|
||||
*reinterpret_cast<uint64_t*>(stack_bottom + i * frame_size) = 0;
|
||||
|
||||
ist_stack(ist) = stack_top;
|
||||
}
|
||||
ist_stack(ist) = stack_top;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,33 +7,33 @@
|
||||
class TSS
|
||||
{
|
||||
public:
|
||||
TSS();
|
||||
TSS();
|
||||
|
||||
/// Get the currently running CPU's TSS.
|
||||
static TSS & current();
|
||||
/// Get the currently running CPU's TSS.
|
||||
static TSS & current();
|
||||
|
||||
/// Ring stack accessor. Returns a mutable reference.
|
||||
/// \arg ring Which ring (0-3) to get the stack for
|
||||
/// \returns A mutable reference to the stack pointer
|
||||
uintptr_t & ring_stack(unsigned ring);
|
||||
/// Ring stack accessor. Returns a mutable reference.
|
||||
/// \arg ring Which ring (0-3) to get the stack for
|
||||
/// \returns A mutable reference to the stack pointer
|
||||
uintptr_t & ring_stack(unsigned ring);
|
||||
|
||||
/// IST stack accessor. Returns a mutable reference.
|
||||
/// \arg ist Which IST entry (1-7) to get the stack for
|
||||
/// \returns A mutable reference to the stack pointer
|
||||
uintptr_t & ist_stack(unsigned ist);
|
||||
/// IST stack accessor. Returns a mutable reference.
|
||||
/// \arg ist Which IST entry (1-7) to get the stack for
|
||||
/// \returns A mutable reference to the stack pointer
|
||||
uintptr_t & ist_stack(unsigned ist);
|
||||
|
||||
/// Allocate new stacks for the given IST entries.
|
||||
/// \arg ist_entries A bitmap of used IST entries
|
||||
void create_ist_stacks(uint8_t ist_entries);
|
||||
/// Allocate new stacks for the given IST entries.
|
||||
/// \arg ist_entries A bitmap of used IST entries
|
||||
void create_ist_stacks(uint8_t ist_entries);
|
||||
|
||||
private:
|
||||
uint32_t m_reserved0;
|
||||
uint32_t m_reserved0;
|
||||
|
||||
uintptr_t m_rsp[3]; // stack pointers for CPL 0-2
|
||||
uintptr_t m_ist[8]; // ist[0] is reserved
|
||||
uintptr_t m_rsp[3]; // stack pointers for CPL 0-2
|
||||
uintptr_t m_ist[8]; // ist[0] is reserved
|
||||
|
||||
uint64_t m_reserved1;
|
||||
uint16_t m_reserved2;
|
||||
uint16_t m_iomap_offset;
|
||||
uint64_t m_reserved1;
|
||||
uint16_t m_reserved2;
|
||||
uint16_t m_iomap_offset;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
@@ -13,293 +13,293 @@ static uint64_t kernel_areas[num_kernel_areas * 2];
|
||||
int
|
||||
vm_space::area::compare(const vm_space::area &o) const
|
||||
{
|
||||
if (base > o.base) return 1;
|
||||
else if (base < o.base) return -1;
|
||||
else return 0;
|
||||
if (base > o.base) return 1;
|
||||
else if (base < o.base) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::area::operator==(const vm_space::area &o) const
|
||||
{
|
||||
return o.base == base && o.area == area;
|
||||
return o.base == base && o.area == area;
|
||||
}
|
||||
|
||||
|
||||
// Kernel address space contsructor
|
||||
vm_space::vm_space(page_table *p) :
|
||||
m_kernel {true},
|
||||
m_pml4 {p},
|
||||
m_areas {reinterpret_cast<vm_space::area*>(kernel_areas), 0, num_kernel_areas}
|
||||
m_kernel {true},
|
||||
m_pml4 {p},
|
||||
m_areas {reinterpret_cast<vm_space::area*>(kernel_areas), 0, num_kernel_areas}
|
||||
{}
|
||||
|
||||
vm_space::vm_space() :
|
||||
m_kernel {false}
|
||||
m_kernel {false}
|
||||
{
|
||||
m_pml4 = page_table::get_table_page();
|
||||
page_table *kpml4 = kernel_space().m_pml4;
|
||||
m_pml4 = page_table::get_table_page();
|
||||
page_table *kpml4 = kernel_space().m_pml4;
|
||||
|
||||
kutil::memset(m_pml4, 0, memory::frame_size/2);
|
||||
for (unsigned i = memory::pml4e_kernel; i < memory::table_entries; ++i)
|
||||
m_pml4->entries[i] = kpml4->entries[i];
|
||||
kutil::memset(m_pml4, 0, memory::frame_size/2);
|
||||
for (unsigned i = memory::pml4e_kernel; i < memory::table_entries; ++i)
|
||||
m_pml4->entries[i] = kpml4->entries[i];
|
||||
}
|
||||
|
||||
vm_space::~vm_space()
|
||||
{
|
||||
for (auto &a : m_areas) {
|
||||
bool free = a.area->remove_from(this);
|
||||
clear(*a.area, 0, memory::page_count(a.area->size()), free);
|
||||
a.area->handle_release();
|
||||
}
|
||||
for (auto &a : m_areas) {
|
||||
bool free = a.area->remove_from(this);
|
||||
clear(*a.area, 0, memory::page_count(a.area->size()), free);
|
||||
a.area->handle_release();
|
||||
}
|
||||
|
||||
kassert(!is_kernel(), "Kernel vm_space destructor!");
|
||||
if (active())
|
||||
kernel_space().activate();
|
||||
kassert(!is_kernel(), "Kernel vm_space destructor!");
|
||||
if (active())
|
||||
kernel_space().activate();
|
||||
|
||||
// All VMAs have been removed by now, so just
|
||||
// free all remaining pages and tables
|
||||
m_pml4->free(page_table::level::pml4);
|
||||
// All VMAs have been removed by now, so just
|
||||
// free all remaining pages and tables
|
||||
m_pml4->free(page_table::level::pml4);
|
||||
}
|
||||
|
||||
vm_space &
|
||||
vm_space::kernel_space()
|
||||
{
|
||||
return process::kernel_process().space();
|
||||
return process::kernel_process().space();
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::add(uintptr_t base, vm_area *area)
|
||||
{
|
||||
//TODO: check for collisions
|
||||
m_areas.sorted_insert({base, area});
|
||||
area->add_to(this);
|
||||
area->handle_retain();
|
||||
return true;
|
||||
//TODO: check for collisions
|
||||
m_areas.sorted_insert({base, area});
|
||||
area->add_to(this);
|
||||
area->handle_retain();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::remove(vm_area *area)
|
||||
{
|
||||
for (auto &a : m_areas) {
|
||||
if (a.area == area) {
|
||||
bool free = area->remove_from(this);
|
||||
clear(*area, 0, memory::page_count(area->size()), free);
|
||||
m_areas.remove(a);
|
||||
area->handle_release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
for (auto &a : m_areas) {
|
||||
if (a.area == area) {
|
||||
bool free = area->remove_from(this);
|
||||
clear(*area, 0, memory::page_count(area->size()), free);
|
||||
m_areas.remove(a);
|
||||
area->handle_release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::can_resize(const vm_area &vma, size_t size) const
|
||||
{
|
||||
uintptr_t base = 0;
|
||||
unsigned n = m_areas.count();
|
||||
for (unsigned i = 0; i < n - 1; ++i) {
|
||||
const area &prev = m_areas[i - 1];
|
||||
if (prev.area != &vma)
|
||||
continue;
|
||||
uintptr_t base = 0;
|
||||
unsigned n = m_areas.count();
|
||||
for (unsigned i = 0; i < n - 1; ++i) {
|
||||
const area &prev = m_areas[i - 1];
|
||||
if (prev.area != &vma)
|
||||
continue;
|
||||
|
||||
base = prev.base;
|
||||
const area &next = m_areas[i];
|
||||
if (prev.base + size > next.base)
|
||||
return false;
|
||||
}
|
||||
base = prev.base;
|
||||
const area &next = m_areas[i];
|
||||
if (prev.base + size > next.base)
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t end = base + size;
|
||||
uintptr_t space_end = is_kernel() ?
|
||||
uint64_t(-1) : 0x7fffffffffff;
|
||||
uintptr_t end = base + size;
|
||||
uintptr_t space_end = is_kernel() ?
|
||||
uint64_t(-1) : 0x7fffffffffff;
|
||||
|
||||
return end <= space_end;
|
||||
return end <= space_end;
|
||||
}
|
||||
|
||||
vm_area *
|
||||
vm_space::get(uintptr_t addr, uintptr_t *base)
|
||||
{
|
||||
for (auto &a : m_areas) {
|
||||
uintptr_t end = a.base + a.area->size();
|
||||
if (addr >= a.base && addr < end) {
|
||||
if (base) *base = a.base;
|
||||
return a.area;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
for (auto &a : m_areas) {
|
||||
uintptr_t end = a.base + a.area->size();
|
||||
if (addr >= a.base && addr < end) {
|
||||
if (base) *base = a.base;
|
||||
return a.area;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::find_vma(const vm_area &vma, uintptr_t &base) const
|
||||
{
|
||||
for (auto &a : m_areas) {
|
||||
if (a.area != &vma) continue;
|
||||
base = a.base;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
for (auto &a : m_areas) {
|
||||
if (a.area != &vma) continue;
|
||||
base = a.base;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::copy_from(const vm_space &source, const vm_area &vma)
|
||||
{
|
||||
uintptr_t to = 0;
|
||||
uintptr_t from = 0;
|
||||
if (!find_vma(vma, from) || !source.find_vma(vma, from))
|
||||
return;
|
||||
uintptr_t to = 0;
|
||||
uintptr_t from = 0;
|
||||
if (!find_vma(vma, from) || !source.find_vma(vma, from))
|
||||
return;
|
||||
|
||||
size_t count = memory::page_count(vma.size());
|
||||
page_table::iterator dit {to, m_pml4};
|
||||
page_table::iterator sit {from, source.m_pml4};
|
||||
size_t count = memory::page_count(vma.size());
|
||||
page_table::iterator dit {to, m_pml4};
|
||||
page_table::iterator sit {from, source.m_pml4};
|
||||
|
||||
while (count--) {
|
||||
uint64_t &e = dit.entry(page_table::level::pt);
|
||||
if (e & page_table::flag::present) {
|
||||
// TODO: handle clobbering mapping
|
||||
}
|
||||
e = sit.entry(page_table::level::pt);
|
||||
}
|
||||
while (count--) {
|
||||
uint64_t &e = dit.entry(page_table::level::pt);
|
||||
if (e & page_table::flag::present) {
|
||||
// TODO: handle clobbering mapping
|
||||
}
|
||||
e = sit.entry(page_table::level::pt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::page_in(const vm_area &vma, uintptr_t offset, uintptr_t phys, size_t count)
|
||||
{
|
||||
using memory::frame_size;
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
using memory::frame_size;
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return;
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return;
|
||||
|
||||
uintptr_t virt = base + offset;
|
||||
page_table::flag flags =
|
||||
page_table::flag::present |
|
||||
(m_kernel ? page_table::flag::none : page_table::flag::user) |
|
||||
((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none) |
|
||||
((vma.flags() && vm_flags::write_combine) ? page_table::flag::wc : page_table::flag::none);
|
||||
uintptr_t virt = base + offset;
|
||||
page_table::flag flags =
|
||||
page_table::flag::present |
|
||||
(m_kernel ? page_table::flag::none : page_table::flag::user) |
|
||||
((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none) |
|
||||
((vma.flags() && vm_flags::write_combine) ? page_table::flag::wc : page_table::flag::none);
|
||||
|
||||
page_table::iterator it {virt, m_pml4};
|
||||
page_table::iterator it {virt, m_pml4};
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uint64_t &entry = it.entry(page_table::level::pt);
|
||||
entry = (phys + i * frame_size) | flags;
|
||||
log::debug(logs::paging, "Setting entry for %016llx: %016llx [%04llx]",
|
||||
it.vaddress(), (phys + i * frame_size), flags);
|
||||
++it;
|
||||
}
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uint64_t &entry = it.entry(page_table::level::pt);
|
||||
entry = (phys + i * frame_size) | flags;
|
||||
log::debug(logs::paging, "Setting entry for %016llx: %016llx [%04llx]",
|
||||
it.vaddress(), (phys + i * frame_size), flags);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::clear(const vm_area &vma, uintptr_t offset, size_t count, bool free)
|
||||
{
|
||||
using memory::frame_size;
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
using memory::frame_size;
|
||||
kutil::scoped_lock lock {m_lock};
|
||||
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return;
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return;
|
||||
|
||||
uintptr_t addr = base + offset;
|
||||
uintptr_t free_start = 0;
|
||||
size_t free_count = 0;
|
||||
uintptr_t addr = base + offset;
|
||||
uintptr_t free_start = 0;
|
||||
size_t free_count = 0;
|
||||
|
||||
frame_allocator &fa = frame_allocator::get();
|
||||
page_table::iterator it {addr, m_pml4};
|
||||
frame_allocator &fa = frame_allocator::get();
|
||||
page_table::iterator it {addr, m_pml4};
|
||||
|
||||
while (count--) {
|
||||
uint64_t &e = it.entry(page_table::level::pt);
|
||||
uintptr_t phys = e & ~0xfffull;
|
||||
while (count--) {
|
||||
uint64_t &e = it.entry(page_table::level::pt);
|
||||
uintptr_t phys = e & ~0xfffull;
|
||||
|
||||
if (e & page_table::flag::present) {
|
||||
if (free_count && phys == free_start + (free_count * frame_size)) {
|
||||
++free_count;
|
||||
} else {
|
||||
if (free && free_count)
|
||||
fa.free(free_start, free_count);
|
||||
free_start = phys;
|
||||
free_count = 1;
|
||||
}
|
||||
}
|
||||
if (e & page_table::flag::present) {
|
||||
if (free_count && phys == free_start + (free_count * frame_size)) {
|
||||
++free_count;
|
||||
} else {
|
||||
if (free && free_count)
|
||||
fa.free(free_start, free_count);
|
||||
free_start = phys;
|
||||
free_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
e = 0;
|
||||
++it;
|
||||
}
|
||||
e = 0;
|
||||
++it;
|
||||
}
|
||||
|
||||
if (free && free_count)
|
||||
fa.free(free_start, free_count);
|
||||
if (free && free_count)
|
||||
fa.free(free_start, free_count);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
vm_space::lookup(const vm_area &vma, uintptr_t offset)
|
||||
{
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return 0;
|
||||
return base + offset;
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return 0;
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::active() const
|
||||
{
|
||||
uintptr_t pml4 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||
return memory::to_virtual<page_table>(pml4 & ~0xfffull) == m_pml4;
|
||||
uintptr_t pml4 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||
return memory::to_virtual<page_table>(pml4 & ~0xfffull) == m_pml4;
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::activate() const
|
||||
{
|
||||
constexpr uint64_t phys_mask = ~memory::page_offset & ~0xfffull;
|
||||
uintptr_t p = reinterpret_cast<uintptr_t>(m_pml4) & phys_mask;
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p) );
|
||||
constexpr uint64_t phys_mask = ~memory::page_offset & ~0xfffull;
|
||||
uintptr_t p = reinterpret_cast<uintptr_t>(m_pml4) & phys_mask;
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p) );
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::initialize_tcb(TCB &tcb)
|
||||
{
|
||||
tcb.pml4 =
|
||||
reinterpret_cast<uintptr_t>(m_pml4) &
|
||||
~memory::page_offset;
|
||||
tcb.pml4 =
|
||||
reinterpret_cast<uintptr_t>(m_pml4) &
|
||||
~memory::page_offset;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::handle_fault(uintptr_t addr, fault_type fault)
|
||||
{
|
||||
// TODO: Handle more fult types
|
||||
if (fault && fault_type::present)
|
||||
return false;
|
||||
// TODO: Handle more fult types
|
||||
if (fault && fault_type::present)
|
||||
return false;
|
||||
|
||||
uintptr_t base = 0;
|
||||
vm_area *area = get(addr, &base);
|
||||
if (!area)
|
||||
return false;
|
||||
uintptr_t base = 0;
|
||||
vm_area *area = get(addr, &base);
|
||||
if (!area)
|
||||
return false;
|
||||
|
||||
uintptr_t offset = (addr & ~0xfffull) - base;
|
||||
uintptr_t phys_page = 0;
|
||||
if (!area->get_page(offset, phys_page))
|
||||
return false;
|
||||
uintptr_t offset = (addr & ~0xfffull) - base;
|
||||
uintptr_t phys_page = 0;
|
||||
if (!area->get_page(offset, phys_page))
|
||||
return false;
|
||||
|
||||
void *mem = memory::to_virtual<void>(phys_page);
|
||||
if (area->flags() && vm_flags::zero)
|
||||
kutil::memset(mem, 0, memory::frame_size);
|
||||
void *mem = memory::to_virtual<void>(phys_page);
|
||||
if (area->flags() && vm_flags::zero)
|
||||
kutil::memset(mem, 0, memory::frame_size);
|
||||
|
||||
page_in(*area, offset, phys_page, 1);
|
||||
return true;
|
||||
page_in(*area, offset, phys_page, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
vm_space::copy(vm_space &source, vm_space &dest, void *from, void *to, size_t length)
|
||||
{
|
||||
uintptr_t ifrom = reinterpret_cast<uintptr_t>(from);
|
||||
uintptr_t ito = reinterpret_cast<uintptr_t>(to);
|
||||
uintptr_t ifrom = reinterpret_cast<uintptr_t>(from);
|
||||
uintptr_t ito = reinterpret_cast<uintptr_t>(to);
|
||||
|
||||
page_table::iterator sit {ifrom, source.m_pml4};
|
||||
page_table::iterator dit {ito, dest.m_pml4};
|
||||
page_table::iterator sit {ifrom, source.m_pml4};
|
||||
page_table::iterator dit {ito, dest.m_pml4};
|
||||
|
||||
// TODO: iterate page mappings and continue copying. For now i'm blindly
|
||||
// assuming both buffers are fully contained within single pages
|
||||
kutil::memcpy(
|
||||
memory::to_virtual<void>((*dit & ~0xfffull) | (ito & 0xffful)),
|
||||
memory::to_virtual<void>((*sit & ~0xfffull) | (ifrom & 0xffful)),
|
||||
length);
|
||||
// TODO: iterate page mappings and continue copying. For now i'm blindly
|
||||
// assuming both buffers are fully contained within single pages
|
||||
kutil::memcpy(
|
||||
memory::to_virtual<void>((*dit & ~0xfffull) | (ito & 0xffful)),
|
||||
memory::to_virtual<void>((*sit & ~0xfffull) | (ifrom & 0xffful)),
|
||||
length);
|
||||
|
||||
return length;
|
||||
return length;
|
||||
}
|
||||
|
||||
@@ -16,120 +16,120 @@ class vm_area;
|
||||
class vm_space
|
||||
{
|
||||
public:
|
||||
/// Constructor for the kernel address space
|
||||
/// \arg pml4 The existing kernel PML4
|
||||
vm_space(page_table *pml4);
|
||||
/// Constructor for the kernel address space
|
||||
/// \arg pml4 The existing kernel PML4
|
||||
vm_space(page_table *pml4);
|
||||
|
||||
/// Constructor. Creates a new address space.
|
||||
vm_space();
|
||||
/// Constructor. Creates a new address space.
|
||||
vm_space();
|
||||
|
||||
~vm_space();
|
||||
~vm_space();
|
||||
|
||||
/// Add a virtual memorty area to this address space
|
||||
/// \arg base The starting address of the area
|
||||
/// \arg area The area to add
|
||||
/// \returns True if the add succeeded
|
||||
bool add(uintptr_t base, vm_area *area);
|
||||
/// Add a virtual memorty area to this address space
|
||||
/// \arg base The starting address of the area
|
||||
/// \arg area The area to add
|
||||
/// \returns True if the add succeeded
|
||||
bool add(uintptr_t base, vm_area *area);
|
||||
|
||||
/// Remove a virtual memory area from this address space
|
||||
/// \arg area The area to remove
|
||||
/// \returns True if the area was removed
|
||||
bool remove(vm_area *area);
|
||||
/// Remove a virtual memory area from this address space
|
||||
/// \arg area The area to remove
|
||||
/// \returns True if the area was removed
|
||||
bool remove(vm_area *area);
|
||||
|
||||
/// Get the virtual memory area corresponding to an address
|
||||
/// \arg addr The address to check
|
||||
/// \arg base [out] if not null, receives the base address of the area
|
||||
/// \returns The vm_area, or nullptr if not found
|
||||
vm_area * get(uintptr_t addr, uintptr_t *base = nullptr);
|
||||
/// Get the virtual memory area corresponding to an address
|
||||
/// \arg addr The address to check
|
||||
/// \arg base [out] if not null, receives the base address of the area
|
||||
/// \returns The vm_area, or nullptr if not found
|
||||
vm_area * get(uintptr_t addr, uintptr_t *base = nullptr);
|
||||
|
||||
/// Check if this is the kernel space
|
||||
inline bool is_kernel() const { return m_kernel; }
|
||||
/// Check if this is the kernel space
|
||||
inline bool is_kernel() const { return m_kernel; }
|
||||
|
||||
/// Get the kernel virtual memory space
|
||||
static vm_space & kernel_space();
|
||||
/// Get the kernel virtual memory space
|
||||
static vm_space & kernel_space();
|
||||
|
||||
/// Map virtual addressses to the given physical pages
|
||||
/// \arg area The VMA this mapping applies to
|
||||
/// \arg offset Offset of the starting virutal address from the VMA base
|
||||
/// \arg phys The starting physical address
|
||||
/// \arg count The number of contiugous physical pages to map
|
||||
void page_in(const vm_area &area, uintptr_t offset, uintptr_t phys, size_t count);
|
||||
/// Map virtual addressses to the given physical pages
|
||||
/// \arg area The VMA this mapping applies to
|
||||
/// \arg offset Offset of the starting virutal address from the VMA base
|
||||
/// \arg phys The starting physical address
|
||||
/// \arg count The number of contiugous physical pages to map
|
||||
void page_in(const vm_area &area, uintptr_t offset, uintptr_t phys, size_t count);
|
||||
|
||||
/// Clear mappings from the given region
|
||||
/// \arg area The VMA these mappings applies to
|
||||
/// \arg offset Offset of the starting virutal address from the VMA base
|
||||
/// \arg count The number of pages worth of mappings to clear
|
||||
/// \arg free If true, free the pages back to the system
|
||||
void clear(const vm_area &vma, uintptr_t start, size_t count, bool free = false);
|
||||
/// Clear mappings from the given region
|
||||
/// \arg area The VMA these mappings applies to
|
||||
/// \arg offset Offset of the starting virutal address from the VMA base
|
||||
/// \arg count The number of pages worth of mappings to clear
|
||||
/// \arg free If true, free the pages back to the system
|
||||
void clear(const vm_area &vma, uintptr_t start, size_t count, bool free = false);
|
||||
|
||||
/// Look up the address of a given VMA's offset
|
||||
uintptr_t lookup(const vm_area &vma, uintptr_t offset);
|
||||
/// Look up the address of a given VMA's offset
|
||||
uintptr_t lookup(const vm_area &vma, uintptr_t offset);
|
||||
|
||||
/// Check if this space is the current active space
|
||||
bool active() const;
|
||||
/// Check if this space is the current active space
|
||||
bool active() const;
|
||||
|
||||
/// Set this space as the current active space
|
||||
void activate() const;
|
||||
/// Set this space as the current active space
|
||||
void activate() const;
|
||||
|
||||
enum class fault_type : uint8_t {
|
||||
none = 0x00,
|
||||
present = 0x01,
|
||||
write = 0x02,
|
||||
user = 0x04,
|
||||
reserved = 0x08,
|
||||
fetch = 0x10
|
||||
};
|
||||
enum class fault_type : uint8_t {
|
||||
none = 0x00,
|
||||
present = 0x01,
|
||||
write = 0x02,
|
||||
user = 0x04,
|
||||
reserved = 0x08,
|
||||
fetch = 0x10
|
||||
};
|
||||
|
||||
/// Allocate pages into virtual memory. May allocate less than requested.
|
||||
/// \arg virt The virtual address at which to allocate
|
||||
/// \arg count The number of pages to allocate
|
||||
/// \arg phys [out] The physical address of the pages allocated
|
||||
/// \returns The number of pages actually allocated
|
||||
size_t allocate(uintptr_t virt, size_t count, uintptr_t *phys);
|
||||
/// Allocate pages into virtual memory. May allocate less than requested.
|
||||
/// \arg virt The virtual address at which to allocate
|
||||
/// \arg count The number of pages to allocate
|
||||
/// \arg phys [out] The physical address of the pages allocated
|
||||
/// \returns The number of pages actually allocated
|
||||
size_t allocate(uintptr_t virt, size_t count, uintptr_t *phys);
|
||||
|
||||
/// Handle a page fault.
|
||||
/// \arg addr Address which caused the fault
|
||||
/// \arg ft Flags from the interrupt about the kind of fault
|
||||
/// \returns True if the fault was successfully handled
|
||||
bool handle_fault(uintptr_t addr, fault_type fault);
|
||||
/// Handle a page fault.
|
||||
/// \arg addr Address which caused the fault
|
||||
/// \arg ft Flags from the interrupt about the kind of fault
|
||||
/// \returns True if the fault was successfully handled
|
||||
bool handle_fault(uintptr_t addr, fault_type fault);
|
||||
|
||||
/// Set up a TCB to operate in this address space.
|
||||
void initialize_tcb(TCB &tcb);
|
||||
/// Set up a TCB to operate in this address space.
|
||||
void initialize_tcb(TCB &tcb);
|
||||
|
||||
/// Copy data from one address space to another
|
||||
/// \arg source The address space data is being copied from
|
||||
/// \arg dest The address space data is being copied to
|
||||
/// \arg from Pointer to the data in the source address space
|
||||
/// \arg to Pointer to the destination in the dest address space
|
||||
/// \arg length Amount of data to copy, in bytes
|
||||
/// \returnd The number of bytes copied
|
||||
static size_t copy(vm_space &source, vm_space &dest, void *from, void *to, size_t length);
|
||||
/// Copy data from one address space to another
|
||||
/// \arg source The address space data is being copied from
|
||||
/// \arg dest The address space data is being copied to
|
||||
/// \arg from Pointer to the data in the source address space
|
||||
/// \arg to Pointer to the destination in the dest address space
|
||||
/// \arg length Amount of data to copy, in bytes
|
||||
/// \returnd The number of bytes copied
|
||||
static size_t copy(vm_space &source, vm_space &dest, void *from, void *to, size_t length);
|
||||
|
||||
private:
|
||||
friend class vm_area;
|
||||
friend class vm_mapper_multi;
|
||||
friend class vm_area;
|
||||
friend class vm_mapper_multi;
|
||||
|
||||
/// Find a given VMA in this address space
|
||||
bool find_vma(const vm_area &vma, uintptr_t &base) const;
|
||||
/// Find a given VMA in this address space
|
||||
bool find_vma(const vm_area &vma, uintptr_t &base) const;
|
||||
|
||||
/// Check if a VMA can be resized
|
||||
bool can_resize(const vm_area &vma, size_t size) const;
|
||||
/// Check if a VMA can be resized
|
||||
bool can_resize(const vm_area &vma, size_t size) const;
|
||||
|
||||
/// Copy a range of mappings from the given address space
|
||||
void copy_from(const vm_space &source, const vm_area &vma);
|
||||
/// Copy a range of mappings from the given address space
|
||||
void copy_from(const vm_space &source, const vm_area &vma);
|
||||
|
||||
bool m_kernel;
|
||||
page_table *m_pml4;
|
||||
bool m_kernel;
|
||||
page_table *m_pml4;
|
||||
|
||||
struct area {
|
||||
uintptr_t base;
|
||||
vm_area *area;
|
||||
int compare(const struct area &o) const;
|
||||
bool operator==(const struct area &o) const;
|
||||
};
|
||||
kutil::vector<area> m_areas;
|
||||
struct area {
|
||||
uintptr_t base;
|
||||
vm_area *area;
|
||||
int compare(const struct area &o) const;
|
||||
bool operator==(const struct area &o) const;
|
||||
};
|
||||
kutil::vector<area> m_areas;
|
||||
|
||||
kutil::spinlock m_lock;
|
||||
kutil::spinlock m_lock;
|
||||
};
|
||||
|
||||
is_bitfield(vm_space::fault_type);
|
||||
|
||||
Reference in New Issue
Block a user