Skip to content

Instantly share code, notes, and snippets.

@dato
Last active May 27, 2017 18:17
Show Gist options
  • Select an option

  • Save dato/e3a5a8fc5ef68b080ace77043c4449f2 to your computer and use it in GitHub Desktop.

Select an option

Save dato/e3a5a8fc5ef68b080ace77043c4449f2 to your computer and use it in GitHub Desktop.

Revisions

  1. dato revised this gist May 27, 2017. 1 changed file with 6 additions and 5 deletions.
    11 changes: 6 additions & 5 deletions task.c
    Original file line number Diff line number Diff line change
    @@ -32,16 +32,17 @@ void task_spawn(void (*entry)(void)) {
    }

    void sched() {
    int i;
    struct Task *new = 0;
    struct Task *old = current;

    for (i = 0; i < MAX_TASK; i++)
    for (int i = 0; i < MAX_TASK; i++) {
    if (Tasks[i].status == READY)
    break;
    new = &Tasks[i];
    }

    if (i < MAX_TASK) {
    if (new) {
    old->status = READY; // XXX 🤔?
    current = &Tasks[i];
    current = new;
    current->status = RUNNING;
    swtch(&old->stack, &current->stack);
    }
  2. dato revised this gist May 27, 2017. 4 changed files with 6 additions and 3 deletions.
    3 changes: 3 additions & 0 deletions funcs.h
    Original file line number Diff line number Diff line change
    @@ -9,6 +9,9 @@ void task_init(void);
    // llame a sched().
    void task_spawn(void (*entry)(void));

    // Finaliza la tarea actual; no volverá a ejecutarse.
    void task_exit(void);

    // Planificador round-robin.
    void sched(void);

    2 changes: 1 addition & 1 deletion kernel.c
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@

    static void contador1(void) { contador(0, 0x2f, 1); } // Verde, rápido.
    static void contador2(void) { contador(3, 0x6f, 5); } // Naranja, lento.
    static void contador3(void) { contador(7, 0x4f, 8); } // Rojo, muy lento.
    static void contador3(void) { contador(7, 0x4f, 7); } // Rojo, muy lento.

    int main(void) {
    task_init();
    2 changes: 1 addition & 1 deletion task.c
    Original file line number Diff line number Diff line change
    @@ -28,7 +28,7 @@ void task_spawn(void (*entry)(void)) {
    // Preparar el stack conforme a lo que espera swtch().
    struct TaskData *d = stack;
    *d = (const struct TaskData){}; // Inicializa a 0.
    d->reg_eip = (unsigned) entry;
    d->entry_fn = (unsigned) entry;
    }

    void sched() {
    2 changes: 1 addition & 1 deletion task.h
    Original file line number Diff line number Diff line change
    @@ -30,7 +30,7 @@ struct TaskData {
    unsigned reg_ebp;

    // Return address used in swtch’s "ret".
    unsigned reg_eip;
    unsigned entry_fn;
    } __attribute__((packed));

    #endif
  3. dato created this gist May 26, 2017.
    28 changes: 28 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    QEMU := qemu-system-i386 -serial mon:stdio -d guest_errors

    CFLAGS := -std=c99 -m32 -O1 -ggdb3 -gdwarf-4 -Wall -fasm -nostdinc
    CFLAGS += -fno-pic -fno-inline -fno-omit-frame-pointer -ffreestanding
    ASFLAGS := $(CFLAGS)

    SOURCES := $(wildcard *.c)
    OBJECTS := $(SOURCES:%.c=%.o)

    kernel: entry.o swtch.o $(OBJECTS)
    ld -m elf_i386 -Ttext 0x100000 -o $@ $^
    objdump -S $@ >$@.asm
    # Verificar que realmente hemos producido una imagen Multiboot v1.
    grub-file --is-x86-multiboot $@

    qemu: kernel
    $(QEMU) -kernel $<

    qemu-gdb: kernel
    $(QEMU) -kernel $< -nographic -S -gdb tcp:127.0.0.1:7508

    gdb:
    gdb -q -s kernel -ex 'target remote 127.0.0.1:7508' -n -x .gdbinit

    clean:
    rm -f kernel kernel.asm *.o core

    .PHONY: clean qemu qemu-gdb gdb
    35 changes: 35 additions & 0 deletions contador.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    #include "funcs.h"

    #define COUNTLEN 40
    #define TICKS (1ULL << 15)
    #define DELAY(x) (TICKS << (x))

    void contador(unsigned char linea, char color, unsigned char delay) {
    char counter[COUNTLEN] = {'0'}; // Our ASCII digit counter (RTL).

    while (1) {
    char *buf = (void *) 0xb8000 + linea * 160;
    char *c = &counter[COUNTLEN];

    unsigned p = 0;
    unsigned long long i = 0;

    while (i++ < DELAY(delay))
    ;

    while (counter[p] == '9') {
    counter[p++] = '0';
    }

    if (!counter[p]++) {
    counter[p] = '1';
    }

    while (c-- > counter) {
    *buf++ = *c;
    *buf++ = color;
    }

    sched();
    }
    }
    29 changes: 29 additions & 0 deletions entry.S
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    #define MAGIC 0x1BADB002
    #define FLAGS 0
    #define CRC ( -(MAGIC + FLAGS) )

    .text
    .globl _start

    .align 4
    constantes_multiboot:
    .long MAGIC
    .long FLAGS
    .long CRC

    _start:
    movl $0, %ebp
    movl $(bootstacktop), %esp
    jmp main
    l:
    hlt
    jmp l

    .data
    .globl bootstack
    .globl bootstacktop

    .align 4096
    bootstack:
    .space 4096
    bootstacktop:
    23 changes: 23 additions & 0 deletions funcs.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    #ifndef FUNCS_H
    #define FUNCS_H

    // Asigna a main() el índice 0, y lo marca como RUNNING. main()
    // se convierte después en la “idle task” del sistema.
    void task_init(void);

    // Crea una nueva tarea, marcándola como READY. No se ejecutará hasta que se
    // llame a sched().
    void task_spawn(void (*entry)(void));

    // Planificador round-robin.
    void sched(void);

    // Imprime en una línea VGA un contador que se auto-incrementa. Para marcar su
    // velocidad, hace (TICKS << delay) iteraciones por incremento. En cada TICKS,
    // llama al planificador.
    void contador(unsigned char linea, char color, unsigned char delay);

    // Utilidades.
    void *stack_alloc(int size);

    #endif
    28 changes: 28 additions & 0 deletions kernel.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    #include "funcs.h"

    static void contador1(void) { contador(0, 0x2f, 1); } // Verde, rápido.
    static void contador2(void) { contador(3, 0x6f, 5); } // Naranja, lento.
    static void contador3(void) { contador(7, 0x4f, 8); } // Rojo, muy lento.

    int main(void) {
    task_init();

    // Con esta línea, no habría concurrencia. Solo se ejecutaría
    // el primer contador.
    // contador1();

    task_spawn(contador1);
    task_spawn(contador2);
    task_spawn(contador3);

    while (1) {
    sched(); // Become the idle task.
    }
    }

    void *stack_alloc(int size) {
    static void *next = (void *) (8 << 20); // 8 MiB, arbitrariamente.
    void *ret = next;
    next -= size;
    return ret;
    }
    7 changes: 7 additions & 0 deletions swtch.S
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    // Context switch
    //
    // void swtch(unsigned **oldsp, unsigned **newsp);

    .globl swtch
    swtch:
    ret
    48 changes: 48 additions & 0 deletions task.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    #include "task.h"
    #include "funcs.h"

    #define MAX_TASK 128
    #define STACK_SIZE 4096

    static struct Task *current;
    static struct Task Tasks[MAX_TASK];

    extern void swtch(unsigned **oldsp, unsigned **newsp);

    void task_init() {
    current = &Tasks[0];
    current->status = RUNNING;
    }

    void task_spawn(void (*entry)(void)) {
    unsigned i = 0;

    // Encontrar la siguiente posición libre.
    while (i < MAX_TASK && Tasks[i].status != FREE)
    i++;

    void *stack = stack_alloc(STACK_SIZE) - sizeof(struct TaskData);
    Tasks[i].stack = stack;
    Tasks[i].status = READY;

    // Preparar el stack conforme a lo que espera swtch().
    struct TaskData *d = stack;
    *d = (const struct TaskData){}; // Inicializa a 0.
    d->reg_eip = (unsigned) entry;
    }

    void sched() {
    int i;
    struct Task *old = current;

    for (i = 0; i < MAX_TASK; i++)
    if (Tasks[i].status == READY)
    break;

    if (i < MAX_TASK) {
    old->status = READY; // XXX 🤔?
    current = &Tasks[i];
    current->status = RUNNING;
    swtch(&old->stack, &current->stack);
    }
    }
    36 changes: 36 additions & 0 deletions task.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    #ifndef TASK_H
    #define TASK_H

    enum TaskStatus {
    FREE = 0,
    READY,
    RUNNING,
    };

    struct Task {
    unsigned *stack;
    enum TaskStatus status;
    };

    struct TaskData {
    // Registers as pushed by pusha.
    unsigned reg_edi;
    unsigned reg_esi;
    unsigned __unused_ebp;
    unsigned __unused_esp;
    unsigned reg_ebx;
    unsigned reg_edx;
    unsigned reg_ecx;
    unsigned reg_eax;

    // Saved eflags.
    unsigned reg_eflags;

    // Saved %ebp; makes swtch’s code simpler.
    unsigned reg_ebp;

    // Return address used in swtch’s "ret".
    unsigned reg_eip;
    } __attribute__((packed));

    #endif