#The Flopperating System (tfos) - x86 32 bit Hobby OS

1 messages · Page 2 of 1

bold grove
#

Guess what guys

#

Floppa os is back

#

Here is what I’ve gotten so far:

#
  • Virtual file system
  • pmm, vmm, slab, heap
  • interrupts
  • fb, vga text, vga graphics
  • round robin scheduler with timing
#

For the most part that’s it

noble remnant
#

nice!!

bold grove
#

Guess I should get a userspace

gray cairn
bold grove
#

Syscalls kinda work. Good night

noble remnant
#

nice!

noble remnant
bold grove
#

Sys calls don’t worry anymore 💀

bold grove
# noble remnant nice!

I dropped into user mode and ran hello world and uhhh triple faulted after returning to kernel mode when writing to memory

noble remnant
#

nice progress

bold grove
bold grove
#

static inline int syscall(int num, uint32_t arg1, uint32_t arg2, uint32_t arg3) {
    int ret;
    __asm__ volatile (
        "int $0x80"
        : "=a"(ret)
        : "a"(num), "b"(arg1), "c"(arg2), "d"(arg3)
        : "memory"
    );
    return ret;
}

static inline void puts(const char *str) {
    syscall(SYS_PUTS, (uint32_t)str, 0, 0);
}
#

And

#
#include "syscall.h"

void main() {
    puts("Hello from user space!");
    while (1);
}
#

:)

#

Here’s how I’m doing it in the kernel

#
global syscall_entry

syscall_entry:
    pusha                ; Save all general-purpose registers
    push ds
    push es
    push fs
    push gs

    mov ax, 0x10       
    mov ds, ax
    mov es, ax

    push esp           
    call syscall_dispatch

    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 4         
    iret            
#

And here’s this

#

#include "syscall.h"

#include "log.h"

typedef uint32_t (*syscall_func_t)(uint32_t, uint32_t, uint32_t, uint32_t);

};

#define SYSCALL_COUNT (sizeof(syscall_table) / sizeof(syscall_func_t))

uint32_t syscall_dispatch(uint32_t *stack) {
    uint32_t num = stack[8];  // EAX (syscall number)
    uint32_t arg1 = stack[9]; // EBX
    uint32_t arg2 = stack[10]; // ECX
    uint32_t arg3 = stack[11]; // EDX

    if (num >= SYSCALL_COUNT || !syscall_table[num]) {
        log_step("Invalid syscall", 0x04);
        return -1;
    }

    return syscall_table[num](arg1, arg2, arg3, 0);
}
#

I’ll obviously make better syscalls

#

But this is the thing I have for now

bold grove
torpid viper
torpid viper
#

@bold grove

bold grove
# torpid viper the `add esp, 4`should be before `pop ds`

Like this?


global syscall_entry

section .text

syscall_entry:
    pusha
    push ds
    push es
    push fs
    push gs
    mov eax, ss
    push eax            

    mov ax, 0x10
    mov ds, ax
    mov es, ax

    sub esp, 4
    push esp

    call syscall_dispatch

    add esp, 4

    pop gs
    pop fs
    pop es
    pop ds

    popa

    add esp, 4
    iret
bold grove
#

How

#

Wait

#

Mb

#

So I should

#

Switch them?

#

Or what

torpid viper
# bold grove How
syscall_entry:
pusha
push ds
push es
push fd
push gs

mov ax, 0x1p
mov ds, ax
mov es, ax
mov gs, ax
mov fs, ax

push esp
call syscall_dispatch
add esp, 4

pop gs
pop fs
pop es
pop ds

popa
iretq```
torpid viper
bold grove
#

Okay

torpid viper
bold grove
#

How should I do it then

torpid viper
torpid viper
bold grove
#

I’m in protected mode btw

#

I think I should do it like this

#
global syscall_entry

section .text

syscall_entry:
    pusha
    push ds
    push es
    push fs
    push gs

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    push esp
    call syscall_dispatch
    add esp, 4

    pop gs
    pop fs
    pop es
    pop ds

    popa
    iret
torpid viper
torpid viper
#

like that

#

@bold grove

bold grove
torpid viper
#

@bold grove so it is working ?

bold grove
#

Yeah the issue is that I’m not in 64bit

bold grove
torpid viper
torpid viper
#

wait i will send the 32 it version

torpid viper
# bold grove So it’s not
syscall_entry:
pusha

mov ax, ds
push eax

mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

push esp
mov edi, esp
call syscall_dispatch
add esp, 4

pop eax
mov ds,ax
mov es, ax
mov fs, ax
mov gs, ax

popa
iret```
#

try this @bold grove

bold grove
#

Iretq doesn’t work in 32 bit

torpid viper
bold grove
#

Yeah it works

#

I’ll show you later

#

When I get the sys call to work fr again

torpid viper
torpid viper
bold grove
#

I’ll get back to you

bold grove
#

So I’ll test later

torpid viper
bold grove
torpid viper
# bold grove

good i use the same watch for over 8 years now LOL

bold grove
#

I’m just gonna uh

#

Work on the heap a bit more

bold grove
#

So I’ve made a free list for my allocator

#
struct alloc_mem_block {
    struct alloc_mem_block *next;
    size_t size;
};
struct free_list {
    struct free_list *next;
    size_t size;
};
struct kernel_region {
    struct kernel_region *next;
    uintptr_t start;
    uintptr_t end;
    struct free_list *free_list;
} kernel_regions;

static struct free_list *free_blocks = NULL;

void add_to_free_list(void *ptr, size_t size) {
    struct free_list *block = (struct free_list *)ptr;
    block->size = size;
    block->next = free_blocks;
    free_blocks = block;
}

void *get_from_free_list(size_t size) {
    struct free_list **prev = &free_blocks;
    struct free_list *current = free_blocks;
    
    while (current) {
        if (current->size >= size) {
            *prev = current->next;
            return (void *)current;
        }
        prev = &current->next;
        current = current->next;
    }
    return NULL;
}

void add_memory_block(uintptr_t start, size_t size, int is_free) {
    struct alloc_mem_block *block = (struct alloc_mem_block *)start;
    block->size = size;
    block->next = NULL;

    if (is_free) {

        block->size |= 1;
        add_to_free_list((void *)start, size);
    } else {

        block->size &= ~1;
    }
} 

#

This is what malloc and free look like now

#

void *kmalloc(size_t size) {
    if (size == 0) return NULL;

    if (size <= SLAB_MAX_SIZE) {
        void *ptr = slab_alloc(size);
        if (ptr) return ptr;
    }
    void *ptr = get_from_free_list(size);
    if (ptr) {
        struct alloc_mem_block *block = (struct alloc_mem_block *)ptr;
        block->size &= ~1; // Mark as used
        return (void *)(block + 1);
    }


    size_t pages = (ALIGN_UP(size + sizeof(struct alloc_mem_block), SLAB_PAGE_SIZE) / SLAB_PAGE_SIZE);

    ptr = pmm_alloc_pages(0, pages);
    if (!ptr) {
        log_step("kmalloc: failed to allocate large block", size);
        return NULL;
    }


    add_memory_block((uintptr_t)ptr, pages * SLAB_PAGE_SIZE, 0);
    return (void *)((struct alloc_mem_block *)ptr + 1);
}

void kfree(void *ptr, size_t size) {
    if (!ptr || size == 0) return;

    if (size <= SLAB_MAX_SIZE) {
        slab_free(ptr);
        return;
    }

    struct alloc_mem_block *block = (struct alloc_mem_block *)ptr - 1;
    add_to_free_list(block, block->size);
    block->size |= 1; // Mark as free

    // Coalesce adjacent free blocks
    struct free_list *current = free_blocks;
    struct free_list *prev = NULL;
    
    while (current && current->next) {
        if ((uintptr_t)current + current->size == (uintptr_t)current->next) {
            current->size += current->next->size;
            current->next = current->next->next;
        } else {
            prev = current;
            current = current->next;
        }
    }
}
bold grove
#

I actually ended up fixing this

small hound
bold grove
#

Hi guys I’m going to work more on my kernel heap

#

So I’m going to make some heap integrity functions

#

And expand heap

#

