diff --git a/include/task.h b/include/task.h index 226246c..33930d1 100644 --- a/include/task.h +++ b/include/task.h @@ -3,32 +3,75 @@ #include +// Сегменты для ядра и пользователя +#define SEG_KCODE 1 // Сегмент кода ядра +#define SEG_KDATA 2 // Сегмент данных ядра +#define SEG_UCODE 3 // Сегмент кода пользователя +#define SEG_UDATA 4 // Сегмент данных пользователя +#define DPL_KERNEL 0 // Уровень привилегий ring 0 +#define DPL_USER 3 // Уровень привилегий ring 3 + +// Флаги процессора +#define FL_IF 0x202 // Разрешить прерывания + typedef enum TaskState { - Ready, - Running, - Waiting, - Terminated + Ready, + Running, + Waiting, + Terminated } TaskState; typedef void (*EntryPoint)(void); +// Структура контекста для переключения между процессами +typedef struct Context { + uint32_t edi; + uint32_t esi; + uint32_t ebx; + uint32_t ebp; + uint32_t eip; +} Context; + +// Структура trap frame для перехода в ring 3 +typedef struct TrapFrame { + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t oesp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint16_t gs; + uint16_t fs; + uint16_t es; + uint16_t ds; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; +} TrapFrame; + typedef struct Process { - uint32_t pid; // Process ID - TaskState state; // Process state - uintptr_t programCounter; // Program Counter - uintptr_t* stackBase; // Top of the stack - uintptr_t* stackPointer; // Stack Pointer - uint32_t flags; // Status/Flags register - struct Process* next; // Pointer to the next task in the list - - uint32_t* page_directory; + uint32_t pid; // Process ID + TaskState state; // Состояние процесса + char* kstack; // Стек ядра + Context* context; // Контекст для переключения + TrapFrame* tf; // Trap frame (только для ring 3) + uint32_t ring; // Уровень привилегий (0 или 3) + struct Process* next; // Следующий процесс + uint32_t* page_directory; // Директория страниц } Process; -Process* task_create(EntryPoint func, void** args, uint32_t arg_count); -void scheduler_init(); -void schedule(); -void scheduler_lock(); -void scheduler_unlock(); -#endif +Process* task_create(EntryPoint func, void** args, uint32_t arg_count, uint32_t ring); +void scheduler_init(); +void schedule(); +void scheduler_lock(); +void scheduler_unlock(); + +#endif \ No newline at end of file diff --git a/src/tasking/switch.asm b/src/tasking/switch.asm index 02ace7e..5c3e760 100644 --- a/src/tasking/switch.asm +++ b/src/tasking/switch.asm @@ -1,117 +1,64 @@ -extern current -global switchProcess - struc PCB - .PID resd 1 ; Offset: 0 - .STATE resd 1 ; Offset: 4 - .PROGRAM_COUNTER resd 1 ; Offset: 8 - .STACK_BASE resd 1 ; Offset: 12 - .STACK_POINTER resd 1 ; Offset: 16 - .FLAGS resd 1 ; Offset: 20 - .NEXT resd 1 ; Offset: 24 + .PID resd 1 ; Offset: 0 + .STATE resd 1 ; Offset: 4 + .KSTACK resd 1 ; Offset: 8 + .CONTEXT resd 1 ; Offset: 12 + .TF resd 1 ; Offset: 16 + .FLAGS resd 1 ; Offset: 20 + .NEXT resd 1 ; Offset: 24 + .PAGE_DIRECTORY resd 1 ; Offset: 28 endstruc -section .text - - ; void switchProcess(Process* next) +section .text +global switchProcess +extern current +extern trapret + switchProcess: - ; __________Stack___________ - ; |------------------------| - ; | Return Address (EIP) | +0 - ; |------------------------| - ; | Process* next | +4 - ; |------------------------| + ; Сохраняем регистры текущего процесса + push ebx + push esi + push edi + push ebp - ; Push volatile registers (per the 32-bit System V ABI) - push ebx - push esi - push edi - push ebp + ; Сохраняем указатель стека в current->context + mov edi, [current] + mov [edi + 12], esp ; PCB.CONTEXT находится по смещению 12 - ; __________Stack___________ - ; |------------------------| - ; | EBP | +0 - ; |------------------------| - ; | EDI | +4 - ; |------------------------| - ; | ESI | +8 - ; |------------------------| - ; | EBX | +12 - ; |-------- ---------------| - ; | Return Address (EIP) | +16 - ; |------------------------| - ; | Process* next | +20 - ; |------------------------| + ; Загружаем следующий процесс + mov esi, [esp + 20] ; next (первый аргумент) + mov [current], esi + mov esp, [esi + 12] ; Загружаем esp из next->context - ; Save current process' state - mov edi, [current] - ; EDI now contains the pointer to [Process* current] - ; |-----| - ; | EDI | <== [Process* current] - ; |-----| + ; Восстанавливаем регистры + pop ebp + pop edi + pop esi + pop ebx - ; Set [current->PCB.STACK_POINTER] to the value in ESP - ; Process* current->PCB.STACK_POINTER <== ESP - mov [edi + PCB.STACK_POINTER], esp + ; Проверяем ring процесса + mov eax, [esi + 20] ; PCB.RING находится по смещению 20 + cmp eax, 3 + je .ring3 + ret ; Для ring 0 просто возвращаемся +.ring3: + jmp trapret ; Для ring 3 переходим через trapret + +trapret: + ; Восстанавливаем общие регистры из trap frame + popal ; edi, esi, ebp, oesp, ebx, edx, ecx, eax + add esp, 4 ; Пропускаем oesp (не используется) + + ; Восстанавливаем сегментные регистры + pop gs + pop fs + pop es + pop ds + + ; Пропускаем trapno и err + add esp, 8 ; trapno (4 байта) + err (4 байта) - ; Load next process' state - ; ESP has been decremented by 16, so instead of +4, - ; we need to do +20. 4 pushes, 1 return address, 4 - ; bytes each. - mov esi, [esp + 20] - ; ESI now contains the pointer to [Process* next] - ; __________Stack___________ - ; |------------------------| - ; | Return Address (EIP) | +16 - ; |-----| |------------------------| - ; | ESI | <== | Process* next | +20 - ; |-----| |------------------------| - - - ; Set [EXTERN Process* current] to the value in ESI - ; Process* current <== ESI (Process* next) - mov [current], esi - - mov esp, [esi + PCB.STACK_POINTER] - ; |-----| |-----| - ; | ESP | <== | ESI | - ; |-----| |-----| - - ; Restore non-volatile registers (per the 32-bit System V ABI) - pop ebp - pop edi - pop esi - pop ebx - - ; The stack is now restored to its original state. - ; __________Stack___________ - ; |------------------------| - ; | Return Address (EIP) | +0 - ; |------------------------| - ; | Process* next | +4 - ; |------------------------| - - ret - -global jump_usermode -extern test_user_function -jump_usermode: - mov ax, (4 * 8) | 3 ; ring 3 data with bottom 2 bits set for ring 3 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax ; SS is handled by iret - - ; and eax, ~0x80000000 - ; mov cr0, eax - - ; set up the stack frame iret expects - mov eax, esp - push (4 * 8) | 3 ; data selector - push eax ; current esp - pushf ; eflags - push (3 * 8) | 3 ; code selector (ring 3 code with bottom 2 bits set for ring 3) - push test_user_function ; instruction address to return to - iret + ; Теперь ESP указывает на eip, cs, eflags, esp, ss + iret \ No newline at end of file diff --git a/src/tasking/task.c b/src/tasking/task.c index 5b18e73..1016ad0 100644 --- a/src/tasking/task.c +++ b/src/tasking/task.c @@ -11,120 +11,144 @@ uint32_t irq_disable_counter = 0; uint32_t pid_counter; uint32_t task_count; -#define STACK_SIZE 4096 +#define KSTACKSIZE 4096 +#define USTACKSIZE 4096 +extern void trapret(void); extern void switchProcess(Process* next); +extern Process* queue; +extern Process* current; +extern uint32_t pid_counter; +extern uint32_t task_count; +// Функция запуска процесса в ядре, после которой произойдёт переход в ring 3 static void processStartup() { - printf("startup\n"); - scheduler_unlock(); + printf("startup\n"); + scheduler_unlock(); + // Возврат из этой функции приведёт к вызову trapret } -Process* task_create(EntryPoint func, void** args, uint32_t arg_count) +Process* task_create(EntryPoint func, void** args, uint32_t arg_count, uint32_t ring) { - Process* p = heap_alloc(sizeof(Process)); - p->pid = ++pid_counter; - p->state = Ready; - p->programCounter = 0; - p->stackBase = heap_alloc(STACK_SIZE); - p->stackPointer = (uintptr_t*)((uint32_t)p->stackBase + STACK_SIZE); + Process* p = alloc_page();//heap_alloc(sizeof(Process)); + p->pid = ++pid_counter; + p->state = Ready; + p->ring = ring; - //put the arguments in stack - for(int i = arg_count-1; i >=0; i--) - *(--p->stackPointer) = (uintptr_t)args[i]; + // Выделяем стек ядра + p->kstack = alloc_page();//heap_alloc(KSTACKSIZE); + uint32_t* sp = (uint32_t*)(p->kstack + KSTACKSIZE); - // Добавляем адрес возврата (фиктивный, для выравнивания) - *(--p->stackPointer) = 0; // Фиктивный адрес возврата (не используется напрямую) + if (ring == DPL_USER) { + // Настраиваем trap frame для ring 3 + sp -= sizeof(TrapFrame) / 4; + p->tf = (TrapFrame*)sp; + p->tf->cs = (SEG_UCODE << 3) | DPL_USER; + p->tf->ds = (SEG_UDATA << 3) | DPL_USER; + p->tf->es = p->tf->ds; + p->tf->fs = p->tf->ds; + p->tf->gs = p->tf->ds; + p->tf->ss = p->tf->ds; + p->tf->eflags = FL_IF; + p->tf->eip = (uint32_t)func; + p->tf->esp = USTACKSIZE; // Предполагаем, что пользовательский стек настроен - *(--p->stackPointer) = (uintptr_t)func; - *(--p->stackPointer) = (uintptr_t)processStartup; - *(--p->stackPointer) = 0;//ebp - *(--p->stackPointer) = 0;//edi - *(--p->stackPointer) = 0;//esi - *(--p->stackPointer) = 0;//ebx + // Добавляем адрес возврата для trapret + sp -= 1; + *sp = (uint32_t)trapret; + } else { + // Для ring 0 не используем trap frame + p->tf = NULL; + } + // Настраиваем начальный контекст + sp -= 5; // Место для edi, esi, ebx, ebp, eip + p->context = (Context*)sp; + p->context->edi = 0; + p->context->esi = 0; + p->context->ebx = 0; + p->context->ebp = 0; + if (ring == DPL_USER) { + p->context->eip = (uint32_t)processStartup; + } else { + p->context->eip = (uint32_t)func; // Прямо в ring 0 + } - p->page_directory = kernel_page_directory; + p->page_directory = kernel_page_directory; - p->next = 0; + p->next = 0; - if(!queue) - { - queue = p; - current = queue; - } - else - { - Process* curr = queue; - while(curr->next) - curr = curr->next; - curr->next = p; - } - task_count++; - return p; + if (!queue) { + queue = p; + current = queue; + } else { + Process* curr = queue; + while (curr->next) + curr = curr->next; + curr->next = p; + } + task_count++; + return p; } +// Остальные функции остаются без изменений void scheduler_lock() { - asm volatile("cli"); - irq_disable_counter++; + asm volatile("cli"); + irq_disable_counter++; } void scheduler_unlock() { - irq_disable_counter--; - if(irq_disable_counter == 0) - asm volatile("sti"); + irq_disable_counter--; + if (irq_disable_counter == 0) + asm volatile("sti"); } void schedule() { - if(!current) - return; + if (!current) + return; - Process* next = current->next; - if(!next) - next = queue; - if(next != current) - { - //loadPageDirectory(next->page_directory); - switchProcess(next); - } + Process* next = current->next; + if (!next) + next = queue; + if (next != current) + { + //loadPageDirectory(next->page_directory); + switchProcess(next); + } } void idle() { - while(1){} + while (1) {} } void task1(int arg1, char* arg2) { - while(1){ - if(arg1 == 42) - printf("task1 0x%x %s\n", arg1, arg2); - else - printf("ZHOPA %s\n", arg2); - } + while (1) { + if (arg1 == 42) + printf("task1 0x%x %s\n", arg1, arg2); + else + printf("ZHOPA %s\n", arg2); + } } void task2(int arg1, int arg2) { - while(1){printf("task2 %X %X\n", arg1, arg2);} + while (1) { arg1++; printf("task2 %X %X\n", arg1, arg2); } } -void jump_usermode2(void); - void scheduler_init() { - jump_usermode2(); - //we create this task two times because in other case it just won't start - task_create((EntryPoint)&idle, NULL, 0); - task_create((EntryPoint)&idle, NULL, 0); + task_create((EntryPoint)idle, NULL, 0, 0); + task_create((EntryPoint)idle, NULL, 0, 0); - void* args1[] = {(void*)42, (void*)"ebalo"}; - task_create((EntryPoint)&task1, args1, 2); - void* args2[] = {(void*)69, (void*)420}; - task_create((EntryPoint)&task2, args2, 2); + void* args1[] = {(void*)42, (void*)"ebalo"}; + task_create((EntryPoint)task1, args1, 2, 0); + void* args2[] = {(void*)69, (void*)420}; + task_create((EntryPoint)task2, args2, 2, 0); } #include "../include/string.h"