Example #1
0
File: idt.cpp Project: yonick/ghost
void g_idt::load() {
    // Load the IDT
    g_log_debug("%! descriptor table lays at %h", "idt", &idt);
    g_log_debug("%! pointer at %h, base %h, limit %h", "idt", &idtPointer, idtPointer.base, idtPointer.limit);
    _loadIdt((uint32_t) & idtPointer);
    g_log_debug("%! loaded on core %i", "idt", g_system::currentProcessorId());
}
Example #2
0
void g_smp::initialize(g_physical_address initialPageDirectoryPhysical) {

	// Write values to lower memory for use within startup code
	*((uint32_t*) G_CONST_SMP_STARTUP_AREA_PAGE_DIRECTORY) = initialPageDirectoryPhysical;
	*((uint32_t*) G_CONST_SMP_STARTUP_AREA_AP_START_ADDRESS) = (g_virtual_address) g_kernel::run_ap;
	*((uint32_t*) G_CONST_SMP_STARTUP_AREA_AP_COUNTER) = 0;

	g_log_debug("%! initial page directory for APs: %h", "smp", *((uint32_t*) G_CONST_SMP_STARTUP_AREA_PAGE_DIRECTORY));
	g_log_debug("%! kernel entry point for APs: %h", "smp", *((uint32_t*) G_CONST_SMP_STARTUP_AREA_AP_START_ADDRESS));
	g_log_debug("%! initial AP counter value: %i", "smp", *((uint32_t*) G_CONST_SMP_STARTUP_AREA_AP_COUNTER));

	// Create enough stacks for all APs
	g_physical_address* stackArray = (g_physical_address*) G_CONST_SMP_STARTUP_AREA_AP_STACK_ARRAY;
	for (uint32_t i = 0; i < g_system::getNumberOfProcessors(); i++) {

		g_physical_address stackPhysical = g_pp_allocator::allocate();
		if (stackPhysical == 0) {
			g_log_info("%*%! could not allocate physical page for AP stack", 0x0C, "smp");
			return;
		}
		g_virtual_address stackVirtual = g_kernel::virtual_range_pool->allocate(1);
		if (stackPhysical == 0) {
			g_log_info("%*%! could not allocate virtual range for AP stack", 0x0C, "smp");
			return;
		}

		g_address_space::map(stackVirtual, stackPhysical, DEFAULT_KERNEL_TABLE_FLAGS, DEFAULT_KERNEL_PAGE_FLAGS);

		g_virtual_address stackTop = (stackVirtual + G_PAGE_SIZE);
		stackArray[i] = stackTop;

		g_log_debug("%! created AP stack (%h) placed at %h", "smp", stackArray[i], &stackArray[i]);
	}

	// Copy start object from ramdisk to lower memory
	const char* ap_startup_location = "system/lib/apstartup.o";
	g_ramdisk_entry* startupObject = g_kernel::ramdisk->findAbsolute(ap_startup_location);
	if (startupObject == 0) {
		g_log_info("%*%! could not initialize due to missing apstartup object at '%s'", 0x0C, "smp", ap_startup_location);
		return;
	}
	g_memory::copy((uint8_t*) G_CONST_SMP_STARTUP_AREA_CODE_START, (uint8_t*) startupObject->data, startupObject->datalength);

	// Start APs
	g_processor* n = g_system::getProcessorList();
	while (n) {
		if (!n->bsp) {
			initialize_core(n);
		}
		n = n->next;
	}
}
Example #3
0
void g_kernel::print_header(g_setup_information* info) {

	// Print header
	g_console_video::clear();
	g_log_infon("");
	g_log_infon("");
	g_log_infon("");
	g_console_video::setColor(0x90);
	g_log_infon("Ghost Kernel");
	g_console_video::setColor(0x0F);
	g_log_info(" Version %i.%i.%i", G_VERSION_MAJOR, G_VERSION_MINOR, G_VERSION_PATCH);
	g_log_info("");
	g_log_info("  Copyright (C) 2014, Max Schluessel <*****@*****.**>");
	g_log_info("");
	g_log_info("%! loading", "prekern");

	// Print setup information
	g_log_debug("%! setup information:", "prekern");
	g_log_debug("%#   reserved: %h - %h", info->kernelImageStart, info->kernelImageEnd);
	g_log_debug("%#   stack:    %h - %h", info->stackStart, info->stackEnd);
	g_log_debug("%#   bitmap:   %h - %h", info->bitmapStart, info->bitmapEnd);
	g_log_debug("%#   heap:     %h - %h", info->heapStart, info->heapEnd);
	g_log_debug("%#   mbstruct: %h", info->multibootInformation);
	g_log_debug("%! started", "kern");
	g_log_debug("%! got setup information at %h", "kern", info);

}
Example #4
0
void g_lapic::startTimer() {

	g_log_debug("%! starting timer", "lapic");

	// Tell APIC timer to use divider 16
	write(APIC_REGISTER_TIMER_DIV, 0x3);

	// Prepare the PIT to sleep for 10ms (10000µs)
	g_pit::prepareSleep(10000);

	// Set APIC init counter to -1
	write(APIC_REGISTER_TIMER_INITCNT, 0xFFFFFFFF);

	// Perform PIT-supported sleep
	g_pit::performSleep();

	// Stop the APIC timer
	write(APIC_REGISTER_LVT_TIMER, APIC_LVT_INT_MASKED);

	// Now we know how often the APIC timer has ticked in 10ms
	uint32_t ticksPer10ms = 0xFFFFFFFF - read(APIC_REGISTER_TIMER_CURRCNT);

	// Start timer as periodic on IRQ 0
	write(APIC_REGISTER_LVT_TIMER, 32 | APIC_LVT_TIMER_MODE_PERIODIC);
	write(APIC_REGISTER_TIMER_DIV, 0x3);
	write(APIC_REGISTER_TIMER_INITCNT, ticksPer10ms);
}
Example #5
0
void g_pipes::remove_reference(g_pipe_id id, g_pid pid) {

	auto pipe_entry = pipes->get(id);
	if (pipe_entry) {
		g_pipe* pipe = pipe_entry->value;

		// find entry and remove
		g_list_entry<g_pid>* prev = 0;
		g_list_entry<g_pid>* entry = pipe->references;
		while (entry) {
			if (entry->value == pid) {
				if (prev == 0) {
					pipe->references = entry->next;
				} else {
					prev->next = entry->next;
				}
				delete entry;
				break;
			}
			prev = entry;
			entry = entry->next;
		}

		// no entry left?
		if (pipe->references == 0) {
			pipes->remove(id);

			g_log_debug("%! removing non-referenced pipe %i", "pipes", id);
			delete pipe->buffer;
			delete pipe;
		}
	}
}
Example #6
0
void g_scheduler::add(g_thread* t) {

	model_lock.lock();

	// set the scheduler on the task
	t->scheduler = this;

	// the idle task is not added to a queue
	if (t->priority == g_thread_priority::IDLE) {
		g_task_entry* entry = new g_task_entry;
		entry->value = t;
		entry->next = 0;
		idle_entry = entry;

	} else {
		// add task to run queue
		g_task_entry* entry = new g_task_entry;
		entry->value = t;
		entry->next = run_queue;
		run_queue = entry;
	}

	g_log_debug("%! task %i assigned to core %i", "scheduler", t->id, coreId);

	model_lock.unlock();
}
Example #7
0
bool g_scheduler::killAllThreadsOf(g_process* process) {

	bool living_threads_remain = false;
	model_lock.lock();

	// set all threads in run queue to dead
	auto entry = run_queue;
	while (entry) {
		if ((entry->value->process->main->id == process->main->id) && entry->value->alive) {
			entry->value->alive = false;
			living_threads_remain = true;
		}

		entry = entry->next;
	}

	// set all threads in wait queue to dead
	entry = wait_queue;
	while (entry) {
		if ((entry->value->process->main->id == process->main->id) && entry->value->alive) {
			entry->value->alive = false;
			living_threads_remain = true;
		}

		entry = entry->next;
	}

	g_log_debug("%! waiting for all threads of process %i to exit: %s", "scheduler", current_entry->value->id,
			(living_threads_remain ? "still waiting" : "all finished"));

	model_lock.unlock();
	return living_threads_remain;
}
Example #8
0
void g_scheduler::printWaiterDeadlockWarning() {

	char* taskName = (char*) "?";
	if (current_entry->value->getIdentifier() != 0) {
		taskName = (char*) current_entry->value->getIdentifier();
	}

	g_log_debug("%! thread %i (process %i, named '%s') waits for '%s'", "deadlock-detector", current_entry->value->id, current_entry->value->process->main->id,
			taskName, current_entry->value->waitManager->debug_name());
}
Example #9
0
void g_filesystem::process_forked(g_pid source, g_pid fork) {
	g_file_descriptor_table* source_table = g_file_descriptors::get_process_table(source);

	// clone each entry
	for (auto iter = source_table->descriptors.begin(); iter != source_table->descriptors.end(); ++iter) {
		g_file_descriptor_content* content = iter->value;

		g_fs_clonefd_status stat;
		clonefd(content->id, source, content->id, fork, &stat);
		g_log_debug("%! forking cloned fd %i from process %i -> %i with status %i", "filesystem", content->id, source, fork, stat);
	}
}
Example #10
0
void g_scheduler::add(g_thread* t) {

	lock();

	g_list_entry<g_thread*>* entry = new g_list_entry<g_thread*>;
	entry->value = t;
	entry->next = taskList;
	taskList = entry;
	g_log_debug("%! task %i assigned to core %i", "scheduler", t->id, coreId);

	unlock();
}
Example #11
0
/**
 * Spawns a ramdisk file as a process.
 */
g_elf32_spawn_status g_elf32_loader::spawnFromRamdisk(g_ramdisk_entry* entry, g_security_level securityLevel, g_thread** target, const char* arguments,
		bool enforceCurrentCore) {

	// Check file
	if (entry == 0 || entry->type != G_RAMDISK_ENTRY_TYPE_FILE) {
		return ELF32_SPAWN_STATUS_FILE_NOT_FOUND;
	}

	// Get and validate ELF header
	elf32_ehdr* header = (elf32_ehdr*) entry->data;
	g_elf32_validation_status status = validate(header);

	if (status == ELF32_VALIDATION_SUCCESSFUL) {

		// Create the process
		g_thread* mainThread = g_thread_manager::createProcess(securityLevel);
		if (mainThread == 0) {
			g_log_warn("%! failed to create main thread to spawn ELF binary from ramdisk", "elf32");
			return ELF32_SPAWN_STATUS_PROCESS_CREATION_FAILED;
		}
		g_process* process = mainThread->process;

		// Temporarily switch to the new processes page directory to load the binary to it
		g_page_directory thisPageDirectory = g_address_space::get_current_space();

		g_address_space::switch_to_space(process->pageDirectory);
		loadBinaryToCurrentAddressSpace(header, process);
		g_thread_manager::prepare_thread_local_storage(mainThread);
		g_address_space::switch_to_space(thisPageDirectory);

		// Set the tasks entry point
		mainThread->cpuState->eip = header->e_entry;

		// Give CLI
		if (arguments) {
			process->cliArguments = new char[G_CLIARGS_BUFFER_LENGTH];
			uint32_t argsLen = g_string::length(arguments);
			g_memory::copy(process->cliArguments, arguments, argsLen);
			process->cliArguments[argsLen] = 0;
			g_log_debug("%! kernel stored cli arguments for task %i", "elf32", process->main->id);
		}

		// Add to scheduling list
		g_tasking::addTask(mainThread, enforceCurrentCore);

		// Set out parameter
		*target = mainThread;
		return ELF32_SPAWN_STATUS_SUCCESSFUL;
	}

	return ELF32_SPAWN_STATUS_VALIDATION_ERROR;
}
Example #12
0
void g_lapic::prepare(g_physical_address lapicAddress) {
	physicalBase = lapicAddress;
	prepared = true;

	// Warn if APIC not at expected location
	if (physicalBase != G_EXPECTED_APIC_PHYSICAL_ADDRESS) {
		g_log_warn("%! is at %h, not %h as expected", "lapic", physicalBase, G_EXPECTED_APIC_PHYSICAL_ADDRESS);
	}
	g_log_debug("%! base is %h", "lapic", physicalBase);

	// Map it to virtual space
	createMapping();
}
Example #13
0
void g_filesystem::process_closed(g_pid pid) {
	g_file_descriptor_table* table = g_file_descriptors::get_process_table(pid);

	// close each entry
	for (auto iter = table->descriptors.begin(); iter != table->descriptors.end(); ++iter) {
		g_file_descriptor_content* content = iter->value;

		auto node_entry = nodes->get(content->node_id);
		if (node_entry) {
			g_fs_close_status stat;
			close(pid, node_entry->value, content, &stat);

			if (stat == G_FS_CLOSE_SUCCESSFUL) {
				g_log_debug("%! successfully closed fd %i when exiting process %i", "filesystem", content->id, pid);
			} else {
				g_log_debug("%! failed to close fd %i when exiting process %i with status %i", "filesystem", content->id, pid, stat);
			}
		}
	}

	// remove all entries
	g_file_descriptors::unmap_all(pid);
}
Example #14
0
void g_kernel::run(g_setup_information* info) {

	// perform initial setup
	pre_setup(info);

	// copy remaining information from loader information
	g_physical_address initial_pd_physical = info->initialPageDirectoryPhysical;
	g_log_debug("%! unmapping old address space area", "kern");
	for (g_virtual_address i = G_CONST_LOWER_MEMORY_END; i < G_CONST_KERNEL_AREA_START; i += G_PAGE_SIZE) {
		g_address_space::unmap(i);
	}
	// NOTE: pointer to info is now invalid

	// run BSP setup
	run_bsp(initial_pd_physical);
}
Example #15
0
void g_kernel::run_ap() {

	ap_setup_lock.lock();
	{
		uint32_t core = g_system::currentProcessorId();
		g_log_debug("%! core %i ready for initialization", "kernap", core);

		// Debug ESP output
		uint32_t esp;
		asm("mov %%esp, %0":"=g"(esp));
		g_log_debug("%! esp is %h", "kernap", esp);

		// Wait for BSP to finish setup
		g_log_debug("%! waiting for bsp to finish setup", "kernap");
		bsp_setup_lock.lock();
		g_log_debug("%! core %i got ready state from bsp", "kernap", core);
		bsp_setup_lock.unlock();

		// Initialize GDT
		g_gdt_manager::initialize();

		// Initialize for AP
		g_system::initializeAp();

		// Enable tasking for this core
		g_tasking::enableForThisCore();

		// Leave initialization
		load_system_process(G_IDLE_BINARY_NAME, g_thread_priority::IDLE);

		// tell bsp that one more is done
		--waiting_aps;
	}
	ap_setup_lock.unlock();

	// wait for APs
	g_log_debug("%! waiting for %i application processors", "kernap", waiting_aps);
	while (waiting_aps > 0) {
		asm("pause");
	}

	// Enable interrupts and wait until the first interrupt causes the scheduler to switch to the initial process
	g_log_debug("%! leaving initialization", "kernap");
	asm("sti");
	for (;;) {
		asm("hlt");
	}
}
Example #16
0
void g_lapic::initialize() {

	// Read version
	uint32_t apicVersionRegVal = read(APIC_REGISTER_VERSION);
	g_log_debug("%! id %i, version %h (%s), maxlvtindex: %i", "lapic", localId, apicVersion, (apicVersion < 0x10 ? "82489DX discrete" : "integrated"),
			maxLvtIndex);

	// Initialize APIC to well-known state
	write(APIC_REGISTER_DEST_FORMAT, 0xFFFFFFFF);
	write(APIC_REGISTER_LOGICAL_DEST, (read(APIC_REGISTER_LOGICAL_DEST) & 0x00FFFFFF) | 1);
	write(APIC_REGISTER_LVT_TIMER, APIC_LVT_INT_MASKED);
	write(APIC_REGISTER_LVT_PERFMON, APIC_LVT_DELIVERY_MODE_NMI);
	write(APIC_REGISTER_LVT_LINT0, APIC_LVT_INT_MASKED);
	write(APIC_REGISTER_LVT_LINT1, APIC_LVT_INT_MASKED);
	write(APIC_REGISTER_TASK_PRIO, 0);

	// Enable the APIC
	write(APIC_REGISTER_SPURIOUS_IVT, 0xFF | APIC_SPURIOUS_IVT_SOFTWARE_ENABLE);

	// Set up timer
	startTimer();
}
Example #17
0
bool g_scheduler::killAllThreadsOf(g_process* process) {

	bool still_has_living_threads = false;
	lock();

	// kill all threads
	auto entry = taskList;
	while (entry) {
		g_thread* thr = entry->value;

		if (thr->process->main->id == process->main->id && thr->alive) {
			thr->alive = false;
			still_has_living_threads = true;
		}

		entry = entry->next;
	}

	g_log_debug("%! waiting for all threads of process %i to exit: %s", "scheduler", current->value->id,
			(still_has_living_threads ? "all finished" : "still waiting"));

	unlock();
	return still_has_living_threads;
}
Example #18
0
void g_system::initializeBsp(g_physical_address initialPageDirectoryPhysical) {

	// Check if the required CPU features are available
	if (g_processor::supportsCpuid()) {
		g_log_debug("%! supports CPUID", "cpu");
	} else {
		g_kernel::panic("%! no CPUID support", "cpu");
	}

	// Do some CPU info output
	g_processor::printInformation();

	// Enable SSE if available
	if (g_processor::hasFeature(g_cpuid_standard_edx_feature::SSE)) {
		g_log_info("%! support enabled", "sse");
		g_processor::enableSSE();
	} else {
		g_log_warn("%! no support detected", "sse");
	}

	// APIC must be available
	if (g_processor::hasFeature(g_cpuid_standard_edx_feature::APIC)) {
		g_log_debug("%! APIC available", "cpu");
	} else {
		g_kernel::panic("%! no APIC available", "cpu");
	}

	// Gather ACPI information
	g_acpi::gatherInformation();
	if (g_acpi::hasEntries()) {
		g_log_debug("%! is available", "acpi");

		// Parse the MADT
		g_acpi_entry* cur = g_acpi::getEntryWithSignature("APIC");
		if (cur) {

			// This creates the list of CPU's
			g_madt::parse(cur->header);
		}

	} else {
		g_kernel::panic("%! ACPI info not available", "system");
	}

	// Initialize the interrupt controllers
	if (g_lapic::isPrepared() && g_ioapic_manager::areAvailable() && processor_list) {

		// Initialize the interrupt descriptor table
		g_idt::prepare();
		g_idt::load();

		// Disable PIC properly
		g_pic::remapIrqs();
		g_pic::maskAll();

		// Initialize local APIC
		g_lapic::initialize();

		// Initialize each IO APIC
		g_ioapic* ioapic = g_ioapic_manager::getEntries();
		while (ioapic) {
			ioapic->initialize();
			ioapic = ioapic->getNext();
		}

		// Print available CPUs
		g_log_info("%! %i available core%s", "system", processors_available, (processors_available > 1 ? "s" : ""));

		// Initialize multiprocessing
		g_smp::initialize(initialPageDirectoryPhysical);

		// Create keyboard and mouse redirection entries
		g_ioapic_manager::createIsaRedirectionEntry(1, 1, 0);
		g_ioapic_manager::createIsaRedirectionEntry(12, 12, 0);

	} else {
		g_kernel::panic("%! pic compatibility mode not implemented. apic/ioapic required!", "system");
		/*
		 PIC::remapIrqs();
		 PIC::unmaskAll();
		 */
	}
}
Example #19
0
void Listener::setCustomArg( void *args ){
	g_log_debug( "Setting custom argument %p for listener at %p .\n", args, this );
	m_custom = args;
}
Example #20
0
net_retcode_t Listener::start(){
   struct sockaddr_in servaddr;
   struct sockaddr_in clientaddr;
   int				  client,
   					  addrlen = sizeof(clientaddr);
   Socket			 *csocket;
   Thread			 *cthread;
   
   memset(	&servaddr,   0, sizeof(servaddr) );
   memset(	&clientaddr, 0, addrlen );

   	m_sd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    if( !m_sd ){
    	g_log_error( "Could not create listener socket ([%d] %s).\n", errno, strerror(errno) );
        return NET_FAILED;
    }

	servaddr.sin_family 	 = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port 		 = htons(m_port);
	
	int opt = 1;
    if( setsockopt( m_sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) ) == -1 ){
		g_log_error( "Could not set SO_REUSEADDR option ([%d] %s).\n", errno, strerror(errno) );
        return NET_FAILED;
    }
    // prevents broken pipe error during acccept
    if( setsockopt( m_sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt) ) == -1 ){
    	g_log_error( "Could not set SO_NOSIGPIPE option ([%d] %s).\n", errno, strerror(errno) );
        return NET_FAILED;
    }
   
	if( bind( m_sd, (sockaddr *)&servaddr, sizeof(servaddr) ) == -1 ){
		g_log_error( "Could not bind listener ([%d] %s).\n", errno, strerror(errno) );
        return NET_FAILED;
	}
	
	if( listen( m_sd, m_backlog ) == -1 ){
		g_log_error( "Could not start listener ([%d] %s).\n", errno, strerror(errno) );
        return NET_FAILED;
	}

    while( 1 ){    	
    	if( (client = accept( m_sd, (sockaddr *)&clientaddr, (socklen_t *)&addrlen )) != -1 ){
    		csocket = new Socket(client);
    		cthread = new Thread(m_acceptor);
    		
    		csocket->setAddress( clientaddr.sin_addr.s_addr );
    		csocket->setNonBlocking();
    		
    		g_log_info( "Accepted new connection from %s .\n", csocket->getAddress() );
    		
    		if( m_custom == NULL ){
				cthread->start( csocket );
			}
			else{
				g_listener_custom_args_t *args = (g_listener_custom_args_t *)calloc( 1, sizeof(g_listener_custom_args_t) );
				
				args->client = csocket;
				args->custom = m_custom;
				
				cthread->start( args );
			}	
			
			g_log_debug( "Client acceptor started .\n" );
    	}
    	else{
    		g_log_warning( "Error while accepting incoming connection ([%d] %s).\n", errno, strerror(errno) );
    	}
    }
   	
   	return NET_OK;
}
Example #21
0
void g_loader::initialize(g_multiboot_information* multibootInformation) {

	// Store multiboot structure
	setupInformation.multibootInformation = multibootInformation;

	// Begin initialization
	g_log_info("%! loader initializing", "loader");

	// End of the loader binary in memory
	uint32_t loaderEndAddress = PAGE_ALIGN_UP((uint32_t ) &endAddress);

	// Find free spaces to place the GDT and the bitmap
	uint32_t gdtAreaStart = findFreeMemory(multibootInformation, loaderEndAddress, 1);
	uint32_t gdtAreaEnd = gdtAreaStart + G_PAGE_SIZE;

	uint32_t bitmapStart = findFreeMemory(multibootInformation, gdtAreaEnd, PAGE_ALIGN_UP(G_BITMAP_SIZE) / G_PAGE_SIZE);
	uint32_t bitmapEnd = PAGE_ALIGN_UP(bitmapStart + G_BITMAP_SIZE);

	// The "reservedAreaEnd" is the end of the memory (somewhere above 1MiB)
	// that is not occupied by the loader binary or the pages that we split
	// of for use as bitmap and GDT.
	uint32_t reservedAreaEnd = bitmapEnd;

#if G_LOGGING_DEBUG
	// Information output
	g_log_debug("%! available modules:", "mmodule");
	for (uint32_t i = 0; i < multibootInformation->modulesCount; i++) {
		g_multiboot_module* module = (g_multiboot_module*) (multibootInformation->modulesAddress + sizeof(g_multiboot_module) * i);
		g_log_debug("%#   '%s' at %h - %h", module->path, module->moduleStart, module->moduleEnd);
	}

	g_log_debug("%! calculated addresses:", "loader");
	g_log_debug("%#   gdt area:            %h", gdtAreaStart);
	g_log_debug("%#   bitmap:              %h", bitmapStart);
	g_log_debug("%#   reserved area end:   %h", reservedAreaEnd);
#endif

	// Store setup information
	setupInformation.bitmapStart = bitmapStart;
	setupInformation.bitmapEnd = bitmapEnd;

	// Set up the GDT. Here we pass the address of the gdt area, which contains enough space to
	// create the descriptor table and its pointer.
	g_gdt_manager::initialize(gdtAreaStart);

	// Read GRUB map to add free pages to the allocator
	physicalAllocator.initialize((g_bitmap_entry*) bitmapStart);
	g_multiboot_mmap_interpreter::load(&physicalAllocator, reservedAreaEnd);

	// Set up paging, this relocates the multiboot modules
	g_paging_initializer::initialize(reservedAreaEnd, &setupInformation);
	// IMPORTANT: Now the multiboot module location has changed!

	// Load kernel binary
	g_log_info("%! locating kernel binary...", "loader");
	g_multiboot_module* kernelModule = g_multiboot_util::findModule(setupInformation.multibootInformation, "/boot/kernel");
	if (kernelModule) {
		g_log_info("%! found kernel binary at %h, loading...", "loader", kernelModule->moduleStart);
		g_kernel_loader::load(kernelModule);
		g_loader::panic("%! something went wrong during boot process, halting", "loader");
	} else {
		g_loader::panic("%! kernel module not found", "loader");
	}
}