And an aligned_kmalloc()

#

This is what I have so far but

#

void *kaligned_alloc(size_t size, size_t alignment) {
    size_t total_size = size + alignment - 1;
    void *ptr = kmalloc(total_size);
    if (!ptr) return NULL;

    uintptr_t aligned_addr = ALIGN_UP((uintptr_t)ptr, alignment);
    return (void *)aligned_addr;
}
bold grove
#

I’m far too depressed to do
Anything rn I swear

#

I got broken up
With

noble remnant
#

take a break from osdev

#

meditate

#

sorry for your pain

sleek mulch
#

Im sorry bro, take a break from osdev for now and try to keep yourself busy with something relaxing <3

bold grove
#

I can get my kernel to run just fine on my laptop but on my desktop it won’t

torpid viper
bold grove
#

I don’t know

#

When I run it on my desktop it gets to pmm, does vmm, then crashes

#

init_kernel_heap() works actually

#

Init interrupts works until I actually enable interrupts

#

So the problem is at __asm__ volatile (“sti”)

bold grove
#

Well I figured out that issue

#

For some reason my repo didn’t sync a double pointer in my slab, so that’s what was nuking the kernel apparently

#

It’s this line of code *(uint8_t **)ptr = cache->free_list; in initialize_free_slab_list(slab_t *slab, slab_cache_t *cache

#

It was typed *(uint8_t *)ptr instead

#

For some reason

#

Even though I synced my repo

#

No clue why

#

Since you guys wanna bully me

#

Now I added meaningful docs

#

To kernel.c

bold grove
#

Girlfriend and I figured things out

bold grove
bold grove
#

Later I’m gonna commit a lot

#

I’m making a vmm_reserve_region()

#

Which basically doesn’t allocate pages but reserves a space

torpid viper
torpid viper
bold grove
#

Yeah I just did

sleek mulch
#

want my 2 pennies? here you have them anyway:

#

if someone broke up with you they are going to do it again (95 out of 100) times

#

I saw it happen for others, and it happened for me

bold grove
#

We didn’t like break up it was like

#

We are taking a break

#

But like for me

#

I’m really attached but she just needed time a little apart because we have been having some arguments so for a bit we will spend some time apart plus I’m going on vacation

#

And when we come back everything will be okay obviously

#

We made up and everything and we just agreed to talk a bit less but we’re still together

sleek mulch
#

not sure what to say about that either, but "taking a break" is vague. It could mean anything from "I just want some alone time" to "ill cheat on you under this pretext"

#

I understand it would be the former for you and I am glad

bold grove
#

It’s “I just want some alone time”

#

Because we kinda have a codependency issue

#

And we really love each other

#

But I think we need to learn how to be happy with ourselves a bit more

#

Which just involves working together

#

And maybe talking a bit less

#

And not hanging out for a week or something

#

Or a bit longer

bold grove
#

I got some good progress

#

I can now expand and shrink the heap

#

void expand_kernel_heap(size_t additional_size) {
    if (additional_size == 0) return;
    
    uintptr_t new_start = kernel_regions.end;
    uintptr_t new_end = new_start + ALIGN_UP(additional_size, PAGE_SIZE);

    if (!pmm_alloc_pages((void *)new_start, (new_end - new_start) / PAGE_SIZE)) {
        log_step("Heap expansion failed!\n", RED);
        return;
    }

    kernel_regions.end = new_end;
    add_memory_block(new_start, new_end - new_start, 1);
    log_step("Kernel heap expanded.\n", GREEN);
}
void shrink_kernel_heap(size_t reduce_size) {
    if (reduce_size == 0 || reduce_size > (kernel_regions.end - kernel_regions.start)) {
        log_step("Failed to all")
        return;
    }

    uintptr_t new_end = kernel_regions.end - ALIGN_UP(reduce_size, PAGE_SIZE);
    
    // Free the pages being removed
    pmm_free_pages((void *)new_end, (kernel_regions.end - new_end) / PAGE_SIZE);

    kernel_regions.end = new_end;

    remove_memory_block(new_end, (kernel_regions.end - new_end));

    log_step("Kernel heap shrunk.\n", YELLOW);
}
#

So my heap is going well

#

I can now allocate virtual regions

#

Contiguous ones

bold grove
#

Hi guys, I’m gonna do some more work on the OS tonight. Gonna try to wrap up the allocator, I mean it works, but I just wanna modularizr it further and add better logging/spinlocks

#

Next, I’m going to actually fix my virtual region functions

#

Right now, it does what it’s supposed to, but there’s no kernel user separation for now, since I rewrote my vmm

#

So I’m going to work on that.

#

I’ll update

#

I think I’m pretty done with init_kernel_heap()

#

void init_kernel_heap(void) {
    log_step("Initializing kernel heap...\n", YELLOW);
    
    size_t total_memory = pmm_get_memory_size();
    if (total_memory == 0) {
        log_step("init_kernel_heap: PMM not initialized or no memory available!\n", RED);
        PANIC_PMM_NOT_INITIALIZED((uintptr_t)first_block);
        return;
    }

    kernel_heap_size = (total_memory * HEAP_PERCENTAGE) / 100;
    kernel_heap_size = ALIGN_UP(kernel_heap_size, PAGE_SIZE);
    if (kernel_heap_size < MIN_HEAP_SIZE) {
        kernel_heap_size = ALIGN_UP(MIN_HEAP_SIZE, PAGE_SIZE);
    } else if (kernel_heap_size > MAX_HEAP_SIZE) {
        kernel_heap_size = ALIGN_UP(MAX_HEAP_SIZE, PAGE_SIZE);
    }

    first_block = (struct alloc_mem_block *)KERNEL_HEAP_START;
    add_memory_block(KERNEL_HEAP_START, kernel_heap_size, 1);
    
    kernel_regions.start = (uintptr_t)KERNEL_HEAP_START;
    kernel_regions.end = (uintptr_t)(KERNEL_HEAP_START + kernel_heap_size);
    kernel_regions.next = NULL;

    log_step("Kernel heap initialized successfully\n", GREEN);
    heap_initialized = 1;
}
bold grove
#

After I go on a walk I’m gonna do some more work

#

Mainly fucking error handling for my slab allocator because there’s literally none

bold grove
#

Well, Slab has error handling, I added guarded and aligned malloc functions, and I made a proper reversed virtual region for the kernel heap, that lazily allocates pages for the heap as needed rather than just allocating it all at once

#

I’ll update my repo tonight, I’m just making the code cleaner

bold grove
bold grove
#

Hi guys im going to do some more work

#

I might need some help with my heap

#

Like it works but it’s messy

bold grove
#

Well

#

My laptop died so

#

I actually did cleanup parts of the heap

#

But I couldn’t push it

#

But it’s saved so get ready for some more dogshit C code from yours truly ™️

bold grove
#

Guyssssss I redid my heap allocator

#

And it FUCKING WORKD

bold grove
#

Okay so now

#

I’m going to push that tonight

#

I added actual decent comments to that file

#

It’s like ridiculously big for what it is

#

I added aligned and guarded allocs

#

Basically the guarded alloc will allocate two extra pages and the aligned one will

#

Well

#

Align to a boundary specified

torpid viper
noble remnant
#

nice

sleek mulch
bold grove
bold grove
#

Because I’m kinda lazily allocating a bit too hard

#

Kinda idk

noble remnant
#

k

bold grove
#

Well I kinda got stuck

#

With some stuff

#

Just getting some stupid errors

#

So im making a virtual region for the heap

#

Like the one on the GitHub works

#

It’s just a bit clunky

bold grove
#

Okay

#

I kinda worked it out

#

Im just going to fix some other shit

#

Mainly with paging

#

I’m gonna try to just clean up code in my memory manager

#

It’s a lot though

bold grove
#

i think ive got something cool

#

for reserved regions

#

so ill push that soonn

sleek mulch
#

wdym reserved regions

bold grove
# sleek mulch wdym reserved regions

Basically pages that aren’t allocated but are reserved for a virtual address like so for the heap the first 100mb or whatever would be “reserved” so the rest of the system can’t access it bit it doesn’t have to allocate it all at once. So it will “lazily” alloc to the regions

#

Anyways yeah I’ve been deshittifying this memory manager

#

I mean this one I have works just fine

#

Just gonna make it cleaner

#

And make sure my sanity isn’t too far gone trying to debug it

#

@noble remnant kinda rewriting some parts lol

#

So sorry for the delay

#

But yeah currently my shit is fragmented weirdly into many files

#

Which is fine but

#

I kinda just arbitrarily chose to do page frame stuff in the pmm and paging file lol

#

So it’s not really clear what does what I guess

#

My alloc.c works
Fine for now

#

Except for when I add virtual regions

#

Which I’ve tested

#

I’ll show you guys later

gusty vine
#

yo

#

whats this about

bold grove
#

My kernel

#

You should like the thread and stay tuned for more progress

#

😊😊

#

Look guys

#

It’s me and girlfriend

#

This is what I was doing last night instead of working on the kernel lol

rigid root
#

Damn

bold grove
#

Infy I’ll show you what I’ve been working on tonight

#

I wanna test my alloc with uacpi

rigid root
#

Do it

blissful grove
#

Lol congrats man

#

What was dinner?

bold grove
#

I’m a simple man of simple cuisines

#

I feel dumb though because she’s like a virtuoso chef and even learned how to make ethnic dishes from bosnia and here I am making eggs and bacon for dinner

gray cairn
blissful grove
#

Lol

blissful grove
#

Even if the meal isn't fancy, it's nice if someone cooks for you

noble remnant
blissful grove
#

unless you are my sister who burns the garlic bread, everytime

bold grove
#

Hi guys

#

Sorry, I haven’t really updated the repo

#

Honestly I haven’t even compiled the os in a day or two

#

Just because of the rewrite I’m doing

#

For parts of the mem manager

bold grove
#

Uhhh

sleek mulch
#

you are cute together

bold grove
#

PAIN

#

MY VMM IS LIKE 1000 LINES

#

TF

#

And my alloc.c is a thousand lines now too

sleek mulch
bold grove
#

The way I’m doing it is kinda complicated now

#

I’m creating a virtual region for each slab

#

And then creating a region that expands and shrinks as necessary for allocs above page size

#

And also I’m adding guarded allocs

#

Which add a certain amount of extra pages

#

And also heap integrity checks

#

And merging and splitting regions

crimson plover
#

oof

#

I mean surely the setting up regions and what not falls under vmm

bold grove
#

I kinda nuked my whole progress

#

That I made in the past 2 days

#

Looked like shit lmao

#

I’m implementing a binary tree for virtual regions

bold grove
#

Sorry for the slow ass progress

noble remnant
#

np

torpid viper
bold grove
bold grove
#

IM WORKING ON FLOPPA

#

LET ME (MAYBE) COOK

crimson plover
#

coooook

bold grove
#

My vmm crashes my vm at an unknown opcode

crimson plover
#

lmk if u need help

#

I just finished up working on what I was doing

bold grove
#

I’m trying to GDB this

bold grove
#

Without over complicating

#

I cleaned up my vmm a lot

#

Here’s how im creating and destroying regions now

#
// Create a new virtual memory region
vmm_region vmm_create_region(PDE *page_directory, uintptr_t start_virt, uintptr_t start_phys, size_t size, PageAttributes attrs) {
    if (!page_directory || size == 0) {
        log("Invalid parameters for creating region!\n", RED);
        return (vmm_region){0};
    }

    // Align size to page size
    size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
    uint32_t pages = size / PAGE_SIZE;

    // Map the region
    for (uint32_t i = 0; i < pages; i++) {
        uintptr_t virt_addr = start_virt + (i * PAGE_SIZE);
        uintptr_t phys_addr = start_phys + (i * PAGE_SIZE);
        vmm_map_page(page_directory, virt_addr, phys_addr, attrs);
    }

    log("Region created successfully.\n", GREEN);
    return (vmm_region){
        .start_virt = start_virt,
        .start_phys = start_phys,
        .size = size,
        .pages = pages,
        .attrs = attrs
    };
}
#

Here’s the cute little structure

#
typedef struct {
    uintptr_t start_virt; // Start of the virtual address range
    uintptr_t start_phys; // Start of the physical address range
    size_t size;          // Size of the region in bytes
    uint32_t pages;       // Number of pages in the region
    PageAttributes attrs; // Attributes of the pages in the region
} vmm_region;
bold grove
#

I’m gonna make my kernel assert OS now

bold grove
#

Man nooo

#

Kinda realized

#

I haven’t really been going anywhere

#

I need something new

#

Rather than a better memory manager

noble remnant
#

scheduler

#

elf parsing

#

exec/fork or spaen

#

userspace with contrxt switch

#

vfs

#

tempfs

#

initrd

#

there's so much to do

blissful grove
#

Don't forget porting to another arch :p

noble remnant
blissful grove
#

Haha nah I only got to 3

#

You're thinking of mishakov

#

What I'm having trouble finding is motivation lol

noble remnant
#

we can race to having doom running :P

near spindle
noble remnant
#

yeah I took like a 4 month break from osdev and just got back into it again recently

blissful grove
blissful grove
near spindle
torpid viper
noble remnant
bold grove
#

Can you help

bold grove
bold grove
torpid viper
#

yes i'm awake

bold grove
#

Sorry I’m just struggling lol

torpid viper
bold grove
#

No

#

It’s 2pm for me

torpid viper
#

for me it's 23:02 everyone in my house is sleeping except me

#

so what is your probleme ?

#

@bold grove ?

bold grove
#

I’m having trouble with priority handling

#

I’ll send it here gimme a sec

torpid viper
bold grove
#

so i have a priority field in my task struct

#

and my scheduler is activated to schedule every milisecond by the PIT

#

and for some reason adding this to it

#

just makes everything stop working and i can not for the life of me figure out why

int original_task = current_task;
int highest_priority = -1;
int next_task = -1;

for (int i = 0; i < task_count; i++) {
 int index = (current_task + 1 + i) % task_count;
         if (task_queue[index].sleep_ticks == 0 && task_queue[index].priority > highest_priority) {
             highest_priority = task_queue[index].priority;
             next_task = index;
         }
     }
 
     if (next_task != -1) {
         current_task = next_task;
     } else {
         current_task = original_task; // No runnable task found, stay on the current one
     }
torpid viper
bold grove
#
// PIT ISR (IRQ0)
void __attribute__((interrupt, no_caller_saved_registers)) pit_isr(void *frame) {
    (void)frame; // Unused
    
    // Call the scheduler on each timer tick if enabled
    if (scheduler_enabled) {
        scheduler();
    }
    
    outb(PIC1_COMMAND, 0x20); // Send EOI to PIC
}
torpid viper
#

first of all maybee try to lower to 100 times per second instead of 1000

bold grove
#

okay

#

what do i do from there

torpid viper
bold grove
#

checking rn

#

it does not

torpid viper
#

when you said it don't work like

#

it crash

bold grove
#

no its just nothing schedules

#

just freezes

torpid viper
#

it prevent other code from being exe uting

bold grove
#

yes

torpid viper
#

comment and make sure it work wihout it

#

are you sure it just donsen't schedule

bold grove
#

yeah i commented it out and it works without

#

im tryna db it rn

torpid viper
#

you spam this command and is if the pid change

bold grove
#

okay

torpid viper
# bold grove okay

there are three cases:

  • it don't change
  • it change a bit
  • it change a lot
bold grove
#

doesnt change at all

torpid viper
#

weird

#

so it freeze

#

note the eip

#

and now don't recompile or change anything
do

#

objdump -D kernel.elf > asm.txt

#

or whanever your kernel is

#

and then inside asm.txt find the instruction it freeze onto

#

@bold grove

bold grove
#

doing it rn

#

i cant even find eip

#

wait

torpid viper
bold grove
#

nope

#

its nowhere in the dissassembly

#

0010101c

#

this is it

#

thats what qemu gives me

torpid viper
#

that why

torpid viper
bold grove
#

not anymore no

torpid viper
torpid viper
bold grove
#

For some reason

#

Nothing ever gets scheduled and executed it
Just skips every time

#

Even though I set priority values

torpid viper
torpid viper
bold grove
bold grove
torpid viper
torpid viper
bold grove
#

Sorry I meant like the scheduler that’s called by the pit

#

Everytime it runs it just gets stuck in that function

#

No clue why

torpid viper
bold grove
#

Yes, like it just hangs in there after calling the scheduler after nothing gets called it just stops

torpid viper
#

in the loop?

#

it loop idefinetly ?

bold grove
#

Wait

#

I added an assert

#

That caught it

#

Give me a min

torpid viper
#

i'm gonna sleep

#

bye

bold grove
#

Ok
Good night

blissful grove
#

What did the assert catch?

#

Also what's the priority of the initial thread?

#

Also I don't actually see you context switching anywhere?

#

The isr fires - > scheduler gets called - > scheduler returns, isr returns to previous context

bold grove
#
void scheduler() {
    if (task_count == 0 || !scheduler_enabled) return;

    // Increment scheduler ticks
    scheduler_ticks++;

    if (scheduler_paused) {
        if (pause_ticks_remaining > 0) {
            pause_ticks_remaining--;
            if (pause_ticks_remaining == 0) {
                sched_resume();
            }
        }
        return;
    }

    // Update sleep timers for all tasks
    for (int i = 0; i < task_count; i++) {
        if (task_queue[i].sleep_ticks > 0) {
            task_queue[i].sleep_ticks--;
            if (task_queue[i].sleep_ticks == 0) {
                log_f("Task %s (PID: %d) woke up.\n", task_queue[i].name, task_queue[i].pid);
            }
        }
    }

    // Find the next task to run based on priority
    int highest_priority = -1;
    int next_task = -1;

    for (int i = 0; i < task_count; i++) {
        int index = (current_task + 1 + i) % task_count;
        if (task_queue[index].sleep_ticks == 0 && task_queue[index].priority > highest_priority) {
            highest_priority = task_queue[index].priority;
            next_task = index;
        }
    }

    // If no eligible task is found, keep the current task
    if (next_task == -1) {
        next_task = current_task;
    }

    if (next_task != -1) {
        current_task = next_task;
    }

    // Run the selected task
    Task *current = &task_queue[current_task];
    if (current->sleep_ticks == 0) {
        current->runtime++;
        if (current->function) {
            current->function(current->arg);
        }
    }
}
#

in its entirety

bold grove
#

okay i flipped the first threadto have the highest prirotity

#

now my first thread works but my other two done lol

#

if i give them all the same priority the same thing happens

#

the first thread works too

blissful grove
#

great thanks

#

some general advice: separate the idea of time-keeping (and a timer interrupt) and the scheduler. There are other times you'll want to reschedule, like waiting on io or a mutex.
So you would have your scheduler that handles which threads should run and when. Then if the timer interrupt fires, you handle the interrupt as per normal, and then notify the scheduler of that. The scheduler can decide what to do with it then.

bold grove
#

ohh okay

blissful grove
#

and it'll help clean things up if sleeping tasks are removed from the work queue entirely. I find its more practical to implement support for threads blocking/waiting on an event/mutex/whatever with timeouts, and to sleep - you simply wait on nothing with a timeout for the sleep duration.

bold grove
#

so have the scheduler handle the timer rather than the timer handle the scheduler

blissful grove
#

you have some mechanism in your kernel to say "hey I want to run this function in xyz amount of time", and your scheduler provides yield()/enqueue()/dequeue() functions. When switching threads, the scheduler would ask to have yield() called in some amount of time (your timeslice), so you're guarenteed to context switch if the thread doesnt block before then.

#

so your timer interrupt would look more like:

  • timer fires
  • you check for anything that needs to be done based on the timer firing (you might have a list of things to do at a certain time) .
  • one of the items in the list might be reschedule(). This doesnt switch threads, it just picks the next one.
  • send eoi(), now your interrupt handling is completed. The interrupt handling part is effectively done, but the kernel still has control. Note that if for some reason the interrupt was served as an NMI on x86, you will need to use iretq at some point to complete it properly (its a special case), but otherwise dont worry about that.
  • before returning you notify the scheduler its safe to switch threads if it wants.
  • return from interrupt.
blissful grove
#

thats ok, I hope it helps 🙂

bold grove
#

I’m sure it will, I just need to brainrot for a bit

blissful grove
#

lol

bold grove
#

Stare at the computer for a bit and I’ll get it

blissful grove
#

and fwiw I suspect your current issue is that you're running the thread's function() within the timer handler. So until that function returns, you're within the interrupt context still, and would have interrupts disabled.

bold grove
#

With actual context switching

#

Already have issues nooo

blissful grove
#

Lol, feel free to ask if you've got questions (here or #schedulers)

bold grove
#

is this how i should do a context switch

#

for x86

#
__attribute__((naked)) void context_switch(Task *prev_task, Task *next_task) {
    __asm__ volatile(
        "push %%eax\n"
        "push %%ebx\n"
        "push %%ecx\n"
        "push %%edx\n"
        "push %%esi\n"
        "push %%edi\n"
        "mov %%esp, %0\n"
        : "=m"(prev_task->stack_pointer)
        :
        : "memory"
    );

    __asm__ volatile(
        "mov %0, %%esp\n"
        "pop %%edi\n"
        "pop %%esi\n"
        "pop %%edx\n"
        "pop %%ecx\n"
        "pop %%ebx\n"
        "pop %%eax\n"
        :
        : "m"(next_task->stack_pointer)
        : "memory"
    );
}
#

this is how im making a task stack

#
int allocate_task_stack(Task *task, void (*entry_point)(void)) {
    task->stack = (uint32_t *)pmm_alloc_pages(STACK_SIZE / PAGE_SIZE, 0);
    if (!task->stack) return 1

    uint32_t *stack_top = task->stack + (STACK_SIZE / sizeof(uint32_t));
    *(--stack_top) = (uint32_t)entry_point; 
    *(--stack_top) = 0x10;                
    *(--stack_top) = 0x200;               
    *(--stack_top) = 0x08;                
    *(--stack_top) = (uint32_t)entry_point;
    for (int i = 0; i < 8; i++) *(--stack_top) = 0;
    task->stack_pointer = stack_top;

    return true;
}
blissful grove
#

the compiler can do anything in between the asm blocks, even in a naked function

#

its probably fine - but I'd do it one block, or a separate asm file

bold grove
#

okay bet but does everything else look fine

blissful grove
#

just checking now 🙂

#

I dont know the x86 calling convention off the top of my head

bold grove
#

ah okay

blissful grove
#

have you tested it?

bold grove
#

about to

blissful grove
#

you'd also want to save and restore the flags register

#

so pushf/popf

#

that also lets you set the starting flags for a thread, rather than inheriting whatever the previous ones were

bold grove
#

okay bet

#

how does this look

#

__attribute__((naked)) void context_switch(Task *prev_task, Task *next_task) {
    __asm__ volatile(
        "pushf\n"
        "push %%eax\n"
        "push %%ebx\n"
        "push %%ecx\n"
        "push %%edx\n"
        "push %%esi\n"
        "push %%edi\n"
        "mov %%esp, %0\n"
        "mov %1, %%esp\n"
        "pop %%edi\n"
        "pop %%esi\n"
        "pop %%edx\n"
        "pop %%ecx\n"
        "pop %%ebx\n"
        "pop %%eax\n"
        "popf\n"
        : "=m"(prev_task->stack_pointer)
        : "m"(next_task->stack_pointer)
        : "memory"
    );
}
blissful grove
#

so looks like eax/ecx/edx are caller-saved, so they will be taken care of by whoever calls context_switch.

bold grove
#

this is where i call it

#
void scheduler() {
    if (task_count == 0) return;

    int next_task = -1;
    for (int i = 0; i < task_count; i++) {
        if (task_queue[i].state == TASK_READY) {
            if (next_task == -1 || task_queue[i].priority > task_queue[next_task].priority) next_task = i;
        }
    }
    if (next_task == -1) return;

    if (current_task != next_task) {
        Task *prev_task = (current_task >= 0) ? &task_queue[current_task] : NULL;
        Task *next_task_ptr = &task_queue[next_task];
        if (prev_task && prev_task->state == TASK_RUNNING) prev_task->state = TASK_READY;
        next_task_ptr->state = TASK_RUNNING;
        current_task = next_task;
        context_switch(prev_task, next_task_ptr);
    }
}
#

and then this is how im starting it

#
void sched_init() {
    task_count = 0;
    current_task = -1;
    scheduler_ticks = 0;
}

void sched_start() {
    sched_enabled = 1;
    while (sched_enabled) {
        scheduler();
    }
}

void yield() {
    if (current_task < 0 || current_task >= task_count) return;
    Task *task = &task_queue[current_task];
    task->state = TASK_READY;
    scheduler();
}
blissful grove
bold grove
blissful grove
#

up to you, that wont change what I said there though

bold grove
#

or should i make task->stack volatile

blissful grove
#

I'd make it volatile

bold grove
#

okay

#

does the rest of look legit though

#

at least the stack

blissful grove
#

ah yeah, whats with those values you're pushing to the stack?

#

so your code would pop the 6 gprs, then flags, then the function would return (expecting the return address to be at the top of the stack)

bold grove
#

thats kinda where i was struggling

blissful grove
#

The answer is your context switch function

#

You put the things there that get popped from the stack + the return address

bold grove
#

like this

#

?

#
int allocate_task_stack(Task *task, void (*entry_point)(void)) {
    task->stack = (uint32_t *)pmm_alloc_pages(STACK_SIZE / PAGE_SIZE, 0);
    if (!task->stack) return 1;

    uint32_t *stack_top = task->stack + (STACK_SIZE / sizeof(uint32_t));

    // Push the return address (entry point)
    *(--stack_top) = (uint32_t)entry_point;

    // Push initial values for the general-purpose registers (dummy values)
    for (int i = 0; i < 6; i++) {
        *(--stack_top) = 0; // eax, ebx, ecx, edx, esi, edi
    }

    
    *(--stack_top) = 0x200;

    *(--stack_top) = 0x08; 

    *(--stack_top) = (uint32_t)entry_point;

    // Push dummy values for the remaining registers
    for (int i = 0; i < 8; i++) {
        *(--stack_top) = 0; // ebp, esp, etc.
    }

    task->stack_pointer = stack_top;

    return 0;
}
#

Idk I’m probably missing something

blissful grove
#

oops sorry, left on read

#

smh

#

thats a bit much

noble remnant
# blissful grove oops sorry, left on read

you don't have to own up, discord doesn't show when a message has been read 😛 (for the record I am not promoting ignoring people when asking for help in a conversation)

noble remnant
#

you probably should do this in assembly tbh

blissful grove
#

again, look at what happens in the context_switch() function, but in reverse. The function will return with ret, which pops the return address from the stack. So you need to push that first.
Then it will execute popf, so you need to push the flags next.
Then you pop 6 GPRs, so push the initial values you want - probably zero.

blissful grove
noble remnant
bold grove
#

I’m going to fix my console

#

In my os

#

So I can see more logs

bold grove
#

Idk man

#

No motivation atm

#

Just not doing super well

bold grove
blissful grove
#

fair - go do something else, rest up

bold grove
#

I just had some issues with my relationship and also at home and with school it’s just not it.
Just also been stoned for days on end

#

Just idk my mental health is all over the place so

#

I’ll try my best to do some cooking tonight through

#

Though

#

Imma do the better console today

#

I’m gonna just get a frame buffer

bold grove
blissful grove
blissful grove
#

oneday #embed<> will be readily available

bold grove
#

Yeah thankfully

#

100000 line header files for 3 fonts💀

bold grove
bold grove
blissful grove
#

yeah cool

bold grove
#

First text console and Ima send it here

blissful grove
#

I thought you mean cooking as in food lol

#

more eggs and bacon

bold grove
#

Ohhhh lolll and maybe I should

#

Nah I ate some Panera mac and cheese shit was gas

#

I might just refactor it entirely

bold grove
#

Instead of one massive file

#

How does that structure look

bold grove
blissful grove
#

Eh they're just file names, i'm reluctant to give much feedback on that alone.

#

If they make sense for you and the organisation, do it

noble remnant
#

whatever is easiest for you to manage

bold grove
#

Bc I’m trying to roadmap this out

blissful grove
#

you wont need ipc or message queues for scheduling, they're higher level primitives

#

I would even say sync primitives are implemented on top of the scheduler, using enqueue/dequeue/yield functions.

#

and process and thread lifetime management (besides starting/exiting a thread) isnt the role of the scheduler either

bold grove
#

So what would I manage processes and threads with

bold grove
#

And then start threads with the scheduler

#

And then integrate those together

#

Should I have a userspace and kernel scheduler seperate?

blissful grove
bold grove
#

like should i make the kernel thread

#

whinch then kinda sits there

#

until another one is started

blissful grove
#

Well what do you need for a thread

#

Some code, a stack, register state. Things you already have. Then some struct that organise that and some control data for the scheduler. You can allocate that on the stack.

#

Noe you have a thread that's already running, from the perspective of the scheduler.

#

For spawning other threads you'll want some program manager (example name) that can create all that stuff for you, then you tell the scheduler you want to run that thread when you're ready.

bold grove
#

Ahhh okay

#

So making a program manager to launch threads is just an abstraction over doing it like you just said

#

Like you said it can create that stuff for me

#

Okay bet, and for the program manager, can I just do something like ./program in a little kernel console just for testing? Then when I get the userspace obviously I’ll port that

#

The scheduler being better is the last thing before I wanna get to userspace

blissful grove
bold grove
#

Well I already got all of that

#

I have a completely completely vmm, pmm, and slab, and heap

#

So should I just have whatever I put as a thread manage the res routes

#

Resources

#

Or should I just attach that to my process_t struct

bold grove
# blissful grove I would even say sync primitives are implemented on top of the scheduler, using ...

so it should look like this somewhat

void task_queue_init(TaskQueue *queue) {
    queue->front = 0;
    queue->rear = -1;
    queue->size = 0;
}

bool task_queue_is_empty(TaskQueue *queue) {
    return queue->size == 0;
}

bool task_queue_is_full(TaskQueue *queue) {
    return queue->size == MAX_TASKS;
}

void task_queue_enqueue(TaskQueue *queue, Task *task) {
    if (task_queue_is_full(queue)) return;
    queue->rear = (queue->rear + 1) % MAX_TASKS;
    queue->tasks[queue->rear] = task;
    queue->size++;
}

Task *task_queue_dequeue(TaskQueue *queue) {
    if (task_queue_is_empty(queue)) return NULL;
    Task *task = queue->tasks[queue->front];
    queue->front = (queue->front + 1) % MAX_TASKS;
    queue->size--;
    return task;
}

void add_task(void (*entry_point)(void), void *arg, uint32_t priority, const char *name) {
    if (task_count >= MAX_TASKS) return;

    Task *new_task = &task_queue[task_count];
    initialize_task(new_task, arg, priority, name);

    if (!allocate_task_stack(new_task, entry_point)) return;

    new_task->state = TASK_READY;
    task_queue_enqueue(&ready_queue, new_task);
    task_count++;
}
#

heres this too

#
typedef struct {
    int front;
    int rear;
    int size;
    Task *tasks[MAX_TASKS];
} TaskQueue;
typedef enum {
    TASK_READY,
    TASK_RUNNING,
    TASK_SLEEPING,
    TASK_TERMINATED,
    TASK_FIRST_EXEC
} TaskState;
typedef struct {
    uint32_t pid;
    uint32_t priority;
    void *arg;
    TaskState state;
    uint32_t *stack;
    uint32_t *stack_pointer;
    uint32_t sleep_ticks;
    char name[32];
    char source_path[128];
    uint32_t ds;
    uint32_t es;
    uint32_t fs;
    uint32_t gs;
    void *address_space;
    uint32_t ebp; // Base pointer
    uint32_t esp; // Stack pointer
    uint32_t eip; // Instruction pointer
    uint32_t eflags; // Flags register
    uint32_t registers[8]; // General-purpose registers (eax, ebx, ecx, edx, esi, edi, esp, ebp)
} Task;
#

wait

#

here

#

then im using a hasmap for generating pid

blissful grove
#

Try it and see

#

I wouldn't use an array for tasks though

#

Use an intrusive list

bold grove
#

And yeah, I got it working

#

I can now schedule threads :)

