From 428e4563d05852f84eba6cb8fa0822065db930ac Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Tue, 1 May 2018 01:03:19 -0700 Subject: [PATCH] Add initial classes representing APIC --- src/kernel/apic.cpp | 106 +++++++++++++++++++++++++++++ src/kernel/apic.h | 69 +++++++++++++++++++ src/kernel/device_manager.cpp | 44 +++++++++---- src/kernel/device_manager.h | 14 ++-- src/kernel/interrupt_isrs.inc | 101 ++++++++++++++-------------- src/kernel/interrupts.cpp | 121 +++++++++++++++++++--------------- src/kernel/interrupts.h | 17 +++++ src/kernel/main.cpp | 4 +- src/kernel/memory_pages.cpp | 54 +++++++-------- src/kernel/memory_pages.h | 10 +-- 10 files changed, 382 insertions(+), 158 deletions(-) create mode 100644 src/kernel/apic.cpp create mode 100644 src/kernel/apic.h diff --git a/src/kernel/apic.cpp b/src/kernel/apic.cpp new file mode 100644 index 0000000..729a527 --- /dev/null +++ b/src/kernel/apic.cpp @@ -0,0 +1,106 @@ +#include "apic.h" +#include "assert.h" +#include "interrupts.h" +#include "log.h" +#include "memory_pages.h" + + +static uint32_t +apic_read(uint32_t *apic, uint16_t offset) +{ + return *(apic + offset/sizeof(uint32_t)); +} + +static void +apic_write(uint32_t *apic, uint16_t offset, uint32_t value) +{ + *(apic + offset/sizeof(uint32_t)) = value; +} + + +apic::apic(uint32_t *base) : + m_base(base) +{ + g_page_manager.map_offset_pointer(reinterpret_cast(&m_base), 0x1000); +} + + +lapic::lapic(uint32_t *base, isr spurious) : + apic(base) +{ + apic_write(m_base, 0xf0, static_cast(spurious)); + log::info(logs::interrupt, "LAPIC created, base %lx", m_base); +} + +void +lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat) +{ + switch (divisor) { + case 1: divisor = 11; break; + case 2: divisor = 0; break; + case 4: divisor = 1; break; + case 8: divisor = 2; break; + case 16: divisor = 3; break; + case 32: divisor = 8; break; + case 64: divisor = 9; break; + case 128: divisor = 10; break; + default: + kassert(0, "Invalid divisor passed to lapic::enable_timer"); + } + + apic_write(m_base, 0x3e0, divisor); + apic_write(m_base, 0x380, count); + + uint32_t lvte = static_cast(vector); + if (repeat) + lvte |= 0x20000; + + log::debug(logs::interrupt, "Enabling APIC timer with isr %d.", vector); + apic_write(m_base, 0x320, lvte); +} + +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."); + + uint16_t off = num ? 0x360 : 0x350; + uint32_t lvte = static_cast(vector); + + uint16_t polarity = flags & 0x3; + if (polarity == 3) + lvte |= (1 << 13); + + uint16_t trigger = (flags >> 2) & 0x3; + if (trigger == 3) + lvte |= (1 << 15); + + apic_write(m_base, off, lvte); + log::debug(logs::interrupt, "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, 0xf0, + apic_read(m_base, 0xf0) | 0x100); + log::debug(logs::interrupt, "LAPIC enabled!"); +} + +void +lapic::disable() +{ + apic_write(m_base, 0xf0, + apic_read(m_base, 0xf0) & ~0x100); + log::debug(logs::interrupt, "LAPIC disabled."); +} + + +ioapic::ioapic(uint32_t *base, uint32_t base_gsr) : + apic(base), + m_base_gsr(base_gsr) +{ +} diff --git a/src/kernel/apic.h b/src/kernel/apic.h new file mode 100644 index 0000000..02fc60e --- /dev/null +++ b/src/kernel/apic.h @@ -0,0 +1,69 @@ +#pragma once +/// \file apic.h +/// Classes to control both local and I/O APICs. + +#include + +enum class isr : uint8_t; + + +/// Base class for other APIC types +class apic +{ +public: + /// Constructor + /// \arg base Base virtual address of the APIC's MMIO registers + apic(uint32_t *base); + +protected: + uint32_t *m_base; +}; + + +/// Controller for processor-local APICs +class lapic : + public apic +{ +public: + /// Constructor + /// \arg base Base virtual address of the APIC's MMIO registers + /// \arg spurious Vector of the spurious interrupt handler + lapic(uint32_t *base, isr spurious); + + /// Enable interrupts for the LAPIC timer. + /// \arg vector Interrupt vector the timer should use + /// \arg divisor The frequency divisor of the bus Hz (power of 2, <= 128) + /// \arg count The count of ticks before an interrupt + /// \arg repeat If false, this timer is one-off, otherwise repeating + void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true); + + /// 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 +}; + + +/// Controller for I/O APICs +class ioapic : + public apic +{ +public: + /// Constructor + /// \arg base Base virtual address of the APIC's MMIO registers + /// \arg base_gsr Starting global system interrupt number of this IOAPIC + ioapic(uint32_t *base, uint32_t base_gsr); + + uint32_t get_base_gsr() const { return m_base_gsr; } + uint32_t get_num_gsr() const { return m_num_gsr; } + +private: + uint32_t m_base_gsr; + uint32_t m_num_gsr; +}; + diff --git a/src/kernel/device_manager.cpp b/src/kernel/device_manager.cpp index e8555b3..8186308 100644 --- a/src/kernel/device_manager.cpp +++ b/src/kernel/device_manager.cpp @@ -3,9 +3,12 @@ #include "kutil/memory.h" #include "acpi_tables.h" +#include "apic.h" #include "assert.h" #include "device_manager.h" +#include "interrupts.h" #include "log.h" +#include "memory.h" static const char expected_signature[] = "RSD PTR "; @@ -50,9 +53,9 @@ acpi_table_header::validate(uint32_t expected_type) const } device_manager::device_manager(const void *root_table) : - m_local_apic(nullptr), - m_io_apic(nullptr), - m_global_interrupt_base(0) + m_lapic(nullptr), + m_ioapics(nullptr), + m_num_ioapics(0) { kassert(root_table != 0, "ACPI root table pointer is null."); @@ -77,6 +80,12 @@ device_manager::device_manager(const void *root_table) : load_xsdt(reinterpret_cast(acpi2->xsdt_address)); } +ioapic * +device_manager::get_ioapic(int i) +{ + return (i < m_num_ioapics) ? m_ioapics + i : nullptr; +} + static void put_sig(char *into, uint32_t type) { @@ -117,8 +126,11 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt) void device_manager::load_apic(const acpi_apic *apic) { - m_local_apic = reinterpret_cast(apic->local_address); - log::info(logs::devices, " APIC local address %lx", apic->local_address); + uint32_t *local = reinterpret_cast(apic->local_address); + + m_lapic = new (kalloc(sizeof(lapic))) lapic(local, isr::isrSpurious); + m_lapic->enable_lint(0, isr::isrLINT0, false, 0); + m_lapic->enable_lint(1, isr::isrLINT1, false, 0); uint8_t const *p = apic->controller_data; uint8_t const *end = p + acpi_table_entries(apic, 1); @@ -131,11 +143,11 @@ device_manager::load_apic(const acpi_apic *apic) case 0: // Local APIC break; - case 1: // I/O APIC - m_io_apic = reinterpret_cast(kutil::read_from(p+4)); - m_global_interrupt_base = kutil::read_from(p+8); - log::info(logs::devices, " IO APIC address %lx base %d", - m_io_apic, m_global_interrupt_base); + case 1: { // I/O APIC + uint32_t *io_apic = reinterpret_cast(kutil::read_from(p+4)); + uint32_t base = kutil::read_from(p+8); + log::info(logs::devices, " IO APIC address %lx base %d", io_apic, base); + } break; case 2: // Interrupt source override @@ -146,12 +158,19 @@ device_manager::load_apic(const acpi_apic *apic) (kutil::read_from(p+8) >> 2) & 0x3); break; - case 4: // Interrupt source override + case 4: {// LAPIC NMI + uint8_t cpu = kutil::read_from(p + 2); + uint8_t num = kutil::read_from(p + 5); + uint16_t flags = kutil::read_from(p + 3); + + m_lapic->enable_lint(num, num == 0 ? isr::isrLINT0 : isr::isrLINT1, true, flags); + log::info(logs::devices, " LAPIC NMI Proc %d LINT%d Pol %d Tri %d", kutil::read_from(p+2), kutil::read_from(p+5), kutil::read_from(p+3) & 0x3, (kutil::read_from(p+3) >> 2) & 0x3); + } break; default: @@ -160,4 +179,7 @@ device_manager::load_apic(const acpi_apic *apic) p += length; } + + // m_lapic->enable_timer(isr::isrTimer, 128, 3000000); + m_lapic->enable(); } diff --git a/src/kernel/device_manager.h b/src/kernel/device_manager.h index 7bfe6ab..ad76b95 100644 --- a/src/kernel/device_manager.h +++ b/src/kernel/device_manager.h @@ -3,6 +3,9 @@ struct acpi_xsdt; struct acpi_apic; +class lapic; +class ioapic; + class device_manager { public: @@ -11,14 +14,13 @@ public: device_manager() = delete; device_manager(const device_manager &) = delete; - uint8_t * local_apic() const; - uint8_t * io_apic() const; + lapic * get_lapic() { return m_lapic; } + ioapic * get_ioapic(int i); private: - uint32_t *m_local_apic; - uint32_t *m_io_apic; - - uint32_t m_global_interrupt_base; + lapic *m_lapic; + ioapic *m_ioapics; + int m_num_ioapics; void load_xsdt(const acpi_xsdt *xsdt); void load_apic(const acpi_apic *apic); diff --git a/src/kernel/interrupt_isrs.inc b/src/kernel/interrupt_isrs.inc index 2db0080..7a830f5 100644 --- a/src/kernel/interrupt_isrs.inc +++ b/src/kernel/interrupt_isrs.inc @@ -1,51 +1,54 @@ -ISR ( 0, isr0); -ISR ( 1, isr1); -ISR ( 2, isr2); -ISR ( 3, isr3); -ISR ( 4, isr4); -ISR ( 5, isr5); -ISR ( 6, isr6); -ISR ( 7, isr7); -EISR( 8, isr8); -ISR ( 9, isr9); -EISR(10, isr10); -EISR(11, isr11); -EISR(12, isr12); -EISR(13, isr13); -EISR(14, isr14); -ISR (15, isr15); -ISR (16, isr16); -ISR (17, isr17); -ISR (18, isr18); -ISR (19, isr19); -ISR (20, isr20); -ISR (21, isr21); -ISR (22, isr22); -ISR (23, isr23); -ISR (24, isr24); -ISR (25, isr25); -ISR (26, isr26); -ISR (27, isr27); -ISR (28, isr28); -ISR (29, isr29); -ISR (30, isr30); -ISR (31, isr31); +ISR ( 0, isr0) +ISR ( 1, isr1) +ISR ( 2, isr2) +ISR ( 3, isr3) +ISR ( 4, isr4) +ISR ( 5, isr5) +ISR ( 6, isr6) +ISR ( 7, isr7) +EISR( 8, isr8) +ISR ( 9, isr9) +EISR(10, isr10) +EISR(11, isr11) +EISR(12, isr12) +EISR(13, isr13) +EISR(14, isr14) +ISR (15, isr15) +ISR (16, isr16) +ISR (17, isr17) +ISR (18, isr18) +ISR (19, isr19) +ISR (20, isr20) +ISR (21, isr21) +ISR (22, isr22) +ISR (23, isr23) +ISR (24, isr24) +ISR (25, isr25) +ISR (26, isr26) +ISR (27, isr27) +ISR (28, isr28) +ISR (29, isr29) +ISR (30, isr30) +ISR (31, isr31) -IRQ (64, 0, irq0); -IRQ (65, 1, irq1); -IRQ (66, 2, irq2); -IRQ (67, 3, irq3); -IRQ (68, 4, irq4); -IRQ (69, 5, irq5); -IRQ (70, 6, irq6); -IRQ (71, 7, irq7); -IRQ (72, 8, irq8); -IRQ (73, 9, irq9); -IRQ (74, 10, irq10); -IRQ (75, 11, irq11); -IRQ (76, 12, irq12); -IRQ (77, 13, irq13); -IRQ (78, 14, irq14); -IRQ (79, 15, irq15); +IRQ (64, 0, irq0) +IRQ (65, 1, irq1) +IRQ (66, 2, irq2) +IRQ (67, 3, irq3) +IRQ (68, 4, irq4) +IRQ (69, 5, irq5) +IRQ (70, 6, irq6) +IRQ (71, 7, irq7) +IRQ (72, 8, irq8) +IRQ (73, 9, irq9) +IRQ (74, 10, irq10) +IRQ (75, 11, irq11) +IRQ (76, 12, irq12) +IRQ (77, 13, irq13) +IRQ (78, 14, irq14) +IRQ (79, 15, irq15) -ISR (0xff, isrSpurious); +ISR (0x7c, isrTimer) +ISR (0x7d, isrLINT0) +ISR (0x7e, isrLINT1) +ISR (0x7f, isrSpurious) diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index 05487e9..87fe876 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -5,6 +5,7 @@ #include "console.h" #include "interrupts.h" #include "log.h" +#include "memory_pages.h" enum class gdt_flags : uint8_t { @@ -29,29 +30,29 @@ operator | (gdt_flags lhs, gdt_flags rhs) struct gdt_descriptor { - uint16_t limit_low; - uint16_t base_low; - uint8_t base_mid; - uint8_t flags; - uint8_t granularity; - uint8_t base_high; + uint16_t limit_low; + uint16_t base_low; + uint8_t base_mid; + uint8_t flags; + uint8_t granularity; + uint8_t base_high; } __attribute__ ((packed)); struct idt_descriptor { - uint16_t base_low; - uint16_t selector; - uint8_t ist; - uint8_t flags; - uint16_t base_mid; - uint32_t base_high; - uint32_t reserved; // must be zero + uint16_t base_low; + uint16_t selector; + uint8_t ist; + uint8_t flags; + uint16_t base_mid; + uint32_t base_high; + uint32_t reserved; // must be zero } __attribute__ ((packed)); struct table_ptr { - uint16_t limit; - uint64_t base; + uint16_t limit; + uint64_t base; } __attribute__ ((packed)); gdt_descriptor g_gdt_table[10]; @@ -59,7 +60,6 @@ idt_descriptor g_idt_table[256]; table_ptr g_gdtr; table_ptr g_idtr; - struct registers; extern "C" { @@ -72,9 +72,9 @@ extern "C" { void isr_handler(registers); void irq_handler(registers); -#define ISR(i, name) extern void name () -#define EISR(i, name) extern void name () -#define IRQ(i, q, name) extern void name () +#define ISR(i, name) extern void name (); +#define EISR(i, name) extern void name (); +#define IRQ(i, q, name) extern void name (); #include "interrupt_isrs.inc" #undef IRQ #undef EISR @@ -87,16 +87,16 @@ void gdt_dump(const table_ptr &table); void set_gdt_entry(uint8_t i, uint32_t base, uint32_t limit, bool is64, gdt_flags flags) { - g_gdt_table[i].limit_low = limit & 0xffff; + g_gdt_table[i].limit_low = limit & 0xffff; - g_gdt_table[i].base_low = base & 0xffff; - g_gdt_table[i].base_mid = (base >> 16) & 0xff; - g_gdt_table[i].base_high = (base >> 24) & 0xff; + g_gdt_table[i].base_low = base & 0xffff; + g_gdt_table[i].base_mid = (base >> 16) & 0xff; + g_gdt_table[i].base_high = (base >> 24) & 0xff; - g_gdt_table[i].granularity = - ((limit >> 16) & 0xf) | (is64 ? 0xa0 : 0xc0); + g_gdt_table[i].granularity = + ((limit >> 16) & 0xf) | (is64 ? 0xa0 : 0xc0); - g_gdt_table[i].flags = static_cast(flags | gdt_flags::res_1 | gdt_flags::pr); + g_gdt_table[i].flags = static_cast(flags | gdt_flags::res_1 | gdt_flags::pr); } void @@ -143,8 +143,6 @@ interrupts_init() idt_write(); log::info(logs::interrupt, "Interrupts enabled."); - gdt_dump(g_gdtr); - idt_dump(g_idtr); } struct registers @@ -162,35 +160,52 @@ isr_handler(registers regs) { console *cons = console::get(); - cons->set_color(9); - cons->puts("\nReceived ISR interrupt:\n"); - cons->set_color(); + switch (static_cast(regs.interrupt & 0xff)) { + case isr::isrTimer: + cons->puts("\nTICK\n"); + break; - uint64_t cr2 = 0; - __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); + case isr::isrLINT0: + cons->puts("\nLINT0\n"); + break; - print_reg("ISR", regs.interrupt); - print_reg("ERR", regs.errorcode); - print_reg("CR2", cr2); - cons->puts("\n"); + case isr::isrLINT1: + cons->puts("\nLINT1\n"); + break; - print_reg(" ds", regs.ds); - print_reg("rdi", regs.rdi); - print_reg("rsi", regs.rsi); - print_reg("rbp", regs.rbp); - print_reg("rsp", regs.rsp); - print_reg("rbx", regs.rbx); - print_reg("rdx", regs.rdx); - print_reg("rcx", regs.rcx); - print_reg("rax", regs.rax); - cons->puts("\n"); + default: + cons->set_color(9); + cons->puts("\nReceived ISR interrupt:\n"); + cons->set_color(); - print_reg("rip", regs.rip); - print_reg(" cs", regs.cs); - print_reg(" ef", regs.eflags); - print_reg("esp", regs.user_esp); - print_reg(" ss", regs.ss); - while(1) asm("hlt"); + uint64_t cr2 = 0; + __asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2)); + + print_reg("ISR", regs.interrupt); + print_reg("ERR", regs.errorcode); + print_reg("CR2", cr2); + cons->puts("\n"); + + print_reg(" ds", regs.ds); + print_reg("rdi", regs.rdi); + print_reg("rsi", regs.rsi); + print_reg("rbp", regs.rbp); + print_reg("rsp", regs.rsp); + print_reg("rbx", regs.rbx); + print_reg("rdx", regs.rdx); + print_reg("rcx", regs.rcx); + print_reg("rax", regs.rax); + cons->puts("\n"); + + print_reg("rip", regs.rip); + print_reg(" cs", regs.cs); + print_reg(" ef", regs.eflags); + print_reg("esp", regs.user_esp); + print_reg(" ss", regs.ss); + while(1) asm("hlt"); + } + + *reinterpret_cast(0xffffff80fee000b0) = 0; } void diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index c001ae0..c9f5794 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -1,4 +1,21 @@ #pragma once +/// \file interrupts.h +/// Free functions and definitions related to interrupt service vectors + + +/// Enum of all defined ISR/IRQ vectors +enum class isr : uint8_t +{ +#define ISR(i, name) name = i, +#define EISR(i, name) name = i, +#define IRQ(i, q, name) name = i, +#include "interrupt_isrs.inc" +#undef IRQ +#undef EISR +#undef ISR + + max +}; extern "C" { void interrupts_enable(); diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 9f5ad58..5d13659 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -48,6 +48,7 @@ kernel_main(popcorn_data *header) cons->puts(GIT_VERSION " booting...\n"); log::init(cons); + log::enable(logs::interrupt, log::level::debug); cpu_id cpu; @@ -62,13 +63,14 @@ kernel_main(popcorn_data *header) cpu_info.ecx_bit(21) ? "yes" : "no"); page_manager *pager = new (&g_page_manager) page_manager; - pager->mark_offset_pointer(&header->frame_buffer, header->frame_buffer_length); memory_initialize_managers( header->memory_map, header->memory_map_length, header->memory_map_desc_size); + pager->map_offset_pointer(&header->frame_buffer, header->frame_buffer_length); + interrupts_init(); interrupts_enable(); diff --git a/src/kernel/memory_pages.cpp b/src/kernel/memory_pages.cpp index a2680c2..f25b4ad 100644 --- a/src/kernel/memory_pages.cpp +++ b/src/kernel/memory_pages.cpp @@ -191,33 +191,6 @@ page_manager::init( // allocated, full of page_block structs. Eventually hand // control of that to a slab allocator. - page_table *pml4 = get_pml4(); - - // Fix up the offset-marked pointers - for (unsigned i = 0; i < m_marked_pointer_count; ++i) { - addr_t *p = reinterpret_cast(m_marked_pointers[i]); - addr_t v = *p + page_offset; - addr_t c = (m_marked_pointer_lengths[i] / page_size) + 1; - - // TODO: cleanly search/split this as a block out of used/free if possible - page_block *block = get_block(); - - // TODO: page-align - block->physical_address = *p; - block->virtual_address = v; - block->count = c; - block->flags = - page_block_flags::used | - page_block_flags::mapped | - page_block_flags::mmio; - - log::info(logs::memory, "Fixing up pointer %lx [%3d] -> %lx", *p, c, v); - - m_used = page_block::insert(m_used, block); - page_in(pml4, *p, v, c); - *p = v; - } - consolidate_blocks(); page_block::dump(m_used, "used", true); @@ -238,10 +211,31 @@ page_manager::init( } void -page_manager::mark_offset_pointer(void **pointer, size_t length) +page_manager::map_offset_pointer(void **pointer, size_t length) { - m_marked_pointers[m_marked_pointer_count] = pointer; - m_marked_pointer_lengths[m_marked_pointer_count++] = length; + addr_t *p = reinterpret_cast(pointer); + addr_t v = *p + page_offset; + addr_t c = ((length - 1) / page_size) + 1; + + // TODO: cleanly search/split this as a block out of used/free if possible + page_block *block = get_block(); + + // TODO: page-align + block->physical_address = *p; + block->virtual_address = v; + block->count = c; + block->flags = + page_block_flags::used | + page_block_flags::mapped | + page_block_flags::mmio; + + log::info(logs::memory, "Fixing up pointer %lx [%3d] -> %lx", *p, c, v); + + m_used = page_block::insert(m_used, block); + + page_table *pml4 = get_pml4(); + page_in(pml4, *p, v, c); + *p = v; } page_block * diff --git a/src/kernel/memory_pages.h b/src/kernel/memory_pages.h index 5a87f18..fa7fd20 100644 --- a/src/kernel/memory_pages.h +++ b/src/kernel/memory_pages.h @@ -40,11 +40,10 @@ public: /// \arg count The number of pages to unmap void unmap_pages(addr_t address, size_t count); - /// Mark a pointer and range to be offset-mapped. This pointer will - /// automatically get updated once page_manager::init() is called. + /// Offset-map a pointer. No physical pages will be mapped. /// \arg pointer Pointer to a pointer to the memory area to be mapped /// \arg length Length of the memory area to be mapped - void mark_offset_pointer(void **pointer, size_t length); + void map_offset_pointer(void **pointer, size_t length); private: friend void memory_initialize_managers(const void *, size_t, size_t); @@ -137,11 +136,6 @@ private: page_block *m_block_cache; ///< Cache of unused page_block structs free_page_header *m_page_cache; ///< Cache of free pages to use for tables - static const unsigned marked_pointer_max = 16; - unsigned m_marked_pointer_count; - void **m_marked_pointers[marked_pointer_max]; - size_t m_marked_pointer_lengths[marked_pointer_max]; - page_manager(const page_manager &) = delete; };