15#include <common/symbols.h>
18using namespace MaxOS::system;
19using namespace MaxOS::common;
20using namespace MaxOS::console;
21using namespace MaxOS::hardwarecommunication;
22using namespace MaxOS::processes;
23using namespace MaxOS::memory;
25extern uint64_t
stack[];
26volatile extern PAGE_ALIGNED
unsigned char p4_table[];
52Core::~Core() =
default;
72 info->activated =
false;
84 for (
int i = 0; i < 2; ++i) {
97 Logger::DEBUG() <<
"Core " <<
id <<
" started successfully \n";
131 memset(&
tss.io_bitmap, 0xFF,
sizeof(
tss.io_bitmap));
133 tss.io_bitmap_offset = offsetof(
tss_t, io_bitmap);
136 auto base = (uint64_t) &
tss;
137 uint16_t base_1 = base & 0xFFFF;
138 uint8_t base_2 = (base >> 16) & 0xFF;
139 uint8_t base_3 = (base >> 24) & 0xFF;
140 uint32_t base_4 = (base >> 32) & 0xFFFFFFFF;
142 auto limit_low = (uint16_t)(
sizeof(
tss) - 1);
145 uint8_t flags_1 = 0x89;
149 uint64_t tss_descriptor_low = (uint64_t) base_3 << 56 | (uint64_t) flags_2 << 48 | (uint64_t) flags_1 << 40 | (uint64_t) base_2 << 32 | (uint64_t) base_1 << 16 | (uint64_t) limit_low;
150 uint64_t tss_descriptor_high = base_4;
153 gdt -> table[5] = tss_descriptor_low;
154 gdt -> table[6] = tss_descriptor_high;
158 Logger::DEBUG() <<
"Loading TSS: 0x0" << tss_descriptor_low <<
" 0x0" << tss_descriptor_high <<
" at 0x" << (uint64_t) &
tss <<
"\n";
159 asm volatile(
"ltr %%ax" : :
"a" (0x28));
170 asm volatile(
"mov %%cr0, %0" :
"=r" (cr0));
174 asm volatile(
"mov %%cr4, %0" :
"=r" (cr4));
184 asm volatile(
"mov %0, %%cr0" : :
"r" (cr0));
187 asm volatile(
"fninit");
195 asm volatile(
"mov %0, %%cr4" : :
"r" (cr4));
204 asm volatile(
"mov %0, %%cr4" : :
"r" (cr4));
208 asm volatile(
"xgetbv" :
"=a" (xcr0) :
"c" (0));
210 asm volatile(
"xsetbv" : :
"c" (0),
"a" (xcr0));
219 asm volatile(
"mov %0, %%cr4" : :
"r" (cr4));
262 bsp -> active =
true;
270CPU::~CPU() =
default;
289 asm volatile(
"mov %%r15, %0" :
"=r" (status->r15));
290 asm volatile(
"mov %%r14, %0" :
"=r" (status->r14));
291 asm volatile(
"mov %%r13, %0" :
"=r" (status->r13));
292 asm volatile(
"mov %%r12, %0" :
"=r" (status->r12));
293 asm volatile(
"mov %%r11, %0" :
"=r" (status->r11));
294 asm volatile(
"mov %%r10, %0" :
"=r" (status->r10));
295 asm volatile(
"mov %%r9, %0" :
"=r" (status->r9));
296 asm volatile(
"mov %%r8, %0" :
"=r" (status->r8));
297 asm volatile(
"mov %%rdi, %0" :
"=r" (status->rdi));
298 asm volatile(
"mov %%rsi, %0" :
"=r" (status->rsi));
299 asm volatile(
"mov %%rbp, %0" :
"=r" (status->rbp));
300 asm volatile(
"mov %%rdx, %0" :
"=r" (status->rdx));
301 asm volatile(
"mov %%rcx, %0" :
"=r" (status->rcx));
302 asm volatile(
"mov %%rbx, %0" :
"=r" (status->rbx));
303 asm volatile(
"mov %%rax, %0" :
"=r" (status->rax));
314 asm volatile(
"mov %0, %%r15" : :
"r" (status->r15));
315 asm volatile(
"mov %0, %%r14" : :
"r" (status->r14));
316 asm volatile(
"mov %0, %%r13" : :
"r" (status->r13));
317 asm volatile(
"mov %0, %%r12" : :
"r" (status->r12));
318 asm volatile(
"mov %0, %%r11" : :
"r" (status->r11));
319 asm volatile(
"mov %0, %%r10" : :
"r" (status->r10));
320 asm volatile(
"mov %0, %%r9" : :
"r" (status->r9));
321 asm volatile(
"mov %0, %%r8" : :
"r" (status->r8));
322 asm volatile(
"mov %0, %%rdi" : :
"r" (status->rdi));
323 asm volatile(
"mov %0, %%rsi" : :
"r" (status->rsi));
324 asm volatile(
"mov %0, %%rbp" : :
"r" (status->rbp));
325 asm volatile(
"mov %0, %%rdx" : :
"r" (status->rdx));
326 asm volatile(
"mov %0, %%rcx" : :
"r" (status->rcx));
327 asm volatile(
"mov %0, %%rbx" : :
"r" (status->rbx));
328 asm volatile(
"mov %0, %%rax" : :
"r" (status->rax));
355 Logger::ERROR() <<
"INT: \t0x" << status->interrupt_number <<
"\n";
356 Logger::ERROR() <<
"ERRCD: \t0x" << status->error_code <<
"\n";
375 asm volatile(
"rdmsr" :
"=a" (low),
"=d" (high) :
"c" (msr));
378 return (uint64_t) low | ((uint64_t) high << 32);
390 asm volatile(
"wrmsr" : :
"a" ((uint32_t) value),
"d" ((uint32_t) (value >> 32)),
"c" (msr));
402void CPU::cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) {
405 __get_cpuid(leaf, eax, ebx, ecx, edx);
419 for (
size_t current_level = 0; current_level < level; current_level++) {
423 Logger::ERROR() <<
"(" << current_level <<
"): " << (function ? function :
"Unknown()") <<
" at 0x" << frame->rip <<
"\n";
427 if (frame ==
nullptr)
433void CPU::enable_write_protect() {
437 asm volatile(
"mov %%cr0, %0" :
"=r"(cr0));
441 asm volatile(
"mov %0, %%cr0" : :
"r"(cr0));
445void CPU::disable_write_protect() {
449 asm volatile(
"mov %%cr0, %0" :
"=r"(cr0));
452 cr0 &= ~(1ULL << 16);
453 asm volatile(
"mov %0, %%cr0" : :
"r"(cr0));
480 Logger::ERROR() <<
"Process: " << (process ? process->
name.c_str() :
"Kernel") <<
"\n";
497 status = &new_status;
503 Logger::ERROR() <<
"There has been a fatal error in MaxOS and the system has been halted.\n";
534 Logger::ERROR() <<
"CPU Panicked (i " << (int)status->interrupt_number <<
") in process " << process->
name.c_str() <<
" at 0x" << status->rip <<
" - killing process\n";
580 cores.push_back(
new Core(processor_apic));
593 ASSERT((
void*)&
core_start == (
void*)0x8000,
"Core start not at expected address");
601 for(
const auto& core :
cores)
615 uint32_t eax, ebx, ecx, edx;
616 cpuid(0x1, &eax, &ebx, &ecx, &edx);
619 return ecx & (uint32_t) feature;
631 uint32_t eax, ebx, ecx, edx;
632 cpuid(0x1, &eax, &ebx, &ecx, &edx);
635 return edx & (uint32_t) feature;
645 uint64_t efer =
read_msr(0xC0000080);
648 bool supported = efer & (1 << 11);
649 Logger::DEBUG() <<
"NX: " << (supported ?
"Supported" :
"Not Supported") <<
"\n";
667 uint32_t eax, ebx, ecx, edx;
669 uint32_t core_id = (ebx >> 24) & 0xFF;
671 return cores[core_id];
struct PACKED MaxOS::hardwarecommunication::MADT_PROCESSOR_APIC madt_processor_apic_t
Alias for MADT_PROCESSOR_APIC struct.
static LogRecord ERROR()
Gets the active logger for error logs.
static Logger * active_logger()
Gets the active logger (to be used to modify the logger)
static LogRecord DEBUG()
Gets the active logger for debug logs.
static LogRecord INFO()
Gets the active logger for info logs.
static LogRecord WARNING()
Gets the active logger for warnings.
static LogRecord Out()
Gets the active logger.
static void print_logo(bool is_panic=false)
Prints the logo to the center of the screen.
IOAPIC * io_apic() const
Get the io apic.
LocalAPIC * local_apic() const
Get the local apic.
void setup_apic_clock(LocalAPIC *local_apic) const
Sets up the APIC clock to fire interrupts at the desired rate.
void delay(uint32_t milliseconds) const
Delays the program for a specified number of milliseconds (rounded up to the nearest degree of accura...
static Clock * active_clock()
Gets the currently active clock.
MADTEntry * get_madt_item(MADT_TYPE type, uint8_t index)
Get an item in the MADT.
static void load_current()
Tell the processor to use the current InterruptManager.
Handles the local APIC for the current core.
void send_startup(uint8_t apic_id, uint8_t vector) const
Send the start up IPI to another apic.
void send_interrupt(uint8_t apic_id, uint8_t vector, size_t flags) const
Send an interrupt to a specific core.
void send_init(uint8_t apic_id, bool assert) const
send the init IPI to another apic
static void * kmalloc(size_t size)
Allocates a block of memory in the KERNEL space.
static void * to_lower_region(uintptr_t virtual_address)
Converts a virtual address to a lower region address if it is in the higher region using the higher h...
static PhysicalMemoryManager * s_current_manager
The current physical memory manager in use.
void identity_map(physical_address_t *physical_address, size_t flags)
Maps a physical address to its virtual address counter-part.
static bool in_higher_region(uintptr_t virtual_address)
Checks if a virtual address is in the higher region.
static Scheduler * core_scheduler()
Gets the scheduler for the currently executing core.
static GlobalScheduler * system_scheduler()
Gets the system scheduler.
static Process * current_process()
Gets the process on the currently executing core.
static void deactivate()
Deactivates each core's scheduler.
static bool is_active()
Checks if the global scheduler is active.
static system::cpu_status_t * force_remove_process(Process *process)
Removes a process from the scheduler and deletes all threads, begins running the next process.
A process that can be scheduled by the Scheduler, wraps & manages threads as well as its own address ...
uint64_t total_ticks()
Gets the total ticks of the threads in the process (not this does not include any past killed threads...
string name
The name of the process.
uint64_t ticks() const
Gets how long the system has been running for.
Manages the CPU and its cores.
static void print_registers(cpu_status_t *status)
Prints the CPU registers from the provided structure.
static void PANIC(const char *message, cpu_status_t *status=nullptr)
Puts the CPU into a panic state and halts it. Dumps the stack trace and registers.
static void halt()
Halts the CPU indefinitely.
CPU(GlobalDescriptorTable *gdt, Multiboot *multiboot)
Constructor for the CPU class.
static void get_status(cpu_status_t *status)
Gets the current CPU status into the provided structure.
void init_cores()
Wake up all the cores.
static bool check_cpu_feature(CPU_FEATURE_ECX feature)
Checks if a CPU feature is supported (ECX Register)
hardwarecommunication::AdvancedProgrammableInterruptController apic
The APIC interface for the CPU.
static void stack_trace(size_t, cpu_status_t *status=nullptr)
Prints a stack trace up to the specified level.
static void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
Executes the CPUID instruction with the specified leaf and returns the results in the provided pointe...
static cpu_status_t * prepare_for_panic(cpu_status_t *status=nullptr, const string &msg="")
Ensure the CPU must panic and prepare for it if so.
static void set_status(cpu_status_t *status)
Sets the CPU registers from the provided structure.
void find_cores() const
Search the madt for cores and store them.
static uint64_t read_msr(uint32_t msr)
Reads the value of the specified MSR.
static common::Vector< Core * > cores
The list of CPU cores in the system (populated during initialization, includes the BSP and cores that...
static Core * executing_core()
Gets the core that is currently executing.
static void write_msr(uint32_t msr, uint64_t value)
Writes the specified value to the specified MSR.
static Core * panic_core
The core that triggered the panic.
static common::Spinlock panic_lock
Lock to prevent multiple panics at once.
static bool check_nx()
Checks if the No Execute page flag is supported.
Represents a CPU core in the system.
void wake_up(CPU *cpu)
Wakes up the core by sending the appropriate IPIs. (see core_loader.s for startup code)
void init_tss()
Initialises the task state segment for this cor.
uint64_t m_stack
The stack pointer for this core.
bool m_bsp
Whether this core is the bootstrap processor.
void init_sse()
Initialises the SSE instructions.
GlobalDescriptorTable * gdt
The GDT for this core.
Core(hardwarecommunication::madt_processor_apic_t *madt_item)
Constructs a new Core object from the MADT entry.
void init()
Initialises the core by setting up the GDT, IDT, TSS, SSE and APIC.
uint8_t id
The ID of this core.
bool active
Whether this core is active.
hardwarecommunication::LocalAPIC * local_apic
The local APIC for this core.
uint8_t m_apic_id
The ID of the apic for this core.
bool xsave_enabled
Whether XSAVE is enabled.
bool m_enabled
Whether the core is enabled.
bool avx_enabled
Whether AVX is enabled.
bool m_can_enable
Whether the core can be enabled.
hardwarecommunication::madt_processor_apic_t * m_madt
The MADT entry for this core.
tss_t tss
The Task State Segment for this core.
Sets up the GDT in the CPU.
Parses and provides access to Multiboot 2 information.
Defines drivers for the Programmable Interval Timer (PIT) and APIC Clock.
void core_start()
The entry point for the core startup assembly code.
volatile PAGE_ALIGNED unsigned char p4_table[]
The PML4 table setup in loader.s.
uint8_t core_boot_info[]
The info passed between the bsp c++ and booting core assembly code.
uint64_t stack[]
The stack setup for the core in loader.s.
Defines Central Processing Unit (CPU) structures and functions for managing CPU state and features.
constexpr size_t IO_BITMAP_SIZE
The size of the IO bitmap to track 65536 ports.
constexpr size_t BOOT_STACK_SIZE
The size of the stack allocated for booting a core (should align with the startup assembly code for t...
struct PACKED MaxOS::system::CPUStatus cpu_status_t
Alias for CPUStatus struct.
struct PACKED MaxOS::system::StackFrame stack_frame_t
Alias for StackFrame struct.
struct PACKED MaxOS::system::CoreBootInfo core_boot_info_t
Alias for CoreBootInfo struct.
CPU_FEATURE_ECX
CPU features indicated by the ECX register after calling CPUID with EAX = 1.
CPU_FEATURE_EDX
CPU features indicated by the EDX register after calling CPUID with EAX = 1.
struct PACKED MaxOS::system::TaskStateSegment tss_t
Alias for TaskStateSegment struct.
Defines the VESABootConsole driver for handling console output during boot using an VESA framebuffer.
uint8_t core_boot_info[]
The boot info structure for the core being started.
Defines a Logger class for logging messages with different severity levels to multiple output streams...
Defines a MemoryManager class for handling memory allocation and deallocation.
volatile PAGE_ALIGNED unsigned char p4_table[]
The PML4 table setup in loader.s.
void physical_address_t
A type representing a physical address (used for readability)
@ PRESENT
The page is present in memory.
@ WRITE
Memory in this page is writable.
Defines a GlobalScheduler and Scheduler for managing processes and threads.
An item in the MADT table.
const char * resolve_symbol(uintptr_t rip, uintptr_t link_base, uintptr_t load_base)
resolve an instruction pointer (rip) to a symbol name and optional offset.