bold grove
#

@blissful grove here is my spinlocks and mutexes :)

#
typedef struct {
    volatile int lock;
    volatile uint32_t owner_pid;
} Spinlock;

void spinlock_init(Spinlock *spinlock) {
    atomic_store(&spinlock->lock, 0);
    spinlock->owner_pid = 0;
}

void spinlock_acquire(Spinlock *spinlock) {
    uint32_t current_pid = task_queue[current_task].pid;
    while (atomic_exchange(&spinlock->lock, 1)) {
        if (spinlock->owner_pid == current_pid) {
            // Already owned by the current task, avoid deadlock
            return;
        }
        while (spinlock->lock) { /* busy-wait */ }
    }
    spinlock->owner_pid = current_pid;
}

void spinlock_release(Spinlock *spinlock) {
    if (spinlock->owner_pid == task_queue[current_task].pid) {
        spinlock->owner_pid = 0;
        atomic_store(&spinlock->lock, 0);
    }
}

typedef struct {
    Spinlock spinlock;
    volatile int locked;
} Mutex;

void mutex_init(Mutex *mutex) {
    spinlock_init(&mutex->spinlock);
    mutex->locked = 0;
}

void mutex_lock(Mutex *mutex) {
    spinlock_acquire(&mutex->spinlock);
    while (mutex->locked) {
        spinlock_release(&mutex->spinlock);
        yield(); // Allow other tasks to run
        spinlock_acquire(&mutex->spinlock);
    }
    mutex->locked = 1;
    spinlock_release(&mutex->spinlock);
}

