Skip to content

Instantly share code, notes, and snippets.

@dato
Last active May 27, 2017 18:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dato/e3a5a8fc5ef68b080ace77043c4449f2 to your computer and use it in GitHub Desktop.
Save dato/e3a5a8fc5ef68b080ace77043c4449f2 to your computer and use it in GitHub Desktop.
lab-sched
#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();
}
}
#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:
#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));
// Finaliza la tarea actual; no volverá a ejecutarse.
void task_exit(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
#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, 7); } // 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;
}
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
// Context switch
//
// void swtch(unsigned **oldsp, unsigned **newsp);
.globl swtch
swtch:
ret
#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->entry_fn = (unsigned) entry;
}
void sched() {
struct Task *new = 0;
struct Task *old = current;
for (int i = 0; i < MAX_TASK; i++) {
if (Tasks[i].status == READY)
new = &Tasks[i];
}
if (new) {
old->status = READY; // XXX 🤔?
current = new;
current->status = RUNNING;
swtch(&old->stack, &current->stack);
}
}
#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 entry_fn;
} __attribute__((packed));
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment