324 lines
No EOL
7.8 KiB
C++
324 lines
No EOL
7.8 KiB
C++
#include "console.hpp"
|
|
#include "pmm.hpp"
|
|
#include "numeric.hpp"
|
|
#include "pointer.hpp"
|
|
#include "multiboot.hpp"
|
|
#include "gdt.hpp"
|
|
#include "idt.hpp"
|
|
#include "compat.h"
|
|
#include "io.hpp"
|
|
#include "vmm.hpp"
|
|
#include "elf.hpp"
|
|
#include "bsod.hpp"
|
|
|
|
#include "driver/timer.hpp"
|
|
#include "driver/keyboard.hpp"
|
|
#include "driver/scheduler.hpp"
|
|
|
|
#include <inttypes.h>
|
|
#include <new>
|
|
|
|
#include <string.h>
|
|
|
|
using namespace multiboot;
|
|
using namespace console_tools;
|
|
|
|
struct dummy;
|
|
|
|
// Symbols generated by linker, no useful content in there...
|
|
extern dummy kernelStartMarker;
|
|
extern dummy kernelEndMarker;
|
|
|
|
driver::Timer timer;
|
|
driver::Keyboard keyboardDriver;
|
|
driver::Scheduler scheduler;
|
|
|
|
VMMContext *kernelContext;
|
|
|
|
static const uint32_t entryPointAddress = 0x40000000;
|
|
|
|
void run_program0(Module const & module)
|
|
{
|
|
using namespace elf;
|
|
|
|
const Header *header = module.start.data<elf::Header>();
|
|
|
|
ProgramHeader *ph;
|
|
int i;
|
|
if (header->magic != MAGIC) {
|
|
BSOD::die(Error::InvalidELFImage, "Keine gueltige ELF-Magic!\n");
|
|
return;
|
|
}
|
|
|
|
ph = (ProgramHeader*)(((char*) header) + header->ph_offset);
|
|
for (i = 0; i < header->ph_entry_count; i++, ph++) {
|
|
void* dest = (void*) ph->virt_addr;
|
|
void* src = ((char*) header) + ph->offset;
|
|
|
|
/* Nur Program Header vom Typ LOAD laden */
|
|
if (ph->type != 1) {
|
|
continue;
|
|
}
|
|
|
|
if(ph->virt_addr < entryPointAddress) {
|
|
BSOD::die(Error::InvalidELFImage, "A LOAD section tried to sneak into the kernel!");
|
|
}
|
|
|
|
for(uint32_t i = 0; i < ph->mem_size; i += 0x1000) {
|
|
kernelContext->provide(
|
|
virtual_t(ph->virt_addr + i),
|
|
VMMFlags::Writable | VMMFlags::UserSpace);
|
|
}
|
|
|
|
memset(dest, 0, ph->mem_size);
|
|
memcpy(dest, src, ph->file_size);
|
|
}
|
|
|
|
using EntryPoint = void (*)();
|
|
EntryPoint ep = (EntryPoint)entryPointAddress;
|
|
ep();
|
|
}
|
|
|
|
static void dump_elf(elf::Header *header)
|
|
{
|
|
using namespace elf;
|
|
ProgramHeader *ph;
|
|
int i;
|
|
|
|
/* Ist es ueberhaupt eine ELF-Datei? */
|
|
if (header->magic != MAGIC) {
|
|
BSOD::die(Error::InvalidELFImage, "Keine gueltige ELF-Magic!\n");
|
|
return;
|
|
}
|
|
ph = (ProgramHeader*)(((char*) header) + header->ph_offset);
|
|
for (i = 0; i < header->ph_entry_count; i++, ph++) {
|
|
void* dest = (void*) ph->virt_addr;
|
|
void* src = ((char*) header) + ph->offset;
|
|
|
|
Console::main
|
|
<< "Header: " << ph->type << ", "
|
|
<< "Source: " << src << ", "
|
|
<< "Dest: " << dest << ", "
|
|
<< "Memsize: " << ph->mem_size << ", "
|
|
<< "Filesize: " << ph->file_size
|
|
<< "\n";
|
|
}
|
|
}
|
|
|
|
void delay()
|
|
{
|
|
for(volatile uint32_t i = 0; i < 0x1000000; i++);
|
|
}
|
|
|
|
void task_a(void)
|
|
{
|
|
while (1) {
|
|
Console::main.put('A');
|
|
delay();
|
|
}
|
|
}
|
|
|
|
void task_b(void)
|
|
{
|
|
while (1) {
|
|
Console::main.put('B');
|
|
delay();
|
|
}
|
|
}
|
|
|
|
static uint8_t stack_a[4096];
|
|
static uint8_t stack_b[4096];
|
|
|
|
/*
|
|
* Jeder Task braucht seinen eigenen Stack, auf dem er beliebig arbeiten kann,
|
|
* ohne dass ihm andere Tasks Dinge ueberschreiben. Ausserdem braucht ein Task
|
|
* einen Einsprungspunkt.
|
|
*/
|
|
CpuState* init_task(uint8_t* stack, void (*entry)())
|
|
{
|
|
/*
|
|
* CPU-Zustand fuer den neuen Task festlegen
|
|
*/
|
|
CpuState new_state;
|
|
{
|
|
new_state.eax = 0;
|
|
new_state.ebx = 0;
|
|
new_state.ecx = 0;
|
|
new_state.edx = 0;
|
|
new_state.esi = 0;
|
|
new_state.edi = 0;
|
|
new_state.ebp = 0;
|
|
//new_state..esp = unbenutzt (kein Ring-Wechsel)
|
|
new_state.eip = reinterpret_cast<uint32_t>(entry);
|
|
|
|
/* Ring-0-Segmentregister */
|
|
new_state.cs = 0x08;
|
|
//new_state..ss = unbenutzt (kein Ring-Wechsel)
|
|
|
|
/* IRQs einschalten (IF = 1) */
|
|
new_state.eflags = 0x202;
|
|
}
|
|
|
|
/*
|
|
* Den angelegten CPU-Zustand auf den Stack des Tasks kopieren, damit es am
|
|
* Ende so aussieht als waere der Task durch einen Interrupt unterbrochen
|
|
* worden. So kann man dem Interrupthandler den neuen Task unterschieben
|
|
* und er stellt einfach den neuen Prozessorzustand "wieder her".
|
|
*/
|
|
CpuState* state = (CpuState*) (stack + 4096 - sizeof(new_state));
|
|
*state = new_state;
|
|
|
|
return state;
|
|
}
|
|
|
|
static int current_task = -1;
|
|
static int num_tasks = 2;
|
|
static CpuState* task_states[2];
|
|
|
|
void init_multitasking(void)
|
|
{
|
|
task_states[0] = init_task(stack_a, task_a);
|
|
task_states[1] = init_task(stack_b, task_b);
|
|
}
|
|
|
|
/*
|
|
* Gibt den Prozessorzustand des naechsten Tasks zurueck. Der aktuelle
|
|
* Prozessorzustand wird als Parameter uebergeben und gespeichert, damit er
|
|
* beim naechsten Aufruf des Tasks wiederhergestellt werden kann
|
|
*/
|
|
void schedule(CpuState *& cpu)
|
|
{
|
|
/*
|
|
* Wenn schon ein Task laeuft, Zustand sichern. Wenn nicht, springen wir
|
|
* gerade zum ersten Mal in einen Task. Diesen Prozessorzustand brauchen
|
|
* wir spaeter nicht wieder.
|
|
*/
|
|
if (current_task >= 0) {
|
|
task_states[current_task] = cpu;
|
|
}
|
|
|
|
/*
|
|
* Naechsten Task auswaehlen. Wenn alle durch sind, geht es von vorne los
|
|
*/
|
|
current_task++;
|
|
current_task %= num_tasks;
|
|
|
|
/* Prozessorzustand des neuen Tasks aktivieren */
|
|
cpu = task_states[current_task];
|
|
}
|
|
|
|
extern "C" void init(Structure const & data)
|
|
{
|
|
Console::main
|
|
<< "Hello World!\n"
|
|
<< FColor(Color::Yellow) << "Hello color!" << FColor() << "\n"
|
|
<< BColor(Color::Blue) << "Hello blue!" << BColor() << "\n"
|
|
<< "Hello default color.\n";
|
|
|
|
GDT::initialize();
|
|
|
|
Console::main
|
|
<< "bootloader name: " << data.bootLoaderName << "\n"
|
|
<< "command line: " << data.commandline << "\n"
|
|
<< "count of modules: " << data.modules.length << "\n"
|
|
<< "count of mmaps: " << data.memoryMaps.length << "\n";
|
|
for(auto &mmap : data.memoryMaps) {
|
|
if(mmap.length == 0) {
|
|
continue;
|
|
}
|
|
if(mmap.isFree() == false) {
|
|
continue;
|
|
}
|
|
Console::main
|
|
<< "mmap: "
|
|
<< "start: " << hex(mmap.base) << ", length: " << hex(mmap.length)
|
|
<< ", " << mmap.entry_size
|
|
<< ", " << sizeof(mmap)
|
|
<< "\n";
|
|
if(mmap.base > 0xFFFFFFFF) {
|
|
Console::main << "mmap out of 4 gigabyte range." << "\n";
|
|
continue;
|
|
}
|
|
if(mmap.isFree()) {
|
|
// Mark all free memory free...
|
|
physical_t lower = physical_t(mmap.base).alignUpper(4096);
|
|
physical_t upper = physical_t(mmap.base + mmap.length).alignLower(4096);
|
|
|
|
uint32_t ptr = lower.numeric();
|
|
while (ptr < upper.numeric()) {
|
|
PMM::markFree(physical_t(ptr));
|
|
ptr += 0x1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark all memory used by the kernel used...
|
|
physical_t lower = physical_t(&kernelStartMarker).alignLower(4096);
|
|
physical_t upper = physical_t(&kernelEndMarker).alignUpper(4096);
|
|
|
|
uint32_t ptr = lower.numeric();
|
|
while (ptr < upper.numeric()) {
|
|
PMM::markUsed(physical_t(ptr));
|
|
ptr += 0x1000;
|
|
}
|
|
// nullptr is not valid.
|
|
PMM::markUsed(physical_t(nullptr));
|
|
|
|
auto freeMemory = PMM::getFreeMemory();
|
|
Console::main
|
|
<< "Free: "
|
|
<< (freeMemory >> 20) << "MB, "
|
|
<< (freeMemory >> 10) << "KB, "
|
|
<< (freeMemory >> 0) << "B, "
|
|
<< (freeMemory >> 12) << "Pages\n";
|
|
|
|
IDT::initialize();
|
|
Console::main << "Interrupts set up.\n";
|
|
|
|
Console::main << "Creating VMM Context...\n";
|
|
kernelContext = new (PMM::alloc().data()) VMMContext();
|
|
|
|
|
|
Console::main << "Mapping memory...\n";
|
|
for(uint32_t addr = 0; addr < 4096 * 1024; addr += 0x1000) {
|
|
kernelContext->map(
|
|
virtual_t(addr),
|
|
physical_t(addr),
|
|
VMMFlags::Writable | VMMFlags::UserSpace);
|
|
}
|
|
kernelContext->map(
|
|
virtual_t(kernelContext),
|
|
physical_t(kernelContext),
|
|
VMMFlags::Writable);
|
|
Console::main << "Active Context...\n";
|
|
VMM::activate(*kernelContext);
|
|
|
|
Console::main << "Active Paging...\n";
|
|
VMM::enable();
|
|
Console::main << "Virtual Memory Management ready.\n";
|
|
|
|
timer.install();
|
|
keyboardDriver.install();
|
|
scheduler.install();
|
|
|
|
IDT::interrupt(0x20) = Interrupt(schedule);
|
|
|
|
init_multitasking();
|
|
|
|
Console::main << "Drivers installed.\n";
|
|
|
|
asm volatile("sti");
|
|
|
|
Console::main << "Interrupts enabled.\n";
|
|
|
|
if(data.modules.length > 0)
|
|
{
|
|
Console::main << "ELF Modukle:\n";
|
|
dump_elf(data.modules[0].start.data<elf::Header>());
|
|
run_program0(data.modules[0]);
|
|
}
|
|
|
|
while(true);
|
|
}
|
|
|
|
static_assert(sizeof(void*) == 4, "Target platform is not 32 bit."); |