LF OS
Hobby operating system for amd64 with high ambitions
Loading...
Searching...
No Matches
scheduler.c
Go to the documentation of this file.
1#include <scheduler.h>
2#include <string.h>
3#include <errno.h>
4#include <mm.h>
5#include <sc.h>
6#include <bitmap.h>
7#include <panic.h>
8#include <log.h>
9#include <mutex.h>
10#include <mq.h>
11#include <signal.h>
12#include <sd.h>
13#include <unused_param.h>
14
15extern void sc_handle_clock_read(uint64_t* nanoseconds);
16
25
49
51
52// TODO: better data structure here. A binary tree with prev/next pointers should be great
53#define MAX_PROCS 4096
55
57 if(!alloc ||
61 ) {
62 logw("scheduler", "process_alloc called for invalid process %u\n", alloc->tag);
63 return 0;
64 }
65
67
68 return vm_alloc(size);
69}
70
73 logw("scheduler", "process_dealloc called for invalid process %u\n", alloc->tag);
74 return;
75 }
76
77 size_t size = *((size_t*)ptr - 1);
78 vm_free(ptr);
79
81}
82
83__attribute__((naked)) static void idle_task(void) {
84 asm("jmp .");
85}
86
87void init_scheduler(void) {
89}
90
92 pid_t i;
93 for(i = 0; i < MAX_PROCS; ++i) {
95 return i;
96 }
97 }
98
99 return -1;
100}
101
103 pid_t pid = free_pid();
104 if(pid == -1) {
105 panic_message("Out of PIDs!");
106 }
107
109 memset((void*)&process->cpu, 0, sizeof(cpu_state));
110
112 process->cpu.cs = 0x2B;
113 process->cpu.ss = 0x23;
114 process->cpu.rflags = 0x200;
115
116 process->heap.start = 0;
117 process->heap.end = 0;
118
119 process->stack.start = ALLOCATOR_REGION_USER_STACK.end;
120 process->stack.end = ALLOCATOR_REGION_USER_STACK.end;
121
122 process->hw.start = ALLOCATOR_REGION_USER_HARDWARE.start;
124
125 process->allocator = (allocator_t){
126 .alloc = process_alloc,
127 .dealloc = process_dealloc,
128 .tag = pid,
129 };
130
131 process->mq = mq_create(&process->allocator);
132
133 return pid;
134}
135
136void start_task(struct vm_table* context, ptr_t entry, ptr_t data_start, ptr_t data_end, const char* name) {
137 if(!entry) {
138 panic_message("Tried to start process without entry");
139 }
140
143
144 process->parent = -1;
145 process->context = context;
146 process->cpu.rip = entry;
148
149 process->heap.start = data_start;
150 process->heap.end = data_end;
151
152 strncpy(process->name, name, 1023);
153}
154
163
164bool scheduler_idle_if_needed(cpu_state** cpu, struct vm_table** context) {
167 *context = VM_KERNEL_CONTEXT;
168 (*cpu)->rip = (uint64_t)idle_task;
169 (*cpu)->cs = 0x2B;
170 (*cpu)->ss = 0x23;
171 (*cpu)->rflags = 0x200;
172
174
175 return true;
176 }
177
178 return false;
179}
180
181void schedule_next(cpu_state** cpu, struct vm_table** context) {
184 }
185
186 uint64_t timestamp_ns_since_boot = 0;
187
188 static pid_t last_scheduled = -1;
189 for(int i = 1; i <= MAX_PROCS; ++i) {
190 pid_t pid = (last_scheduled + i) % MAX_PROCS;
191
193
194 if(process->state == process_state_waiting && process->waiting_for == wait_reason_time) {
195 if(process->waiting_data.timestamp_ns_since_boot && !timestamp_ns_since_boot) {
196 sc_handle_clock_read(&timestamp_ns_since_boot); // the kernel calling a syscall handler ... oh deer, but why not? :>
197 }
198
199 if(process->waiting_data.timestamp_ns_since_boot <= timestamp_ns_since_boot) {
201 }
202 }
203
206 break;
207 }
208 }
209
210 if(scheduler_idle_if_needed(cpu, context)) {
211 return;
212 }
213
214 last_scheduled = scheduler_current_process;
215
219}
220
221bool schedule_next_if_needed(cpu_state** cpu, struct vm_table** context) {
224 schedule_next(cpu, context);
225 return true;
226 }
227
228 return false;
229}
230
231
234
235 if(processes[pid].parent >= 0) {
237
238 if(parent->state != process_state_empty &&
239 parent->state != process_state_exited &&
240 parent->state != process_state_killed
241 ) {
242 size_t user_size = sizeof(struct SignalUserData);
243 size_t size = sizeof(struct Message) + user_size;
244
245 struct Message* signal = vm_alloc(size);
246 signal->size = size;
247 signal->user_size = user_size,
248 signal->sender = -1,
249 signal->type = MT_Signal,
250 signal->user_data.Signal.signal = SIGCHLD,
251
252 mq_push(parent->mq, signal);
253
254 vm_free(signal);
255 }
256 }
257
259}
260
268
276
277void sc_handle_scheduler_clone(bool share_memory, ptr_t entry, pid_t* newPid) {
279
280 // make new process
282
283 if(pid < 0) {
284 *newPid = -ENOMEM;
285 return;
286 }
287
288 process_t* new = &processes[pid];
289 strncpy(new->name, old->name, 1023);
290
291 // new memory context ...
292 new->context = vm_context_new();
293
294 new->heap.start = old->heap.start;
295 new->heap.end = old->heap.end;
296 new->stack.start = old->stack.start;
297 new->stack.end = old->stack.end;
298 new->hw.start = old->hw.start;
299 new->hw.end = old->hw.end;
300
301 // .. copy heap ..
302 if(!share_memory) {
303 vm_copy_range(new->context, old->context, old->heap.start, old->heap.end - old->heap.start);
304 }
305 else {
306 // make heap shared
307 }
308
309 // .. and stack ..
310 vm_copy_range(new->context, old->context, old->stack.start, old->stack.end - old->stack.start);
311
312 // .. and remap hardware resources
313 for(ptr_t i = old->hw.start; i < old->hw.end; i += 4096) {
315
316 if(hw) {
317 vm_context_map(new->context, i, hw, 0x07);
318 }
319 }
320
321 if(share_memory && entry) {
322 new->cpu.rip = entry;
323 }
324
325 *newPid = pid;
326
327 // copy cpu state
328 memcpy(&new->cpu, &old->cpu, sizeof(cpu_state));
329 new->cpu.rax = 0;
330
331 new->parent = scheduler_current_process;
332}
333
334bool scheduler_handle_pf(ptr_t fault_address, uint64_t error_code) {
335 if(fault_address >= ALLOCATOR_REGION_USER_STACK.start && fault_address < ALLOCATOR_REGION_USER_STACK.end) {
336 ptr_t page_v = fault_address & ~0xFFF;
337 ptr_t page_p = (ptr_t)mm_alloc_pages(1);
338 memset((void*)(page_p + ALLOCATOR_REGION_DIRECT_MAPPING.start), 0, 0x1000);
339 vm_context_map(processes[scheduler_current_process].context, page_v, page_p, 0);
340
341 if(page_v < processes[scheduler_current_process].stack.start) {
343 }
344
345 return true;
346 }
347
348 logw("scheduler", "Not handling page fault for %s (PID %d) at 0x%x (RIP: 0x%x, error 0x%x)", processes[scheduler_current_process].name, scheduler_current_process, fault_address, processes[scheduler_current_process].cpu.rip, error_code);
349
350 return false;
351}
352
353void scheduler_wait_for(pid_t pid, enum wait_reason reason, union wait_data data) {
354 if(pid == -1) {
356 }
357
359 processes[pid].waiting_for = reason;
360 processes[pid].waiting_data = data;
361}
362
363void scheduler_waitable_done(enum wait_reason reason, union wait_data data, size_t max_amount) {
364 for(int i = 0; i < MAX_PROCS; ++i) {
366
367 process_t* p = &processes[pid];
368
369 if(p->state == process_state_waiting && p->waiting_for == reason) {
370 switch(reason) {
372 if(p->waiting_data.mutex == data.mutex) {
373 if(mutex_lock(data.mutex, pid)) {
375 }
376 }
377 break;
379 if(p->waiting_data.condvar == data.condvar && max_amount--) {
381 }
382 break;
386 }
387 break;
388 case wait_reason_time:
389 // handled in schedule_next instead
390 break;
391 }
392 }
393 }
394}
395
398 ptr_t new_end = old_end + inc;
399
400 if(inc > 0) {
401 for(ptr_t i = old_end & ~0xFFF; i < new_end; i += 0x1000) {
403 ptr_t phys = (ptr_t)mm_alloc_pages(1);
404 memset((void*)(phys + ALLOCATOR_REGION_DIRECT_MAPPING.start), 0, 0x1000);
406 }
407 }
408 }
409 if(inc < 0) {
410 for(ptr_t i = old_end; i > new_end; i -= 0x1000) {
411 if(!(i & ~0xFFF)) {
414 }
415 }
416 }
417
419 *data_end = old_end;
420}
421
423 union wait_data wait_data;
425
426 if(nanoseconds) {
427 uint64_t current_timestamp = 0;
428 sc_handle_clock_read(&current_timestamp); // the kernel calling a syscall handler ... oh deer, but why not? :>
429
430 wait_data.timestamp_ns_since_boot = current_timestamp + nanoseconds;
431 }
432
434}
435
437 *error = 0;
439
440 if(!process->iopb) {
441 if(!turn_on) {
442 return;
443 }
444
446 set_iopb(process->context, process->iopb);
447 memset((void*)(ALLOCATOR_REGION_DIRECT_MAPPING.start + process->iopb), 0xFF, 8*KiB);
448 }
449
451 for(size_t i = 0; i < num; ++i) {
452 if(turn_on) {
453 bitmap_clear(bitmap, from + i);
454 }
455 else {
456 bitmap_set(bitmap, from + i);
457 }
458 }
459
460 if(!turn_on) {
461 bool some_enabled = false;
462
463 for(size_t i = 0; i < 8*KiB; ++i) {
464 if(((uint8_t*)bitmap)[i] != 0xFF) {
465 some_enabled = true;
466 break;
467 }
468 }
469
470 if(!some_enabled) {
472 process->iopb = 0;
473 set_iopb(process->context, process->iopb);
474 }
475 }
476}
477
479 if(interrupt > 16) {
480 *error = ENOENT;
481 return;
482 }
483
484 if(!mq) {
486 }
487
488 if(enable) {
489 interrupt_add_queue(interrupt, mq);
490 }
491 else {
492 interrupt_del_queue(interrupt, mq);
493 }
494
495 *error = 0;
496}
497
498void sc_handle_ipc_mq_create(bool global_read, bool global_write, size_t msg_limit, size_t data_limit, uint64_t* mq, uint64_t* error) {
499 UNUSED_PARAM(global_read);
501 UNUSED_PARAM(msg_limit);
504
505 *error = -ENOSYS;
506}
507
513
515 if(!mq) {
517 }
518
519 struct Message peeked = { .size = sizeof(struct Message) };
520 *error = mq_peek(mq, &peeked);
521
522 if(*error == ENOMSG && wait) {
523 union wait_data data;
524 data.message_queue = mq;
526 // this makes the syscall return EAGAIN as soon as a message is available,
527 // making the process poll again and then receive the message.
528 // TODO: implement a way to deliver syscall results when a process is
529 // scheduled the next time, so we don't have to poll twice
530 *error = EAGAIN;
531 }
532 else if(*error == EMSGSIZE) {
533 if(peeked.size > msg->size) {
534 msg->size = peeked.size;
535 msg->type = MT_Invalid;
536 }
537 else {
538 *error = 0;
539 }
540 }
541
542 if(*error) {
543 return;
544 }
545
546 *error = mq_pop(mq, msg);
547}
548
551
552 if(!mq) {
553 if(pid != -1) {
555 mq = processes[pid].mq;
556 }
557 }
558 else {
560 }
561 }
562
563 if(mq) {
564 *error = mq_push(mq, msg);
565 }
566}
567
575
577 if(!mq) {
579 }
580
581 if(!msg || msg->type != MT_ServiceDiscovery) {
582 *error = EINVAL;
583 return;
584 }
585
589
590 int64_t delivered = sd_send(uuid, msg);
591
592 if(delivered < 0) {
593 *error = -delivered;
594 return;
595 }
596
597 *error = 0;
598}
599
604
606 ptr_t res = vm_map_hardware(hw, len);
607
608 if(res + len > processes[scheduler_current_process].hw.end) {
610 }
611
612 return res;
613}
struct allocator allocator_t
unsigned short uint16_t
Definition arch.h:8
uint64_t ptr_t
Definition arch.h:17
unsigned long uint64_t
Definition arch.h:14
int64_t pid_t
Definition arch.h:21
unsigned char uint8_t
Definition arch.h:5
signed long int64_t
Definition arch.h:13
uint8_t * bitmap_t
Type for a single entry in the array.
Definition bitmap.h:8
static void bitmap_clear(bitmap_t bitmap, uint64_t entry)
Unset given entry in bitmap.
Definition bitmap.h:36
static void bitmap_set(bitmap_t bitmap, uint64_t entry)
Set given entry in bitmap.
Definition bitmap.h:31
Definition cpu.h:7
#define ENOENT
Definition errno-defs.h:5
#define EINVAL
Definition errno-defs.h:17
#define ENOSYS
Definition errno-defs.h:28
#define ENOMEM
Definition errno-defs.h:11
#define EMSGSIZE
Definition errno-defs.h:33
#define ENOMSG
Definition errno-defs.h:29
#define EAGAIN
Definition errno-defs.h:10
static VTermState * state
Definition harness.c:105
#define ALLOCATOR_REGION_DIRECT_MAPPING
Definition vm.h:29
ptr_t start
Definition vm.h:9
#define ALLOCATOR_REGION_USER_STACK
Definition vm.h:18
#define ALLOCATOR_REGION_USER_HARDWARE
Definition vm.h:19
ptr_t end
Definition vm.h:10
Definition vm.h:7
void * memcpy(void *dest, void const *source, size_t size)
Definition string.c:80
void * memset(void *dest, int c, size_t size)
Definition string.c:72
char * strncpy(char *s1, const char *s2, size_t n)
Definition string.c:42
allocator_t * alloc
char name[256]
Zero terminated string with the name of the file.
Definition loader.h:1
uint16_t size
Size of the loaded file.
Definition loader.h:5
#define logd(component, fmt,...)
Definition log.h:28
#define logw(component, fmt,...)
Definition log.h:44
struct Message::UserData::SignalUserData Signal
size_t user_size
Size of the user data.
size_t size
Size of the message, including metadata.
struct Message::UserData::ServiceDiscoveryData ServiceDiscovery
pid_t sender
Sender of the message.
union Message::UserData user_data
@ MT_Signal
@ MT_ServiceDiscovery
@ MT_Invalid
Invalid message, only size is valid.
size_t user_size
Size of the user data.
uint16_t signal
Signal identifier.
enum MessageType type
Type of the message.
void * mm_alloc_pages(uint64_t count)
Definition mm.c:24
void mm_mark_physical_pages(ptr_t start, uint64_t count, mm_page_status_t status)
Definition mm.c:93
#define KiB
Definition mm.h:6
@ MM_FREE
Definition mm.h:13
uint64_t mq_push(uint64_t mq, struct Message *message)
Definition mq.c:144
uint64_t mq_pop(uint64_t mq, struct Message *msg)
Definition mq.c:190
uint64_t mq_peek(uint64_t mq, struct Message *msg)
Definition mq.c:218
uint64_t mq_create(allocator_t *alloc)
Definition mq.c:69
void mq_destroy(uint64_t mq)
Definition mq.c:93
uint64_t mq_id_t
Definition mq.h:9
void mutex_unlock_holder(pid_t pid)
Definition mutex.c:95
bool mutex_lock(mutex_t mutex, pid_t holder)
Definition mutex.c:52
void panic_message(const char *message)
Definition panic.c:64
void set_iopb(struct vm_table *context, ptr_t new_iopb)
Definition sc.c:134
void interrupt_del_queue(uint8_t interrupt, uint64_t mq)
Definition sc.c:211
void interrupt_add_queue(uint8_t interrupt, uint64_t mq)
Definition sc.c:226
void sc_handle_ipc_mq_poll(uint64_t mq, bool wait, struct Message *msg, uint64_t *error)
Definition scheduler.c:514
void scheduler_process_save(cpu_state *cpu)
Definition scheduler.c:155
bool scheduler_idle_if_needed(cpu_state **cpu, struct vm_table **context)
Definition scheduler.c:164
void * process_alloc(allocator_t *alloc, size_t size)
Definition scheduler.c:56
#define MAX_PROCS
Definition scheduler.c:53
void scheduler_process_cleanup(pid_t pid)
Definition scheduler.c:232
void scheduler_wait_for(pid_t pid, enum wait_reason reason, union wait_data data)
Definition scheduler.c:353
void sc_handle_ipc_service_register(uuid_t *uuid, uint64_t mq, uint64_t *error)
Definition scheduler.c:568
void sc_handle_scheduler_exit(uint64_t exit_code)
Definition scheduler.c:269
void start_task(struct vm_table *context, ptr_t entry, ptr_t data_start, ptr_t data_end, const char *name)
Definition scheduler.c:136
void sc_handle_scheduler_sleep(uint64_t nanoseconds)
Definition scheduler.c:422
cpu_state cpu
Definition scheduler.c:41
void sc_handle_ipc_mq_destroy(uint64_t mq, uint64_t *error)
Definition scheduler.c:508
static process_t processes[MAX_PROCS]
Definition scheduler.c:54
void sc_handle_ipc_mq_create(bool global_read, bool global_write, size_t msg_limit, size_t data_limit, uint64_t *mq, uint64_t *error)
Definition scheduler.c:498
process_state
Definition scheduler.c:17
@ process_state_killed
Definition scheduler.c:23
@ process_state_running
Definition scheduler.c:21
@ process_state_exited
Definition scheduler.c:22
@ process_state_empty
Definition scheduler.c:18
@ process_state_runnable
Definition scheduler.c:20
@ process_state_waiting
Definition scheduler.c:19
void scheduler_waitable_done(enum wait_reason reason, union wait_data data, size_t max_amount)
Definition scheduler.c:363
ptr_t iopb
Physical address of the first of two IOPB pages, 0 if no IO privilege granted.
Definition scheduler.c:44
mq_id_t mq
Definition scheduler.c:39
pid_t setup_process(void)
Definition scheduler.c:102
process_state state
Definition scheduler.c:31
uint8_t exit_code
Definition scheduler.c:30
void sc_handle_ipc_mq_send(uint64_t mq, pid_t pid, struct Message *msg, uint64_t *error)
Definition scheduler.c:549
char name[1024]
Definition scheduler.c:27
region_t stack
Definition scheduler.c:37
void sc_handle_scheduler_clone(bool share_memory, ptr_t entry, pid_t *newPid)
Definition scheduler.c:277
region_t hw
Definition scheduler.c:38
void sc_handle_clock_read(uint64_t *nanoseconds)
Definition hpet.c:141
region_t heap
Definition scheduler.c:36
void sc_handle_ipc_service_discover(uuid_t *uuid, uint64_t mq, struct Message *msg, uint64_t *error)
Definition scheduler.c:576
void init_scheduler(void)
Definition scheduler.c:87
enum wait_reason waiting_for
Definition scheduler.c:33
void sc_handle_memory_sbrk(int64_t inc, ptr_t *data_end)
Definition scheduler.c:396
void sc_handle_hardware_ioperm(uint16_t from, uint16_t num, bool turn_on, uint16_t *error)
Definition scheduler.c:436
pid_t free_pid(void)
Definition scheduler.c:91
pid_t parent
Definition scheduler.c:29
void schedule_next(cpu_state **cpu, struct vm_table **context)
Definition scheduler.c:181
bool schedule_next_if_needed(cpu_state **cpu, struct vm_table **context)
Definition scheduler.c:221
union wait_data waiting_data
Definition scheduler.c:34
bool scheduler_handle_pf(ptr_t fault_address, uint64_t error_code)
Definition scheduler.c:334
void sc_handle_hardware_interrupt_notify(uint8_t interrupt, bool enable, uint64_t mq, uint64_t *error)
Definition scheduler.c:478
void process_dealloc(allocator_t *alloc, void *ptr)
Definition scheduler.c:71
ptr_t scheduler_map_hardware(ptr_t hw, size_t len)
Map a given memory area in the currently running userspace process at a random location.
Definition scheduler.c:605
struct vm_table * context
Definition scheduler.c:40
void sc_handle_scheduler_get_pid(bool parent, pid_t *pid)
Definition scheduler.c:600
allocator_t allocator
Definition scheduler.c:46
size_t allocatedMemory
Definition scheduler.c:47
volatile pid_t scheduler_current_process
Definition scheduler.c:50
void scheduler_kill_current(enum kill_reason reason)
Definition scheduler.c:261
condvar_t condvar
Definition scheduler.h:27
mutex_t mutex
Definition scheduler.h:26
uint64_t timestamp_ns_since_boot
Definition scheduler.h:29
uint64_t message_queue
Definition scheduler.h:28
kill_reason
Definition scheduler.h:8
wait_reason
Definition scheduler.h:13
@ wait_reason_message
Definition scheduler.h:16
@ wait_reason_time
Definition scheduler.h:17
@ wait_reason_condvar
Definition scheduler.h:15
@ wait_reason_mutex
Definition scheduler.h:14
int64_t sd_send(uuid_t *uuid, struct Message *msg)
Definition sd.c:109
uint64_t sd_register(uuid_t *uuid, mq_id_t svc_queue)
Definition sd.c:45
#define SIGCHLD
Definition signal.h:4
uint64_t tag
Definition allocator.h:12
static void pid_t * pid
Definition syscalls.h:34
static void * entry
Definition syscalls.h:34
static bool size_t size_t uint64_t * mq
Definition syscalls.h:303
static uint64_t process
Definition syscalls.h:357
static uint16_t num
Definition syscalls.h:126
static bool wait
Definition syscalls.h:338
static bool size_t size_t data_limit
Definition syscalls.h:303
static bool global_write
Definition syscalls.h:303
static void ** data_end
Definition syscalls.h:82
static uint16_t bool turn_on
Definition syscalls.h:126
static uint16_t bool uint64_t * error
Definition syscalls.h:126
#define UNUSED_PARAM(v)
Definition unused_param.h:5
uint8_t data[16]
Definition uuid.h:15
Definition uuid.h:6
void vm_context_unmap(struct vm_table *context, ptr_t virtual)
Definition vm.c:427
void vm_context_map(struct vm_table *pml4, ptr_t virtual, ptr_t physical, uint8_t pat)
Definition vm.c:406
void vm_copy_range(struct vm_table *dst, struct vm_table *src, ptr_t addr, size_t size)
Definition vm.c:597
ptr_t vm_map_hardware(ptr_t hw, size_t len)
Map a given memory area in the currently running userspace process at a random location.
Definition vm.c:755
ptr_t vm_context_get_physical_for_virtual(struct vm_table *context, ptr_t virtual)
Definition vm.c:475
struct vm_table * vm_context_new(void)
Definition vm.c:381
void * vm_alloc(size_t size)
Like malloc but allocates full pages only. 16 byte data overhead.
Definition vm.c:697
struct vm_table * VM_KERNEL_CONTEXT
Definition vm.c:16
void vm_free(void *ptr)
the matching free() like function for vm_alloc
Definition vm.c:707
A paging table, when this is a PML4 it may also be called context.
Definition vm.c:42