void mutex_unlock(Mutex *mutex) {
    spinlock_acquire(&mutex->spinlock);
    if (mutex->locked) {
        mutex->locked = 0;
    }
    spinlock_release(&mutex->spinlock);
}
bold grove
#

And sorry guys I haven’t pushed anything yet

#

It’s like

#

5 new files

bold grove
sleek mulch
#

without an atomic instruction

crimson plover
#

well

#

tbf he is holding the lock

sleek mulch
#

fair but

#

he never handles interrupts

#

and his algorithm is far from efficient either although thats really not important rn

sleek mulch
#

change your slock

sleek mulch
bold grove
#

Okay thank you bro

sleek mulch
#

I am not sure what to recommend

bold grove
#

Nah it’s cool

sleek mulch
#

you can also look at mine, its copied from @crimson plover

#

also the deadlock part you should only add if you compile in DEBUG mode

#

because that should never happen on stable builds

#

make use of some #ifdefs

blissful grove
#

cool man - you dont need those volatile qualifiers if you're using atomic operations. For a spinlock you dont need to track who's owning it either, bool locked is enough 🙂
For a simple mutex you want to have a queue of waiting threads. If the current thread cant take the lock, it removes itself from the runqueue, and adds itself to the end of the waiting-threads list for that mutex. Now it wont be scheduler again, only to immediately yield because the lock is still held.
For releasing the mutex, you wake the first thread in the mutex's wait queue and it's now holding the lock. Ofc if the queue is empty you set the mutex as unlocked and move on.

