Hallo Lowlevel-Forum!
Ich bin der Tele, 20 Jahre alt und Student. Ich hab bereits eine Ausbildung zum Informationstechnischen Assistenten hinter mir und jetzt studiere ich dual
Wirtschaftsinformatik. Ich interessiere mich schon lange für das Entwickeln von Betriebssystemen, insbesondere Linux hat es mir angetan.
Jetzt habe ich den Schritt gewagt und damit angefangen und, tja stehe direkt vor einem (für mich) unlösbaren Problems:
Ich habe fleissig das Tutorial verfolgt und bin jetzt beim Multitasking. Ich muss dazu sagen, dass ich mein Betriebssystem anhand anderer Tutorials bereits erweitert habe, so verfüge ich über einen GCC-Crosscompiler und eine (gaaaaaanz kleine) eigene libc Implementierung (stdio, etc.).
Leider spuckt mir Qemu ein "Trying to execute code outside RAM or ROM at 0x66000000" aus und da komme ich nicht weiter.
Ich habe alle Beiträge in diesem Forum bezüglich ähnlicher Fehler durch, half leider nichts. Lange Rede, kurzer Sinn: Ich erbitte euch um Hilfe und hier ist mein Code:
kernel.c
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <kernel/tty.h>
#include <kernel/gdt.h>
#include <kernel/idt.h>
#include <kernel/kbc.h>
#include <kernel/task.h>
static uint8_t stack_a[4096];
static uint8_t stack_b[4096];
static uint8_t user_stack_a[4096];
static uint8_t user_stack_b[4096];
void task_a(void);
void task_b(void);
void kernel_early(void) {
terminal_init();
gdt_init();
kbc_init();
task_init_task(stack_a, user_stack_a, task_a, 0);
task_init_task(stack_b, user_stack_b, task_b, 1);
idt_init();
}
void kernel_main(void) {
printf("OSWAN has successfully booted\n");
while(1);
}
void task_a(void) {
while (1) {
putchar('A');
}
}
void task_b(void) {
while (1) {
putchar('B');
}
}
idt.c
#include <kernel/idt.h>
#include <kernel/gdt.h>
#include <kernel/kbc.h>
#include <kernel/syscl.h>
#include <kernel/ports.h>
#include <kernel/task.h>
#include <stdio.h>
#define IDT_FLAG_INTERRUPT_GATE 0xe
#define IDT_FLAG_PRESENT 0x80
#define IDT_FLAG_RING0 0x00
#define IDT_FLAG_RING3 0x60
#define IDT_NR_EXCEPTION_LAST 0x1f
#define IDT_NR_IRQ_FIRST 0x20
#define IDT_NR_IRQ_LAST 0x2f
#define IDT_NR_SOFTWARE 0x30
#define IDT_ENTRIES 256
// Exceptions
extern void intr_stub_0(void);
extern void intr_stub_1(void);
extern void intr_stub_2(void);
extern void intr_stub_3(void);
extern void intr_stub_4(void);
extern void intr_stub_5(void);
extern void intr_stub_6(void);
extern void intr_stub_7(void);
extern void intr_stub_8(void);
extern void intr_stub_9(void);
extern void intr_stub_10(void);
extern void intr_stub_11(void);
extern void intr_stub_12(void);
extern void intr_stub_13(void);
extern void intr_stub_14(void);
extern void intr_stub_15(void);
extern void intr_stub_16(void);
extern void intr_stub_17(void);
extern void intr_stub_18(void);
// IRQ
extern void intr_stub_32(void);
extern void intr_stub_33(void);
extern void intr_stub_34(void);
extern void intr_stub_35(void);
extern void intr_stub_36(void);
extern void intr_stub_37(void);
extern void intr_stub_38(void);
extern void intr_stub_39(void);
extern void intr_stub_40(void);
extern void intr_stub_41(void);
extern void intr_stub_42(void);
extern void intr_stub_43(void);
extern void intr_stub_44(void);
extern void intr_stub_45(void);
extern void intr_stub_46(void);
extern void intr_stub_47(void);
// System Call
extern void intr_stub_48(void);
static long long unsigned int idt[IDT_ENTRIES];
static void idt_set_entry(int i, void (*fn)(), unsigned int selector, int flags) {
unsigned long int handler = (unsigned long int)fn;
idt[i] = handler & 0xffffLL;
idt[i] |= (selector & 0xffffLL) << 16;
idt[i] |= (flags & 0xffLL) << 40;
idt[i] |= ((handler >> 16) & 0xffffLL) << 48;
}
static void pic_init(void) {
/* Initialize Master-PIC */
/* ICW (Initializazion Control Word) 1-4 */
// Initial command
outb(0x20, 0x11);
// Interrupt NR for IRQ 0
outb(0x21, 0x20);
// Slave-PIC at IRQ 2
outb(0x21, 0x04);
// ICW 4 0x01 is set for PC's
outb(0x21, 0x01);
/* Initialize Slave-PIC */
/* ICW (Initializazion Control Word) 1-4 */
// Initial command
outb(0xa0, 0x11);
// Interrupt NR for IRQ 8
outb(0xa1, 0x28);
// Slave-PIC at IRQ 2
outb(0xa1, 0x02);
// ICW 4 0x01 is set for PC's
outb(0xa1, 0x01);
// Activate all IRQ's for Master and Slave
outb(0x20, 0x0);
outb(0xa0, 0x0);
}
void idt_init(void) {
struct {
unsigned short int limit;
void* base;
} __attribute__((packed)) idtp = {
.limit = IDT_ENTRIES * 8 - 1,
.base = idt,
};
pic_init();
idt_set_entry(0, intr_stub_0, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(1, intr_stub_1, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(2, intr_stub_2, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(3, intr_stub_3, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(4, intr_stub_4, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(5, intr_stub_5, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(6, intr_stub_6, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(7, intr_stub_7, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(8, intr_stub_8, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(9, intr_stub_9, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(10, intr_stub_10, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(11, intr_stub_11, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(12, intr_stub_12, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(13, intr_stub_13, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(14, intr_stub_14, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(15, intr_stub_15, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(16, intr_stub_16, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(17, intr_stub_17, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(18, intr_stub_18, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(32, intr_stub_32, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(33, intr_stub_33, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(34, intr_stub_34, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(35, intr_stub_35, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(36, intr_stub_36, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(37, intr_stub_37, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(38, intr_stub_38, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(39, intr_stub_39, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(40, intr_stub_40, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(41, intr_stub_41, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(42, intr_stub_42, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(43, intr_stub_43, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(44, intr_stub_44, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(45, intr_stub_45, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(46, intr_stub_46, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(47, intr_stub_47, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING0 | IDT_FLAG_PRESENT);
idt_set_entry(48, intr_stub_48, 0x8, IDT_FLAG_INTERRUPT_GATE | IDT_FLAG_RING3 | IDT_FLAG_PRESENT);
// load the adress of idtp to register the IDT in the CPU
asm volatile("lidt %0" : : "m" (idtp));
// set the Interrupt Flag (if) in the cpu
asm volatile("sti");
}
struct cpu_state* idt_handle_interrupt(struct cpu_state* cpu) {
struct cpu_state* new_cpu = cpu;
uint32_t intr = cpu->intr;
if (intr <= IDT_NR_EXCEPTION_LAST) {
printf("Exception %d! System halt\n", intr);
// cli unsets the Interrupt Flag (if) in the cpu so interrupts are ignored.
// hlt halts until an interrupt occures.
// cli and hlt are used together to hang the kernel
while (1) { asm volatile("cli; hlt"); }
}
else if (intr >= IDT_NR_IRQ_FIRST && intr <= IDT_NR_IRQ_LAST) {
if (intr == 0x20) {
new_cpu = task_schedule(cpu);
tss[1] = (uint32_t)(new_cpu + 1);
//printf("%d, %d, %d\n", cpu->intr, cpu->esp, cpu->eip);
//while (1) { asm volatile("cli; hlt"); }
}
// IRQ 1 is Keyboard. Handle control to kbc
if (intr == IDT_NR_IRQ_FIRST + 1) {
kbc_handle_key();
}
// EOI (End Of Interrupt)
// Tells the pic the interrupt has been handled successfully.
// If this isn't send, we wont receive further hardware interrupts.
if (intr >= 0x28) {
// EOI for Slave-PIC
outb(0xa0, 0x20);
}
// EOI for Master-PIC
outb(0x20, 0x20);
}
// Software Interrupt. Handle control to syscl
else if (intr == IDT_NR_SOFTWARE) {
syscl_handle_interrupt(cpu);
}
else {
printf("Unknown Interrupt! System halt\n");
while (1) { asm volatile("cli; hlt"); }
}
return new_cpu;
}
gdt.c
#include <kernel/gdt.h>
#include <kernel/tss.h>
#include <stdint.h>
#define GDT_FLAG_DATASEG 0x02
#define GDT_FLAG_CODESEG 0x0a
#define GDT_FLAG_TSS 0x09
#define GDT_FLAG_SEGMENT 0x10
#define GDT_FLAG_RING0 0x00
#define GDT_FLAG_RING3 0x60
#define GDT_FLAG_PRESENT 0x80
#define GDT_FLAG_4K 0x800
#define GDT_FLAG_32_BIT 0x400
#define GDT_ENTRIES 6
static uint64_t gdt[GDT_ENTRIES];
static void gdt_set_entry(int i, unsigned int base, unsigned int limit, int flags) {
gdt[i] = limit & 0xffffLL;
gdt[i] |= (base & 0xffffffLL) << 16;
gdt[i] |= (flags & 0xffLL) << 40;
gdt[i] |= ((limit >> 16) & 0xfLL) << 48;
gdt[i] |= ((flags >> 8) & 0xffLL) << 52;
gdt[i] |= ((base >> 24) & 0xffLL) << 56;
}
void gdt_init(void) {
struct {
uint16_t limit;
void* base;
} __attribute__((packed)) gdtp = {
.limit = GDT_ENTRIES * 8 - 1,
.base = gdt,
};
gdt_set_entry(0, 0, 0, 0);
gdt_set_entry(1, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_CODESEG | GDT_FLAG_4K | GDT_FLAG_PRESENT);
gdt_set_entry(2, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_DATASEG | GDT_FLAG_4K | GDT_FLAG_PRESENT);
gdt_set_entry(3, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_CODESEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
gdt_set_entry(4, 0, 0xfffff, GDT_FLAG_SEGMENT | GDT_FLAG_32_BIT | GDT_FLAG_DATASEG | GDT_FLAG_4K | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
gdt_set_entry(5, (uint32_t)tss, sizeof(tss), GDT_FLAG_TSS | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
asm volatile("lgdt %0" : : "m" (gdtp));
asm volatile(
"mov $0x10, %ax;"
"mov %ax, %ds;"
"mov %ax, %es;"
"mov %ax, %ss;"
"ljmp $0x8, $.1;"
".1:"
);
// load Task Register
asm volatile("ltr %%ax" : : "a" (5 << 3));
}
intr_stub.S
// Macro function for non-exception stubs
// pushes zero and stub nr on stack
// jumps into intr_common_handler
.macro intr_stub nr
.global intr_stub_\nr
intr_stub_\nr:
pushl $0
pushl $\nr
jmp intr_common_handler
.endm
// Macro function for exception stubs
// pushes exception nr and stub nr on stack
// jumps into intr_common_handler
.macro intr_stub_error_code nr
.global intr_stub_\nr
intr_stub_\nr:
pushl $\nr
jmp intr_common_handler
.endm
// Some exceptions have an additional error code
// for this exceptions, the intr_stub_error_code macro is used
// other stubs pushes instead zero of the stack
intr_stub 0
intr_stub 1
intr_stub 2
intr_stub 3
intr_stub 4
intr_stub 5
intr_stub 6
intr_stub 7
intr_stub_error_code 8
intr_stub 9
intr_stub_error_code 10
intr_stub_error_code 11
intr_stub_error_code 12
intr_stub_error_code 13
intr_stub_error_code 14
intr_stub 15
intr_stub 16
intr_stub_error_code 17
intr_stub 18
// IRQs
intr_stub 32
intr_stub 33
intr_stub 34
intr_stub 35
intr_stub 36
intr_stub 37
intr_stub 38
intr_stub 39
intr_stub 40
intr_stub 41
intr_stub 42
intr_stub 43
intr_stub 44
intr_stub 45
intr_stub 46
intr_stub 47
// Syscall
intr_stub 48
// c function from idt.h
.extern idt_handle_interrupt
intr_common_handler:
// Save cpu registers
push %ebp
push %edi
push %esi
push %edx
push %ecx
push %ebx
push %eax
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
// Call c function handler
push %esp
call idt_handle_interrupt
mov %eax, %esp
mov $0x23, %ax
mov %ax, %ds
mov %ax, %es
// load cpu register
pop %eax
pop %ebx
pop %ecx
pop %edx
pop %esi
pop %edi
pop %ebp
// pop error code and interrupt nr from stack
add $8, %esp
iret
task.c
#include <kernel/task.h>
static int current_task = -1;
static int num_tasks = 2;
static struct cpu_state* task_states[2];
void task_init_task(uint8_t* stack, uint8_t* user_stack, void* entry, unsigned int nr) {
struct cpu_state new_state = {
.eax = 0,
.ebx = 0,
.ecx = 0,
.edx = 0,
.esi = 0,
.edi = 0,
.ebp = 0,
.esp = (uint32_t) user_stack + 4096,
.eip = (uint32_t) entry,
/* Ring-3-Segmentregister */
.cs = 0x18 | 0x03,
.ss = 0x20 | 0x03,
/* IRQs einschalten (IF = 1) */
.eflags = 0x200,
};
/*
* 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".
*/
struct cpu_state* state = (void*) (stack + 4096 - sizeof(new_state));
*state = new_state;
task_states[nr] = state;
}
/*
* 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
*/
struct cpu_state* task_schedule(struct cpu_state* 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];
return cpu;
}
So das wärs dann soweit (sofern ich nichts vergessen habe).
Ich freue mich auf eine schöne Zeit hier im Forum!
Grüße,
Der Tele