Some changes in VMM stuff. Mainly a "safe" commit.
This commit is contained in:
parent
2ad677e750
commit
02364038bd
6 changed files with 152 additions and 106 deletions
45
prototypes/base/concepts.txt
Normal file
45
prototypes/base/concepts.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
# DasOS Concepts
|
||||
|
||||
## Memory Layout
|
||||
|
||||
| Start | Length | Length -h | Description |
|
||||
|------------|------------|-----------|--------------------------------------------------------------------------|
|
||||
| 0x00000000 | 0x40000000 | 1 GB | Kernel Space. This is where the kernel and its functionality is located. |
|
||||
| 0x00100000 | ??? | ??? | Kernel entry point and load target. Here the kernel will be loaded. |
|
||||
| 0x20000000 | 0x20000000 | 512 MB | Kernel Heap. This memory is used for dynamic allocations in the kernel. |
|
||||
| 0x40000000 | 0xC0000000 | 3 GB | User Space. This is where Artifacts are loaded and executed. |
|
||||
|
||||
### Kernel Heap
|
||||
|
||||
A simple storage allocator.
|
||||
|
||||
Storage of:
|
||||
- Task Descriptions
|
||||
- Memory Mapping Information
|
||||
- Location of loaded artifacts
|
||||
- ...
|
||||
|
||||
## Starting a Task
|
||||
|
||||
### Requirements
|
||||
|
||||
- allocate task structure
|
||||
- allocate mapping directory
|
||||
- fill mapping directory with
|
||||
- Lower End: Copy kernel mappings
|
||||
- Upper End: Copy artifact data
|
||||
- allocate task stack
|
||||
|
||||
## Executables, Libraries and Stuff
|
||||
|
||||
Artifact = Executable + Library + Shared Memory
|
||||
|
||||
### Artifact Types
|
||||
|
||||
| Type | Entry Point | Description |
|
||||
|------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Program | _main | A program targeted at user or system administrator. Can be executed with command line arguments. Uses stdin, stdout, stderr. |
|
||||
| Library | _libmain | Can be loaded by other artifacts and allows utilization of a shared set of functions. |
|
||||
| Service | _svcmain | A service is a background worker that won't be terminated when its main function returns. Can be 'woken up' by an external artifact. |
|
||||
| Driver | _drvinit | A driver is loaded at system start and is allowed to request and dispatch interrupts. Can be used to create a flexible OS. |
|
|
@ -6,16 +6,21 @@
|
|||
|
||||
namespace driver
|
||||
{
|
||||
using EntryPoint = void (*)();
|
||||
|
||||
class Task
|
||||
{
|
||||
friend class Scheduler;
|
||||
private:
|
||||
physical_t stackBottom;
|
||||
Task *previous, *next;
|
||||
CpuState *cpu;
|
||||
public:
|
||||
Task();
|
||||
physical_t stackBottom;
|
||||
|
||||
Task(EntryPoint ep);
|
||||
Task(const Task &) = delete;
|
||||
Task(Task &&) = delete;
|
||||
~Task();
|
||||
public:
|
||||
};
|
||||
|
||||
class Scheduler :
|
||||
|
@ -23,12 +28,17 @@ namespace driver
|
|||
{
|
||||
private:
|
||||
static Scheduler *current;
|
||||
static void dispatch(CpuState *cpu);
|
||||
static void dispatch(CpuState *& cpu);
|
||||
|
||||
void next(CpuState *cpu);
|
||||
Task *currentTask;
|
||||
Task *firstTask;
|
||||
Task *lastTask;
|
||||
CpuState * next(CpuState *cpu);
|
||||
public:
|
||||
Scheduler();
|
||||
|
||||
void install() override;
|
||||
|
||||
Task *spawn(EntryPoint ep);
|
||||
};
|
||||
}
|
|
@ -43,7 +43,6 @@ public:
|
|||
* Unmaps a given page from the virtual memory.
|
||||
*/
|
||||
void unmap(virtual_t virt);
|
||||
|
||||
};
|
||||
|
||||
class VMM
|
||||
|
|
|
@ -31,9 +31,9 @@ extern dummy kernelEndMarker;
|
|||
|
||||
driver::Timer timer;
|
||||
driver::Keyboard keyboardDriver;
|
||||
driver::Scheduler scheduler;
|
||||
// driver::Scheduler scheduler;
|
||||
|
||||
VMMContext *kernelContext;
|
||||
VMMContext * kernelContext;
|
||||
|
||||
static const uint32_t entryPointAddress = 0x40000000;
|
||||
|
||||
|
@ -126,87 +126,6 @@ void task_b(void)
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -299,11 +218,7 @@ extern "C" void init(Structure const & data)
|
|||
|
||||
timer.install();
|
||||
keyboardDriver.install();
|
||||
scheduler.install();
|
||||
|
||||
IDT::interrupt(0x20) = Interrupt(schedule);
|
||||
|
||||
init_multitasking();
|
||||
//scheduler.install();
|
||||
|
||||
Console::main << "Drivers installed.\n";
|
||||
|
||||
|
@ -311,7 +226,14 @@ extern "C" void init(Structure const & data)
|
|||
|
||||
Console::main << "Interrupts enabled.\n";
|
||||
|
||||
if(data.modules.length > 0)
|
||||
//driver::Task *taskB = scheduler.spawn(task_b);
|
||||
//driver::Task *taskA = scheduler.spawn(task_a);
|
||||
//
|
||||
//Console::main
|
||||
//<< "Task A: " << taskA << "\n"
|
||||
//<< "Task B: " << taskB << "\n";
|
||||
|
||||
if(data.modules.length > 0 && false)
|
||||
{
|
||||
Console::main << "ELF Modukle:\n";
|
||||
dump_elf(data.modules[0].start.data<elf::Header>());
|
||||
|
|
10
prototypes/base/memory.txt
Normal file
10
prototypes/base/memory.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
[0-1] GB Kernel Space
|
||||
|
||||
[1-4] GB User Space
|
||||
Entry Point: 0x40000000
|
||||
Stack Start: 0xFFFFFFFF
|
|
@ -1,12 +1,21 @@
|
|||
#include "driver/scheduler.hpp"
|
||||
#include "bsod.hpp"
|
||||
#include "pmm.hpp"
|
||||
#include "vmm.hpp"
|
||||
#include "idt.hpp"
|
||||
#include "console.hpp"
|
||||
#include <new>
|
||||
|
||||
extern VMMContext * kernelContext;
|
||||
|
||||
namespace driver
|
||||
{
|
||||
Scheduler * Scheduler::current = nullptr;
|
||||
|
||||
Scheduler::Scheduler()
|
||||
Scheduler::Scheduler() :
|
||||
currentTask(nullptr),
|
||||
firstTask(nullptr),
|
||||
lastTask(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -18,29 +27,80 @@ namespace driver
|
|||
}
|
||||
Scheduler::current = this;
|
||||
|
||||
|
||||
IDT::interrupt(0x20) = Interrupt(Scheduler::dispatch);
|
||||
}
|
||||
|
||||
void Scheduler::next(CpuState *cpu)
|
||||
CpuState * Scheduler::next(CpuState *cpu)
|
||||
{
|
||||
|
||||
if (this->currentTask != nullptr) {
|
||||
this->currentTask->cpu = cpu;
|
||||
}
|
||||
|
||||
void Scheduler::dispatch(CpuState *cpu)
|
||||
this->currentTask = this->currentTask->next;
|
||||
|
||||
/* Prozessorzustand des neuen Tasks aktivieren */
|
||||
return this->currentTask->cpu;
|
||||
}
|
||||
|
||||
void Scheduler::dispatch(CpuState *& cpu)
|
||||
{
|
||||
if(Scheduler::current != nullptr) {
|
||||
Scheduler::current->next(cpu);
|
||||
} else {
|
||||
|
||||
cpu = Scheduler::current->next(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t memoryPointer = 0x20000000; // start at 512 MB
|
||||
|
||||
|
||||
Task::Task() :
|
||||
stackBottom(PMM::alloc()), cpu(nullptr)
|
||||
static virtual_t alloc()
|
||||
{
|
||||
virtual_t ptr(memoryPointer);
|
||||
kernelContext->provide(ptr, VMMFlags::Writable);
|
||||
memoryPointer += 0x1000;
|
||||
Console::main << "Providing " << ptr << "\n";
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Task *Scheduler::spawn(EntryPoint ep)
|
||||
{
|
||||
void *memory = alloc().data();
|
||||
Task *task = new (memory) Task(ep);
|
||||
|
||||
asm volatile("cli");
|
||||
if(this->firstTask != nullptr) {
|
||||
task->next = this->firstTask;
|
||||
this->lastTask->next = task;
|
||||
this->firstTask = task;
|
||||
} else {
|
||||
this->lastTask = task;
|
||||
this->firstTask = task;
|
||||
task->next = task;
|
||||
}
|
||||
asm volatile("sti");
|
||||
return task;
|
||||
}
|
||||
|
||||
Task::Task(EntryPoint ep) :
|
||||
previous(nullptr), next(nullptr),
|
||||
cpu(nullptr),
|
||||
stackBottom(alloc().data())
|
||||
{
|
||||
this->cpu = (CpuState*)(this->stackBottom.numeric() + 4096 - sizeof(CpuState));
|
||||
this->cpu->eax = 0;
|
||||
this->cpu->ebx = 0;
|
||||
this->cpu->ecx = 0;
|
||||
this->cpu->edx = 0;
|
||||
this->cpu->esi = 0;
|
||||
this->cpu->edi = 0;
|
||||
this->cpu->ebp = 0;
|
||||
//this->cpu->.esp = unbenutzt (kein Ring-Wechsel)
|
||||
this->cpu->eip = reinterpret_cast<uint32_t>(ep);
|
||||
|
||||
/* Ring-0-Segmentregister */
|
||||
this->cpu->cs = 0x08;
|
||||
//this->cpu->.ss = unbenutzt (kein Ring-Wechsel)
|
||||
|
||||
/* IRQs einschalten (IF = 1) */
|
||||
this->cpu->eflags = 0x202;
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
|
|
Loading…
Reference in a new issue