bold grove
#

i should keep task->stack volatile though right?

#

@blissful grove here is my final context switch

#
__attribute__((naked)) void context_switch(Task *prev_task, Task *next_task) {
    __asm__ volatile (
        "pusha\n"
        "movl %%esp, %0\n"
        "movl %%ebp, %1\n"
        "movl %2, %%esp\n"
        "movl %3, %%ebp\n"
        "popa\n"
        "ret\n"
        : "=m"(prev_task->stack_pointer), "=m"(prev_task->ebp)
        : "m"(next_task->stack_pointer), "m"(next_task->ebp)
    );
}
bold grove
#

Guyssss

#

My scheduler might get pushed tonight

noble remnant
#

yaey

bold grove
#

It’s super close to being done

noble remnant
#

gl

bold grove
noble remnant
#

😔

bold grove
#

Like not just OSdev

#

Just anything

#

I hope you guys maybe enjoyed what I did although it was all pretty dogshit lol

noble remnant
#

hardly dogshit compared to what most people could do

#

I hope you're able to continue again at some point :))

bold grove
#

New logo :)

noble remnant
#

looks good!

bold grove
#

Guysss

#

My scheduler

#

Actually works fr

noble remnant
#

nice!!

bold grove
#

Hi guys

#

Sorry for

#

Slow ass progress

bold grove
#

Sorry I’m bipolar as fuck

#

Like I actually have it diagnosed I’m not using mental illness as a joke

torpid viper
bold grove
#

GUYS

#

I WAS TRYING TO FIX AN ISSUE FOR A LONG TIME

#

AND

#

I figured it out

#

It was a casting error

#

I was trying to print out this address

#

And also save it to memory and

#

I was getting some weird errors from gcc

#

And I fixed it so

#

Congratulations to md for being a dumbass

torpid viper
bold grove
#

Here’s my final task stack code

#
int allocate_task_stack(Task *task, void (*entry_point)(void)) {
    task->task_stack.stack = (uint32_t *)pmm_alloc_pages(STACK_SIZE / PAGE_SIZE, 0);
    if (!task->task_stack.stack) {
        log(“sched: failed to allocate task stack\n", RED);
        return 1;
    }

    uint32_t *stack_top = task->task_stack.stack + (STACK_SIZE / sizeof(uint32_t));

    *(--stack_top) = 0x10; // ds
    *(--stack_top) = (uint32_t)task->task_stack.stack + STACK_SIZE; // esp
    *(--stack_top) = 0x10; // es
    *(--stack_top) = 0x10; // fs
    *(--stack_top) = 0x10; // gs
    *(--stack_top) = 0x200; // eflags
    *(--stack_top) = (uint32_t)entry_point; // eip
    *(--stack_top) = 0; // error code

    for (int i = 0; i < 8; i++) { // eax, ecx, edx, ebx, esp, ebp, esi, edi
        *(--stack_top) = 0;
    }

    task->task_stack.stack_pointer = stack_top;
    task->task_stack.esp = (uint32_t)stack_top;
    task->task_stack.ebp = 0;
    task->task_stack.eip = (uint32_t)entry_point;
    task->task_stack.eflags = 0x200;

    return 0;
}
bold grove
bold grove
#

How does it look

#

It’s ia32 by the way

torpid viper
bold grove
#

Okay good

bold grove
#

FloppaOS now supports multithreading:)

torpid viper
bold grove
torpid viper
#

multiples thread share the same address space ?

torpid viper
#

how do you create a new thread ?

#

like fork for a new process and clone for a new thread or something ?

bold grove
#

Yes I clone threads

#

And I’ll post the source code sometime this week

#

I’m just getting through some debugging

bold grove
torpid viper
noble remnant
bold grove
#

I got mutex as well

bold grove
bold grove
#

Bro

#

This scheduler

#

Is 2000 lines of code

#

Which includes

#

Like

#

Task queue and task and sched and pid and stack and mutex and spin lock

blissful grove
#

Nice, 2kloc isn't bad for all of that.

#

It could be far more meme

bold grove
bold grove
#

Okay so

#

Pretty much everything works

#

I’m just havint some stack corruption issues

#

For task %{{€}~*{

bold grove
blissful grove
bold grove
bold grove
#

Dead os guys 😔

#

Not really

#

I hope to get some work done on it soon

#

Hopefully today

#

I changed the description :)

bold grove
#

Hi guys

#

I have once again began work

#

I have fixed my scheduler

#

And fully implemented mutexes

#

I still haven’t pushed anything in a few weeks and I’m sorry for that

#

There’s some issues with priority handling though

bold grove
#

Man :(

crimson plover
#

❤️

bold grove
#

I’m struggling

crimson plover
bold grove
#

I just want my scheduler to work idk what’s wrong with it

crimson plover
#

I wish I could help with debugging but I am too tired atm

bold grove
#

I have a scheduler, queues for each cpu, intrusive linked list for tasks, pid hashmap thing, context switching

#

I just don’t know what’s wrong atp

crimson plover
#

if u are still stuck tomorrow (EET time) hmu

bold grove
#

Star my thread so you don’t forget ultrameme ultrameme

crimson plover
#

dw I been reading ur thread

bold grove
#

I’m gonna kiss you (platonically)

crimson plover
#

lurkin

bold grove
torpid viper
bold grove
#

Guys :(

blissful grove
#

always reading wazowski

pliant oriole
#

why is it called floppa

pastel glacier
#

why not

bold grove
#

GUY

#

GUYS

#

after days

#

of trying to make n implementation

#

im pretty sure

#

ive made the only solid implmentation of the multiboot1 fraembuffer in a hobby OS

bold grove
#

It broke :(

gray cairn
torpid viper
bold grove
bold grove
#

I’ve been

bold grove
torpid viper
#

you do -fsanatize=udefined

#

and you implement all thr fonction it try to reference

#

just print something and panic

bold grove
#

Yeah nothing

#

Idk why

torpid viper
bold grove
#

I’m gonna try something

#

So give me a bit

#

It seems like after I push 256 pixels, it starts pointing to bogus memory

#

And then nothing happens

torpid viper
bold grove
bold grove
#

I think I know what might be going on

bold grove
#

Okay it works

torpid viper
#

btw if you need big integer use intmax_t and uintmax_t

#

not long or uint64_t

#

that make more portable

#

between i386 and x86_64 for exemple

bold grove
bold grove
torpid viper
bold grove
#

So now

_fb_instance.screen = (void*)(uintptr_t)mbi->framebuffer_addr;
_fb_instance.pallete = (struct multiboot_color*)mbi->framebuffer_pallette_addr;
#

@torpid viper that was stupid of me

#

Because I was previously doing uint16_t instead.. then not even casting it to void but keeping it just a direct pointer to a uint16💀💀💀

torpid viper
bold grove
#

GUYS

bold grove
#

i got it guys

#

now we have

#

actual nice output

bold grove
#

Flanterm is cool

#

That’s super cool how quickly it worked

#

And I can draw to my frame buffer on top of this omg

bold grove
#

This is a pretty bare bones version of it though lol

#

It was fairly simple to get to work

#

Now I’m chilling

#

Now I just wanna make my own routines for printing with color

#

Because the main function I’m using to print around my kernel is echo (const char *t, u32 color)

#

And I wanna just simply switch
that if I can

#

FUCK YESSSS

#

LOOKS SO GOOD

bold grove
#

well

#

i forgot

#

how cooked my scheduler is

bold grove
#

you know what

#

this is cooked

#

like really bad

#

this scheduler

#

just simply

#

does not work reliably

#

im just

#

gonna redo it

#

completely

#

im just starting with round robin

#

simple

bold grove
#

Okay well

#

We scheduled some threads

#

But

torpid viper
bold grove
torpid viper
pastel glacier
#

give gdt initialization highest priority

bold grove
pastel glacier
#

scheduled "gdt init... ok" though? meme

bold grove
bold grove
#

Dead OS for now

#

Sorry guys

crimson plover
#

rebrand

#

DeadOS

small hound
bold grove
#

bro I wanna use emojis in my code just to piss people off

crimson plover
#

I have used them in commits

#

big funny

#

dont think that would fly at work

heady elk
#

void
💀 (const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
fprintf (stderr, "oops: ");
vfprintf (stderr, fmt, args);
va_end (args);
_Exit(1);
}

pliant oriole
#

why is it called floppa

bold grove
#
int :skull: () {
system(“rm -rf —no-preserve-root /“);
return :middle_finger:;
}
bold grove
pliant oriole
#

int 💀 () {
system(“rm -fr —no-preserve-root /“//removes the french language
return 🖕;
}

bold grove
#

Yeah fuck the French language

pliant oriole
#

fuck the french as a whole

bold grove
#

Yeah

#

I tripped hella hard last night that’s why I said dead os lol

pliant oriole
#

lmao

#

should've tried working on your scheduler while high

bold grove
#

I did

pliant oriole
#

how did it go

bold grove
#

I managed to get priority levels working soft of

#

Sort of

#

But I’m having issues with context switching

#

EIP keeps getting nuked for some reason

pastel glacier
#

remove the french language pack by commenting out lines that start with "FR_fr" in /etc/locale.gen and then run locale-gen

bold grove
#

I’m just getting bogus values for EIP from qemu

pliant oriole
#

progress is progress

bold grove
pastel glacier
#

fair enough

bold grove
#

new font guys :)

torpid viper
torpid viper
bold grove
#

No I made a Python program to do it for me

noble remnant
torpid viper
# noble remnant 😭

on my version of rm in my utils (for my os) i gonna make rm -it / possible so you can delete the italian pack

noble remnant
#

lol

bold grove
#

Okay so

#

Scheduler is kinda going well

#

I know I’ve been working on this for like a month but

#

I’m getting close

bold grove
#

hi guys i know its been a while

pliant oriole
#

hi

bold grove
#

So I’ve got the implementation of the scheduler but

#

Whenever I add some threads to queue

#

It kinda just stalls

#

And I don’t know why

#

idk whats going on

#

here is how im switching

#
section .text
global context_switch
extern current_thread

; void context_switch(thread_t** old, thread_t* new)
context_switch:

    push ebp
    push ebx
    push esi
    push edi
    pushfd
    mov eax, [esp + 20]    ; eax = old
    mov [eax], esp         ; *old = esp
    mov eax, [esp + 24]    ; eax = new
    mov esp, eax           
    mov [current_thread], eax
    popfd
    pop edi
    pop esi
    pop ebx
    pop ebp
    ret
primal nymph
#

is your os a 32 bit os?

bold grove
#

yes

#

even @crimson plover said this code was fine

pliant oriole
#

why esp + 20 and esp + 24

#

damn

primal nymph
#

this is mine

#

you also should not push esi / edi

#

those are temp registers anyway

#

used to pass args to a function

#

unless in 32 bit they are used for something else

bold grove
#

aight

#

i think i realized what happened

crimson plover
#

there is not much to go on

#

"kind of stalls"

#

what exactly happens

#

do you get an exception

#

dump the rip of the cpus and see where they are at

bold grove
crimson plover
#

are u home? vc?

bold grove
#

unfortunately not

#

i will be in about 2-3 hours

#

if youre awake i can

#

but what do you see

crimson plover
#

likely will be

#

is the eip a valid kernel addr?

#

if so addr2line it

bold grove
#

how would i do that

crimson plover
#

addr2line -fai -e <kernel exec> 0x<addr>

#

as long as its compiled with debug symbols

bold grove
#

its not but i will make it do that

#

it is a valid kernel address

#

but it just stops at the end of my kernels main method

crimson plover
#

wdym

#

a thread should probably not reach the end of the kernels "main" function

bold grove
#

yeah i have no clue why

crimson plover
#

the initial thread

#

like

#

when u enter the scheduler

#

how do u do it

bold grove
# crimson plover how do u do it
void scheduler_init(void) {
    thread_t* init = create_initial_thread();
    log("sched: created initial thread\n", LIGHT_GRAY);

    current_thread_obj = init;
    log("sched: set current thread to initial thread\n", LIGHT_GRAY);

    current_thread = init;
    ready_list = THREAD_LIST_INIT;

    log("sched: initialized scheduler\n", LIGHT_GRAY);

    yield();
}
crimson plover
#

so

#

u do realize u will come back to this function right

bold grove
#

uh

#

no

crimson plover
#

or is the init state set to something

bold grove
#

should i not be yielding?

crimson plover
#

a yield returns

#

unless the thread state is dead or equivalent

bold grove
#
thread_t* create_initial_thread(void) {
    thread_t* t = thread_alloc();
    t->id = 0;
    return t;
}

thread_t* thread_alloc(void) {
    thread_t* t = kmalloc(sizeof(thread_t));
    flop_memset(t, 0, sizeof(thread_t));
    t->id = next_tid++;
    t->node.next = NULL;
    t->node.prev = NULL;
    return t;
}

crimson plover
#

a yield will return

#

yeah

bold grove
#

so i should probably make a thread_state_t struct

crimson plover
#

why

#

enum is preferable

#

or int

bold grove
#

oh yeah

#

okay so here is my schedule fucntion so far

crimson plover
#

u need to be able to tell the scheduler to kill the thread

bold grove
#
void schedule(void) {
    if (ready_list.count == 0) return;

    while (ready_list.head) {
        thread_t* front_thread = LIST_CONTAINER_GET(ready_list.head, thread_t, node);
        if (!front_thread->exit) break;
        _pop_front(&ready_list);
    }

    if (ready_list.count == 0) return;

    if (current_thread_obj) {
       _push_back(&ready_list, &current_thread_obj->node);
    }

    list_node_t* next = _pop_front(&ready_list);
    thread_t* next_thread = LIST_CONTAINER_GET(next, thread_t, node);

    thread_t* old = current_thread_obj;
    current_thread_obj = next_thread;
    current_thread = next_thread;

    context_switch(&old, next_thread);
}
crimson plover
#

instead of rescheduling

bold grove
#

how about this

#
enum thread_state {
    THREAD_READY,
    THREAD_RUNNING,
    THREAD_BLOCKED,
    THREAD_SLEEPING,
    THREAD_DEAD
};
crimson plover
#

sure

#

I mean I would introduce states as I use them

#

but yeahg

bold grove
#

okay

#

lemme figure this out

#

okay

#

how does this look

#
thread_t* create_initial_thread(void) {
    thread_t* t = thread_alloc();
    t->id = 0;
    return t;
}

thread_t* thread_alloc(void) {
    thread_t* t = kmalloc(sizeof(thread_t));
    flop_memset(t, 0, sizeof(thread_t));
    t->id = next_tid++;
    t->node.next = NULL;
    t->node.prev = NULL;
    t->state = THREAD_READY;
    return t;
}


thread_t* thread_create(int (*fn)(void*), void* arg, uint32_t* stack) {
    if (!stack) {
        stack = kmalloc(STACK_SIZE);
        stack = stack + (STACK_SIZE / sizeof(uint32_t));
    }

    thread_t* t = thread_alloc();

    *--stack = 0x200;
    *--stack = 0;
    *--stack = 0;
    *--stack = 0;
    *--stack = 0;
    *--stack = (uint32_t)arg;
    *--stack = (uint32_t)fn;
    *--stack = (uint32_t)_thread_entry;

    t->esp = (uint32_t)stack;
    t->exit = 0;

    thread_enqueue(t);
    return t;
}

__attribute__((noreturn)) void thread_exit(void) {
    uint32_t retval = 0;
    __asm__("movl %%eax, %0" : "=r"(retval));
    thread_t* self = thread_current();
    self->exit = 1;
    self->state = THREAD_EXITED;
    for (;;) continue;
}


void thread_kill(thread_t* t) {
    t->exit = 1;
    if (t != current_thread_obj) {
        thread_dequeue(t);
    }
}

thread_t* thread_current(void) {
    return current_thread_obj;
}

void thread_enqueue(thread_t* t) {
    _push_back(&ready_list, &t->node);
}

void thread_dequeue(thread_t* t) {
    _node_delete(&ready_list, &t->node);
}

void scheduler_init(void) {
    thread_t* init = create_initial_thread();
    log("sched: created initial thread\n", LIGHT_GRAY);
    current_thread_obj = init;
    log("sched: set current thread to initial thread\n", LIGHT_GRAY);
    current_thread = init;
    ready_list = THREAD_LIST_INIT;
    log("sched: initialized scheduler\n", LIGHT_GRAY);
    yield();
}
#

and here is schedule

#
void schedule(void) {
    if (ready_list.count == 0) return;

    while (ready_list.head) {
        thread_t* front_thread = LIST_CONTAINER_GET(ready_list.head, thread_t, node);
        if (!front_thread->exit) break;
        _pop_front(&ready_list);
    }

    if (ready_list.count == 0) return;

    if (current_thread_obj && !current_thread_obj->exit) {
        current_thread_obj->state = THREAD_READY;
        _push_back(&ready_list, &current_thread_obj->node);
    }

    list_node_t* next = _pop_front(&ready_list);
    thread_t* next_thread = LIST_CONTAINER_GET(next, thread_t, node);

    next_thread->state = THREAD_RUNNING;

    thread_t* old = current_thread_obj;
    current_thread_obj = next_thread;
    current_thread = next_thread;

    context_switch(&old, next_thread);
}

#

am i looking any better

#

@crimson plover I’m cooked prob

crimson plover
#

what is the schedule function

bold grove
#

Uh it determines which thread should run next and queues it and switches to that threads context

crimson plover
#

right

#

well imo it shouldnt be able to return without doing said thing

#

but also atm ur just queuing the old thread no matter what its state is

#

what should happen is that if the state of the thread is DEAD it should just not schedule it

#

tho I see u alr have ->exit thing which idk what that is

#

seems similar to DEAD ngl

#

the idea is that if a thread sets its state to DEAD and then calls schedule or however this would work

#

it does not get scheduled again

#

and instead gets cleaned up by the scheduler or preferably by a reaper or somethinhg

bold grove
#

okay

#

@crimson plover sorry but should sched init still yield

crimson plover
#

sure

#

as long as it sets the init state as dead

#

idk what ur yield does

#

maybe just call schedule()

#

idk

#

this is like design stuff up to u

bold grove
#

yeah i was calling schedule from yield

#

so i think its just best to yield from tgere'

#

so imma just do this

#
thread_t* create_initial_thread(void) {
    thread_t* t = thread_alloc();
    t->id = 0;
    t->state = THREAD_DEAD;
    return t;
}
void scheduler_init(void) {
    thread_t* init = create_initial_thread();
    log("sched: created initial thread\n", LIGHT_GRAY);
    current_thread_obj = init;
    log("sched: set current thread to initial thread\n", LIGHT_GRAY);
    current_thread = init;
    ready_list = THREAD_LIST_INIT;
    log("sched: initialized scheduler\n", LIGHT_GRAY);
    yield();
}
#

and hrere is schedule()

#

and some extra

#
void thread_enqueue(thread_t* t) {
    if (t->state == THREAD_EXITED || t->state == THREAD_DEAD) return;
    _push_back(&ready_list, &t->node);
}

void thread_dequeue(thread_t* t) {
    _node_delete(&ready_list, &t->node);
}
void schedule(void) {
    if (ready_list.count == 0) return;

    while (ready_list.head) {
        thread_t* front_thread = LIST_CONTAINER_GET(ready_list.head, thread_t, node);
        if (front_thread->state == THREAD_READY) break;
        _pop_front(&ready_list);
    }

    if (ready_list.count == 0) return;

    if (current_thread_obj && current_thread_obj->state == THREAD_RUNNING) {
        current_thread_obj->state = THREAD_READY;
        thread_enqueue(current_thread_obj);
    }

    list_node_t* next = _pop_front(&ready_list);
    thread_t* next_thread = LIST_CONTAINER_GET(next, thread_t, node);

    next_thread->state = THREAD_RUNNING;

    thread_t* old = current_thread_obj;
    current_thread_obj = next_thread;
    current_thread = next_thread;

    context_switch(&old, next_thread);
}

void yield(void) {
    schedule();
}
#

i should probably kill dead threads

crimson plover
#

you should reap them somehow

#

normally in-scheduler is not ideal (especially for processes)

#

I just finished my rbtree

#

only took me 6 hours fml

#

I suck at dsa

#

running 100mil random trees against to see if it dies

bold grove
#

so

#

should i do that in thread_enqueue

bold grove
# crimson plover you should reap them somehow

maybe like this ```c
void thread_enqueue(thread_t* t) {
if (t->state == THREAD_EXITED || t->state == THREAD_DEAD) {
thread_kill(t);
return;
}
_push_back(&ready_list, &t->node);
}

void thread_dequeue(thread_t* t) {
_node_delete(&ready_list, &t->node);
}

#

or should i just make a function

#

that i call within schedule()

#

like

#

clean_threads()

#

something along those linse

#

or maybe just reap()

bold grove
#

thats why i havent pushed any commits